mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
Compare commits
8 Commits
rework-get
...
update-edi
Author | SHA1 | Date | |
---|---|---|---|
![]() |
4b01779b2d | ||
![]() |
297b88ed60 | ||
![]() |
06cef654b0 | ||
![]() |
c5dd26c93b | ||
![]() |
e0f3b221bd | ||
![]() |
896f56e850 | ||
![]() |
cee43ce9df | ||
![]() |
5e61712e55 |
@@ -43,7 +43,9 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
|
||||
.AddCondition("CompareTimer",
|
||||
_("Value of a scene timer"),
|
||||
_("Compare the elapsed time of a scene timer. This "
|
||||
"condition doesn't start the timer."),
|
||||
"condition doesn't start the timer and will always be "
|
||||
"false if the timer was not started previously (whatever "
|
||||
"the comparison being made)."),
|
||||
_("The timer _PARAM1_ _PARAM2_ _PARAM3_ seconds"),
|
||||
|
||||
"",
|
||||
|
157
Core/GDCore/IDE/EventsBasedObjectVariantHelper.cpp
Normal file
157
Core/GDCore/IDE/EventsBasedObjectVariantHelper.cpp
Normal file
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#include "EventsBasedObjectVariantHelper.h"
|
||||
|
||||
#include "GDCore/Project/EventsBasedObject.h"
|
||||
#include "GDCore/Project/InitialInstancesContainer.h"
|
||||
#include "GDCore/Project/Object.h"
|
||||
#include "GDCore/Project/ObjectGroup.h"
|
||||
#include "GDCore/Project/ObjectsContainer.h"
|
||||
#include "GDCore/Project/ObjectsContainersList.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Project/Variable.h"
|
||||
#include "GDCore/Project/VariablesContainer.h"
|
||||
#include "GDCore/String.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
void EventsBasedObjectVariantHelper::ComplyVariantsToEventsBasedObject(
|
||||
const gd::Project &project, gd::EventsBasedObject &eventsBasedObject) {
|
||||
auto &defaultObjects = eventsBasedObject.GetDefaultVariant().GetObjects();
|
||||
|
||||
for (const auto &variant :
|
||||
eventsBasedObject.GetVariants().GetInternalVector()) {
|
||||
auto &objects = variant->GetObjects();
|
||||
|
||||
// Delete extra objects
|
||||
for (auto it = objects.GetObjects().begin();
|
||||
it != objects.GetObjects().end(); ++it) {
|
||||
const auto &objectName = it->get()->GetName();
|
||||
if (!defaultObjects.HasObjectNamed(objectName)) {
|
||||
variant->GetInitialInstances().RemoveInitialInstancesOfObject(
|
||||
objectName);
|
||||
// Do it in last because it unalloc objectName.
|
||||
objects.RemoveObject(objectName);
|
||||
--it;
|
||||
}
|
||||
}
|
||||
for (const auto &defaultObject : defaultObjects.GetObjects()) {
|
||||
const auto &objectName = defaultObject->GetName();
|
||||
const auto &defaultVariables = defaultObject->GetVariables();
|
||||
const auto &defaultBehaviors = defaultObject->GetAllBehaviorContents();
|
||||
|
||||
// Copy missing objects
|
||||
if (!objects.HasObjectNamed(objectName)) {
|
||||
objects.InsertObject(*defaultObject,
|
||||
defaultObjects.GetObjectPosition(objectName));
|
||||
objects.AddMissingObjectsInRootFolder();
|
||||
continue;
|
||||
}
|
||||
// Change object types
|
||||
auto &object = objects.GetObject(objectName);
|
||||
if (object.GetType() != defaultObject->GetType()) {
|
||||
// Keep a copy of the old object.
|
||||
auto oldObject = objects.GetObject(objectName);
|
||||
objects.RemoveObject(objectName);
|
||||
objects.InsertObject(*defaultObject,
|
||||
defaultObjects.GetObjectPosition(objectName));
|
||||
object.CopyWithoutConfiguration(oldObject);
|
||||
objects.AddMissingObjectsInRootFolder();
|
||||
}
|
||||
|
||||
// Copy missing behaviors
|
||||
auto &behaviors = object.GetAllBehaviorContents();
|
||||
for (const auto &pair : defaultBehaviors) {
|
||||
const auto &behaviorName = pair.first;
|
||||
const auto &defaultBehavior = pair.second;
|
||||
|
||||
if (object.HasBehaviorNamed(behaviorName) &&
|
||||
object.GetBehavior(behaviorName).GetTypeName() !=
|
||||
defaultBehavior->GetTypeName()) {
|
||||
object.RemoveBehavior(behaviorName);
|
||||
}
|
||||
if (!object.HasBehaviorNamed(behaviorName)) {
|
||||
auto *behavior = object.AddNewBehavior(
|
||||
project, defaultBehavior->GetTypeName(), behaviorName);
|
||||
gd::SerializerElement element;
|
||||
defaultBehavior->SerializeTo(element);
|
||||
behavior->UnserializeFrom(element);
|
||||
}
|
||||
}
|
||||
// Delete extra behaviors
|
||||
for (auto it = behaviors.begin(); it != behaviors.end(); ++it) {
|
||||
const auto &behaviorName = it->first;
|
||||
if (!defaultObject->HasBehaviorNamed(behaviorName)) {
|
||||
object.RemoveBehavior(behaviorName);
|
||||
--it;
|
||||
}
|
||||
}
|
||||
|
||||
// Sort and copy missing variables
|
||||
auto &variables = object.GetVariables();
|
||||
for (size_t defaultVariableIndex = 0;
|
||||
defaultVariableIndex < defaultVariables.Count();
|
||||
defaultVariableIndex++) {
|
||||
const auto &variableName =
|
||||
defaultVariables.GetNameAt(defaultVariableIndex);
|
||||
const auto &defaultVariable =
|
||||
defaultVariables.Get(defaultVariableIndex);
|
||||
|
||||
auto variableIndex = variables.GetPosition(variableName);
|
||||
if (variableIndex == gd::String::npos) {
|
||||
variables.Insert(variableName, defaultVariable, defaultVariableIndex);
|
||||
} else {
|
||||
variables.Move(variableIndex, defaultVariableIndex);
|
||||
}
|
||||
if (variables.Get(variableName).GetType() != defaultVariable.GetType()) {
|
||||
variables.Remove(variableName);
|
||||
variables.Insert(variableName, defaultVariable, defaultVariableIndex);
|
||||
}
|
||||
}
|
||||
// Remove extra variables
|
||||
auto variableToRemoveCount = variables.Count() - defaultVariables.Count();
|
||||
for (size_t iteration = 0; iteration < variableToRemoveCount;
|
||||
iteration++) {
|
||||
variables.Remove(variables.GetNameAt(variables.Count() - 1));
|
||||
}
|
||||
|
||||
// Remove extra instance variables
|
||||
variant->GetInitialInstances().IterateOverInstances(
|
||||
[&objectName,
|
||||
&defaultVariables](gd::InitialInstance &initialInstance) {
|
||||
if (initialInstance.GetObjectName() != objectName) {
|
||||
return false;
|
||||
}
|
||||
auto &instanceVariables = initialInstance.GetVariables();
|
||||
for (size_t instanceVariableIndex = 0;
|
||||
instanceVariableIndex < instanceVariables.Count();
|
||||
instanceVariableIndex++) {
|
||||
const auto &variableName =
|
||||
defaultVariables.GetNameAt(instanceVariableIndex);
|
||||
|
||||
if (!defaultVariables.Has(variableName)) {
|
||||
instanceVariables.Remove(variableName);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
auto &defaultObjectGroups =
|
||||
eventsBasedObject.GetDefaultVariant().GetObjects().GetObjectGroups();
|
||||
auto &objectGroups = variant->GetObjects().GetObjectGroups();
|
||||
auto objectGroupsCount = objectGroups.Count();
|
||||
// Clear groups
|
||||
for (size_t index = 0; index < objectGroupsCount; index++) {
|
||||
objectGroups.Remove(objectGroups.Get(0).GetName());
|
||||
}
|
||||
// Copy groups
|
||||
for (size_t index = 0; index < defaultObjectGroups.Count(); index++) {
|
||||
objectGroups.Insert(defaultObjectGroups.Get(index), index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gd
|
26
Core/GDCore/IDE/EventsBasedObjectVariantHelper.h
Normal file
26
Core/GDCore/IDE/EventsBasedObjectVariantHelper.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace gd {
|
||||
class EventsBasedObject;
|
||||
class Project;
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
|
||||
class GD_CORE_API EventsBasedObjectVariantHelper {
|
||||
public:
|
||||
/**
|
||||
* @brief Apply the changes done on events-based object children to all its
|
||||
* variants.
|
||||
*/
|
||||
static void
|
||||
ComplyVariantsToEventsBasedObject(const gd::Project &project,
|
||||
gd::EventsBasedObject &eventsBasedObject);
|
||||
};
|
||||
|
||||
} // namespace gd
|
@@ -17,6 +17,7 @@
|
||||
#include "GDCore/IDE/Project/ResourcesRenamer.h"
|
||||
#include "GDCore/Project/Behavior.h"
|
||||
#include "GDCore/Project/CustomBehavior.h"
|
||||
#include "GDCore/Project/CustomObjectConfiguration.h"
|
||||
#include "GDCore/Project/EventsFunctionsExtension.h"
|
||||
#include "GDCore/Project/Layout.h"
|
||||
#include "GDCore/Project/Object.h"
|
||||
@@ -75,6 +76,16 @@ void ObjectAssetSerializer::SerializeTo(
|
||||
|
||||
cleanObject->SerializeTo(objectAssetElement.AddChild("object"));
|
||||
|
||||
if (project.HasEventsBasedObject(object.GetType())) {
|
||||
SerializerElement &variantsElement =
|
||||
objectAssetElement.AddChild("variants");
|
||||
variantsElement.ConsiderAsArrayOf("variant");
|
||||
|
||||
std::unordered_set<gd::String> alreadyUsedVariantIdentifiers;
|
||||
gd::ObjectAssetSerializer::SerializeUsedVariantsTo(
|
||||
project, object, variantsElement, alreadyUsedVariantIdentifiers);
|
||||
}
|
||||
|
||||
SerializerElement &resourcesElement =
|
||||
objectAssetElement.AddChild("resources");
|
||||
resourcesElement.ConsiderAsArrayOf("resource");
|
||||
@@ -108,4 +119,46 @@ void ObjectAssetSerializer::SerializeTo(
|
||||
objectAssetElement.AddChild("customization");
|
||||
customizationElement.ConsiderAsArrayOf("empty");
|
||||
}
|
||||
|
||||
void ObjectAssetSerializer::SerializeUsedVariantsTo(
|
||||
gd::Project &project, const gd::Object &object,
|
||||
SerializerElement &variantsElement,
|
||||
std::unordered_set<gd::String> &alreadyUsedVariantIdentifiers) {
|
||||
|
||||
if (!project.HasEventsBasedObject(object.GetType())) {
|
||||
return;
|
||||
}
|
||||
const auto *customObjectConfiguration =
|
||||
dynamic_cast<const gd::CustomObjectConfiguration *>(
|
||||
&object.GetConfiguration());
|
||||
if (customObjectConfiguration
|
||||
->IsMarkedAsOverridingEventsBasedObjectChildrenConfiguration() ||
|
||||
customObjectConfiguration
|
||||
->IsForcedToOverrideEventsBasedObjectChildrenConfiguration()) {
|
||||
return;
|
||||
}
|
||||
const auto &variantName = customObjectConfiguration->GetVariantName();
|
||||
const auto &variantIdentifier =
|
||||
object.GetType() + gd::PlatformExtension::GetNamespaceSeparator() +
|
||||
variantName;
|
||||
auto insertResult = alreadyUsedVariantIdentifiers.insert(variantIdentifier);
|
||||
if (insertResult.second) {
|
||||
const auto &eventsBasedObject =
|
||||
project.GetEventsBasedObject(object.GetType());
|
||||
const auto &variants = eventsBasedObject.GetVariants();
|
||||
const auto &variant = variants.HasVariantNamed(variantName)
|
||||
? variants.GetVariant(variantName)
|
||||
: eventsBasedObject.GetDefaultVariant();
|
||||
|
||||
SerializerElement &pairElement = variantsElement.AddChild("variant");
|
||||
pairElement.SetAttribute("objectType", object.GetType());
|
||||
SerializerElement &variantElement = pairElement.AddChild("variant");
|
||||
variant.SerializeTo(variantElement);
|
||||
// TODO Recursivity
|
||||
for (auto &object : variant.GetObjects().GetObjects()) {
|
||||
gd::ObjectAssetSerializer::SerializeUsedVariantsTo(
|
||||
project, *object, variantsElement, alreadyUsedVariantIdentifiers);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace gd
|
||||
|
@@ -6,6 +6,7 @@
|
||||
#pragma once
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "GDCore/String.h"
|
||||
|
||||
@@ -52,6 +53,11 @@ private:
|
||||
ObjectAssetSerializer(){};
|
||||
|
||||
static gd::String GetObjectExtensionName(const gd::Object &object);
|
||||
|
||||
static void SerializeUsedVariantsTo(
|
||||
gd::Project &project, const gd::Object &object,
|
||||
SerializerElement &variantsElement,
|
||||
std::unordered_set<gd::String> &alreadyUsedVariantIdentifiers);
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -6,6 +6,7 @@
|
||||
#include "ObjectVariableHelper.h"
|
||||
|
||||
#include "GDCore/IDE/WholeProjectRefactorer.h"
|
||||
#include "GDCore/Project/EventsBasedObject.h"
|
||||
#include "GDCore/Project/InitialInstancesContainer.h"
|
||||
#include "GDCore/Project/Object.h"
|
||||
#include "GDCore/Project/ObjectGroup.h"
|
||||
@@ -173,6 +174,7 @@ void ObjectVariableHelper::ApplyChangesToObjects(
|
||||
groupVariablesContainer.Get(variableName),
|
||||
variablesContainer.Count());
|
||||
}
|
||||
// TODO Check what happens if 2 variables exchange their names.
|
||||
for (const auto &pair : changeset.oldToNewVariableNames) {
|
||||
const gd::String &oldVariableName = pair.first;
|
||||
const gd::String &newVariableName = pair.second;
|
||||
@@ -215,6 +217,7 @@ void ObjectVariableHelper::ApplyChangesToObjectInstances(
|
||||
destinationVariablesContainer.Remove(variableName);
|
||||
}
|
||||
}
|
||||
// TODO Check what happens if 2 variables exchange their names.
|
||||
for (const auto &pair : changeset.oldToNewVariableNames) {
|
||||
const gd::String &oldVariableName = pair.first;
|
||||
const gd::String &newVariableName = pair.second;
|
||||
@@ -236,6 +239,66 @@ void ObjectVariableHelper::ApplyChangesToObjectInstances(
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
void ObjectVariableHelper::ApplyChangesToVariants(
|
||||
gd::EventsBasedObject &eventsBasedObject, const gd::String &objectName,
|
||||
const gd::VariablesChangeset &changeset) {
|
||||
auto &defaultVariablesContainer = eventsBasedObject.GetDefaultVariant()
|
||||
.GetObjects()
|
||||
.GetObject(objectName)
|
||||
.GetVariables();
|
||||
for (auto &variant : eventsBasedObject.GetVariants().GetInternalVector()) {
|
||||
if (!variant->GetObjects().HasObjectNamed(objectName)) {
|
||||
continue;
|
||||
}
|
||||
auto &object = variant->GetObjects().GetObject(objectName);
|
||||
auto &variablesContainer = object.GetVariables();
|
||||
|
||||
for (const gd::String &variableName : changeset.removedVariableNames) {
|
||||
variablesContainer.Remove(variableName);
|
||||
}
|
||||
for (const gd::String &variableName : changeset.addedVariableNames) {
|
||||
if (variablesContainer.Has(variableName)) {
|
||||
// It can happens if a child-object already had the variable but it was
|
||||
// missing in other variant child-object.
|
||||
continue;
|
||||
}
|
||||
variablesContainer.Insert(variableName,
|
||||
defaultVariablesContainer.Get(variableName),
|
||||
variablesContainer.Count());
|
||||
}
|
||||
// TODO Check what happens if 2 variables exchange their names.
|
||||
for (const auto &pair : changeset.oldToNewVariableNames) {
|
||||
const gd::String &oldVariableName = pair.first;
|
||||
const gd::String &newVariableName = pair.second;
|
||||
if (variablesContainer.Has(newVariableName)) {
|
||||
// It can happens if a child-object already had the variable but it was
|
||||
// missing in other variant child-object.
|
||||
variablesContainer.Remove(oldVariableName);
|
||||
} else {
|
||||
variablesContainer.Rename(oldVariableName, newVariableName);
|
||||
}
|
||||
}
|
||||
// Apply type changes
|
||||
for (const gd::String &variableName : changeset.valueChangedVariableNames) {
|
||||
size_t index = variablesContainer.GetPosition(variableName);
|
||||
|
||||
if (variablesContainer.Has(variableName) &&
|
||||
variablesContainer.Get(variableName).GetType() !=
|
||||
defaultVariablesContainer.Get(variableName).GetType()) {
|
||||
variablesContainer.Remove(variableName);
|
||||
variablesContainer.Insert(
|
||||
variableName, defaultVariablesContainer.Get(variableName), index);
|
||||
}
|
||||
}
|
||||
|
||||
gd::ObjectVariableHelper::ApplyChangesToObjectInstances(
|
||||
variablesContainer, variant->GetInitialInstances(), objectName,
|
||||
changeset);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -8,6 +8,7 @@
|
||||
#include "GDCore/Project/VariablesContainer.h"
|
||||
|
||||
namespace gd {
|
||||
class EventsBasedObject;
|
||||
class InitialInstancesContainer;
|
||||
class ObjectsContainersList;
|
||||
class ObjectsContainer;
|
||||
@@ -53,7 +54,7 @@ public:
|
||||
* Objects can be added during the group edition and may not necessarily have
|
||||
* all the variables initially shared by the group.
|
||||
*
|
||||
* \see gd::GroupVariableHelper::MergeVariableContainers
|
||||
* \see gd::ObjectVariableHelper::MergeVariableContainers
|
||||
*/
|
||||
static void FillMissingGroupVariablesToObjects(
|
||||
gd::ObjectsContainer &globalObjectsContainer,
|
||||
@@ -72,16 +73,21 @@ public:
|
||||
const gd::ObjectGroup &objectGroup,
|
||||
const gd::VariablesChangeset &changeset);
|
||||
|
||||
/**
|
||||
* @brief Apply the changes done on an object to all its instances.
|
||||
*/
|
||||
static void ApplyChangesToObjectInstances(
|
||||
gd::VariablesContainer &objectVariablesContainer,
|
||||
gd::InitialInstancesContainer &initialInstancesContainer,
|
||||
const gd::String &objectName, const gd::VariablesChangeset &changeset);
|
||||
|
||||
private:
|
||||
static void ApplyChangesToVariableContainer(
|
||||
const gd::VariablesContainer &originalVariablesContainer,
|
||||
gd::VariablesContainer &destinationVariablesContainer,
|
||||
const gd::VariablesChangeset &changeset, bool shouldApplyValueChanges);
|
||||
/**
|
||||
* @brief Apply the changes done on events-based object child to all its
|
||||
* variants.
|
||||
*/
|
||||
static void ApplyChangesToVariants(gd::EventsBasedObject &eventsBasedObject,
|
||||
const gd::String &objectName,
|
||||
const gd::VariablesChangeset &changeset);
|
||||
};
|
||||
|
||||
} // namespace gd
|
@@ -314,6 +314,12 @@ void ProjectBrowserHelper::ExposeProjectObjects(
|
||||
eventsFunctionsExtension.GetEventsBasedObjects().GetInternalVector()) {
|
||||
auto eventsBasedObject = eventsBasedObjectUniquePtr.get();
|
||||
worker.Launch(eventsBasedObject->GetObjects());
|
||||
|
||||
for (auto &&variantUniquePtr :
|
||||
eventsBasedObject->GetVariants().GetInternalVector()) {
|
||||
auto variant = variantUniquePtr.get();
|
||||
worker.Launch(variant->GetObjects());
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@@ -2129,6 +2129,26 @@ void WholeProjectRefactorer::ObjectOrGroupRenamedInEventsBasedObject(
|
||||
groups[g].RenameObject(oldName, newName);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &variant : eventsBasedObject.GetVariants().GetInternalVector()) {
|
||||
auto &variantObjects = variant->GetObjects();
|
||||
auto &variantObjectGroups = variantObjects.GetObjectGroups();
|
||||
if (isObjectGroup) {
|
||||
if (variantObjectGroups.Has(oldName)) {
|
||||
variantObjectGroups.Get(oldName).SetName(newName);
|
||||
}
|
||||
// Object groups can't have instances or be in other groups
|
||||
}
|
||||
else {
|
||||
if (variantObjects.HasObjectNamed(oldName)) {
|
||||
variantObjects.GetObject(oldName).SetName(newName);
|
||||
}
|
||||
variant->GetInitialInstances().RenameInstancesOfObject(oldName, newName);
|
||||
for (std::size_t g = 0; g < variantObjectGroups.size(); ++g) {
|
||||
variantObjectGroups[g].RenameObject(oldName, newName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WholeProjectRefactorer::ObjectOrGroupRenamedInEventsFunction(
|
||||
|
@@ -19,6 +19,7 @@ using namespace gd;
|
||||
|
||||
void CustomObjectConfiguration::Init(const gd::CustomObjectConfiguration& objectConfiguration) {
|
||||
project = objectConfiguration.project;
|
||||
variantName = objectConfiguration.variantName;
|
||||
objectContent = objectConfiguration.objectContent;
|
||||
animations = objectConfiguration.animations;
|
||||
isMarkedAsOverridingEventsBasedObjectChildrenConfiguration =
|
||||
@@ -165,6 +166,7 @@ void CustomObjectConfiguration::DoSerializeTo(SerializerElement& element) const
|
||||
animations.SerializeTo(animatableElement);
|
||||
}
|
||||
|
||||
element.SetAttribute("variant", variantName);
|
||||
if (IsOverridingEventsBasedObjectChildrenConfiguration()) {
|
||||
auto &childrenContentElement = element.AddChild("childrenContent");
|
||||
for (auto &pair : childObjectConfigurations) {
|
||||
@@ -184,6 +186,7 @@ void CustomObjectConfiguration::DoUnserializeFrom(Project& project,
|
||||
animations.UnserializeFrom(animatableElement);
|
||||
}
|
||||
|
||||
variantName = element.GetStringAttribute("variant");
|
||||
isMarkedAsOverridingEventsBasedObjectChildrenConfiguration =
|
||||
element.HasChild("childrenContent");
|
||||
if (isMarkedAsOverridingEventsBasedObjectChildrenConfiguration) {
|
||||
@@ -247,9 +250,26 @@ void CustomObjectConfiguration::ExposeResources(gd::ArbitraryResourceWorker& wor
|
||||
}
|
||||
const auto &eventsBasedObject = project->GetEventsBasedObject(GetType());
|
||||
|
||||
for (auto& childObject : eventsBasedObject.GetObjects().GetObjects()) {
|
||||
auto &configuration = GetChildObjectConfiguration(childObject->GetName());
|
||||
configuration.ExposeResources(worker);
|
||||
if (isMarkedAsOverridingEventsBasedObjectChildrenConfiguration) {
|
||||
for (auto &childObject : eventsBasedObject.GetObjects().GetObjects()) {
|
||||
auto &configuration = GetChildObjectConfiguration(childObject->GetName());
|
||||
configuration.ExposeResources(worker);
|
||||
}
|
||||
} else {
|
||||
if (variantName.empty() ||
|
||||
!eventsBasedObject.GetVariants().HasVariantNamed(variantName)) {
|
||||
for (auto &childObject :
|
||||
eventsBasedObject.GetDefaultVariant().GetObjects().GetObjects()) {
|
||||
childObject->GetConfiguration().ExposeResources(worker);
|
||||
}
|
||||
} else {
|
||||
for (auto &childObject : eventsBasedObject.GetVariants()
|
||||
.GetVariant(variantName)
|
||||
.GetObjects()
|
||||
.GetObjects()) {
|
||||
childObject->GetConfiguration().ExposeResources(worker);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -29,9 +29,9 @@ namespace gd {
|
||||
* "resource".
|
||||
*/
|
||||
class CustomObjectConfiguration : public gd::ObjectConfiguration {
|
||||
public:
|
||||
CustomObjectConfiguration(const Project& project_, const String& type_)
|
||||
: project(&project_), isMarkedAsOverridingEventsBasedObjectChildrenConfiguration(false) {
|
||||
public:
|
||||
CustomObjectConfiguration(const Project &project_, const String &type_)
|
||||
: project(&project_) {
|
||||
SetType(type_);
|
||||
}
|
||||
std::unique_ptr<gd::ObjectConfiguration> Clone() const override;
|
||||
@@ -66,6 +66,18 @@ class CustomObjectConfiguration : public gd::ObjectConfiguration {
|
||||
|
||||
void ExposeResources(gd::ArbitraryResourceWorker& worker) override;
|
||||
|
||||
/**
|
||||
* \brief Get the name of the events-based object variant used by this custom object.
|
||||
*/
|
||||
const gd::String &GetVariantName() const { return variantName; };
|
||||
|
||||
/**
|
||||
* \brief Set the name of the events-based object variant used by this custom object.
|
||||
*/
|
||||
void SetVariantName(const gd::String &variantName_) {
|
||||
variantName = variantName_;
|
||||
}
|
||||
|
||||
bool IsForcedToOverrideEventsBasedObjectChildrenConfiguration() const;
|
||||
|
||||
bool IsMarkedAsOverridingEventsBasedObjectChildrenConfiguration() const {
|
||||
@@ -145,6 +157,7 @@ protected:
|
||||
gd::SerializerElement objectContent;
|
||||
std::unordered_set<gd::String> unfoldedChildren;
|
||||
|
||||
gd::String variantName = "";
|
||||
bool isMarkedAsOverridingEventsBasedObjectChildrenConfiguration = false;
|
||||
mutable std::map<gd::String, std::unique_ptr<gd::ObjectConfiguration>> childObjectConfigurations;
|
||||
|
||||
|
@@ -17,19 +17,13 @@ EventsBasedObject::EventsBasedObject()
|
||||
isAnimatable(false),
|
||||
isTextContainer(false),
|
||||
isInnerAreaFollowingParentSize(false),
|
||||
isUsingLegacyInstancesRenderer(false),
|
||||
areaMinX(0),
|
||||
areaMinY(0),
|
||||
areaMinZ(0),
|
||||
areaMaxX(64),
|
||||
areaMaxY(64),
|
||||
areaMaxZ(64),
|
||||
objectsContainer(gd::ObjectsContainer::SourceType::Object) {
|
||||
isUsingLegacyInstancesRenderer(false) {
|
||||
}
|
||||
|
||||
EventsBasedObject::~EventsBasedObject() {}
|
||||
|
||||
void EventsBasedObject::SerializeTo(SerializerElement& element) const {
|
||||
|
||||
void EventsBasedObject::SerializeToExternal(SerializerElement& element) const {
|
||||
element.SetAttribute("defaultName", defaultName);
|
||||
if (isRenderedIn3D) {
|
||||
element.SetBoolAttribute("is3D", true);
|
||||
@@ -44,20 +38,16 @@ void EventsBasedObject::SerializeTo(SerializerElement& element) const {
|
||||
element.SetBoolAttribute("isInnerAreaFollowingParentSize", true);
|
||||
}
|
||||
element.SetBoolAttribute("isUsingLegacyInstancesRenderer", isUsingLegacyInstancesRenderer);
|
||||
element.SetIntAttribute("areaMinX", areaMinX);
|
||||
element.SetIntAttribute("areaMinY", areaMinY);
|
||||
element.SetIntAttribute("areaMinZ", areaMinZ);
|
||||
element.SetIntAttribute("areaMaxX", areaMaxX);
|
||||
element.SetIntAttribute("areaMaxY", areaMaxY);
|
||||
element.SetIntAttribute("areaMaxZ", areaMaxZ);
|
||||
|
||||
// The EventsBasedObjectVariant SerializeTo method override the name.
|
||||
// AbstractEventsBasedEntity::SerializeTo must be done after.
|
||||
defaultVariant.SerializeTo(element);
|
||||
AbstractEventsBasedEntity::SerializeTo(element);
|
||||
objectsContainer.SerializeObjectsTo(element.AddChild("objects"));
|
||||
objectsContainer.SerializeFoldersTo(element.AddChild("objectsFolderStructure"));
|
||||
objectsContainer.GetObjectGroups().SerializeTo(element.AddChild("objectsGroups"));
|
||||
}
|
||||
|
||||
layers.SerializeLayersTo(element.AddChild("layers"));
|
||||
initialInstances.SerializeTo(element.AddChild("instances"));
|
||||
void EventsBasedObject::SerializeTo(SerializerElement& element) const {
|
||||
SerializeToExternal(element);
|
||||
variants.SerializeVariantsTo(element.AddChild("variants"));
|
||||
}
|
||||
|
||||
void EventsBasedObject::UnserializeFrom(gd::Project& project,
|
||||
@@ -68,36 +58,22 @@ void EventsBasedObject::UnserializeFrom(gd::Project& project,
|
||||
isTextContainer = element.GetBoolAttribute("isTextContainer", false);
|
||||
isInnerAreaFollowingParentSize =
|
||||
element.GetBoolAttribute("isInnerAreaFollowingParentSize", false);
|
||||
areaMinX = element.GetIntAttribute("areaMinX", 0);
|
||||
areaMinY = element.GetIntAttribute("areaMinY", 0);
|
||||
areaMinZ = element.GetIntAttribute("areaMinZ", 0);
|
||||
areaMaxX = element.GetIntAttribute("areaMaxX", 64);
|
||||
areaMaxY = element.GetIntAttribute("areaMaxY", 64);
|
||||
areaMaxZ = element.GetIntAttribute("areaMaxZ", 64);
|
||||
|
||||
defaultVariant.UnserializeFrom(project, element);
|
||||
defaultVariant.SetName("");
|
||||
AbstractEventsBasedEntity::UnserializeFrom(project, element);
|
||||
objectsContainer.UnserializeObjectsFrom(project, element.GetChild("objects"));
|
||||
if (element.HasChild("objectsFolderStructure")) {
|
||||
objectsContainer.UnserializeFoldersFrom(project, element.GetChild("objectsFolderStructure", 0));
|
||||
}
|
||||
objectsContainer.AddMissingObjectsInRootFolder();
|
||||
objectsContainer.GetObjectGroups().UnserializeFrom(
|
||||
element.GetChild("objectsGroups"));
|
||||
|
||||
if (element.HasChild("layers")) {
|
||||
layers.UnserializeLayersFrom(element.GetChild("layers"));
|
||||
} else {
|
||||
layers.Reset();
|
||||
if (element.HasChild("variants")) {
|
||||
variants.UnserializeVariantsFrom(project, element.GetChild("variants"));
|
||||
}
|
||||
|
||||
initialInstances.UnserializeFrom(element.GetChild("instances"));
|
||||
if (element.HasChild("isUsingLegacyInstancesRenderer")) {
|
||||
isUsingLegacyInstancesRenderer =
|
||||
element.GetBoolAttribute("isUsingLegacyInstancesRenderer", false);
|
||||
}
|
||||
else {
|
||||
// Compatibility with GD <= 5.4.212
|
||||
isUsingLegacyInstancesRenderer = initialInstances.GetInstancesCount() == 0;
|
||||
isUsingLegacyInstancesRenderer = GetInitialInstances().GetInstancesCount() == 0;
|
||||
// end of compatibility code
|
||||
}
|
||||
}
|
||||
|
@@ -7,6 +7,8 @@
|
||||
|
||||
#include <vector>
|
||||
#include "GDCore/Project/AbstractEventsBasedEntity.h"
|
||||
#include "GDCore/Project/EventsBasedObjectVariant.h"
|
||||
#include "GDCore/Project/EventsBasedObjectVariantsContainer.h"
|
||||
#include "GDCore/Project/ObjectsContainer.h"
|
||||
#include "GDCore/Project/InitialInstancesContainer.h"
|
||||
#include "GDCore/Project/LayersContainer.h"
|
||||
@@ -162,18 +164,38 @@ class GD_CORE_API EventsBasedObject: public AbstractEventsBasedEntity {
|
||||
*/
|
||||
bool IsTextContainer() const { return isTextContainer; }
|
||||
|
||||
/**
|
||||
* \brief Get the default variant of the custom object.
|
||||
*/
|
||||
const gd::EventsBasedObjectVariant& GetDefaultVariant() const { return defaultVariant; }
|
||||
|
||||
/**
|
||||
* \brief Get the default variant of the custom object.
|
||||
*/
|
||||
gd::EventsBasedObjectVariant& GetDefaultVariant() { return defaultVariant; }
|
||||
|
||||
/**
|
||||
* \brief Get the variants of the custom object.
|
||||
*/
|
||||
const gd::EventsBasedObjectVariantsContainer& GetVariants() const { return variants; }
|
||||
|
||||
/**
|
||||
* \brief Get the variants of the custom object.
|
||||
*/
|
||||
gd::EventsBasedObjectVariantsContainer& GetVariants() { return variants; }
|
||||
|
||||
/** \name Layers
|
||||
*/
|
||||
///@{
|
||||
/**
|
||||
* \brief Get the layers of the custom object.
|
||||
*/
|
||||
const gd::LayersContainer& GetLayers() const { return layers; }
|
||||
const gd::LayersContainer& GetLayers() const { return defaultVariant.GetLayers(); }
|
||||
|
||||
/**
|
||||
* \brief Get the layers of the custom object.
|
||||
*/
|
||||
gd::LayersContainer& GetLayers() { return layers; }
|
||||
gd::LayersContainer& GetLayers() { return defaultVariant.GetLayers(); }
|
||||
///@}
|
||||
|
||||
/** \name Child objects
|
||||
@@ -183,14 +205,14 @@ class GD_CORE_API EventsBasedObject: public AbstractEventsBasedEntity {
|
||||
* \brief Get the objects of the custom object.
|
||||
*/
|
||||
gd::ObjectsContainer& GetObjects() {
|
||||
return objectsContainer;
|
||||
return defaultVariant.GetObjects();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the objects of the custom object.
|
||||
*/
|
||||
const gd::ObjectsContainer& GetObjects() const {
|
||||
return objectsContainer;
|
||||
return defaultVariant.GetObjects();
|
||||
}
|
||||
///@}
|
||||
|
||||
@@ -201,14 +223,14 @@ class GD_CORE_API EventsBasedObject: public AbstractEventsBasedEntity {
|
||||
* \brief Get the instances of the custom object.
|
||||
*/
|
||||
gd::InitialInstancesContainer& GetInitialInstances() {
|
||||
return initialInstances;
|
||||
return defaultVariant.GetInitialInstances();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the instances of the custom object.
|
||||
*/
|
||||
const gd::InitialInstancesContainer& GetInitialInstances() const {
|
||||
return initialInstances;
|
||||
return defaultVariant.GetInitialInstances();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -219,14 +241,14 @@ class GD_CORE_API EventsBasedObject: public AbstractEventsBasedEntity {
|
||||
* \see EventsBasedObject::GetInitialInstances
|
||||
*/
|
||||
int GetAreaMinX() const {
|
||||
return areaMinX;
|
||||
return defaultVariant.GetAreaMinX();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set the left bound of the custom object.
|
||||
*/
|
||||
void SetAreaMinX(int areaMinX_) {
|
||||
areaMinX = areaMinX_;
|
||||
void SetAreaMinX(int areaMinX) {
|
||||
defaultVariant.SetAreaMinX(areaMinX);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -237,14 +259,14 @@ class GD_CORE_API EventsBasedObject: public AbstractEventsBasedEntity {
|
||||
* \see EventsBasedObject::GetInitialInstances
|
||||
*/
|
||||
int GetAreaMinY() const {
|
||||
return areaMinY;
|
||||
return defaultVariant.GetAreaMinY();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set the top bound of the custom object.
|
||||
*/
|
||||
void SetAreaMinY(int areaMinY_) {
|
||||
areaMinY = areaMinY_;
|
||||
void SetAreaMinY(int areaMinY) {
|
||||
defaultVariant.SetAreaMinY(areaMinY);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -255,14 +277,14 @@ class GD_CORE_API EventsBasedObject: public AbstractEventsBasedEntity {
|
||||
* \see EventsBasedObject::GetInitialInstances
|
||||
*/
|
||||
int GetAreaMinZ() const {
|
||||
return areaMinZ;
|
||||
return defaultVariant.GetAreaMinZ();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set the min Z bound of the custom object.
|
||||
*/
|
||||
void SetAreaMinZ(int areaMinZ_) {
|
||||
areaMinZ = areaMinZ_;
|
||||
void SetAreaMinZ(int areaMinZ) {
|
||||
defaultVariant.SetAreaMinZ(areaMinZ);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -273,14 +295,14 @@ class GD_CORE_API EventsBasedObject: public AbstractEventsBasedEntity {
|
||||
* \see EventsBasedObject::GetInitialInstances
|
||||
*/
|
||||
int GetAreaMaxX() const {
|
||||
return areaMaxX;
|
||||
return defaultVariant.GetAreaMaxX();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set the right bound of the custom object.
|
||||
*/
|
||||
void SetAreaMaxX(int areaMaxX_) {
|
||||
areaMaxX = areaMaxX_;
|
||||
void SetAreaMaxX(int areaMaxX) {
|
||||
defaultVariant.SetAreaMaxX(areaMaxX);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -291,14 +313,14 @@ class GD_CORE_API EventsBasedObject: public AbstractEventsBasedEntity {
|
||||
* \see EventsBasedObject::GetInitialInstances
|
||||
*/
|
||||
int GetAreaMaxY() const {
|
||||
return areaMaxY;
|
||||
return defaultVariant.GetAreaMaxY();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set the bottom bound of the custom object.
|
||||
*/
|
||||
void SetAreaMaxY(int areaMaxY_) {
|
||||
areaMaxY = areaMaxY_;
|
||||
void SetAreaMaxY(int areaMaxY) {
|
||||
defaultVariant.SetAreaMaxY(areaMaxY);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -309,16 +331,22 @@ class GD_CORE_API EventsBasedObject: public AbstractEventsBasedEntity {
|
||||
* \see EventsBasedObject::GetInitialInstances
|
||||
*/
|
||||
int GetAreaMaxZ() const {
|
||||
return areaMaxZ;
|
||||
return defaultVariant.GetAreaMaxZ();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set the bottom bound of the custom object.
|
||||
*/
|
||||
void SetAreaMaxZ(int areaMaxZ_) {
|
||||
areaMaxZ = areaMaxZ_;
|
||||
void SetAreaMaxZ(int areaMaxZ) {
|
||||
defaultVariant.SetAreaMaxZ(areaMaxZ);
|
||||
}
|
||||
///@}
|
||||
|
||||
/**
|
||||
* @brief Serialize the events-based object for an extension in an external file.
|
||||
* Variants are not serialized.
|
||||
*/
|
||||
void SerializeToExternal(SerializerElement& element) const;
|
||||
|
||||
void SerializeTo(SerializerElement& element) const override;
|
||||
|
||||
@@ -332,15 +360,8 @@ class GD_CORE_API EventsBasedObject: public AbstractEventsBasedEntity {
|
||||
bool isTextContainer;
|
||||
bool isInnerAreaFollowingParentSize;
|
||||
bool isUsingLegacyInstancesRenderer;
|
||||
gd::InitialInstancesContainer initialInstances;
|
||||
gd::LayersContainer layers;
|
||||
gd::ObjectsContainer objectsContainer;
|
||||
double areaMinX;
|
||||
double areaMinY;
|
||||
double areaMinZ;
|
||||
double areaMaxX;
|
||||
double areaMaxY;
|
||||
double areaMaxZ;
|
||||
gd::EventsBasedObjectVariant defaultVariant;
|
||||
gd::EventsBasedObjectVariantsContainer variants;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
71
Core/GDCore/Project/EventsBasedObjectVariant.cpp
Normal file
71
Core/GDCore/Project/EventsBasedObjectVariant.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#include "EventsBasedObjectVariant.h"
|
||||
#include "GDCore/Project/Object.h"
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
EventsBasedObjectVariant::EventsBasedObjectVariant()
|
||||
: areaMinX(0), areaMinY(0), areaMinZ(0), areaMaxX(64), areaMaxY(64),
|
||||
areaMaxZ(64), objectsContainer(gd::ObjectsContainer::SourceType::Object) {
|
||||
}
|
||||
|
||||
EventsBasedObjectVariant::~EventsBasedObjectVariant() {}
|
||||
|
||||
void EventsBasedObjectVariant::SerializeTo(SerializerElement &element) const {
|
||||
element.SetAttribute("name", name);
|
||||
if (!GetAssetStoreAssetId().empty() && !GetAssetStoreOriginalName().empty()) {
|
||||
element.SetAttribute("assetStoreAssetId", GetAssetStoreAssetId());
|
||||
element.SetAttribute("assetStoreOriginalName", GetAssetStoreOriginalName());
|
||||
}
|
||||
element.SetIntAttribute("areaMinX", areaMinX);
|
||||
element.SetIntAttribute("areaMinY", areaMinY);
|
||||
element.SetIntAttribute("areaMinZ", areaMinZ);
|
||||
element.SetIntAttribute("areaMaxX", areaMaxX);
|
||||
element.SetIntAttribute("areaMaxY", areaMaxY);
|
||||
element.SetIntAttribute("areaMaxZ", areaMaxZ);
|
||||
|
||||
objectsContainer.SerializeObjectsTo(element.AddChild("objects"));
|
||||
objectsContainer.SerializeFoldersTo(
|
||||
element.AddChild("objectsFolderStructure"));
|
||||
objectsContainer.GetObjectGroups().SerializeTo(
|
||||
element.AddChild("objectsGroups"));
|
||||
|
||||
layers.SerializeLayersTo(element.AddChild("layers"));
|
||||
initialInstances.SerializeTo(element.AddChild("instances"));
|
||||
}
|
||||
|
||||
void EventsBasedObjectVariant::UnserializeFrom(
|
||||
gd::Project &project, const SerializerElement &element) {
|
||||
name = element.GetStringAttribute("name");
|
||||
assetStoreAssetId = element.GetStringAttribute("assetStoreAssetId");
|
||||
assetStoreOriginalName = element.GetStringAttribute("assetStoreOriginalName");
|
||||
areaMinX = element.GetIntAttribute("areaMinX", 0);
|
||||
areaMinY = element.GetIntAttribute("areaMinY", 0);
|
||||
areaMinZ = element.GetIntAttribute("areaMinZ", 0);
|
||||
areaMaxX = element.GetIntAttribute("areaMaxX", 64);
|
||||
areaMaxY = element.GetIntAttribute("areaMaxY", 64);
|
||||
areaMaxZ = element.GetIntAttribute("areaMaxZ", 64);
|
||||
|
||||
objectsContainer.UnserializeObjectsFrom(project, element.GetChild("objects"));
|
||||
if (element.HasChild("objectsFolderStructure")) {
|
||||
objectsContainer.UnserializeFoldersFrom(
|
||||
project, element.GetChild("objectsFolderStructure", 0));
|
||||
}
|
||||
objectsContainer.AddMissingObjectsInRootFolder();
|
||||
objectsContainer.GetObjectGroups().UnserializeFrom(
|
||||
element.GetChild("objectsGroups"));
|
||||
|
||||
if (element.HasChild("layers")) {
|
||||
layers.UnserializeLayersFrom(element.GetChild("layers"));
|
||||
} else {
|
||||
layers.Reset();
|
||||
}
|
||||
initialInstances.UnserializeFrom(element.GetChild("instances"));
|
||||
}
|
||||
|
||||
} // namespace gd
|
229
Core/GDCore/Project/EventsBasedObjectVariant.h
Normal file
229
Core/GDCore/Project/EventsBasedObjectVariant.h
Normal file
@@ -0,0 +1,229 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2025 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "GDCore/Project/InitialInstancesContainer.h"
|
||||
#include "GDCore/Project/LayersContainer.h"
|
||||
#include "GDCore/Project/ObjectsContainer.h"
|
||||
#include "GDCore/String.h"
|
||||
#include <vector>
|
||||
|
||||
namespace gd {
|
||||
class SerializerElement;
|
||||
class Project;
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
/**
|
||||
* \brief Represents a variation of style of an events-based object.
|
||||
*
|
||||
* \ingroup PlatformDefinition
|
||||
*/
|
||||
class GD_CORE_API EventsBasedObjectVariant {
|
||||
public:
|
||||
EventsBasedObjectVariant();
|
||||
virtual ~EventsBasedObjectVariant();
|
||||
|
||||
/**
|
||||
* \brief Return a pointer to a new EventsBasedObjectVariant constructed from
|
||||
* this one.
|
||||
*/
|
||||
EventsBasedObjectVariant *Clone() const {
|
||||
return new EventsBasedObjectVariant(*this);
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Get the name of the variant.
|
||||
*/
|
||||
const gd::String &GetName() const { return name; };
|
||||
|
||||
/**
|
||||
* \brief Set the name of the variant.
|
||||
*/
|
||||
EventsBasedObjectVariant &SetName(const gd::String &name_) {
|
||||
name = name_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** \name Layers
|
||||
*/
|
||||
///@{
|
||||
/**
|
||||
* \brief Get the layers of the variant.
|
||||
*/
|
||||
const gd::LayersContainer &GetLayers() const { return layers; }
|
||||
|
||||
/**
|
||||
* \brief Get the layers of the variant.
|
||||
*/
|
||||
gd::LayersContainer &GetLayers() { return layers; }
|
||||
///@}
|
||||
|
||||
/** \name Child objects
|
||||
*/
|
||||
///@{
|
||||
/**
|
||||
* \brief Get the objects of the variant.
|
||||
*/
|
||||
gd::ObjectsContainer &GetObjects() { return objectsContainer; }
|
||||
|
||||
/**
|
||||
* \brief Get the objects of the variant.
|
||||
*/
|
||||
const gd::ObjectsContainer &GetObjects() const { return objectsContainer; }
|
||||
///@}
|
||||
|
||||
/** \name Instances
|
||||
*/
|
||||
///@{
|
||||
/**
|
||||
* \brief Get the instances of the variant.
|
||||
*/
|
||||
gd::InitialInstancesContainer &GetInitialInstances() {
|
||||
return initialInstances;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the instances of the variant.
|
||||
*/
|
||||
const gd::InitialInstancesContainer &GetInitialInstances() const {
|
||||
return initialInstances;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the left bound of the variant.
|
||||
*
|
||||
* This is used only if there is any initial instances.
|
||||
*
|
||||
* \see EventsBasedObjectVariant::GetInitialInstances
|
||||
*/
|
||||
int GetAreaMinX() const { return areaMinX; }
|
||||
|
||||
/**
|
||||
* \brief Set the left bound of the variant.
|
||||
*/
|
||||
void SetAreaMinX(int areaMinX_) { areaMinX = areaMinX_; }
|
||||
|
||||
/**
|
||||
* \brief Get the top bound of the variant.
|
||||
*
|
||||
* This is used only if there is any initial instances.
|
||||
*
|
||||
* \see EventsBasedObjectVariant::GetInitialInstances
|
||||
*/
|
||||
int GetAreaMinY() const { return areaMinY; }
|
||||
|
||||
/**
|
||||
* \brief Set the top bound of the variant.
|
||||
*/
|
||||
void SetAreaMinY(int areaMinY_) { areaMinY = areaMinY_; }
|
||||
|
||||
/**
|
||||
* \brief Get the min Z bound of the variant.
|
||||
*
|
||||
* This is used only if there is any initial instances.
|
||||
*
|
||||
* \see EventsBasedObjectVariant::GetInitialInstances
|
||||
*/
|
||||
int GetAreaMinZ() const { return areaMinZ; }
|
||||
|
||||
/**
|
||||
* \brief Set the min Z bound of the variant.
|
||||
*/
|
||||
void SetAreaMinZ(int areaMinZ_) { areaMinZ = areaMinZ_; }
|
||||
|
||||
/**
|
||||
* \brief Get the right bound of the variant.
|
||||
*
|
||||
* This is used only if there is any initial instances.
|
||||
*
|
||||
* \see EventsBasedObjectVariant::GetInitialInstances
|
||||
*/
|
||||
int GetAreaMaxX() const { return areaMaxX; }
|
||||
|
||||
/**
|
||||
* \brief Set the right bound of the variant.
|
||||
*/
|
||||
void SetAreaMaxX(int areaMaxX_) { areaMaxX = areaMaxX_; }
|
||||
|
||||
/**
|
||||
* \brief Get the bottom bound of the variant.
|
||||
*
|
||||
* This is used only if there is any initial instances.
|
||||
*
|
||||
* \see EventsBasedObjectVariant::GetInitialInstances
|
||||
*/
|
||||
int GetAreaMaxY() const { return areaMaxY; }
|
||||
|
||||
/**
|
||||
* \brief Set the bottom bound of the variant.
|
||||
*/
|
||||
void SetAreaMaxY(int areaMaxY_) { areaMaxY = areaMaxY_; }
|
||||
|
||||
/**
|
||||
* \brief Get the max Z bound of the variant.
|
||||
*
|
||||
* This is used only if there is any initial instances.
|
||||
*
|
||||
* \see EventsBasedObjectVariant::GetInitialInstances
|
||||
*/
|
||||
int GetAreaMaxZ() const { return areaMaxZ; }
|
||||
|
||||
/**
|
||||
* \brief Set the bottom bound of the variant.
|
||||
*/
|
||||
void SetAreaMaxZ(int areaMaxZ_) { areaMaxZ = areaMaxZ_; }
|
||||
///@}
|
||||
|
||||
/** \brief Change the object asset store id of this variant.
|
||||
*/
|
||||
void SetAssetStoreAssetId(const gd::String &assetStoreId_) {
|
||||
assetStoreAssetId = assetStoreId_;
|
||||
};
|
||||
|
||||
/** \brief Return the object asset store id of this variant.
|
||||
*/
|
||||
const gd::String &GetAssetStoreAssetId() const { return assetStoreAssetId; };
|
||||
|
||||
/** \brief Change the original name of the variant in the asset.
|
||||
*/
|
||||
void SetAssetStoreOriginalName(const gd::String &assetStoreOriginalName_) {
|
||||
assetStoreOriginalName = assetStoreOriginalName_;
|
||||
};
|
||||
|
||||
/** \brief Return the original name of the variant in the asset.
|
||||
*/
|
||||
const gd::String &GetAssetStoreOriginalName() const {
|
||||
return assetStoreOriginalName;
|
||||
};
|
||||
|
||||
void SerializeTo(SerializerElement &element) const;
|
||||
|
||||
void UnserializeFrom(gd::Project &project, const SerializerElement &element);
|
||||
|
||||
private:
|
||||
gd::String name;
|
||||
gd::InitialInstancesContainer initialInstances;
|
||||
gd::LayersContainer layers;
|
||||
gd::ObjectsContainer objectsContainer;
|
||||
double areaMinX;
|
||||
double areaMinY;
|
||||
double areaMinZ;
|
||||
double areaMaxX;
|
||||
double areaMaxY;
|
||||
double areaMaxZ;
|
||||
/**
|
||||
* The ID of the asset if the object comes from the store.
|
||||
*/
|
||||
gd::String assetStoreAssetId;
|
||||
/**
|
||||
* The original name of the variant in the asset if the object comes from the
|
||||
* store.
|
||||
*/
|
||||
gd::String assetStoreOriginalName;
|
||||
};
|
||||
|
||||
} // namespace gd
|
160
Core/GDCore/Project/EventsBasedObjectVariantsContainer.h
Normal file
160
Core/GDCore/Project/EventsBasedObjectVariantsContainer.h
Normal file
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "GDCore/Project/EventsBasedObjectVariant.h"
|
||||
#include "GDCore/String.h"
|
||||
#include "GDCore/Tools/SerializableWithNameList.h"
|
||||
|
||||
namespace gd {
|
||||
class SerializerElement;
|
||||
}
|
||||
|
||||
namespace gd {
|
||||
|
||||
/**
|
||||
* \brief Used as a base class for classes that will own events-backed
|
||||
* variants.
|
||||
*
|
||||
* \see gd::EventsBasedObjectVariantContainer
|
||||
* \ingroup PlatformDefinition
|
||||
*/
|
||||
class GD_CORE_API EventsBasedObjectVariantsContainer
|
||||
: private SerializableWithNameList<gd::EventsBasedObjectVariant> {
|
||||
public:
|
||||
EventsBasedObjectVariantsContainer() {}
|
||||
|
||||
EventsBasedObjectVariantsContainer(const EventsBasedObjectVariantsContainer &other) {
|
||||
Init(other);
|
||||
}
|
||||
|
||||
EventsBasedObjectVariantsContainer &operator=(const EventsBasedObjectVariantsContainer &other) {
|
||||
if (this != &other) {
|
||||
Init(other);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** \name Events Functions management
|
||||
*/
|
||||
///@{
|
||||
/**
|
||||
* \brief Check if the variant with the specified name exists.
|
||||
*/
|
||||
bool HasVariantNamed(const gd::String& name) const {
|
||||
return Has(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the variant with the specified name.
|
||||
*
|
||||
* \warning Trying to access to a not existing variant will result in
|
||||
* undefined behavior.
|
||||
*/
|
||||
gd::EventsBasedObjectVariant& GetVariant(const gd::String& name) {
|
||||
return Get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the variant with the specified name.
|
||||
*
|
||||
* \warning Trying to access to a not existing variant will result in
|
||||
* undefined behavior.
|
||||
*/
|
||||
const gd::EventsBasedObjectVariant& GetVariant(const gd::String& name) const {
|
||||
return Get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the variant at the specified index in the list.
|
||||
*
|
||||
* \warning Trying to access to a not existing variant will result in
|
||||
* undefined behavior.
|
||||
*/
|
||||
gd::EventsBasedObjectVariant& GetVariant(std::size_t index) {
|
||||
return Get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the variant at the specified index in the list.
|
||||
*
|
||||
* \warning Trying to access to a not existing variant will result in
|
||||
* undefined behavior.
|
||||
*/
|
||||
const gd::EventsBasedObjectVariant& GetVariant(std::size_t index) const {
|
||||
return Get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return the number of variants.
|
||||
*/
|
||||
std::size_t GetVariantsCount() const { return GetCount(); }
|
||||
|
||||
gd::EventsBasedObjectVariant& InsertNewVariant(const gd::String& name,
|
||||
std::size_t position) {
|
||||
return InsertNew(name, position);
|
||||
}
|
||||
gd::EventsBasedObjectVariant& InsertVariant(const gd::EventsBasedObjectVariant& object,
|
||||
std::size_t position) {
|
||||
return Insert(object, position);
|
||||
}
|
||||
void RemoveVariant(const gd::String& name) { return Remove(name); }
|
||||
void ClearVariants() { return Clear(); }
|
||||
void MoveVariant(std::size_t oldIndex, std::size_t newIndex) {
|
||||
return Move(oldIndex, newIndex);
|
||||
};
|
||||
std::size_t GetVariantPosition(const gd::EventsBasedObjectVariant& eventsFunction) {
|
||||
return GetPosition(eventsFunction);
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Provide a raw access to the vector containing the variants.
|
||||
*/
|
||||
const std::vector<std::unique_ptr<gd::EventsBasedObjectVariant>>& GetInternalVector()
|
||||
const {
|
||||
return elements;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Provide a raw access to the vector containing the variants.
|
||||
*/
|
||||
std::vector<std::unique_ptr<gd::EventsBasedObjectVariant>>& GetInternalVector() {
|
||||
return elements;
|
||||
};
|
||||
///@}
|
||||
|
||||
/** \name Serialization
|
||||
*/
|
||||
///@{
|
||||
/**
|
||||
* \brief Serialize events variants.
|
||||
*/
|
||||
void SerializeVariantsTo(SerializerElement& element) const {
|
||||
return SerializeElementsTo("variant", element);
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Unserialize the events variants.
|
||||
*/
|
||||
void UnserializeVariantsFrom(gd::Project& project,
|
||||
const SerializerElement& element) {
|
||||
return UnserializeElementsFrom("variant", project, element);
|
||||
};
|
||||
///@}
|
||||
protected:
|
||||
/**
|
||||
* Initialize object using another object. Used by copy-ctor and assign-op.
|
||||
* Don't forget to update me if members were changed!
|
||||
*/
|
||||
void Init(const gd::EventsBasedObjectVariantsContainer& other) {
|
||||
return SerializableWithNameList<gd::EventsBasedObjectVariant>::Init(other);
|
||||
};
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
} // namespace gd
|
@@ -55,7 +55,7 @@ void EventsFunctionsExtension::Init(const gd::EventsFunctionsExtension& other) {
|
||||
sceneVariables = other.GetSceneVariables();
|
||||
}
|
||||
|
||||
void EventsFunctionsExtension::SerializeTo(SerializerElement& element) const {
|
||||
void EventsFunctionsExtension::SerializeTo(SerializerElement& element, bool isExternal) const {
|
||||
element.SetAttribute("version", version);
|
||||
element.SetAttribute("extensionNamespace", extensionNamespace);
|
||||
element.SetAttribute("shortDescription", shortDescription);
|
||||
@@ -102,8 +102,18 @@ void EventsFunctionsExtension::SerializeTo(SerializerElement& element) const {
|
||||
element.AddChild("eventsFunctions"));
|
||||
eventsBasedBehaviors.SerializeElementsTo(
|
||||
"eventsBasedBehavior", element.AddChild("eventsBasedBehaviors"));
|
||||
eventsBasedObjects.SerializeElementsTo(
|
||||
"eventsBasedObject", element.AddChild("eventsBasedObjects"));
|
||||
if (isExternal) {
|
||||
auto &eventsBasedObjectElement = element.AddChild("eventsBasedObjects");
|
||||
eventsBasedObjectElement.ConsiderAsArrayOf("eventsBasedObject");
|
||||
for (const auto &eventsBasedObject :
|
||||
eventsBasedObjects.GetInternalVector()) {
|
||||
eventsBasedObject->SerializeToExternal(
|
||||
eventsBasedObjectElement.AddChild("eventsBasedObject"));
|
||||
}
|
||||
} else {
|
||||
eventsBasedObjects.SerializeElementsTo(
|
||||
"eventsBasedObject", element.AddChild("eventsBasedObjects"));
|
||||
}
|
||||
}
|
||||
|
||||
void EventsFunctionsExtension::UnserializeFrom(
|
||||
|
@@ -286,7 +286,14 @@ class GD_CORE_API EventsFunctionsExtension {
|
||||
/**
|
||||
* \brief Serialize the EventsFunctionsExtension to the specified element
|
||||
*/
|
||||
void SerializeTo(gd::SerializerElement& element) const;
|
||||
void SerializeTo(gd::SerializerElement& element, bool isExternal = false) const;
|
||||
|
||||
/**
|
||||
* \brief Serialize the EventsFunctionsExtension to the specified element
|
||||
*/
|
||||
void SerializeToExternal(gd::SerializerElement& element) const {
|
||||
SerializeTo(element, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Load the EventsFunctionsExtension from the specified element.
|
||||
|
@@ -42,8 +42,13 @@ void InitialInstancesContainer::IterateOverInstances(
|
||||
}
|
||||
|
||||
void InitialInstancesContainer::IterateOverInstances(
|
||||
const std::function< void(gd::InitialInstance &) >& func) {
|
||||
for (auto& instance : initialInstances) func(instance);
|
||||
const std::function< bool(gd::InitialInstance &) >& func) {
|
||||
for (auto& instance : initialInstances) {
|
||||
bool shouldStop = func(instance);
|
||||
if (shouldStop) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InitialInstancesContainer::IterateOverInstancesWithZOrdering(
|
||||
|
@@ -92,7 +92,7 @@ class GD_CORE_API InitialInstancesContainer {
|
||||
* \see InitialInstanceFunctor
|
||||
*/
|
||||
void IterateOverInstances(
|
||||
const std::function< void(gd::InitialInstance &) >& func);
|
||||
const std::function< bool(gd::InitialInstance &) >& func);
|
||||
|
||||
/**
|
||||
* Get the instances on the specified layer,
|
||||
|
@@ -41,6 +41,11 @@ Object::Object(const gd::String& name_,
|
||||
}
|
||||
|
||||
void Object::Init(const gd::Object& object) {
|
||||
CopyWithoutConfiguration(object);
|
||||
configuration = object.configuration->Clone();
|
||||
}
|
||||
|
||||
void Object::CopyWithoutConfiguration(const gd::Object& object) {
|
||||
persistentUuid = object.persistentUuid;
|
||||
name = object.name;
|
||||
assetStoreId = object.assetStoreId;
|
||||
@@ -51,8 +56,6 @@ void Object::Init(const gd::Object& object) {
|
||||
for (auto& it : object.behaviors) {
|
||||
behaviors[it.first] = gd::make_unique<gd::Behavior>(*it.second);
|
||||
}
|
||||
|
||||
configuration = object.configuration->Clone();
|
||||
}
|
||||
|
||||
gd::ObjectConfiguration& Object::GetConfiguration() { return *configuration; }
|
||||
|
@@ -82,6 +82,8 @@ class GD_CORE_API Object {
|
||||
return gd::make_unique<gd::Object>(*this);
|
||||
}
|
||||
|
||||
void CopyWithoutConfiguration(const gd::Object& object);
|
||||
|
||||
/**
|
||||
* \brief Return the object configuration.
|
||||
*/
|
||||
|
@@ -920,6 +920,7 @@ void Project::UnserializeAndInsertExtensionsFrom(
|
||||
"eventsFunctionsExtension");
|
||||
|
||||
std::map<gd::String, size_t> extensionNameToElementIndex;
|
||||
std::map<gd::String, gd::SerializerElement> objectTypeToVariantsElement;
|
||||
|
||||
// First, only unserialize behaviors and objects names.
|
||||
// As event based objects can contains custom behaviors and custom objects,
|
||||
@@ -938,6 +939,16 @@ void Project::UnserializeAndInsertExtensionsFrom(
|
||||
? GetEventsFunctionsExtension(name)
|
||||
: InsertNewEventsFunctionsExtension(
|
||||
name, GetEventsFunctionsExtensionsCount());
|
||||
|
||||
// Backup the events-based object variants
|
||||
for (auto &eventsBasedObject :
|
||||
eventsFunctionsExtension.GetEventsBasedObjects().GetInternalVector()) {
|
||||
gd::SerializerElement variantsElement;
|
||||
eventsBasedObject->GetVariants().SerializeVariantsTo(variantsElement);
|
||||
objectTypeToVariantsElement[gd::PlatformExtension::GetObjectFullType(
|
||||
name, eventsBasedObject->GetName())] = variantsElement;
|
||||
}
|
||||
|
||||
eventsFunctionsExtension.UnserializeExtensionDeclarationFrom(
|
||||
*this, eventsFunctionsExtensionElement);
|
||||
}
|
||||
@@ -966,6 +977,15 @@ void Project::UnserializeAndInsertExtensionsFrom(
|
||||
partiallyLoadedExtension
|
||||
->UnserializeExtensionImplementationFrom(
|
||||
*this, eventsFunctionsExtensionElement);
|
||||
|
||||
for (auto &pair : objectTypeToVariantsElement) {
|
||||
auto &objectType = pair.first;
|
||||
auto &variantsElement = pair.second;
|
||||
|
||||
auto &eventsBasedObject = GetEventsBasedObject(objectType);
|
||||
eventsBasedObject.GetVariants().UnserializeVariantsFrom(*this,
|
||||
variantsElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
522
Core/tests/EventsBasedObjectVariantHelper.cpp
Normal file
522
Core/tests/EventsBasedObjectVariantHelper.cpp
Normal file
@@ -0,0 +1,522 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
/**
|
||||
* @file Tests covering events of GDevelop Core.
|
||||
*/
|
||||
#include "catch.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <initializer_list>
|
||||
#include <map>
|
||||
|
||||
#include "GDCore/CommonTools.h"
|
||||
#include "GDCore/IDE/EventsBasedObjectVariantHelper.h"
|
||||
|
||||
#include "DummyPlatform.h"
|
||||
#include "GDCore/Extensions/Platform.h"
|
||||
#include "GDCore/Extensions/PlatformExtension.h"
|
||||
#include "GDCore/Project/Behavior.h"
|
||||
#include "GDCore/Project/EventsFunctionsExtension.h"
|
||||
#include "GDCore/Project/Object.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Project/ProjectScopedContainers.h"
|
||||
#include "GDCore/Project/Variable.h"
|
||||
#include "catch.hpp"
|
||||
|
||||
gd::InitialInstance *
|
||||
GetFirstInstanceOf(const gd::String objectName,
|
||||
gd::InitialInstancesContainer &initialInstances) {
|
||||
gd::InitialInstance *variantInstance = nullptr;
|
||||
initialInstances.IterateOverInstances(
|
||||
[&variantInstance, &objectName](gd::InitialInstance &instance) {
|
||||
if (instance.GetObjectName() == objectName) {
|
||||
variantInstance = &instance;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
return variantInstance;
|
||||
}
|
||||
|
||||
gd::EventsBasedObject &SetupEventsBasedObject(gd::Project &project) {
|
||||
auto &eventsExtension =
|
||||
project.InsertNewEventsFunctionsExtension("MyEventsExtension", 0);
|
||||
auto &eventsBasedObject = eventsExtension.GetEventsBasedObjects().InsertNew(
|
||||
"MyEventsBasedObject", 0);
|
||||
auto &object = eventsBasedObject.GetObjects().InsertNewObject(
|
||||
project, "MyExtension::Sprite", "MyChildObject", 0);
|
||||
object.GetVariables().InsertNew("MyVariable").SetValue(123);
|
||||
object.AddNewBehavior(project, "MyExtension::MyBehavior", "MyBehavior");
|
||||
auto &instance =
|
||||
eventsBasedObject.GetInitialInstances().InsertNewInitialInstance();
|
||||
instance.SetObjectName("MyChildObject");
|
||||
instance.GetVariables().InsertNew("MyVariable").SetValue(111);
|
||||
auto &objectGroup =
|
||||
eventsBasedObject.GetObjects().GetObjectGroups().InsertNew(
|
||||
"MyObjectGroup");
|
||||
objectGroup.AddObject("MyChildObject");
|
||||
return eventsBasedObject;
|
||||
}
|
||||
|
||||
TEST_CASE("EventsBasedObjectVariantHelper", "[common]") {
|
||||
SECTION("Can add missing objects") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &eventsBasedObject = SetupEventsBasedObject(project);
|
||||
auto &variant = eventsBasedObject.GetVariants().InsertVariant(
|
||||
eventsBasedObject.GetDefaultVariant(), 0);
|
||||
|
||||
// Do the changes and launch the refactoring.
|
||||
eventsBasedObject.GetObjects().InsertNewObject(
|
||||
project, "MyExtension::Sprite", "MyChildObject2", 0);
|
||||
eventsBasedObject.GetObjects().InsertNewObject(
|
||||
project, "MyExtension::Sprite", "MyChildObject3", 0);
|
||||
|
||||
gd::EventsBasedObjectVariantHelper::ComplyVariantsToEventsBasedObject(
|
||||
project, eventsBasedObject);
|
||||
|
||||
REQUIRE(variant.GetObjects().HasObjectNamed("MyChildObject"));
|
||||
REQUIRE(variant.GetObjects().HasObjectNamed("MyChildObject2"));
|
||||
REQUIRE(variant.GetObjects().HasObjectNamed("MyChildObject3"));
|
||||
}
|
||||
|
||||
SECTION("Can remove objects") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &eventsBasedObject = SetupEventsBasedObject(project);
|
||||
|
||||
eventsBasedObject.GetObjects().InsertNewObject(
|
||||
project, "MyExtension::Sprite", "MyChildObject2", 0);
|
||||
eventsBasedObject.GetObjects().InsertNewObject(
|
||||
project, "MyExtension::Sprite", "MyChildObject3", 0);
|
||||
|
||||
auto &variant = eventsBasedObject.GetVariants().InsertVariant(
|
||||
eventsBasedObject.GetDefaultVariant(), 0);
|
||||
variant.GetInitialInstances().InsertNewInitialInstance().SetObjectName(
|
||||
"MyChildObject2");
|
||||
REQUIRE(variant.GetInitialInstances().HasInstancesOfObject(
|
||||
"MyChildObject2") == true);
|
||||
|
||||
// Do the changes and launch the refactoring.
|
||||
eventsBasedObject.GetObjects().RemoveObject("MyChildObject2");
|
||||
eventsBasedObject.GetObjects().RemoveObject("MyChildObject3");
|
||||
|
||||
gd::EventsBasedObjectVariantHelper::ComplyVariantsToEventsBasedObject(
|
||||
project, eventsBasedObject);
|
||||
|
||||
REQUIRE(variant.GetObjects().HasObjectNamed("MyChildObject"));
|
||||
REQUIRE(variant.GetObjects().HasObjectNamed("MyChildObject2") == false);
|
||||
REQUIRE(variant.GetObjects().HasObjectNamed("MyChildObject3") == false);
|
||||
REQUIRE(variant.GetInitialInstances().HasInstancesOfObject(
|
||||
"MyChildObject2") == false);
|
||||
}
|
||||
|
||||
SECTION("Can change object type") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &eventsBasedObject = SetupEventsBasedObject(project);
|
||||
auto &variant = eventsBasedObject.GetVariants().InsertVariant(
|
||||
eventsBasedObject.GetDefaultVariant(), 0);
|
||||
|
||||
// Do the changes and launch the refactoring.
|
||||
eventsBasedObject.GetObjects().RemoveObject("MyChildObject");
|
||||
eventsBasedObject.GetObjects().InsertNewObject(
|
||||
project, "MyExtension::FakeObjectWithDefaultBehavior", "MyChildObject",
|
||||
0);
|
||||
|
||||
gd::EventsBasedObjectVariantHelper::ComplyVariantsToEventsBasedObject(
|
||||
project, eventsBasedObject);
|
||||
|
||||
REQUIRE(variant.GetObjects().HasObjectNamed("MyChildObject"));
|
||||
REQUIRE(variant.GetObjects().GetObject("MyChildObject").GetType() ==
|
||||
"MyExtension::FakeObjectWithDefaultBehavior");
|
||||
REQUIRE(variant.GetInitialInstances().GetInstancesCount() == 1);
|
||||
}
|
||||
|
||||
SECTION("Can add missing object groups") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &eventsBasedObject = SetupEventsBasedObject(project);
|
||||
auto &variant = eventsBasedObject.GetVariants().InsertVariant(
|
||||
eventsBasedObject.GetDefaultVariant(), 0);
|
||||
|
||||
// Do the changes and launch the refactoring.
|
||||
eventsBasedObject.GetObjects().GetObjectGroups().InsertNew("MyObjectGroup2",
|
||||
0);
|
||||
eventsBasedObject.GetObjects()
|
||||
.GetObjectGroups()
|
||||
.InsertNew("MyObjectGroup3", 0)
|
||||
.AddObject("MyChildObject");
|
||||
|
||||
gd::EventsBasedObjectVariantHelper::ComplyVariantsToEventsBasedObject(
|
||||
project, eventsBasedObject);
|
||||
|
||||
auto &variantObjectGroups = variant.GetObjects().GetObjectGroups();
|
||||
REQUIRE(variantObjectGroups.Has("MyObjectGroup"));
|
||||
REQUIRE(variantObjectGroups.Has("MyObjectGroup2"));
|
||||
REQUIRE(variantObjectGroups.Has("MyObjectGroup3"));
|
||||
REQUIRE(
|
||||
variantObjectGroups.Get("MyObjectGroup").GetAllObjectsNames().size() ==
|
||||
1);
|
||||
REQUIRE(
|
||||
variantObjectGroups.Get("MyObjectGroup2").GetAllObjectsNames().size() ==
|
||||
0);
|
||||
REQUIRE(
|
||||
variantObjectGroups.Get("MyObjectGroup3").GetAllObjectsNames().size() ==
|
||||
1);
|
||||
}
|
||||
|
||||
SECTION("Can remove object groups") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &eventsBasedObject = SetupEventsBasedObject(project);
|
||||
|
||||
// Do the changes and launch the refactoring.
|
||||
eventsBasedObject.GetObjects().GetObjectGroups().InsertNew("MyObjectGroup2",
|
||||
0);
|
||||
eventsBasedObject.GetObjects().GetObjectGroups().InsertNew("MyObjectGroup3",
|
||||
0);
|
||||
|
||||
auto &variant = eventsBasedObject.GetVariants().InsertVariant(
|
||||
eventsBasedObject.GetDefaultVariant(), 0);
|
||||
|
||||
eventsBasedObject.GetObjects().GetObjectGroups().Remove("MyObjectGroup2");
|
||||
eventsBasedObject.GetObjects().GetObjectGroups().Remove("MyObjectGroup3");
|
||||
|
||||
gd::EventsBasedObjectVariantHelper::ComplyVariantsToEventsBasedObject(
|
||||
project, eventsBasedObject);
|
||||
|
||||
auto &variantObjectGroups = variant.GetObjects().GetObjectGroups();
|
||||
REQUIRE(variantObjectGroups.Has("MyObjectGroup"));
|
||||
REQUIRE(variantObjectGroups.Has("MyObjectGroup2") == false);
|
||||
REQUIRE(variantObjectGroups.Has("MyObjectGroup3") == false);
|
||||
}
|
||||
|
||||
SECTION("Can add objects to groups") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &eventsBasedObject = SetupEventsBasedObject(project);
|
||||
eventsBasedObject.GetObjects().InsertNewObject(
|
||||
project, "MyExtension::Sprite", "MyChildObject2", 0);
|
||||
eventsBasedObject.GetObjects().InsertNewObject(
|
||||
project, "MyExtension::Sprite", "MyChildObject3", 0);
|
||||
|
||||
auto &variant = eventsBasedObject.GetVariants().InsertVariant(
|
||||
eventsBasedObject.GetDefaultVariant(), 0);
|
||||
|
||||
// Do the changes and launch the refactoring.
|
||||
auto &objectGroup =
|
||||
eventsBasedObject.GetObjects().GetObjectGroups().Get("MyObjectGroup");
|
||||
objectGroup.AddObject("MyChildObject2");
|
||||
objectGroup.AddObject("MyChildObject3");
|
||||
|
||||
gd::EventsBasedObjectVariantHelper::ComplyVariantsToEventsBasedObject(
|
||||
project, eventsBasedObject);
|
||||
|
||||
auto &variantObjectGroups = variant.GetObjects().GetObjectGroups();
|
||||
REQUIRE(variantObjectGroups.Has("MyObjectGroup"));
|
||||
REQUIRE(
|
||||
variantObjectGroups.Get("MyObjectGroup").GetAllObjectsNames().size() ==
|
||||
3);
|
||||
}
|
||||
|
||||
SECTION("Can remove objects from groups") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &eventsBasedObject = SetupEventsBasedObject(project);
|
||||
eventsBasedObject.GetObjects().InsertNewObject(
|
||||
project, "MyExtension::Sprite", "MyChildObject2", 0);
|
||||
eventsBasedObject.GetObjects().InsertNewObject(
|
||||
project, "MyExtension::Sprite", "MyChildObject3", 0);
|
||||
auto &objectGroup =
|
||||
eventsBasedObject.GetObjects().GetObjectGroups().Get("MyObjectGroup");
|
||||
objectGroup.AddObject("MyChildObject2");
|
||||
objectGroup.AddObject("MyChildObject3");
|
||||
|
||||
auto &variant = eventsBasedObject.GetVariants().InsertVariant(
|
||||
eventsBasedObject.GetDefaultVariant(), 0);
|
||||
|
||||
// Do the changes and launch the refactoring.
|
||||
objectGroup.RemoveObject("MyChildObject2");
|
||||
objectGroup.RemoveObject("MyChildObject3");
|
||||
|
||||
gd::EventsBasedObjectVariantHelper::ComplyVariantsToEventsBasedObject(
|
||||
project, eventsBasedObject);
|
||||
|
||||
auto &variantObjectGroups = variant.GetObjects().GetObjectGroups();
|
||||
REQUIRE(variantObjectGroups.Has("MyObjectGroup"));
|
||||
REQUIRE(
|
||||
variantObjectGroups.Get("MyObjectGroup").GetAllObjectsNames().size() ==
|
||||
1);
|
||||
}
|
||||
|
||||
SECTION("Can add missing behaviors") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &eventsBasedObject = SetupEventsBasedObject(project);
|
||||
auto &variant = eventsBasedObject.GetVariants().InsertVariant(
|
||||
eventsBasedObject.GetDefaultVariant(), 0);
|
||||
|
||||
// Do the changes and launch the refactoring.
|
||||
auto &object = eventsBasedObject.GetObjects().GetObject("MyChildObject");
|
||||
object.AddNewBehavior(project, "MyExtension::MyBehavior", "MyBehavior2");
|
||||
object.AddNewBehavior(project, "MyExtension::MyBehavior", "MyBehavior3");
|
||||
|
||||
gd::EventsBasedObjectVariantHelper::ComplyVariantsToEventsBasedObject(
|
||||
project, eventsBasedObject);
|
||||
|
||||
REQUIRE(variant.GetObjects().HasObjectNamed("MyChildObject"));
|
||||
auto &variantObject = variant.GetObjects().GetObject("MyChildObject");
|
||||
REQUIRE(variantObject.HasBehaviorNamed("MyBehavior"));
|
||||
REQUIRE(variantObject.HasBehaviorNamed("MyBehavior2"));
|
||||
REQUIRE(variantObject.HasBehaviorNamed("MyBehavior3"));
|
||||
}
|
||||
|
||||
SECTION("Can remove missing behaviors") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &eventsBasedObject = SetupEventsBasedObject(project);
|
||||
|
||||
auto &object = eventsBasedObject.GetObjects().GetObject("MyChildObject");
|
||||
object.AddNewBehavior(project, "MyExtension::MyBehavior", "MyBehavior2");
|
||||
object.AddNewBehavior(project, "MyExtension::MyBehavior", "MyBehavior3");
|
||||
|
||||
auto &variant = eventsBasedObject.GetVariants().InsertVariant(
|
||||
eventsBasedObject.GetDefaultVariant(), 0);
|
||||
|
||||
// Do the changes and launch the refactoring.
|
||||
object.RemoveBehavior("MyBehavior2");
|
||||
object.RemoveBehavior("MyBehavior3");
|
||||
|
||||
gd::EventsBasedObjectVariantHelper::ComplyVariantsToEventsBasedObject(
|
||||
project, eventsBasedObject);
|
||||
|
||||
REQUIRE(variant.GetObjects().HasObjectNamed("MyChildObject"));
|
||||
auto &variantObject = variant.GetObjects().GetObject("MyChildObject");
|
||||
REQUIRE(variantObject.HasBehaviorNamed("MyBehavior"));
|
||||
REQUIRE(variantObject.HasBehaviorNamed("MyBehavior2") == false);
|
||||
REQUIRE(variantObject.HasBehaviorNamed("MyBehavior3") == false);
|
||||
}
|
||||
|
||||
SECTION("Can change behavior type") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &eventsBasedObject = SetupEventsBasedObject(project);
|
||||
|
||||
auto &variant = eventsBasedObject.GetVariants().InsertVariant(
|
||||
eventsBasedObject.GetDefaultVariant(), 0);
|
||||
|
||||
// Do the changes and launch the refactoring.
|
||||
auto &object = eventsBasedObject.GetObjects().GetObject("MyChildObject");
|
||||
object.RemoveBehavior("MyBehavior");
|
||||
object.AddNewBehavior(project, "MyExtension::MyOtherBehavior",
|
||||
"MyBehavior");
|
||||
|
||||
gd::EventsBasedObjectVariantHelper::ComplyVariantsToEventsBasedObject(
|
||||
project, eventsBasedObject);
|
||||
|
||||
REQUIRE(variant.GetObjects().HasObjectNamed("MyChildObject"));
|
||||
auto &variantObject = variant.GetObjects().GetObject("MyChildObject");
|
||||
REQUIRE(variantObject.HasBehaviorNamed("MyBehavior"));
|
||||
REQUIRE(variantObject.GetBehavior("MyBehavior").GetTypeName() ==
|
||||
"MyExtension::MyOtherBehavior");
|
||||
}
|
||||
|
||||
SECTION("Can add missing variables") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &eventsBasedObject = SetupEventsBasedObject(project);
|
||||
auto &variant = eventsBasedObject.GetVariants().InsertVariant(
|
||||
eventsBasedObject.GetDefaultVariant(), 0);
|
||||
|
||||
// Do the changes and launch the refactoring.
|
||||
auto &object = eventsBasedObject.GetObjects().GetObject("MyChildObject");
|
||||
object.GetVariables().InsertNew("MyVariable2", 1).SetValue(456);
|
||||
object.GetVariables().InsertNew("MyVariable3", 2).SetValue(789);
|
||||
|
||||
gd::EventsBasedObjectVariantHelper::ComplyVariantsToEventsBasedObject(
|
||||
project, eventsBasedObject);
|
||||
|
||||
REQUIRE(variant.GetObjects().HasObjectNamed("MyChildObject"));
|
||||
auto &variantObject = variant.GetObjects().GetObject("MyChildObject");
|
||||
REQUIRE(variantObject.GetVariables().Get("MyVariable").GetValue() == 123);
|
||||
REQUIRE(variantObject.GetVariables().Get("MyVariable2").GetValue() == 456);
|
||||
REQUIRE(variantObject.GetVariables().Get("MyVariable3").GetValue() == 789);
|
||||
{
|
||||
auto *objectInstance =
|
||||
GetFirstInstanceOf("MyChildObject", variant.GetInitialInstances());
|
||||
REQUIRE(objectInstance != nullptr);
|
||||
REQUIRE(objectInstance->GetVariables().Has("MyVariable"));
|
||||
REQUIRE(objectInstance->GetVariables().Has("MyVariable2") == false);
|
||||
REQUIRE(objectInstance->GetVariables().Has("MyVariable3") == false);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Can keep variable value") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &eventsBasedObject = SetupEventsBasedObject(project);
|
||||
auto &variant = eventsBasedObject.GetVariants().InsertVariant(
|
||||
eventsBasedObject.GetDefaultVariant(), 0);
|
||||
auto &object = eventsBasedObject.GetObjects().GetObject("MyChildObject");
|
||||
|
||||
// Do the changes and launch the refactoring.
|
||||
object.GetVariables().Get("MyVariable").SetValue(456);
|
||||
gd::EventsBasedObjectVariantHelper::ComplyVariantsToEventsBasedObject(
|
||||
project, eventsBasedObject);
|
||||
|
||||
REQUIRE(variant.GetObjects().HasObjectNamed("MyChildObject"));
|
||||
auto &variantObject = variant.GetObjects().GetObject("MyChildObject");
|
||||
REQUIRE(variantObject.GetVariables().Get("MyVariable").GetValue() == 123);
|
||||
{
|
||||
auto *objectInstance =
|
||||
GetFirstInstanceOf("MyChildObject", variant.GetInitialInstances());
|
||||
REQUIRE(objectInstance != nullptr);
|
||||
REQUIRE(objectInstance->GetVariables().Get("MyVariable").GetValue() ==
|
||||
111);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Must not propagate instance variable value changes") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &eventsBasedObject = SetupEventsBasedObject(project);
|
||||
auto &variant = eventsBasedObject.GetVariants().InsertVariant(
|
||||
eventsBasedObject.GetDefaultVariant(), 0);
|
||||
|
||||
// Do the changes and launch the refactoring.
|
||||
{
|
||||
auto *objectInstance = GetFirstInstanceOf(
|
||||
"MyChildObject", eventsBasedObject.GetInitialInstances());
|
||||
REQUIRE(objectInstance != nullptr);
|
||||
objectInstance->GetVariables().Get("MyVariable").SetValue(222);
|
||||
}
|
||||
gd::EventsBasedObjectVariantHelper::ComplyVariantsToEventsBasedObject(
|
||||
project, eventsBasedObject);
|
||||
|
||||
{
|
||||
auto *objectInstance =
|
||||
GetFirstInstanceOf("MyChildObject", variant.GetInitialInstances());
|
||||
REQUIRE(objectInstance != nullptr);
|
||||
REQUIRE(objectInstance->GetVariables().Get("MyVariable").GetValue() ==
|
||||
111);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Can move variables") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &eventsBasedObject = SetupEventsBasedObject(project);
|
||||
auto &object = eventsBasedObject.GetObjects().GetObject("MyChildObject");
|
||||
object.GetVariables().InsertNew("MyVariable2", 1).SetValue(456);
|
||||
object.GetVariables().InsertNew("MyVariable3", 2).SetValue(789);
|
||||
{
|
||||
auto *objectInstance = GetFirstInstanceOf(
|
||||
"MyChildObject", eventsBasedObject.GetInitialInstances());
|
||||
REQUIRE(objectInstance != nullptr);
|
||||
objectInstance->GetVariables().Get("MyVariable2").SetValue(222);
|
||||
objectInstance->GetVariables().Get("MyVariable3").SetValue(333);
|
||||
}
|
||||
|
||||
auto &variant = eventsBasedObject.GetVariants().InsertVariant(
|
||||
eventsBasedObject.GetDefaultVariant(), 0);
|
||||
|
||||
// Do the changes and launch the refactoring.
|
||||
object.GetVariables().Move(2, 0);
|
||||
object.GetVariables().Get("MyVariable").SetValue(111);
|
||||
object.GetVariables().Get("MyVariable2").SetValue(222);
|
||||
object.GetVariables().Get("MyVariable3").SetValue(333);
|
||||
REQUIRE(object.GetVariables().GetNameAt(0) == "MyVariable3");
|
||||
REQUIRE(object.GetVariables().GetNameAt(1) == "MyVariable");
|
||||
REQUIRE(object.GetVariables().GetNameAt(2) == "MyVariable2");
|
||||
gd::EventsBasedObjectVariantHelper::ComplyVariantsToEventsBasedObject(
|
||||
project, eventsBasedObject);
|
||||
|
||||
REQUIRE(variant.GetObjects().HasObjectNamed("MyChildObject"));
|
||||
auto &variantObject = variant.GetObjects().GetObject("MyChildObject");
|
||||
REQUIRE(variantObject.GetVariables().Get("MyVariable").GetValue() == 123);
|
||||
REQUIRE(variantObject.GetVariables().Get("MyVariable2").GetValue() == 456);
|
||||
REQUIRE(variantObject.GetVariables().Get("MyVariable3").GetValue() == 789);
|
||||
REQUIRE(variantObject.GetVariables().GetNameAt(0) == "MyVariable3");
|
||||
REQUIRE(variantObject.GetVariables().GetNameAt(1) == "MyVariable");
|
||||
REQUIRE(variantObject.GetVariables().GetNameAt(2) == "MyVariable2");
|
||||
}
|
||||
|
||||
SECTION("Can remove variables") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &eventsBasedObject = SetupEventsBasedObject(project);
|
||||
auto &variant = eventsBasedObject.GetVariants().InsertVariant(
|
||||
eventsBasedObject.GetDefaultVariant(), 0);
|
||||
auto &object = eventsBasedObject.GetObjects().GetObject("MyChildObject");
|
||||
object.GetVariables().InsertNew("MyVariable2", 1).SetValue(456);
|
||||
object.GetVariables().InsertNew("MyVariable3", 2).SetValue(789);
|
||||
{
|
||||
auto *objectInstance = GetFirstInstanceOf(
|
||||
"MyChildObject", eventsBasedObject.GetInitialInstances());
|
||||
REQUIRE(objectInstance != nullptr);
|
||||
objectInstance->GetVariables().Get("MyVariable2").SetValue(222);
|
||||
objectInstance->GetVariables().Get("MyVariable3").SetValue(333);
|
||||
}
|
||||
|
||||
// Do the changes and launch the refactoring.
|
||||
object.GetVariables().Remove("MyVariable2");
|
||||
object.GetVariables().Remove("MyVariable3");
|
||||
gd::EventsBasedObjectVariantHelper::ComplyVariantsToEventsBasedObject(
|
||||
project, eventsBasedObject);
|
||||
|
||||
REQUIRE(variant.GetObjects().HasObjectNamed("MyChildObject"));
|
||||
auto &variantObject = variant.GetObjects().GetObject("MyChildObject");
|
||||
REQUIRE(variantObject.GetVariables().Has("MyVariable"));
|
||||
REQUIRE(variantObject.GetVariables().Has("MyVariable2") == false);
|
||||
REQUIRE(variantObject.GetVariables().Has("MyVariable3") == false);
|
||||
{
|
||||
auto *objectInstance =
|
||||
GetFirstInstanceOf("MyChildObject", variant.GetInitialInstances());
|
||||
REQUIRE(objectInstance != nullptr);
|
||||
REQUIRE(objectInstance->GetVariables().Has("MyVariable"));
|
||||
REQUIRE(objectInstance->GetVariables().Has("MyVariable2") == false);
|
||||
REQUIRE(objectInstance->GetVariables().Has("MyVariable3") == false);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Can change variable type") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &eventsBasedObject = SetupEventsBasedObject(project);
|
||||
auto &variant = eventsBasedObject.GetVariants().InsertVariant(
|
||||
eventsBasedObject.GetDefaultVariant(), 0);
|
||||
auto &object = eventsBasedObject.GetObjects().GetObject("MyChildObject");
|
||||
|
||||
// Do the changes and launch the refactoring.
|
||||
object.GetVariables().Get("MyVariable").SetString("abc");
|
||||
gd::EventsBasedObjectVariantHelper::ComplyVariantsToEventsBasedObject(
|
||||
project, eventsBasedObject);
|
||||
|
||||
REQUIRE(variant.GetObjects().HasObjectNamed("MyChildObject"));
|
||||
auto &variantObject = variant.GetObjects().GetObject("MyChildObject");
|
||||
REQUIRE(variantObject.GetVariables().Get("MyVariable").GetString() ==
|
||||
"abc");
|
||||
REQUIRE(variant.GetInitialInstances().HasInstancesOfObject("MyVariable") ==
|
||||
false);
|
||||
}
|
||||
}
|
@@ -59,6 +59,8 @@ TEST_CASE("ObjectAssetSerializer", "[common]") {
|
||||
auto &configuration = object.GetConfiguration();
|
||||
auto *customObjectConfiguration =
|
||||
dynamic_cast<gd::CustomObjectConfiguration *>(&configuration);
|
||||
customObjectConfiguration
|
||||
->SetMarkedAsOverridingEventsBasedObjectChildrenConfiguration(true);
|
||||
auto *spriteConfiguration = dynamic_cast<gd::SpriteObject *>(
|
||||
&customObjectConfiguration->GetChildObjectConfiguration("MyChild"));
|
||||
REQUIRE(spriteConfiguration != nullptr);
|
||||
|
@@ -150,7 +150,7 @@ TEST_CASE("ObjectContainersList (GetTypeOfObject)", "[common]") {
|
||||
gd::Object &object1 = layout.GetObjects().InsertNewObject(
|
||||
project, "MyExtension::Sprite", "MyObject1", 0);
|
||||
gd::Object &object2 = layout.GetObjects().InsertNewObject(
|
||||
project, "FakeObjectWithDefaultBehavior", "MyObject2", 0);
|
||||
project, "MyExtension::FakeObjectWithDefaultBehavior", "MyObject2", 0);
|
||||
|
||||
auto &group = layout.GetObjects().GetObjectGroups().InsertNew("MyGroup", 0);
|
||||
group.AddObject(object1.GetName());
|
||||
|
@@ -1072,6 +1072,99 @@ TEST_CASE("WholeProjectRefactorer::ApplyRefactoringForVariablesContainer",
|
||||
REQUIRE(instance.GetVariables().Get("MyRenamedVariable").GetValue() == 456);
|
||||
}
|
||||
|
||||
SECTION("Can rename an object variable (in events-based object)") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
|
||||
auto &eventsExtension =
|
||||
project.InsertNewEventsFunctionsExtension("MyEventsExtension", 0);
|
||||
auto &eventsBasedObject = eventsExtension.GetEventsBasedObjects().InsertNew(
|
||||
"MyEventsBasedObject", 0);
|
||||
auto &object = eventsBasedObject.GetObjects().InsertNewObject(
|
||||
project, "MyExtension::Sprite", "MyChildObject", 0);
|
||||
object.GetVariables().InsertNew("MyVariable").SetValue(123);
|
||||
auto &instance =
|
||||
eventsBasedObject.GetInitialInstances().InsertNewInitialInstance();
|
||||
instance.SetObjectName("MyChildObject");
|
||||
instance.GetVariables().InsertNew("MyVariable").SetValue(456);
|
||||
|
||||
auto &variant = eventsBasedObject.GetVariants().InsertVariant(
|
||||
eventsBasedObject.GetDefaultVariant(), 0);
|
||||
gd::InitialInstance *variantInstance = nullptr;
|
||||
variant.GetInitialInstances().IterateOverInstances(
|
||||
[&variantInstance](gd::InitialInstance &instance) {
|
||||
variantInstance = &instance;
|
||||
return true;
|
||||
});
|
||||
REQUIRE(variantInstance != nullptr);
|
||||
variant.GetObjects()
|
||||
.GetObject("MyChildObject")
|
||||
.GetVariables()
|
||||
.Get("MyVariable")
|
||||
.SetValue(111);
|
||||
variantInstance->GetVariables().Get("MyVariable").SetValue(222);
|
||||
|
||||
auto &objectFunction =
|
||||
eventsBasedObject.GetEventsFunctions().InsertNewEventsFunction(
|
||||
"MyObjectEventsFunction", 0);
|
||||
gd::StandardEvent &event = dynamic_cast<gd::StandardEvent &>(
|
||||
objectFunction.GetEvents().InsertNewEvent(
|
||||
project, "BuiltinCommonInstructions::Standard"));
|
||||
|
||||
{
|
||||
gd::Instruction action;
|
||||
action.SetType("SetNumberObjectVariable");
|
||||
action.SetParametersCount(4);
|
||||
action.SetParameter(0, gd::Expression("MyChildObject"));
|
||||
action.SetParameter(1, gd::Expression("MyVariable"));
|
||||
action.SetParameter(2, gd::Expression("="));
|
||||
action.SetParameter(3, gd::Expression("MyChildObject.MyVariable"));
|
||||
event.GetActions().Insert(action);
|
||||
}
|
||||
|
||||
// Do the changes and launch the refactoring.
|
||||
object.GetVariables().ResetPersistentUuid();
|
||||
gd::SerializerElement originalSerializedVariables;
|
||||
object.GetVariables().SerializeTo(originalSerializedVariables);
|
||||
|
||||
object.GetVariables().Rename("MyVariable", "MyRenamedVariable");
|
||||
auto changeset =
|
||||
gd::WholeProjectRefactorer::ComputeChangesetForVariablesContainer(
|
||||
originalSerializedVariables, object.GetVariables());
|
||||
|
||||
REQUIRE(changeset.oldToNewVariableNames.size() == 1);
|
||||
|
||||
gd::WholeProjectRefactorer::ApplyRefactoringForObjectVariablesContainer(
|
||||
project, object.GetVariables(), eventsBasedObject.GetInitialInstances(),
|
||||
object.GetName(), changeset, originalSerializedVariables);
|
||||
gd::ObjectVariableHelper::ApplyChangesToVariants(
|
||||
eventsBasedObject, "MyChildObject", changeset);
|
||||
|
||||
REQUIRE(event.GetActions()[0].GetParameter(1).GetPlainString() ==
|
||||
"MyRenamedVariable");
|
||||
REQUIRE(event.GetActions()[0].GetParameter(3).GetPlainString() ==
|
||||
"MyChildObject.MyRenamedVariable");
|
||||
|
||||
REQUIRE(eventsBasedObject.GetObjects().HasObjectNamed("MyChildObject"));
|
||||
REQUIRE(eventsBasedObject.GetObjects()
|
||||
.GetObject("MyChildObject")
|
||||
.GetVariables()
|
||||
.Get("MyRenamedVariable")
|
||||
.GetValue() == 123);
|
||||
REQUIRE(instance.GetVariables().Get("MyRenamedVariable").GetValue() == 456);
|
||||
|
||||
REQUIRE(variant.GetObjects().HasObjectNamed("MyChildObject"));
|
||||
REQUIRE(variant.GetObjects()
|
||||
.GetObject("MyChildObject")
|
||||
.GetVariables()
|
||||
.Get("MyRenamedVariable")
|
||||
.GetValue() == 111);
|
||||
REQUIRE(
|
||||
variantInstance->GetVariables().Get("MyRenamedVariable").GetValue() ==
|
||||
222);
|
||||
}
|
||||
|
||||
SECTION("Can delete an object variable") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
|
@@ -1754,6 +1754,9 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
eventsBasedObject.GetObjects().InsertNewObject(
|
||||
project, "MyExtension::Sprite", "Object2", 0);
|
||||
|
||||
auto &variant = eventsBasedObject.GetVariants().InsertVariant(
|
||||
eventsBasedObject.GetDefaultVariant(), 0);
|
||||
|
||||
// Create the objects container for the events function
|
||||
gd::ObjectsContainer parametersObjectsContainer(
|
||||
gd::ObjectsContainer::SourceType::Function);
|
||||
@@ -1765,6 +1768,10 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
gd::WholeProjectRefactorer::ObjectOrGroupRenamedInEventsBasedObject(
|
||||
project, projectScopedContainers, eventsBasedObject, "Object1",
|
||||
"Object3", /* isObjectGroup =*/false);
|
||||
|
||||
REQUIRE(variant.GetObjects().HasObjectNamed("Object1") == false);
|
||||
REQUIRE(variant.GetObjects().HasObjectNamed("Object2") == true);
|
||||
REQUIRE(variant.GetObjects().HasObjectNamed("Object3") == true);
|
||||
REQUIRE(eventsBasedObject.GetObjects().GetObjectGroups().size() == 1);
|
||||
REQUIRE(eventsBasedObject.GetObjects().GetObjectGroups()[0].Find(
|
||||
"Object1") == false);
|
||||
@@ -1772,6 +1779,17 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
"Object2") == true);
|
||||
REQUIRE(eventsBasedObject.GetObjects().GetObjectGroups()[0].Find(
|
||||
"Object3") == true);
|
||||
|
||||
REQUIRE(variant.GetObjects().HasObjectNamed("Object1") == false);
|
||||
REQUIRE(variant.GetObjects().HasObjectNamed("Object2") == true);
|
||||
REQUIRE(variant.GetObjects().HasObjectNamed("Object3") == true);
|
||||
REQUIRE(variant.GetObjects().GetObjectGroups().size() == 1);
|
||||
REQUIRE(variant.GetObjects().GetObjectGroups()[0].Find("Object1") ==
|
||||
false);
|
||||
REQUIRE(variant.GetObjects().GetObjectGroups()[0].Find("Object2") ==
|
||||
true);
|
||||
REQUIRE(variant.GetObjects().GetObjectGroups()[0].Find("Object3") ==
|
||||
true);
|
||||
}
|
||||
|
||||
SECTION("Initial instances") {
|
||||
@@ -1796,6 +1814,9 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
eventsBasedObject.GetInitialInstances().InsertInitialInstance(instance1);
|
||||
eventsBasedObject.GetInitialInstances().InsertInitialInstance(instance2);
|
||||
|
||||
auto &variant = eventsBasedObject.GetVariants().InsertVariant(
|
||||
eventsBasedObject.GetDefaultVariant(), 0);
|
||||
|
||||
// Create the objects container for the events function
|
||||
gd::ObjectsContainer parametersObjectsContainer(
|
||||
gd::ObjectsContainer::SourceType::Function);
|
||||
@@ -1807,10 +1828,16 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
gd::WholeProjectRefactorer::ObjectOrGroupRenamedInEventsBasedObject(
|
||||
project, projectScopedContainers, eventsBasedObject, "Object1",
|
||||
"Object3", /* isObjectGroup =*/false);
|
||||
|
||||
REQUIRE(eventsBasedObject.GetInitialInstances().HasInstancesOfObject(
|
||||
"Object1") == false);
|
||||
REQUIRE(eventsBasedObject.GetInitialInstances().HasInstancesOfObject(
|
||||
"Object3") == true);
|
||||
|
||||
REQUIRE(variant.GetInitialInstances().HasInstancesOfObject("Object1") ==
|
||||
false);
|
||||
REQUIRE(variant.GetInitialInstances().HasInstancesOfObject("Object3") ==
|
||||
true);
|
||||
}
|
||||
|
||||
SECTION("Events") {
|
||||
|
@@ -507,14 +507,16 @@ module.exports = {
|
||||
instance,
|
||||
associatedObjectConfiguration,
|
||||
pixiContainer,
|
||||
pixiResourcesLoader
|
||||
pixiResourcesLoader,
|
||||
propertyOverridings
|
||||
) {
|
||||
super(
|
||||
project,
|
||||
instance,
|
||||
associatedObjectConfiguration,
|
||||
pixiContainer,
|
||||
pixiResourcesLoader
|
||||
pixiResourcesLoader,
|
||||
propertyOverridings
|
||||
);
|
||||
|
||||
const bbTextStyles = {
|
||||
@@ -553,7 +555,9 @@ module.exports = {
|
||||
gd.ObjectJsImplementation
|
||||
);
|
||||
|
||||
const rawText = object.content.text;
|
||||
const rawText = this._propertyOverridings.has('Text')
|
||||
? this._propertyOverridings.get('Text')
|
||||
: object.content.text;
|
||||
if (rawText !== this._pixiObject.text) {
|
||||
this._pixiObject.text = rawText;
|
||||
}
|
||||
|
@@ -630,14 +630,16 @@ module.exports = {
|
||||
instance,
|
||||
associatedObjectConfiguration,
|
||||
pixiContainer,
|
||||
pixiResourcesLoader
|
||||
pixiResourcesLoader,
|
||||
propertyOverridings
|
||||
) {
|
||||
super(
|
||||
project,
|
||||
instance,
|
||||
associatedObjectConfiguration,
|
||||
pixiContainer,
|
||||
pixiResourcesLoader
|
||||
pixiResourcesLoader,
|
||||
propertyOverridings
|
||||
);
|
||||
|
||||
// We'll track changes of the font to trigger the loading of the new font.
|
||||
@@ -663,8 +665,9 @@ module.exports = {
|
||||
|
||||
// Update the rendered text properties (note: Pixi is only
|
||||
// applying changes if there were changed).
|
||||
const rawText = object.content.text;
|
||||
this._pixiObject.text = rawText;
|
||||
this._pixiObject.text = this._propertyOverridings.has('Text')
|
||||
? this._propertyOverridings.get('Text')
|
||||
: object.content.text;
|
||||
|
||||
const align = object.content.align;
|
||||
this._pixiObject.align = align;
|
||||
|
4
Extensions/JsExtensionTypes.d.ts
vendored
4
Extensions/JsExtensionTypes.d.ts
vendored
@@ -15,6 +15,7 @@ class RenderedInstance {
|
||||
_pixiContainer: PIXI.Container;
|
||||
_pixiResourcesLoader: Class<PixiResourcesLoader>;
|
||||
_pixiObject: PIXI.DisplayObject | null;
|
||||
_propertyOverridings: Map<string, string>;
|
||||
wasUsed: boolean;
|
||||
|
||||
/** Set to true when onRemovedFromScene is called. Allows to cancel promises/asynchronous operations (notably: waiting for a resource load). */
|
||||
@@ -25,7 +26,8 @@ class RenderedInstance {
|
||||
instance: gdInitialInstance,
|
||||
associatedObjectConfiguration: gdObjectConfiguration,
|
||||
pixiContainer: PIXI.Container,
|
||||
pixiResourcesLoader: Class<PixiResourcesLoader>
|
||||
pixiResourcesLoader: Class<PixiResourcesLoader>,
|
||||
propertyOverridings: Map<string, string> = new Map<string, string>()
|
||||
);
|
||||
|
||||
/**
|
||||
|
@@ -917,7 +917,7 @@ module.exports = {
|
||||
'number',
|
||||
'LinearVelocityX',
|
||||
_('Linear velocity X'),
|
||||
_('the object linear velocity on X.'),
|
||||
_('the object linear velocity on X'),
|
||||
_('the linear velocity on X'),
|
||||
_('Velocity'),
|
||||
'JsPlatform/Extensions/physics3d.svg'
|
||||
@@ -938,7 +938,7 @@ module.exports = {
|
||||
'number',
|
||||
'LinearVelocityY',
|
||||
_('Linear velocity Y'),
|
||||
_('the object linear velocity on Y.'),
|
||||
_('the object linear velocity on Y'),
|
||||
_('the linear velocity on Y'),
|
||||
_('Velocity'),
|
||||
'JsPlatform/Extensions/physics3d.svg'
|
||||
@@ -959,7 +959,7 @@ module.exports = {
|
||||
'number',
|
||||
'LinearVelocityZ',
|
||||
_('Linear velocity Z'),
|
||||
_('the object linear velocity on Z.'),
|
||||
_('the object linear velocity on Z'),
|
||||
_('the linear velocity on Z'),
|
||||
_('Velocity'),
|
||||
'JsPlatform/Extensions/physics3d.svg'
|
||||
@@ -980,7 +980,7 @@ module.exports = {
|
||||
'number',
|
||||
'LinearVelocityLength',
|
||||
_('Linear velocity'),
|
||||
_('the object linear velocity length.'),
|
||||
_('the object linear velocity length'),
|
||||
_('the linear velocity length'),
|
||||
_('Velocity'),
|
||||
'JsPlatform/Extensions/physics3d.svg'
|
||||
@@ -1000,7 +1000,7 @@ module.exports = {
|
||||
'number',
|
||||
'AngularVelocityX',
|
||||
_('Angular velocity X'),
|
||||
_('the object angular velocity around X.'),
|
||||
_('the object angular velocity around X'),
|
||||
_('the angular velocity around X'),
|
||||
_('Velocity'),
|
||||
'JsPlatform/Extensions/physics3d.svg'
|
||||
@@ -1021,7 +1021,7 @@ module.exports = {
|
||||
'number',
|
||||
'AngularVelocityY',
|
||||
_('Angular velocity Y'),
|
||||
_('the object angular velocity around Y.'),
|
||||
_('the object angular velocity around Y'),
|
||||
_('the angular velocity around Y'),
|
||||
_('Velocity'),
|
||||
'JsPlatform/Extensions/physics3d.svg'
|
||||
@@ -1042,7 +1042,7 @@ module.exports = {
|
||||
'number',
|
||||
'AngularVelocityZ',
|
||||
_('Angular velocity Z'),
|
||||
_('the object angular velocity around Z.'),
|
||||
_('the object angular velocity around Z'),
|
||||
_('the angular velocity around Z'),
|
||||
_('Velocity'),
|
||||
'JsPlatform/Extensions/physics3d.svg'
|
||||
@@ -1569,7 +1569,7 @@ module.exports = {
|
||||
.getOrCreate('physics3D')
|
||||
.setValue(behaviorContent.getChild('physics3D').getStringValue())
|
||||
.setType('Behavior')
|
||||
.setLabel('3D capability')
|
||||
.setLabel('3D physics')
|
||||
.setQuickCustomizationVisibility(gd.QuickCustomization.Hidden)
|
||||
.addExtraInfo('Physics3D::Physics3DBehavior');
|
||||
|
||||
@@ -2144,7 +2144,7 @@ module.exports = {
|
||||
'number',
|
||||
'ForwardAcceleration',
|
||||
_('Forward acceleration'),
|
||||
_('the forward acceleration of an object.'),
|
||||
_('the forward acceleration of an object'),
|
||||
_('the forward acceleration'),
|
||||
_('Character configuration'),
|
||||
'JsPlatform/Extensions/physics_character3d.svg'
|
||||
@@ -2166,7 +2166,7 @@ module.exports = {
|
||||
'number',
|
||||
'ForwardDeceleration',
|
||||
_('Forward deceleration'),
|
||||
_('the forward deceleration of an object.'),
|
||||
_('the forward deceleration of an object'),
|
||||
_('the forward deceleration'),
|
||||
_('Character configuration'),
|
||||
'JsPlatform/Extensions/physics_character3d.svg'
|
||||
@@ -2188,7 +2188,7 @@ module.exports = {
|
||||
'number',
|
||||
'ForwardSpeedMax',
|
||||
_('Forward max speed'),
|
||||
_('the forward max speed of the object.'),
|
||||
_('the forward max speed of the object'),
|
||||
_('the forward max speed'),
|
||||
_('Character configuration'),
|
||||
'JsPlatform/Extensions/physics_character3d.svg'
|
||||
@@ -2232,7 +2232,7 @@ module.exports = {
|
||||
'number',
|
||||
'SidewaysAcceleration',
|
||||
_('Sideways acceleration'),
|
||||
_('the sideways acceleration of an object.'),
|
||||
_('the sideways acceleration of an object'),
|
||||
_('the sideways acceleration'),
|
||||
_('Character configuration'),
|
||||
'JsPlatform/Extensions/physics_character3d.svg'
|
||||
@@ -2254,7 +2254,7 @@ module.exports = {
|
||||
'number',
|
||||
'SidewaysDeceleration',
|
||||
_('Sideways deceleration'),
|
||||
_('the sideways deceleration of an object.'),
|
||||
_('the sideways deceleration of an object'),
|
||||
_('the sideways deceleration'),
|
||||
_('Character configuration'),
|
||||
'JsPlatform/Extensions/physics_character3d.svg'
|
||||
@@ -2276,7 +2276,7 @@ module.exports = {
|
||||
'number',
|
||||
'SidewaysSpeedMax',
|
||||
_('Sideways max speed'),
|
||||
_('the sideways max speed of the object.'),
|
||||
_('the sideways max speed of the object'),
|
||||
_('the sideways max speed'),
|
||||
_('Character configuration'),
|
||||
'JsPlatform/Extensions/physics_character3d.svg'
|
||||
@@ -2345,7 +2345,7 @@ module.exports = {
|
||||
'number',
|
||||
'JumpSpeed',
|
||||
_('Jump speed'),
|
||||
_('the jump speed of an object. Its value is always positive.'),
|
||||
_('the jump speed of an object. Its value is always positive'),
|
||||
_('the jump speed'),
|
||||
_('Character configuration'),
|
||||
'JsPlatform/Extensions/physics_character3d.svg'
|
||||
@@ -2389,7 +2389,7 @@ module.exports = {
|
||||
'number',
|
||||
'Gravity',
|
||||
_('Gravity'),
|
||||
_('the gravity applied on an object.'),
|
||||
_('the gravity applied on an object'),
|
||||
_('the gravity'),
|
||||
_('Character configuration'),
|
||||
'JsPlatform/Extensions/physics_character3d.svg'
|
||||
@@ -2411,7 +2411,7 @@ module.exports = {
|
||||
'number',
|
||||
'FallingSpeedMax',
|
||||
_('Maximum falling speed'),
|
||||
_('the maximum falling speed of an object.'),
|
||||
_('the maximum falling speed of an object'),
|
||||
_('the maximum falling speed'),
|
||||
_('Character configuration'),
|
||||
'JsPlatform/Extensions/physics_character3d.svg'
|
||||
|
@@ -283,11 +283,11 @@ namespace gdjs {
|
||||
gdjs.registerRuntimeSceneUnloadedCallback(function (runtimeScene) {
|
||||
const physics3DSharedData = runtimeScene.physics3DSharedData;
|
||||
if (physics3DSharedData) {
|
||||
Jolt.destroy(physics3DSharedData.jolt);
|
||||
Jolt.destroy(physics3DSharedData.contactListener);
|
||||
Jolt.destroy(physics3DSharedData._tempVec3);
|
||||
Jolt.destroy(physics3DSharedData._tempRVec3);
|
||||
Jolt.destroy(physics3DSharedData._tempQuat);
|
||||
Jolt.destroy(physics3DSharedData.jolt);
|
||||
runtimeScene.physics3DSharedData = null;
|
||||
}
|
||||
});
|
||||
@@ -302,9 +302,9 @@ namespace gdjs {
|
||||
fixedRotation: boolean;
|
||||
private shape: string;
|
||||
private shapeOrientation: string;
|
||||
private shapeDimensionA: any;
|
||||
private shapeDimensionB: any;
|
||||
private shapeDimensionC: any;
|
||||
private shapeDimensionA: float;
|
||||
private shapeDimensionB: float;
|
||||
private shapeDimensionC: float;
|
||||
private density: float;
|
||||
friction: float;
|
||||
restitution: float;
|
||||
@@ -387,7 +387,8 @@ namespace gdjs {
|
||||
this.bullet = behaviorData.bullet;
|
||||
this.fixedRotation = behaviorData.fixedRotation;
|
||||
this.shape = behaviorData.shape;
|
||||
this.shapeOrientation = behaviorData.shapeOrientation;
|
||||
this.shapeOrientation =
|
||||
behaviorData.shape === 'Box' ? 'Z' : behaviorData.shapeOrientation;
|
||||
this.shapeDimensionA = behaviorData.shapeDimensionA;
|
||||
this.shapeDimensionB = behaviorData.shapeDimensionB;
|
||||
this.shapeDimensionC = behaviorData.shapeDimensionC;
|
||||
@@ -424,7 +425,7 @@ namespace gdjs {
|
||||
return tempQuat;
|
||||
}
|
||||
|
||||
updateFromBehaviorData(oldBehaviorData, newBehaviorData): boolean {
|
||||
override updateFromBehaviorData(oldBehaviorData, newBehaviorData): boolean {
|
||||
if (oldBehaviorData.bullet !== newBehaviorData.bullet) {
|
||||
this.setBullet(newBehaviorData.bullet);
|
||||
}
|
||||
@@ -477,7 +478,7 @@ namespace gdjs {
|
||||
return true;
|
||||
}
|
||||
|
||||
getNetworkSyncData(): Physics3DNetworkSyncData {
|
||||
override getNetworkSyncData(): Physics3DNetworkSyncData {
|
||||
let bodyProps;
|
||||
if (this._body) {
|
||||
const position = this._body.GetPosition();
|
||||
@@ -528,7 +529,9 @@ namespace gdjs {
|
||||
};
|
||||
}
|
||||
|
||||
updateFromNetworkSyncData(networkSyncData: Physics3DNetworkSyncData) {
|
||||
override updateFromNetworkSyncData(
|
||||
networkSyncData: Physics3DNetworkSyncData
|
||||
) {
|
||||
super.updateFromNetworkSyncData(networkSyncData);
|
||||
|
||||
const behaviorSpecificProps = networkSyncData.props;
|
||||
@@ -608,7 +611,7 @@ namespace gdjs {
|
||||
}
|
||||
}
|
||||
|
||||
onDeActivate() {
|
||||
override onDeActivate() {
|
||||
this._sharedData.removeFromBehaviorsList(this);
|
||||
this.bodyUpdater.destroyBody();
|
||||
this._contactsEndedThisFrame.length = 0;
|
||||
@@ -616,7 +619,7 @@ namespace gdjs {
|
||||
this._currentContacts.length = 0;
|
||||
}
|
||||
|
||||
onActivate() {
|
||||
override onActivate() {
|
||||
this._sharedData.addToBehaviorsList(this);
|
||||
|
||||
this._contactsEndedThisFrame.length = 0;
|
||||
@@ -625,7 +628,7 @@ namespace gdjs {
|
||||
this.updateBodyFromObject();
|
||||
}
|
||||
|
||||
onDestroy() {
|
||||
override onDestroy() {
|
||||
this._destroyedDuringFrameLogic = true;
|
||||
this.onDeActivate();
|
||||
}
|
||||
@@ -728,15 +731,13 @@ namespace gdjs {
|
||||
this._shapeHalfDepth = radius;
|
||||
}
|
||||
shapeSettings.mDensity = this.density;
|
||||
const rotatedShape = new Jolt.RotatedTranslatedShapeSettings(
|
||||
const rotatedShapeSettings = new Jolt.RotatedTranslatedShapeSettings(
|
||||
this.getVec3(0, 0, 0),
|
||||
quat,
|
||||
shapeSettings
|
||||
)
|
||||
.Create()
|
||||
.Get();
|
||||
|
||||
Jolt.destroy(shapeSettings);
|
||||
);
|
||||
const rotatedShape = rotatedShapeSettings.Create().Get();
|
||||
Jolt.destroy(rotatedShapeSettings);
|
||||
return rotatedShape;
|
||||
}
|
||||
|
||||
@@ -822,7 +823,7 @@ namespace gdjs {
|
||||
: this.masks;
|
||||
}
|
||||
|
||||
doStepPreEvents(instanceContainer: gdjs.RuntimeInstanceContainer) {
|
||||
override doStepPreEvents(instanceContainer: gdjs.RuntimeInstanceContainer) {
|
||||
// Step the world if not done this frame yet.
|
||||
// Don't step at the first frame to allow events to handle overlapping objects.
|
||||
if (
|
||||
@@ -836,7 +837,9 @@ namespace gdjs {
|
||||
}
|
||||
}
|
||||
|
||||
doStepPostEvents(instanceContainer: gdjs.RuntimeInstanceContainer) {
|
||||
override doStepPostEvents(
|
||||
instanceContainer: gdjs.RuntimeInstanceContainer
|
||||
) {
|
||||
// Reset world step to update next frame
|
||||
this._sharedData.stepped = false;
|
||||
}
|
||||
@@ -1765,7 +1768,9 @@ namespace gdjs {
|
||||
destroyBody(): void;
|
||||
}
|
||||
|
||||
export class DefaultBodyUpdater {
|
||||
export class DefaultBodyUpdater
|
||||
implements gdjs.Physics3DRuntimeBehavior.BodyUpdater
|
||||
{
|
||||
behavior: gdjs.Physics3DRuntimeBehavior;
|
||||
|
||||
constructor(behavior: gdjs.Physics3DRuntimeBehavior) {
|
||||
|
@@ -1,11 +1,5 @@
|
||||
/// <reference path="./jolt-physics.d.ts" />
|
||||
|
||||
namespace Jolt {
|
||||
export interface Body {
|
||||
gdjsAssociatedCharacterBehavior: gdjs.PhysicsCharacter3DRuntimeBehavior | null;
|
||||
}
|
||||
}
|
||||
|
||||
namespace gdjs {
|
||||
interface PhysicsCharacter3DNetworkSyncDataType {
|
||||
fwa: float;
|
||||
@@ -215,7 +209,7 @@ namespace gdjs {
|
||||
return this._physics3D;
|
||||
}
|
||||
|
||||
updateFromBehaviorData(oldBehaviorData, newBehaviorData): boolean {
|
||||
override updateFromBehaviorData(oldBehaviorData, newBehaviorData): boolean {
|
||||
if (oldBehaviorData.gravity !== newBehaviorData.gravity) {
|
||||
this.setGravity(newBehaviorData.gravity);
|
||||
}
|
||||
@@ -274,7 +268,7 @@ namespace gdjs {
|
||||
return true;
|
||||
}
|
||||
|
||||
getNetworkSyncData(): PhysicsCharacter3DNetworkSyncData {
|
||||
override getNetworkSyncData(): PhysicsCharacter3DNetworkSyncData {
|
||||
// This method is called, so we are synchronizing this object.
|
||||
// Let's clear the inputs between frames as we control it.
|
||||
this._dontClearInputsBetweenFrames = false;
|
||||
@@ -302,7 +296,7 @@ namespace gdjs {
|
||||
};
|
||||
}
|
||||
|
||||
updateFromNetworkSyncData(
|
||||
override updateFromNetworkSyncData(
|
||||
networkSyncData: PhysicsCharacter3DNetworkSyncData
|
||||
) {
|
||||
super.updateFromNetworkSyncData(networkSyncData);
|
||||
@@ -395,13 +389,13 @@ namespace gdjs {
|
||||
this.owner3D.setAngle(gdjs.toDegrees(euler.z));
|
||||
}
|
||||
|
||||
onDeActivate() {
|
||||
override onDeActivate() {
|
||||
this.collisionChecker.clearContacts();
|
||||
}
|
||||
|
||||
onActivate() {}
|
||||
override onActivate() {}
|
||||
|
||||
onDestroy() {
|
||||
override onDestroy() {
|
||||
this._destroyedDuringFrameLogic = true;
|
||||
this.onDeActivate();
|
||||
this._destroyCharacter();
|
||||
@@ -420,6 +414,7 @@ namespace gdjs {
|
||||
if (this.character) {
|
||||
if (this._canBePushed) {
|
||||
this.charactersManager.removeCharacter(this.character);
|
||||
Jolt.destroy(this.character.GetListener());
|
||||
}
|
||||
// The body is destroyed with the character.
|
||||
Jolt.destroy(this.character);
|
||||
@@ -443,7 +438,14 @@ namespace gdjs {
|
||||
}
|
||||
}
|
||||
|
||||
doStepPreEvents(instanceContainer: gdjs.RuntimeInstanceContainer) {
|
||||
override doStepPreEvents(instanceContainer: gdjs.RuntimeInstanceContainer) {
|
||||
// Trigger createAndAddBody()
|
||||
this.getPhysics3D();
|
||||
}
|
||||
|
||||
override doStepPostEvents(
|
||||
instanceContainer: gdjs.RuntimeInstanceContainer
|
||||
) {
|
||||
// Trigger createAndAddBody()
|
||||
this.getPhysics3D();
|
||||
}
|
||||
@@ -625,7 +627,7 @@ namespace gdjs {
|
||||
this._wasRightKeyPressed = this._hasPressedRightKey;
|
||||
this._wasLeftKeyPressed = this._hasPressedLeftKey;
|
||||
this._wasJumpKeyPressed = this._hasPressedJumpKey;
|
||||
this._wasStickUsed = this._hasPressedJumpKey;
|
||||
this._wasStickUsed = this._hasUsedStick;
|
||||
|
||||
if (!this._dontClearInputsBetweenFrames) {
|
||||
this._hasPressedForwardKey = false;
|
||||
@@ -846,8 +848,6 @@ namespace gdjs {
|
||||
return shouldFollow;
|
||||
}
|
||||
|
||||
doStepPostEvents(instanceContainer: gdjs.RuntimeInstanceContainer) {}
|
||||
|
||||
onObjectHotReloaded() {}
|
||||
|
||||
/**
|
||||
@@ -1486,7 +1486,9 @@ namespace gdjs {
|
||||
).destroy();
|
||||
});
|
||||
|
||||
export class CharacterBodyUpdater {
|
||||
export class CharacterBodyUpdater
|
||||
implements gdjs.Physics3DRuntimeBehavior.BodyUpdater
|
||||
{
|
||||
characterBehavior: gdjs.PhysicsCharacter3DRuntimeBehavior;
|
||||
|
||||
constructor(characterBehavior: gdjs.PhysicsCharacter3DRuntimeBehavior) {
|
||||
@@ -1546,9 +1548,20 @@ namespace gdjs {
|
||||
behavior.getPhysicsRotation(_sharedData.getQuat(0, 0, 0, 1)),
|
||||
_sharedData.physicsSystem
|
||||
);
|
||||
Jolt.destroy(settings);
|
||||
const body = _sharedData.physicsSystem
|
||||
.GetBodyLockInterface()
|
||||
.TryGetBody(character.GetInnerBodyID());
|
||||
if (this.characterBehavior.character) {
|
||||
if (this.characterBehavior._canBePushed) {
|
||||
this.characterBehavior.charactersManager.removeCharacter(
|
||||
this.characterBehavior.character
|
||||
);
|
||||
// Character.mListener is a plain pointer, it's not destroyed with the character.
|
||||
Jolt.destroy(this.characterBehavior.character.GetListener());
|
||||
}
|
||||
Jolt.destroy(this.characterBehavior.character);
|
||||
}
|
||||
this.characterBehavior.character = character;
|
||||
|
||||
if (this.characterBehavior._canBePushed) {
|
||||
|
@@ -12,6 +12,7 @@ namespace gdjs {
|
||||
|
||||
export type CustomObjectConfiguration = ObjectConfiguration & {
|
||||
animatable?: SpriteAnimationData[];
|
||||
variant: string;
|
||||
childrenContent: { [objectName: string]: ObjectConfiguration & any };
|
||||
};
|
||||
|
||||
@@ -92,34 +93,50 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
private _initializeFromObjectData(
|
||||
objectData: ObjectData & CustomObjectConfiguration
|
||||
customObjectData: ObjectData & CustomObjectConfiguration
|
||||
) {
|
||||
const eventsBasedObjectData = this._runtimeScene
|
||||
.getGame()
|
||||
.getEventsBasedObjectData(objectData.type);
|
||||
.getEventsBasedObjectData(customObjectData.type);
|
||||
if (!eventsBasedObjectData) {
|
||||
logger.error(
|
||||
`A CustomRuntimeObject was initialized (or re-initialized) from object data referring to an non existing events based object data with type "${objectData.type}".`
|
||||
`A CustomRuntimeObject was initialized (or re-initialized) from object data referring to an non existing events based object data with type "${customObjectData.type}".`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let usedVariantData: EventsBasedObjectVariantData = eventsBasedObjectData;
|
||||
if (customObjectData.variant) {
|
||||
for (
|
||||
let variantIndex = 0;
|
||||
variantIndex < eventsBasedObjectData.variants.length;
|
||||
variantIndex++
|
||||
) {
|
||||
const variantData = eventsBasedObjectData.variants[variantIndex];
|
||||
if (variantData.name === customObjectData.variant) {
|
||||
usedVariantData = variantData;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._isInnerAreaFollowingParentSize =
|
||||
eventsBasedObjectData.isInnerAreaFollowingParentSize;
|
||||
if (eventsBasedObjectData.instances.length > 0) {
|
||||
if (usedVariantData.instances.length > 0) {
|
||||
if (!this._innerArea) {
|
||||
this._innerArea = {
|
||||
min: [0, 0, 0],
|
||||
max: [0, 0, 0],
|
||||
};
|
||||
}
|
||||
this._innerArea.min[0] = eventsBasedObjectData.areaMinX;
|
||||
this._innerArea.min[1] = eventsBasedObjectData.areaMinY;
|
||||
this._innerArea.min[2] = eventsBasedObjectData.areaMinZ;
|
||||
this._innerArea.max[0] = eventsBasedObjectData.areaMaxX;
|
||||
this._innerArea.max[1] = eventsBasedObjectData.areaMaxY;
|
||||
this._innerArea.max[2] = eventsBasedObjectData.areaMaxZ;
|
||||
this._innerArea.min[0] = usedVariantData.areaMinX;
|
||||
this._innerArea.min[1] = usedVariantData.areaMinY;
|
||||
this._innerArea.min[2] = usedVariantData.areaMinZ;
|
||||
this._innerArea.max[0] = usedVariantData.areaMaxX;
|
||||
this._innerArea.max[1] = usedVariantData.areaMaxY;
|
||||
this._innerArea.max[2] = usedVariantData.areaMaxZ;
|
||||
}
|
||||
this._instanceContainer.loadFrom(objectData, eventsBasedObjectData);
|
||||
this._instanceContainer.loadFrom(customObjectData, usedVariantData);
|
||||
}
|
||||
|
||||
protected abstract _createRender():
|
||||
|
@@ -65,21 +65,21 @@ namespace gdjs {
|
||||
*/
|
||||
loadFrom(
|
||||
customObjectData: ObjectData & CustomObjectConfiguration,
|
||||
eventsBasedObjectData: EventsBasedObjectData
|
||||
eventsBasedObjectVariantData: EventsBasedObjectVariantData
|
||||
) {
|
||||
if (this._isLoaded) {
|
||||
this.onDestroyFromScene(this._parent);
|
||||
}
|
||||
|
||||
this._setOriginalInnerArea(eventsBasedObjectData);
|
||||
this._setOriginalInnerArea(eventsBasedObjectVariantData);
|
||||
|
||||
// Registering objects
|
||||
for (
|
||||
let i = 0, len = eventsBasedObjectData.objects.length;
|
||||
let i = 0, len = eventsBasedObjectVariantData.objects.length;
|
||||
i < len;
|
||||
++i
|
||||
) {
|
||||
const childObjectData = eventsBasedObjectData.objects[i];
|
||||
const childObjectData = eventsBasedObjectVariantData.objects[i];
|
||||
if (customObjectData.childrenContent) {
|
||||
this.registerObject({
|
||||
...childObjectData,
|
||||
@@ -92,14 +92,14 @@ namespace gdjs {
|
||||
}
|
||||
}
|
||||
|
||||
if (eventsBasedObjectData.layers.length > 0) {
|
||||
if (eventsBasedObjectVariantData.layers.length > 0) {
|
||||
// Load layers
|
||||
for (
|
||||
let i = 0, len = eventsBasedObjectData.layers.length;
|
||||
let i = 0, len = eventsBasedObjectVariantData.layers.length;
|
||||
i < len;
|
||||
++i
|
||||
) {
|
||||
this.addLayer(eventsBasedObjectData.layers[i]);
|
||||
this.addLayer(eventsBasedObjectVariantData.layers[i]);
|
||||
}
|
||||
} else {
|
||||
// Add a default layer
|
||||
@@ -128,7 +128,7 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
this.createObjectsFrom(
|
||||
eventsBasedObjectData.instances,
|
||||
eventsBasedObjectVariantData.instances,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
@@ -147,7 +147,7 @@ namespace gdjs {
|
||||
* `_initialInnerArea` is shared by every instance to save memory.
|
||||
*/
|
||||
private _setOriginalInnerArea(
|
||||
eventsBasedObjectData: EventsBasedObjectData
|
||||
eventsBasedObjectData: EventsBasedObjectVariantData
|
||||
) {
|
||||
if (eventsBasedObjectData.instances.length > 0) {
|
||||
if (!eventsBasedObjectData._initialInnerArea) {
|
||||
|
12
GDJS/Runtime/types/project-data.d.ts
vendored
12
GDJS/Runtime/types/project-data.d.ts
vendored
@@ -206,9 +206,16 @@ declare interface SceneAndExtensionsData {
|
||||
usedExtensionsWithVariablesData: EventsFunctionsExtensionData[];
|
||||
}
|
||||
|
||||
declare interface EventsBasedObjectData extends InstanceContainerData {
|
||||
declare interface EventsBasedObjectData
|
||||
extends EventsBasedObjectVariantData,
|
||||
InstanceContainerData {
|
||||
name: string;
|
||||
isInnerAreaFollowingParentSize: boolean;
|
||||
variants: Array<EventsBasedObjectVariantData>;
|
||||
}
|
||||
|
||||
declare interface EventsBasedObjectVariantData extends InstanceContainerData {
|
||||
name: string;
|
||||
// The flat representation of defaultSize.
|
||||
areaMinX: float;
|
||||
areaMinY: float;
|
||||
@@ -225,6 +232,9 @@ declare interface EventsBasedObjectData extends InstanceContainerData {
|
||||
min: [float, float, float];
|
||||
max: [float, float, float];
|
||||
} | null;
|
||||
instances: InstanceData[];
|
||||
objects: ObjectData[];
|
||||
layers: LayerData[];
|
||||
}
|
||||
|
||||
declare interface BehaviorSharedData {
|
||||
|
@@ -15,6 +15,7 @@ describe('gdjs.CustomRuntimeObject', function () {
|
||||
const customObject = new gdjs.CustomRuntimeObject2D(instanceContainer, {
|
||||
name: 'MyCustomObject',
|
||||
type: 'MyExtension::MyEventsBasedObject',
|
||||
variant: '',
|
||||
variables: [],
|
||||
behaviors: [],
|
||||
effects: [],
|
||||
|
@@ -93,6 +93,7 @@ describe('gdjs.HotReloader._hotReloadRuntimeGame', () => {
|
||||
/** @type {ObjectData & gdjs.CustomObjectConfiguration} */
|
||||
const defaultCustomObject = {
|
||||
type: 'MyExtension::MyCustomObject',
|
||||
variant: '',
|
||||
name: 'MyCustomObject',
|
||||
behaviors: [],
|
||||
variables: [],
|
||||
@@ -214,6 +215,7 @@ describe('gdjs.HotReloader._hotReloadRuntimeGame', () => {
|
||||
areaMaxZ: 0,
|
||||
_initialInnerArea: null,
|
||||
isInnerAreaFollowingParentSize: false,
|
||||
variants: [],
|
||||
};
|
||||
};
|
||||
|
||||
|
@@ -372,6 +372,16 @@ interface ObjectVariableHelper {
|
||||
[Ref] ObjectsContainer globalObjectsContainer,
|
||||
[Ref] ObjectsContainer objectsContainer,
|
||||
[Const, Ref] ObjectGroup objectGroup);
|
||||
void STATIC_ApplyChangesToVariants(
|
||||
[Ref] EventsBasedObject eventsBasedObject,
|
||||
[Const] DOMString objectName,
|
||||
[Const, Ref] VariablesChangeset changeset);
|
||||
};
|
||||
|
||||
interface EventsBasedObjectVariantHelper {
|
||||
void STATIC_ComplyVariantsToEventsBasedObject(
|
||||
[Ref, Const] Project project,
|
||||
[Ref] EventsBasedObject eventsBasedObject);
|
||||
};
|
||||
|
||||
interface ObjectGroupsContainer {
|
||||
@@ -923,6 +933,8 @@ enum CustomObjectConfiguration_EdgeAnchor {
|
||||
interface CustomObjectConfiguration {
|
||||
[Value] UniquePtrObjectConfiguration Clone();
|
||||
|
||||
[Const, Ref] DOMString GetVariantName();
|
||||
void SetVariantName([Const] DOMString name);
|
||||
boolean IsForcedToOverrideEventsBasedObjectChildrenConfiguration();
|
||||
boolean IsMarkedAsOverridingEventsBasedObjectChildrenConfiguration();
|
||||
void SetMarkedAsOverridingEventsBasedObjectChildrenConfiguration(
|
||||
@@ -3146,6 +3158,9 @@ interface EventsBasedObject {
|
||||
[Ref] EventsBasedObject MakAsUsingLegacyInstancesRenderer(boolean value);
|
||||
boolean IsUsingLegacyInstancesRenderer();
|
||||
|
||||
[Ref] EventsBasedObjectVariant GetDefaultVariant();
|
||||
[Ref] EventsBasedObjectVariantsContainer GetVariants();
|
||||
|
||||
[Ref] InitialInstancesContainer GetInitialInstances();
|
||||
[Ref] LayersContainer GetLayers();
|
||||
[Ref] ObjectsContainer GetObjects();
|
||||
@@ -3169,6 +3184,48 @@ interface EventsBasedObject {
|
||||
};
|
||||
EventsBasedObject implements AbstractEventsBasedEntity;
|
||||
|
||||
interface EventsBasedObjectVariant {
|
||||
void EventsBasedObjectVariant();
|
||||
|
||||
[Const, Ref] DOMString GetName();
|
||||
[Ref] EventsBasedObjectVariant SetName([Const] DOMString name);
|
||||
|
||||
[Ref] InitialInstancesContainer GetInitialInstances();
|
||||
[Ref] LayersContainer GetLayers();
|
||||
[Ref] ObjectsContainer GetObjects();
|
||||
double GetAreaMinX();
|
||||
double GetAreaMinY();
|
||||
double GetAreaMinZ();
|
||||
double GetAreaMaxX();
|
||||
double GetAreaMaxY();
|
||||
double GetAreaMaxZ();
|
||||
void SetAreaMinX(double value);
|
||||
void SetAreaMinY(double value);
|
||||
void SetAreaMinZ(double value);
|
||||
void SetAreaMaxX(double value);
|
||||
void SetAreaMaxY(double value);
|
||||
void SetAreaMaxZ(double value);
|
||||
void SetAssetStoreAssetId([Const] DOMString assetStoreAssetId);
|
||||
[Const, Ref] DOMString GetAssetStoreAssetId();
|
||||
void SetAssetStoreOriginalName([Const] DOMString assetStoreOriginalName);
|
||||
[Const, Ref] DOMString GetAssetStoreOriginalName();
|
||||
|
||||
void SerializeTo([Ref] SerializerElement element);
|
||||
void UnserializeFrom([Ref] Project project, [Const, Ref] SerializerElement element);
|
||||
};
|
||||
|
||||
interface EventsBasedObjectVariantsContainer {
|
||||
[Ref] EventsBasedObjectVariant InsertNewVariant([Const] DOMString name, unsigned long pos);
|
||||
[Ref] EventsBasedObjectVariant InsertVariant([Const, Ref] EventsBasedObjectVariant variant, unsigned long pos);
|
||||
boolean HasVariantNamed([Const] DOMString name);
|
||||
[Ref] EventsBasedObjectVariant GetVariant([Const] DOMString name);
|
||||
[Ref] EventsBasedObjectVariant GetVariantAt(unsigned long pos);
|
||||
void RemoveVariant([Const] DOMString name);
|
||||
void MoveVariant(unsigned long oldIndex, unsigned long newIndex);
|
||||
unsigned long GetVariantsCount();
|
||||
unsigned long GetVariantPosition([Const, Ref] EventsBasedObjectVariant variant);
|
||||
};
|
||||
|
||||
interface EventsBasedObjectsList {
|
||||
[Ref] EventsBasedObject InsertNew([Const] DOMString name, unsigned long pos);
|
||||
[Ref] EventsBasedObject Insert([Const, Ref] EventsBasedObject item, unsigned long pos);
|
||||
@@ -3249,6 +3306,7 @@ interface EventsFunctionsExtension {
|
||||
[Ref] EventsBasedObjectsList GetEventsBasedObjects();
|
||||
|
||||
void SerializeTo([Ref] SerializerElement element);
|
||||
void SerializeToExternal([Ref] SerializerElement element);
|
||||
void UnserializeFrom([Ref] Project project, [Const, Ref] SerializerElement element);
|
||||
|
||||
boolean STATIC_IsExtensionLifecycleEventsFunction([Const] DOMString eventsFunctionName);
|
||||
|
@@ -47,6 +47,7 @@
|
||||
#include <GDCore/IDE/Events/ExampleExtensionUsagesFinder.h>
|
||||
#include <GDCore/IDE/EventsFunctionTools.h>
|
||||
#include <GDCore/IDE/ObjectVariableHelper.h>
|
||||
#include <GDCore/IDE/EventsBasedObjectVariantHelper.h>
|
||||
#include <GDCore/IDE/Project/ArbitraryResourceWorker.h>
|
||||
#include <GDCore/IDE/Project/ArbitraryObjectsWorker.h>
|
||||
#include <GDCore/IDE/Project/ObjectsUsingResourceCollector.h>
|
||||
@@ -727,6 +728,8 @@ typedef ExtensionAndMetadata<ExpressionMetadata> ExtensionAndExpressionMetadata;
|
||||
ComputeChangesetForVariablesContainer
|
||||
#define STATIC_MergeVariableContainers MergeVariableContainers
|
||||
#define STATIC_FillAnyVariableBetweenObjects FillAnyVariableBetweenObjects
|
||||
#define STATIC_ApplyChangesToVariants ApplyChangesToVariants
|
||||
#define STATIC_ComplyVariantsToEventsBasedObject ComplyVariantsToEventsBasedObject
|
||||
#define STATIC_RenameEventsFunctionsExtension RenameEventsFunctionsExtension
|
||||
#define STATIC_UpdateExtensionNameInEventsBasedBehavior \
|
||||
UpdateExtensionNameInEventsBasedBehavior
|
||||
@@ -846,6 +849,7 @@ typedef ExtensionAndMetadata<ExpressionMetadata> ExtensionAndExpressionMetadata;
|
||||
#define RemoveEventAt RemoveEvent
|
||||
#define RemoveAt Remove
|
||||
#define GetEventsFunctionAt GetEventsFunction
|
||||
#define GetVariantAt GetVariant
|
||||
#define GetEffectAt GetEffect
|
||||
#define GetParameterAt GetParameter
|
||||
|
||||
|
49
GDevelop.js/types.d.ts
vendored
49
GDevelop.js/types.d.ts
vendored
@@ -384,6 +384,11 @@ export class ObjectGroup extends EmscriptenObject {
|
||||
export class ObjectVariableHelper extends EmscriptenObject {
|
||||
static mergeVariableContainers(objectsContainersList: ObjectsContainersList, objectGroup: ObjectGroup): VariablesContainer;
|
||||
static fillAnyVariableBetweenObjects(globalObjectsContainer: ObjectsContainer, objectsContainer: ObjectsContainer, objectGroup: ObjectGroup): void;
|
||||
static applyChangesToVariants(eventsBasedObject: EventsBasedObject, objectName: string, changeset: VariablesChangeset): void;
|
||||
}
|
||||
|
||||
export class EventsBasedObjectVariantHelper extends EmscriptenObject {
|
||||
static complyVariantsToEventsBasedObject(project: Project, eventsBasedObject: EventsBasedObject): void;
|
||||
}
|
||||
|
||||
export class ObjectGroupsContainer extends EmscriptenObject {
|
||||
@@ -768,6 +773,8 @@ export class ObjectJsImplementation extends ObjectConfiguration {
|
||||
|
||||
export class CustomObjectConfiguration extends ObjectConfiguration {
|
||||
clone(): UniquePtrObjectConfiguration;
|
||||
getVariantName(): string;
|
||||
setVariantName(name: string): void;
|
||||
isForcedToOverrideEventsBasedObjectChildrenConfiguration(): boolean;
|
||||
isMarkedAsOverridingEventsBasedObjectChildrenConfiguration(): boolean;
|
||||
setMarkedAsOverridingEventsBasedObjectChildrenConfiguration(isOverridingEventsBasedObjectChildrenConfiguration: boolean): void;
|
||||
@@ -2273,6 +2280,8 @@ export class EventsBasedObject extends AbstractEventsBasedEntity {
|
||||
isInnerAreaFollowingParentSize(): boolean;
|
||||
makAsUsingLegacyInstancesRenderer(value: boolean): EventsBasedObject;
|
||||
isUsingLegacyInstancesRenderer(): boolean;
|
||||
getDefaultVariant(): EventsBasedObjectVariant;
|
||||
getVariants(): EventsBasedObjectVariantsContainer;
|
||||
getInitialInstances(): InitialInstancesContainer;
|
||||
getLayers(): LayersContainer;
|
||||
getObjects(): ObjectsContainer;
|
||||
@@ -2294,6 +2303,45 @@ export class EventsBasedObject extends AbstractEventsBasedEntity {
|
||||
static getPropertyToggleActionName(propertyName: string): string;
|
||||
}
|
||||
|
||||
export class EventsBasedObjectVariant extends EmscriptenObject {
|
||||
constructor();
|
||||
getName(): string;
|
||||
setName(name: string): EventsBasedObjectVariant;
|
||||
getInitialInstances(): InitialInstancesContainer;
|
||||
getLayers(): LayersContainer;
|
||||
getObjects(): ObjectsContainer;
|
||||
getAreaMinX(): number;
|
||||
getAreaMinY(): number;
|
||||
getAreaMinZ(): number;
|
||||
getAreaMaxX(): number;
|
||||
getAreaMaxY(): number;
|
||||
getAreaMaxZ(): number;
|
||||
setAreaMinX(value: number): void;
|
||||
setAreaMinY(value: number): void;
|
||||
setAreaMinZ(value: number): void;
|
||||
setAreaMaxX(value: number): void;
|
||||
setAreaMaxY(value: number): void;
|
||||
setAreaMaxZ(value: number): void;
|
||||
setAssetStoreAssetId(assetStoreAssetId: string): void;
|
||||
getAssetStoreAssetId(): string;
|
||||
setAssetStoreOriginalName(assetStoreOriginalName: string): void;
|
||||
getAssetStoreOriginalName(): string;
|
||||
serializeTo(element: SerializerElement): void;
|
||||
unserializeFrom(project: Project, element: SerializerElement): void;
|
||||
}
|
||||
|
||||
export class EventsBasedObjectVariantsContainer extends EmscriptenObject {
|
||||
insertNewVariant(name: string, pos: number): EventsBasedObjectVariant;
|
||||
insertVariant(variant: EventsBasedObjectVariant, pos: number): EventsBasedObjectVariant;
|
||||
hasVariantNamed(name: string): boolean;
|
||||
getVariant(name: string): EventsBasedObjectVariant;
|
||||
getVariantAt(pos: number): EventsBasedObjectVariant;
|
||||
removeVariant(name: string): void;
|
||||
moveVariant(oldIndex: number, newIndex: number): void;
|
||||
getVariantsCount(): number;
|
||||
getVariantPosition(variant: EventsBasedObjectVariant): number;
|
||||
}
|
||||
|
||||
export class EventsBasedObjectsList extends EmscriptenObject {
|
||||
insertNew(name: string, pos: number): EventsBasedObject;
|
||||
insert(item: EventsBasedObject, pos: number): EventsBasedObject;
|
||||
@@ -2364,6 +2412,7 @@ export class EventsFunctionsExtension extends EmscriptenObject {
|
||||
getEventsBasedBehaviors(): EventsBasedBehaviorsList;
|
||||
getEventsBasedObjects(): EventsBasedObjectsList;
|
||||
serializeTo(element: SerializerElement): void;
|
||||
serializeToExternal(element: SerializerElement): void;
|
||||
unserializeFrom(project: Project, element: SerializerElement): void;
|
||||
static isExtensionLifecycleEventsFunction(eventsFunctionName: string): boolean;
|
||||
}
|
||||
|
@@ -6,6 +6,8 @@ declare class gdCustomObjectConfiguration extends gdObjectConfiguration {
|
||||
static Proportional: 3;
|
||||
static Center: 4;
|
||||
clone(): gdUniquePtrObjectConfiguration;
|
||||
getVariantName(): string;
|
||||
setVariantName(name: string): void;
|
||||
isForcedToOverrideEventsBasedObjectChildrenConfiguration(): boolean;
|
||||
isMarkedAsOverridingEventsBasedObjectChildrenConfiguration(): boolean;
|
||||
setMarkedAsOverridingEventsBasedObjectChildrenConfiguration(isOverridingEventsBasedObjectChildrenConfiguration: boolean): void;
|
||||
|
@@ -17,6 +17,8 @@ declare class gdEventsBasedObject extends gdAbstractEventsBasedEntity {
|
||||
isInnerAreaFollowingParentSize(): boolean;
|
||||
makAsUsingLegacyInstancesRenderer(value: boolean): gdEventsBasedObject;
|
||||
isUsingLegacyInstancesRenderer(): boolean;
|
||||
getDefaultVariant(): gdEventsBasedObjectVariant;
|
||||
getVariants(): gdEventsBasedObjectVariantsContainer;
|
||||
getInitialInstances(): gdInitialInstancesContainer;
|
||||
getLayers(): gdLayersContainer;
|
||||
getObjects(): gdObjectsContainer;
|
||||
|
29
GDevelop.js/types/gdeventsbasedobjectvariant.js
Normal file
29
GDevelop.js/types/gdeventsbasedobjectvariant.js
Normal file
@@ -0,0 +1,29 @@
|
||||
// Automatically generated by GDevelop.js/scripts/generate-types.js
|
||||
declare class gdEventsBasedObjectVariant {
|
||||
constructor(): void;
|
||||
getName(): string;
|
||||
setName(name: string): gdEventsBasedObjectVariant;
|
||||
getInitialInstances(): gdInitialInstancesContainer;
|
||||
getLayers(): gdLayersContainer;
|
||||
getObjects(): gdObjectsContainer;
|
||||
getAreaMinX(): number;
|
||||
getAreaMinY(): number;
|
||||
getAreaMinZ(): number;
|
||||
getAreaMaxX(): number;
|
||||
getAreaMaxY(): number;
|
||||
getAreaMaxZ(): number;
|
||||
setAreaMinX(value: number): void;
|
||||
setAreaMinY(value: number): void;
|
||||
setAreaMinZ(value: number): void;
|
||||
setAreaMaxX(value: number): void;
|
||||
setAreaMaxY(value: number): void;
|
||||
setAreaMaxZ(value: number): void;
|
||||
setAssetStoreAssetId(assetStoreAssetId: string): void;
|
||||
getAssetStoreAssetId(): string;
|
||||
setAssetStoreOriginalName(assetStoreOriginalName: string): void;
|
||||
getAssetStoreOriginalName(): string;
|
||||
serializeTo(element: gdSerializerElement): void;
|
||||
unserializeFrom(project: gdProject, element: gdSerializerElement): void;
|
||||
delete(): void;
|
||||
ptr: number;
|
||||
};
|
6
GDevelop.js/types/gdeventsbasedobjectvarianthelper.js
Normal file
6
GDevelop.js/types/gdeventsbasedobjectvarianthelper.js
Normal file
@@ -0,0 +1,6 @@
|
||||
// Automatically generated by GDevelop.js/scripts/generate-types.js
|
||||
declare class gdEventsBasedObjectVariantHelper {
|
||||
static complyVariantsToEventsBasedObject(project: gdProject, eventsBasedObject: gdEventsBasedObject): void;
|
||||
delete(): void;
|
||||
ptr: number;
|
||||
};
|
14
GDevelop.js/types/gdeventsbasedobjectvariantscontainer.js
Normal file
14
GDevelop.js/types/gdeventsbasedobjectvariantscontainer.js
Normal file
@@ -0,0 +1,14 @@
|
||||
// Automatically generated by GDevelop.js/scripts/generate-types.js
|
||||
declare class gdEventsBasedObjectVariantsContainer {
|
||||
insertNewVariant(name: string, pos: number): gdEventsBasedObjectVariant;
|
||||
insertVariant(variant: gdEventsBasedObjectVariant, pos: number): gdEventsBasedObjectVariant;
|
||||
hasVariantNamed(name: string): boolean;
|
||||
getVariant(name: string): gdEventsBasedObjectVariant;
|
||||
getVariantAt(pos: number): gdEventsBasedObjectVariant;
|
||||
removeVariant(name: string): void;
|
||||
moveVariant(oldIndex: number, newIndex: number): void;
|
||||
getVariantsCount(): number;
|
||||
getVariantPosition(variant: gdEventsBasedObjectVariant): number;
|
||||
delete(): void;
|
||||
ptr: number;
|
||||
};
|
@@ -40,6 +40,7 @@ declare class gdEventsFunctionsExtension {
|
||||
getEventsBasedBehaviors(): gdEventsBasedBehaviorsList;
|
||||
getEventsBasedObjects(): gdEventsBasedObjectsList;
|
||||
serializeTo(element: gdSerializerElement): void;
|
||||
serializeToExternal(element: gdSerializerElement): void;
|
||||
unserializeFrom(project: gdProject, element: gdSerializerElement): void;
|
||||
static isExtensionLifecycleEventsFunction(eventsFunctionName: string): boolean;
|
||||
delete(): void;
|
||||
|
@@ -2,6 +2,7 @@
|
||||
declare class gdObjectVariableHelper {
|
||||
static mergeVariableContainers(objectsContainersList: gdObjectsContainersList, objectGroup: gdObjectGroup): gdVariablesContainer;
|
||||
static fillAnyVariableBetweenObjects(globalObjectsContainer: gdObjectsContainer, objectsContainer: gdObjectsContainer, objectGroup: gdObjectGroup): void;
|
||||
static applyChangesToVariants(eventsBasedObject: gdEventsBasedObject, objectName: string, changeset: gdVariablesChangeset): void;
|
||||
delete(): void;
|
||||
ptr: number;
|
||||
};
|
@@ -74,6 +74,7 @@ declare class libGDevelop {
|
||||
VariablesContainersList: Class<gdVariablesContainersList>;
|
||||
ObjectGroup: Class<gdObjectGroup>;
|
||||
ObjectVariableHelper: Class<gdObjectVariableHelper>;
|
||||
EventsBasedObjectVariantHelper: Class<gdEventsBasedObjectVariantHelper>;
|
||||
ObjectGroupsContainer: Class<gdObjectGroupsContainer>;
|
||||
PlatformSpecificAssets: Class<gdPlatformSpecificAssets>;
|
||||
LoadingScreen: Class<gdLoadingScreen>;
|
||||
@@ -222,6 +223,8 @@ declare class libGDevelop {
|
||||
EventsBasedBehavior: Class<gdEventsBasedBehavior>;
|
||||
EventsBasedBehaviorsList: Class<gdEventsBasedBehaviorsList>;
|
||||
EventsBasedObject: Class<gdEventsBasedObject>;
|
||||
EventsBasedObjectVariant: Class<gdEventsBasedObjectVariant>;
|
||||
EventsBasedObjectVariantsContainer: Class<gdEventsBasedObjectVariantsContainer>;
|
||||
EventsBasedObjectsList: Class<gdEventsBasedObjectsList>;
|
||||
PropertiesContainer: Class<gdPropertiesContainer>;
|
||||
EventsFunctionsExtension: Class<gdEventsFunctionsExtension>;
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 779 KiB After Width: | Height: | Size: 819 KiB |
@@ -24,7 +24,7 @@ import ErrorBoundary from '../../UI/ErrorBoundary';
|
||||
type Props = {|
|
||||
project: gdProject,
|
||||
onClose: () => void,
|
||||
onInstallExtension: ExtensionShortHeader => void,
|
||||
onInstallExtension: (extensionName: string) => void,
|
||||
onExtensionInstalled: (extensionName: string) => void,
|
||||
onCreateNew?: () => void,
|
||||
|};
|
||||
@@ -63,7 +63,7 @@ const ExtensionsSearchDialog = ({
|
||||
try {
|
||||
let installedOrImportedExtensionName: string | null = null;
|
||||
if (!!extensionShortHeader) {
|
||||
onInstallExtension(extensionShortHeader);
|
||||
onInstallExtension(extensionShortHeader.name);
|
||||
const wasExtensionInstalledOrImported = await installDisplayedExtension(
|
||||
i18n,
|
||||
project,
|
||||
@@ -77,7 +77,8 @@ const ExtensionsSearchDialog = ({
|
||||
installedOrImportedExtensionName = await importExtension(
|
||||
i18n,
|
||||
eventsFunctionsExtensionsState,
|
||||
project
|
||||
project,
|
||||
onInstallExtension
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -47,7 +47,8 @@ export const installExtension = async (
|
||||
export const importExtension = async (
|
||||
i18n: I18nType,
|
||||
eventsFunctionsExtensionsState: EventsFunctionsExtensionsState,
|
||||
project: gdProject
|
||||
project: gdProject,
|
||||
onWillInstallExtension: (extensionName: string) => void
|
||||
): Promise<string | null> => {
|
||||
const eventsFunctionsExtensionOpener = eventsFunctionsExtensionsState.getEventsFunctionsExtensionOpener();
|
||||
if (!eventsFunctionsExtensionOpener) return null;
|
||||
@@ -69,6 +70,8 @@ export const importExtension = async (
|
||||
if (!answer) return null;
|
||||
}
|
||||
|
||||
onWillInstallExtension(serializedExtension.name);
|
||||
|
||||
await addSerializedExtensionsToProject(
|
||||
eventsFunctionsExtensionsState,
|
||||
project,
|
||||
|
@@ -155,6 +155,23 @@ export type InstallAssetArgs = {|
|
||||
targetObjectFolderOrObject?: ?gdObjectFolderOrObject,
|
||||
|};
|
||||
|
||||
const findVariant = (
|
||||
container: gdEventsBasedObjectVariantsContainer,
|
||||
assetStoreAssetId: string,
|
||||
assetStoreOriginalName: string
|
||||
): gdEventsBasedObjectVariant | null => {
|
||||
for (let index = 0; index < container.getVariantsCount(); index++) {
|
||||
const variant = container.getVariantAt(index);
|
||||
if (
|
||||
variant.getAssetStoreAssetId() === assetStoreAssetId &&
|
||||
variant.getAssetStoreOriginalName() === assetStoreOriginalName
|
||||
) {
|
||||
return variant;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export const addAssetToProject = async ({
|
||||
asset,
|
||||
project,
|
||||
@@ -170,6 +187,88 @@ export const addAssetToProject = async ({
|
||||
const type: ?string = objectAsset.object.type;
|
||||
if (!type) throw new Error('An object has no type specified');
|
||||
|
||||
const variantRenamings: Array<{
|
||||
objectType: string,
|
||||
oldVariantName: string,
|
||||
newVariantName: string,
|
||||
}> = [];
|
||||
const serializedVariants = objectAsset.variants;
|
||||
if (serializedVariants) {
|
||||
// Install variants
|
||||
for (const {
|
||||
objectType,
|
||||
variant: serializedVariant,
|
||||
} of serializedVariants) {
|
||||
if (project.hasEventsBasedObject(objectType)) {
|
||||
const eventsBasedObject = project.getEventsBasedObject(objectType);
|
||||
const variants = eventsBasedObject.getVariants();
|
||||
let variant = findVariant(variants, asset.id, serializedVariant.name);
|
||||
if (!variant) {
|
||||
// TODO Forbid name with `::`
|
||||
const uniqueNewName = newNameGenerator(
|
||||
serializedVariant.name || asset.name,
|
||||
tentativeNewName => variants.hasVariantNamed(tentativeNewName)
|
||||
);
|
||||
variant = variants.insertNewVariant(
|
||||
uniqueNewName,
|
||||
variants.getVariantsCount()
|
||||
);
|
||||
const variantName = variant.getName();
|
||||
unserializeFromJSObject(
|
||||
variant,
|
||||
serializedVariant,
|
||||
'unserializeFrom',
|
||||
project
|
||||
);
|
||||
variant.setName(variantName);
|
||||
variant.setAssetStoreAssetId(asset.id);
|
||||
variant.setAssetStoreOriginalName(serializedVariant.name);
|
||||
}
|
||||
if (variant.getName() !== serializedVariant.name) {
|
||||
variantRenamings.push({
|
||||
objectType,
|
||||
oldVariantName: serializedVariant.name,
|
||||
newVariantName: variant.getName(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
// Update variant names into variants object configurations.
|
||||
for (const {
|
||||
objectType,
|
||||
variant: serializedVariant,
|
||||
} of serializedVariants) {
|
||||
if (project.hasEventsBasedObject(objectType)) {
|
||||
const eventsBasedObject = project.getEventsBasedObject(objectType);
|
||||
const variants = eventsBasedObject.getVariants();
|
||||
let variant = findVariant(variants, asset.id, serializedVariant.name);
|
||||
if (variant) {
|
||||
for (
|
||||
let index = 0;
|
||||
index < variant.getObjects().getObjectsCount();
|
||||
index++
|
||||
) {
|
||||
const object = variant.getObjects().getObjectAt(index);
|
||||
|
||||
if (project.hasEventsBasedObject(object.getType())) {
|
||||
const customObjectConfiguration = gd.asCustomObjectConfiguration(
|
||||
object.getConfiguration()
|
||||
);
|
||||
const customObjectVariantRenaming = variantRenamings.find(
|
||||
renaming => renaming.objectType === object.getType()
|
||||
);
|
||||
if (customObjectVariantRenaming) {
|
||||
customObjectConfiguration.setVariantName(
|
||||
customObjectVariantRenaming.newVariantName
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Insert the object
|
||||
const originalName = sanitizeObjectName(objectAsset.object.name);
|
||||
const newName = newNameGenerator(originalName, name =>
|
||||
@@ -204,10 +303,27 @@ export const addAssetToProject = async ({
|
||||
'unserializeFrom',
|
||||
project
|
||||
);
|
||||
|
||||
object.setAssetStoreId(asset.id);
|
||||
// The name was overwritten after unserialization.
|
||||
object.setName(newName);
|
||||
object.setAssetStoreId(asset.id);
|
||||
if (project.hasEventsBasedObject(object.getType())) {
|
||||
const customObjectConfiguration = gd.asCustomObjectConfiguration(
|
||||
object.getConfiguration()
|
||||
);
|
||||
if (customObjectConfiguration.getVariantName()) {
|
||||
customObjectConfiguration.setMarkedAsOverridingEventsBasedObjectChildrenConfiguration(
|
||||
false
|
||||
);
|
||||
}
|
||||
const customObjectVariantRenaming = variantRenamings.find(
|
||||
renaming => renaming.objectType === object.getType()
|
||||
);
|
||||
if (customObjectVariantRenaming) {
|
||||
customObjectConfiguration.setVariantName(
|
||||
customObjectVariantRenaming.newVariantName
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Add resources used by the object
|
||||
objectAsset.resources.forEach(serializedResource => {
|
||||
|
@@ -75,6 +75,10 @@ const Physics3DEditor = (props: Props) => {
|
||||
|
||||
const isStatic = properties.get('bodyType').getValue() === 'Static';
|
||||
|
||||
const canShapeBeOriented =
|
||||
properties.get('shape').getValue() !== 'Sphere' &&
|
||||
properties.get('shape').getValue() !== 'Box';
|
||||
|
||||
return (
|
||||
<Column
|
||||
expand
|
||||
@@ -160,14 +164,15 @@ const Physics3DEditor = (props: Props) => {
|
||||
id="physics3d-parameter-shape-orientation"
|
||||
fullWidth
|
||||
floatingLabelText={properties.get('shapeOrientation').getLabel()}
|
||||
value={properties.get('shapeOrientation').getValue()}
|
||||
value={
|
||||
canShapeBeOriented
|
||||
? properties.get('shapeOrientation').getValue()
|
||||
: 'Z'
|
||||
}
|
||||
onChange={(e, i, newValue: string) =>
|
||||
updateBehaviorProperty('shapeOrientation', newValue)
|
||||
}
|
||||
disabled={
|
||||
properties.get('shape').getValue() === 'Sphere' ||
|
||||
properties.get('shape').getValue() === 'Box'
|
||||
}
|
||||
disabled={!canShapeBeOriented}
|
||||
>
|
||||
<SelectOption key={'shape-orientation-z'} value={'Z'} label={t`Z`} />
|
||||
<SelectOption key={'shape-orientation-y'} value={'Y'} label={t`Y`} />
|
||||
|
@@ -45,6 +45,7 @@ import CopyIcon from '../UI/CustomSvgIcons/Copy';
|
||||
import ResponsiveFlatButton from '../UI/ResponsiveFlatButton';
|
||||
import { useResponsiveWindowSize } from '../UI/Responsive/ResponsiveWindowMeasurer';
|
||||
import QuickCustomizationPropertiesVisibilityDialog from '../QuickCustomization/QuickCustomizationPropertiesVisibilityDialog';
|
||||
import Text from '../UI/Text';
|
||||
|
||||
const gd: libGDevelop = global.gd;
|
||||
|
||||
@@ -80,6 +81,7 @@ type BehaviorConfigurationEditorProps = {|
|
||||
openBehaviorPropertiesQuickCustomizationDialog: (
|
||||
behaviorName: string
|
||||
) => void,
|
||||
isListLocked: boolean,
|
||||
|};
|
||||
|
||||
const BehaviorConfigurationEditor = React.forwardRef<
|
||||
@@ -100,6 +102,7 @@ const BehaviorConfigurationEditor = React.forwardRef<
|
||||
pasteBehaviors,
|
||||
openExtension,
|
||||
openBehaviorPropertiesQuickCustomizationDialog,
|
||||
isListLocked,
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
@@ -195,6 +198,7 @@ const BehaviorConfigurationEditor = React.forwardRef<
|
||||
{
|
||||
label: i18n._(t`Delete`),
|
||||
click: () => onRemoveBehavior(behaviorName),
|
||||
enabled: !isListLocked,
|
||||
},
|
||||
{
|
||||
label: i18n._(t`Copy`),
|
||||
@@ -203,7 +207,8 @@ const BehaviorConfigurationEditor = React.forwardRef<
|
||||
{
|
||||
label: i18n._(t`Paste`),
|
||||
click: pasteBehaviors,
|
||||
enabled: canPasteBehaviors,
|
||||
// TODO Allow to paste behaviors that are already in the list.
|
||||
enabled: canPasteBehaviors && !isListLocked,
|
||||
},
|
||||
...(project.hasEventsBasedBehavior(behaviorTypeName)
|
||||
? [
|
||||
@@ -618,6 +623,7 @@ type Props = {|
|
||||
behaviorName: string
|
||||
) => Promise<void>,
|
||||
onExtensionInstalled: (extensionName: string) => void,
|
||||
isListLocked: boolean,
|
||||
|};
|
||||
|
||||
const BehaviorsEditor = (props: Props) => {
|
||||
@@ -636,6 +642,7 @@ const BehaviorsEditor = (props: Props) => {
|
||||
onUpdateBehaviorsSharedData,
|
||||
openBehaviorEvents,
|
||||
onExtensionInstalled,
|
||||
isListLocked,
|
||||
} = props;
|
||||
const forceUpdate = useForceUpdate();
|
||||
|
||||
@@ -725,30 +732,41 @@ const BehaviorsEditor = (props: Props) => {
|
||||
return (
|
||||
<Column noMargin expand useFullHeight noOverflowParent>
|
||||
{allVisibleBehaviors.length === 0 ? (
|
||||
<Column noMargin expand justifyContent="center">
|
||||
<EmptyPlaceholder
|
||||
title={<Trans>Add your first behavior</Trans>}
|
||||
description={
|
||||
<Trans>
|
||||
Behaviors add features to objects in a matter of clicks.
|
||||
</Trans>
|
||||
}
|
||||
helpPagePath="/behaviors"
|
||||
tutorialId="intro-behaviors-and-functions"
|
||||
actionButtonId="add-behavior-button"
|
||||
actionLabel={
|
||||
isMobile ? <Trans>Add</Trans> : <Trans>Add a behavior</Trans>
|
||||
}
|
||||
onAction={openNewBehaviorDialog}
|
||||
secondaryActionIcon={<PasteIcon />}
|
||||
secondaryActionLabel={
|
||||
isClipboardContainingBehaviors ? <Trans>Paste</Trans> : null
|
||||
}
|
||||
onSecondaryAction={() => {
|
||||
pasteBehaviors();
|
||||
}}
|
||||
/>
|
||||
</Column>
|
||||
isListLocked ? (
|
||||
<Column noMargin expand justifyContent="center">
|
||||
<Text size="block-title" align="center">
|
||||
<Trans>No behavior</Trans>
|
||||
</Text>
|
||||
<Text align="center" noMargin>
|
||||
<Trans>There is no behavior to set up for this object.</Trans>
|
||||
</Text>
|
||||
</Column>
|
||||
) : (
|
||||
<Column noMargin expand justifyContent="center">
|
||||
<EmptyPlaceholder
|
||||
title={<Trans>Add your first behavior</Trans>}
|
||||
description={
|
||||
<Trans>
|
||||
Behaviors add features to objects in a matter of clicks.
|
||||
</Trans>
|
||||
}
|
||||
helpPagePath="/behaviors"
|
||||
tutorialId="intro-behaviors-and-functions"
|
||||
actionButtonId="add-behavior-button"
|
||||
actionLabel={
|
||||
isMobile ? <Trans>Add</Trans> : <Trans>Add a behavior</Trans>
|
||||
}
|
||||
onAction={openNewBehaviorDialog}
|
||||
secondaryActionIcon={<PasteIcon />}
|
||||
secondaryActionLabel={
|
||||
isClipboardContainingBehaviors ? <Trans>Paste</Trans> : null
|
||||
}
|
||||
onSecondaryAction={() => {
|
||||
pasteBehaviors();
|
||||
}}
|
||||
/>
|
||||
</Column>
|
||||
)
|
||||
) : (
|
||||
<React.Fragment>
|
||||
<ScrollView ref={scrollView}>
|
||||
@@ -778,6 +796,7 @@ const BehaviorsEditor = (props: Props) => {
|
||||
canPasteBehaviors={isClipboardContainingBehaviors}
|
||||
pasteBehaviors={pasteBehaviors}
|
||||
resourceManagementProps={props.resourceManagementProps}
|
||||
isListLocked={isListLocked}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
@@ -806,7 +825,7 @@ const BehaviorsEditor = (props: Props) => {
|
||||
onClick={() => {
|
||||
pasteBehaviors();
|
||||
}}
|
||||
disabled={!isClipboardContainingBehaviors}
|
||||
disabled={!isClipboardContainingBehaviors || isListLocked}
|
||||
/>
|
||||
</LineStackLayout>
|
||||
<LineStackLayout justifyContent="flex-end" expand>
|
||||
@@ -823,6 +842,7 @@ const BehaviorsEditor = (props: Props) => {
|
||||
onClick={openNewBehaviorDialog}
|
||||
icon={<Add />}
|
||||
id="add-behavior-button"
|
||||
disabled={isListLocked}
|
||||
/>
|
||||
</LineStackLayout>
|
||||
</LineStackLayout>
|
||||
|
@@ -21,7 +21,9 @@ type Props = {|
|
||||
onEventsFunctionsAdded: () => void,
|
||||
onOpenCustomObjectEditor: () => void,
|
||||
unsavedChanges?: ?UnsavedChanges,
|
||||
onEventsBasedObjectChildrenEdited: () => void,
|
||||
onEventsBasedObjectChildrenEdited: (
|
||||
eventsBasedObject: gdEventsBasedObject
|
||||
) => void,
|
||||
|};
|
||||
|
||||
export default function EventsBasedObjectEditorPanel({
|
||||
|
@@ -23,7 +23,9 @@ type Props = {|
|
||||
eventsBasedObject: gdEventsBasedObject,
|
||||
onOpenCustomObjectEditor: () => void,
|
||||
unsavedChanges?: ?UnsavedChanges,
|
||||
onEventsBasedObjectChildrenEdited: () => void,
|
||||
onEventsBasedObjectChildrenEdited: (
|
||||
eventsBasedObject: gdEventsBasedObject
|
||||
) => void,
|
||||
|};
|
||||
|
||||
export default function EventsBasedObjectEditor({
|
||||
@@ -127,7 +129,7 @@ export default function EventsBasedObjectEditor({
|
||||
onCheck={(e, checked) => {
|
||||
eventsBasedObject.markAsInnerAreaFollowingParentSize(checked);
|
||||
onChange();
|
||||
onEventsBasedObjectChildrenEdited();
|
||||
onEventsBasedObjectChildrenEdited(eventsBasedObject);
|
||||
}}
|
||||
/>
|
||||
{isDev && (
|
||||
@@ -137,7 +139,7 @@ export default function EventsBasedObjectEditor({
|
||||
onCheck={(e, checked) => {
|
||||
eventsBasedObject.makAsUsingLegacyInstancesRenderer(checked);
|
||||
onChange();
|
||||
onEventsBasedObjectChildrenEdited();
|
||||
onEventsBasedObjectChildrenEdited(eventsBasedObject);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
@@ -147,7 +149,7 @@ export default function EventsBasedObjectEditor({
|
||||
onCheck={(e, checked) => {
|
||||
eventsBasedObject.setPrivate(checked);
|
||||
onChange();
|
||||
onEventsBasedObjectChildrenEdited();
|
||||
onEventsBasedObjectChildrenEdited(eventsBasedObject);
|
||||
}}
|
||||
tooltipOrHelperText={
|
||||
eventsBasedObject.isPrivate() ? (
|
||||
|
@@ -77,7 +77,9 @@ type Props = {|
|
||||
unsavedChanges?: ?UnsavedChanges,
|
||||
onOpenCustomObjectEditor: gdEventsBasedObject => void,
|
||||
hotReloadPreviewButtonProps: HotReloadPreviewButtonProps,
|
||||
onEventsBasedObjectChildrenEdited: () => void,
|
||||
onEventsBasedObjectChildrenEdited: (
|
||||
eventsBasedObject: gdEventsBasedObject
|
||||
) => void,
|
||||
onRenamedEventsBasedObject: (
|
||||
eventsFunctionsExtension: gdEventsFunctionsExtension,
|
||||
oldName: string,
|
||||
@@ -778,7 +780,7 @@ export default class EventsFunctionsExtensionEditor extends React.Component<
|
||||
// Some custom object instances may target the pasted event-based object name.
|
||||
// It can happen when an event-based object is deleted and another one is
|
||||
// pasted to replace it.
|
||||
this.props.onEventsBasedObjectChildrenEdited();
|
||||
this.props.onEventsBasedObjectChildrenEdited(eventsBasedObject);
|
||||
};
|
||||
|
||||
_onEventsBasedBehaviorRenamed = () => {
|
||||
@@ -797,7 +799,7 @@ export default class EventsFunctionsExtensionEditor extends React.Component<
|
||||
}
|
||||
};
|
||||
|
||||
_onEventsBasedObjectRenamed = () => {
|
||||
_onEventsBasedObjectRenamed = (eventsBasedObject: gdEventsBasedObject) => {
|
||||
// Name of an object changed, so notify parent
|
||||
// that an object was edited (to trigger reload of extensions)
|
||||
if (this.props.onObjectEdited) {
|
||||
@@ -814,7 +816,7 @@ export default class EventsFunctionsExtensionEditor extends React.Component<
|
||||
// Some custom object instances may target the new event-based object name.
|
||||
// It can happen when an event-based object is deleted and another one is
|
||||
// renamed to replace it.
|
||||
this.props.onEventsBasedObjectChildrenEdited();
|
||||
this.props.onEventsBasedObjectChildrenEdited(eventsBasedObject);
|
||||
};
|
||||
|
||||
_onDeleteEventsBasedBehavior = (
|
||||
@@ -853,7 +855,7 @@ export default class EventsFunctionsExtensionEditor extends React.Component<
|
||||
eventsFunctionsExtension,
|
||||
eventsBasedObject.getName()
|
||||
);
|
||||
onEventsBasedObjectChildrenEdited();
|
||||
onEventsBasedObjectChildrenEdited(eventsBasedObject);
|
||||
};
|
||||
|
||||
_onCloseExtensionFunctionSelectorDialog = (
|
||||
@@ -1702,6 +1704,7 @@ export default class EventsFunctionsExtensionEditor extends React.Component<
|
||||
onCancel={() => this._editVariables(null)}
|
||||
onApply={() => this._editVariables(null)}
|
||||
hotReloadPreviewButtonProps={this.props.hotReloadPreviewButtonProps}
|
||||
isListLocked={false}
|
||||
/>
|
||||
)}
|
||||
{objectMethodSelectorDialogOpen && selectedEventsBasedObject && (
|
||||
|
@@ -32,7 +32,10 @@ export default class BrowserEventsFunctionsExtensionWriter {
|
||||
extension: gdEventsFunctionsExtension,
|
||||
filename: string
|
||||
): Promise<void> => {
|
||||
const serializedObject = serializeToJSObject(extension);
|
||||
const serializedObject = serializeToJSObject(
|
||||
extension,
|
||||
'serializeToExternal'
|
||||
);
|
||||
try {
|
||||
await downloadStringContentAsFile(
|
||||
filename,
|
||||
|
@@ -56,7 +56,10 @@ export default class LocalEventsFunctionsExtensionWriter {
|
||||
extension: gdEventsFunctionsExtension,
|
||||
filepath: string
|
||||
): Promise<void> => {
|
||||
const serializedObject = serializeToJSObject(extension);
|
||||
const serializedObject = serializeToJSObject(
|
||||
extension,
|
||||
'serializeToExternal'
|
||||
);
|
||||
return writeJSONFile(serializedObject, filepath).catch(err => {
|
||||
console.error('Unable to write the events function extension:', err);
|
||||
throw err;
|
||||
|
@@ -133,6 +133,7 @@ export default React.forwardRef<ParameterFieldProps, ParameterFieldInterface>(
|
||||
initiallySelectedVariableName={editorOpen.variableName}
|
||||
shouldCreateInitiallySelectedVariable={editorOpen.shouldCreate}
|
||||
hotReloadPreviewButtonProps={null}
|
||||
isListLocked={false}
|
||||
/>
|
||||
)}
|
||||
</React.Fragment>
|
||||
|
@@ -131,6 +131,7 @@ export default React.forwardRef<ParameterFieldProps, ParameterFieldInterface>(
|
||||
initiallySelectedVariableName={editorOpen.variableName}
|
||||
shouldCreateInitiallySelectedVariable={editorOpen.shouldCreate}
|
||||
hotReloadPreviewButtonProps={null}
|
||||
isListLocked={false}
|
||||
/>
|
||||
)}
|
||||
</React.Fragment>
|
||||
|
@@ -127,6 +127,7 @@ export default React.forwardRef<ParameterFieldProps, ParameterFieldInterface>(
|
||||
initiallySelectedVariableName={editorOpen.variableName}
|
||||
shouldCreateInitiallySelectedVariable={editorOpen.shouldCreate}
|
||||
hotReloadPreviewButtonProps={null}
|
||||
isListLocked={false}
|
||||
/>
|
||||
)}
|
||||
</React.Fragment>
|
||||
|
@@ -84,6 +84,7 @@ export default React.forwardRef<ParameterFieldProps, ParameterFieldInterface>(
|
||||
initiallySelectedVariableName={editorOpen.variableName}
|
||||
shouldCreateInitiallySelectedVariable={editorOpen.shouldCreate}
|
||||
hotReloadPreviewButtonProps={null}
|
||||
isListLocked={false}
|
||||
/>
|
||||
)}
|
||||
</React.Fragment>
|
||||
|
@@ -227,6 +227,7 @@ export default React.forwardRef<ParameterFieldProps, ParameterFieldInterface>(
|
||||
shouldCreateInitiallySelectedVariable={editorOpen.shouldCreate}
|
||||
onComputeAllVariableNames={onComputeAllVariableNames}
|
||||
hotReloadPreviewButtonProps={null}
|
||||
isListLocked={false}
|
||||
/>
|
||||
)}
|
||||
{editorOpen &&
|
||||
@@ -246,6 +247,7 @@ export default React.forwardRef<ParameterFieldProps, ParameterFieldInterface>(
|
||||
initiallySelectedVariableName={editorOpen.variableName}
|
||||
shouldCreateInitiallySelectedVariable={editorOpen.shouldCreate}
|
||||
onComputeAllVariableNames={onComputeAllVariableNames}
|
||||
isListLocked={false}
|
||||
/>
|
||||
)}
|
||||
</React.Fragment>
|
||||
|
@@ -110,6 +110,7 @@ export default React.forwardRef<ParameterFieldProps, ParameterFieldInterface>(
|
||||
editorOpen.shouldCreate || false
|
||||
}
|
||||
hotReloadPreviewButtonProps={null}
|
||||
isListLocked={false}
|
||||
/>
|
||||
)}
|
||||
{editorOpen && eventsFunctionsExtension && !layout && (
|
||||
@@ -122,6 +123,7 @@ export default React.forwardRef<ParameterFieldProps, ParameterFieldInterface>(
|
||||
initiallySelectedVariableName={editorOpen.variableName}
|
||||
shouldCreateInitiallySelectedVariable={editorOpen.shouldCreate}
|
||||
hotReloadPreviewButtonProps={null}
|
||||
isListLocked={false}
|
||||
/>
|
||||
)}
|
||||
</React.Fragment>
|
||||
|
@@ -2158,6 +2158,7 @@ export class EventsSheetComponentWithoutHandle extends React.Component<
|
||||
shouldCreateInitiallySelectedVariable={
|
||||
this.state.editedVariable.shouldCreateVariable
|
||||
}
|
||||
isListLocked={false}
|
||||
/>
|
||||
)}
|
||||
{this.state.layoutVariablesDialogOpen && (
|
||||
@@ -2167,6 +2168,7 @@ export class EventsSheetComponentWithoutHandle extends React.Component<
|
||||
onCancel={() => this.editLayoutVariables(false)}
|
||||
onApply={() => this.editLayoutVariables(false)}
|
||||
hotReloadPreviewButtonProps={hotReloadPreviewButtonProps}
|
||||
isListLocked={false}
|
||||
/>
|
||||
)}
|
||||
{this.state.textEditedEvent && (
|
||||
|
@@ -57,16 +57,18 @@ const CustomTooltip = ({
|
||||
payload,
|
||||
label,
|
||||
customStyle,
|
||||
labelSuffix,
|
||||
}: {|
|
||||
payload: ?Array<any>,
|
||||
label: string,
|
||||
customStyle: Object,
|
||||
labelSuffix: ?string,
|
||||
|}) =>
|
||||
payload ? (
|
||||
<Paper style={customStyle} background="light">
|
||||
<ColumnStackLayout>
|
||||
<Text size="sub-title" noMargin>
|
||||
{label}
|
||||
{label} {labelSuffix ? labelSuffix : ''}
|
||||
</Text>
|
||||
{payload.length > 0 &&
|
||||
payload.map(
|
||||
@@ -167,7 +169,7 @@ export const BounceRateChart = ({
|
||||
<LineChart data={chartData.overTime} margin={chartMargins}>
|
||||
<RechartsLine
|
||||
name={i18n._(t`Bounce rate`)}
|
||||
unit="%"
|
||||
unit={'%'}
|
||||
formatter={minutesFormatter}
|
||||
type="monotone"
|
||||
dataKey="bounceRatePercent"
|
||||
@@ -185,6 +187,7 @@ export const BounceRateChart = ({
|
||||
style={styles.tickLabel}
|
||||
/>
|
||||
<YAxis
|
||||
unit={'%'}
|
||||
dataKey="bounceRatePercent"
|
||||
stroke={gdevelopTheme.chart.textColor}
|
||||
style={styles.tickLabel}
|
||||
@@ -234,6 +237,7 @@ export const MeanPlayTimeChart = ({
|
||||
style={styles.tickLabel}
|
||||
/>
|
||||
<YAxis
|
||||
unit={` ` + i18n._(t`min`)}
|
||||
dataKey="meanPlayedDurationInMinutes"
|
||||
stroke={gdevelopTheme.chart.textColor}
|
||||
style={styles.tickLabel}
|
||||
@@ -272,6 +276,7 @@ export const PlayersRepartitionPerDurationChart = ({
|
||||
yAxisId={0}
|
||||
/>
|
||||
<XAxis
|
||||
unit={` ` + i18n._(t`min`)}
|
||||
name={i18n._(t`Played time`)}
|
||||
dataKey="duration"
|
||||
type="number"
|
||||
@@ -295,6 +300,7 @@ export const PlayersRepartitionPerDurationChart = ({
|
||||
CustomTooltip({
|
||||
...props,
|
||||
customStyle: styles.tooltipContent,
|
||||
labelSuffix: i18n._(t`minutes`),
|
||||
})
|
||||
}
|
||||
/>
|
||||
@@ -320,7 +326,7 @@ export const PlayersDurationPerDayChart = ({
|
||||
type="monotone"
|
||||
dataKey="over60sPlayersPercent"
|
||||
formatter={percentFormatter}
|
||||
unit={' %'}
|
||||
unit={'%'}
|
||||
stroke={gdevelopTheme.chart.dataColor1}
|
||||
fill={gdevelopTheme.chart.dataColor1}
|
||||
fillOpacity={0.15}
|
||||
@@ -331,7 +337,7 @@ export const PlayersDurationPerDayChart = ({
|
||||
type="monotone"
|
||||
dataKey="over180sPlayersPercent"
|
||||
formatter={percentFormatter}
|
||||
unit={' %'}
|
||||
unit={'%'}
|
||||
stroke={gdevelopTheme.chart.dataColor1}
|
||||
fill={gdevelopTheme.chart.dataColor1}
|
||||
fillOpacity={0.15}
|
||||
@@ -342,7 +348,7 @@ export const PlayersDurationPerDayChart = ({
|
||||
type="monotone"
|
||||
dataKey="over300sPlayersPercent"
|
||||
formatter={percentFormatter}
|
||||
unit={' %'}
|
||||
unit={'%'}
|
||||
stroke={gdevelopTheme.chart.dataColor1}
|
||||
fill={gdevelopTheme.chart.dataColor1}
|
||||
fillOpacity={0.15}
|
||||
@@ -353,7 +359,7 @@ export const PlayersDurationPerDayChart = ({
|
||||
type="monotone"
|
||||
dataKey="over600sPlayersPercent"
|
||||
formatter={percentFormatter}
|
||||
unit={' %'}
|
||||
unit={'%'}
|
||||
stroke={gdevelopTheme.chart.dataColor1}
|
||||
fill={gdevelopTheme.chart.dataColor1}
|
||||
fillOpacity={0.15}
|
||||
@@ -364,7 +370,7 @@ export const PlayersDurationPerDayChart = ({
|
||||
type="monotone"
|
||||
dataKey="over900sPlayersPercent"
|
||||
formatter={percentFormatter}
|
||||
unit={' %'}
|
||||
unit={'%'}
|
||||
stroke={gdevelopTheme.chart.dataColor1}
|
||||
fill={gdevelopTheme.chart.dataColor1}
|
||||
fillOpacity={0.15}
|
||||
@@ -383,7 +389,7 @@ export const PlayersDurationPerDayChart = ({
|
||||
dataKey="over60sPlayersPercent"
|
||||
stroke={gdevelopTheme.chart.textColor}
|
||||
style={styles.tickLabel}
|
||||
unit={' %'}
|
||||
unit={'%'}
|
||||
/>
|
||||
<Tooltip
|
||||
content={props =>
|
||||
|
@@ -3,6 +3,7 @@ import * as React from 'react';
|
||||
import { Trans, t } from '@lingui/macro';
|
||||
import { I18n } from '@lingui/react';
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
import Tooltip from '@material-ui/core/Tooltip';
|
||||
import { formatISO, subDays } from 'date-fns';
|
||||
import { Column, Line } from '../UI/Grid';
|
||||
import {
|
||||
@@ -21,6 +22,7 @@ import PlaceholderError from '../UI/PlaceholderError';
|
||||
import SelectField from '../UI/SelectField';
|
||||
import SelectOption from '../UI/SelectOption';
|
||||
import PlaceholderLoader from '../UI/PlaceholderLoader';
|
||||
import InfoIcon from '../UI/CustomSvgIcons/CircledInfo';
|
||||
import { buildChartData, daysShownForYear } from './GameAnalyticsEvaluator';
|
||||
import {
|
||||
BounceRateChart,
|
||||
@@ -142,6 +144,20 @@ export const GameAnalyticsPanel = ({
|
||||
<Column noMargin alignItems="center" expand>
|
||||
<Text size="block-title" align="center">
|
||||
<Trans>{chartData.overview.playersCount} sessions</Trans>
|
||||
<Tooltip
|
||||
style={{ verticalAlign: 'bottom' }}
|
||||
title={
|
||||
<Text>
|
||||
<Trans>
|
||||
Number of people who launched the game. Viewers are
|
||||
considered players when they stayed at least 60
|
||||
seconds including loading screens.
|
||||
</Trans>
|
||||
</Text>
|
||||
}
|
||||
>
|
||||
<InfoIcon />
|
||||
</Tooltip>
|
||||
</Text>
|
||||
<SessionsChart
|
||||
chartData={chartData}
|
||||
@@ -169,6 +185,19 @@ export const GameAnalyticsPanel = ({
|
||||
{Math.round(chartData.overview.bounceRatePercent)}% bounce
|
||||
rate
|
||||
</Trans>
|
||||
<Tooltip
|
||||
style={{ verticalAlign: 'bottom' }}
|
||||
title={
|
||||
<Text>
|
||||
<Trans>
|
||||
Percentage of people who leave before 60 seconds
|
||||
including loading screens.
|
||||
</Trans>
|
||||
</Text>
|
||||
}
|
||||
>
|
||||
<InfoIcon />
|
||||
</Tooltip>
|
||||
</Text>
|
||||
<BounceRateChart
|
||||
chartData={chartData}
|
||||
@@ -186,6 +215,18 @@ export const GameAnalyticsPanel = ({
|
||||
)}{' '}
|
||||
minutes per player
|
||||
</Trans>
|
||||
<Tooltip
|
||||
style={{ verticalAlign: 'bottom' }}
|
||||
title={
|
||||
<Text>
|
||||
<Trans>
|
||||
Is the average time a player spends in the game.
|
||||
</Trans>
|
||||
</Text>
|
||||
}
|
||||
>
|
||||
<InfoIcon />
|
||||
</Tooltip>
|
||||
</Text>
|
||||
<MeanPlayTimeChart
|
||||
chartData={chartData}
|
||||
@@ -209,6 +250,21 @@ export const GameAnalyticsPanel = ({
|
||||
}{' '}
|
||||
minutes
|
||||
</Trans>
|
||||
<Tooltip
|
||||
style={{ verticalAlign: 'bottom' }}
|
||||
title={
|
||||
<Text>
|
||||
<Trans>
|
||||
Average of players still active after 15 minutes.
|
||||
This graph shows how long players stay in the game
|
||||
after X minutes. It helps to see if the players quit
|
||||
quickly or keep playing for a while.
|
||||
</Trans>
|
||||
</Text>
|
||||
}
|
||||
>
|
||||
<InfoIcon />
|
||||
</Tooltip>
|
||||
</Text>
|
||||
<PlayersRepartitionPerDurationChart
|
||||
chartData={chartData}
|
||||
@@ -232,6 +288,23 @@ export const GameAnalyticsPanel = ({
|
||||
}{' '}
|
||||
minutes
|
||||
</Trans>
|
||||
<Tooltip
|
||||
style={{ verticalAlign: 'bottom' }}
|
||||
title={
|
||||
<Text>
|
||||
<Trans>
|
||||
Shows how long players stay in the game over time.
|
||||
The percentages are showing people playing for more
|
||||
than 3, 5, 10, and 15 minutes based on the best day.
|
||||
A higher value means better player retention on the
|
||||
day. This helps you understand when players are most
|
||||
engaged — and when they drop off quickly.
|
||||
</Trans>
|
||||
</Text>
|
||||
}
|
||||
>
|
||||
<InfoIcon />
|
||||
</Tooltip>
|
||||
</Text>
|
||||
<PlayersDurationPerDayChart
|
||||
chartData={chartData}
|
||||
|
@@ -119,7 +119,7 @@ const AnalyticsWidget = ({ game, onSeeAll, gameMetrics, gameUrl }: Props) => {
|
||||
) : (
|
||||
<Column expand noMargin>
|
||||
<Line alignItems="center" justifyContent="space-between">
|
||||
<Text size="block-title" noMargin>
|
||||
<Text size="sub-title" noMargin>
|
||||
<Trans>Sessions</Trans>
|
||||
</Text>
|
||||
<RaisedButton
|
||||
|
@@ -64,6 +64,7 @@ type Props = {|
|
||||
historyHandler?: HistoryHandler,
|
||||
tileMapTileSelection: ?TileMapTileSelection,
|
||||
onSelectTileMapTile: (?TileMapTileSelection) => void,
|
||||
isVariableListLocked: boolean,
|
||||
|};
|
||||
|
||||
export const CompactInstancePropertiesEditor = ({
|
||||
@@ -83,6 +84,7 @@ export const CompactInstancePropertiesEditor = ({
|
||||
projectScopedContainersAccessor,
|
||||
tileMapTileSelection,
|
||||
onSelectTileMapTile,
|
||||
isVariableListLocked,
|
||||
}: Props) => {
|
||||
const forceUpdate = useForceUpdate();
|
||||
const variablesListRef = React.useRef<?VariablesListInterface>(null);
|
||||
@@ -276,16 +278,18 @@ export const CompactInstancePropertiesEditor = ({
|
||||
>
|
||||
<ShareExternal style={styles.icon} />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={
|
||||
variablesListRef.current
|
||||
? variablesListRef.current.addVariable
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
<Add style={styles.icon} />
|
||||
</IconButton>
|
||||
{isVariableListLocked ? null : (
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={
|
||||
variablesListRef.current
|
||||
? variablesListRef.current.addVariable
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
<Add style={styles.icon} />
|
||||
</IconButton>
|
||||
)}
|
||||
</Line>
|
||||
</Line>
|
||||
</Column>
|
||||
@@ -314,6 +318,7 @@ export const CompactInstancePropertiesEditor = ({
|
||||
compactEmptyPlaceholderText={
|
||||
<Trans>There are no variables on this instance.</Trans>
|
||||
}
|
||||
isListLocked={isVariableListLocked}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
|
@@ -7,14 +7,14 @@ import Rectangle from '../Utils/Rectangle';
|
||||
type Props = {|
|
||||
project: gdProject,
|
||||
layout: gdLayout | null,
|
||||
eventsBasedObject: gdEventsBasedObject | null,
|
||||
eventsBasedObjectVariant: gdEventsBasedObjectVariant | null,
|
||||
toCanvasCoordinates: (x: number, y: number) => [number, number],
|
||||
|};
|
||||
|
||||
export default class WindowBorder {
|
||||
project: gdProject;
|
||||
layout: gdLayout | null;
|
||||
eventsBasedObject: gdEventsBasedObject | null;
|
||||
eventsBasedObjectVariant: gdEventsBasedObjectVariant | null;
|
||||
toCanvasCoordinates: (x: number, y: number) => [number, number];
|
||||
pixiRectangle = new PIXI.Graphics();
|
||||
windowRectangle: Rectangle = new Rectangle();
|
||||
@@ -22,12 +22,12 @@ export default class WindowBorder {
|
||||
constructor({
|
||||
project,
|
||||
layout,
|
||||
eventsBasedObject,
|
||||
eventsBasedObjectVariant,
|
||||
toCanvasCoordinates,
|
||||
}: Props) {
|
||||
this.project = project;
|
||||
this.layout = layout;
|
||||
this.eventsBasedObject = eventsBasedObject;
|
||||
this.eventsBasedObjectVariant = eventsBasedObjectVariant;
|
||||
this.toCanvasCoordinates = toCanvasCoordinates;
|
||||
|
||||
this.pixiRectangle.hitArea = new PIXI.Rectangle(0, 0, 0, 0);
|
||||
@@ -38,15 +38,15 @@ export default class WindowBorder {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { layout, eventsBasedObject } = this;
|
||||
const { layout, eventsBasedObjectVariant } = this;
|
||||
|
||||
this.windowRectangle.set(
|
||||
eventsBasedObject
|
||||
eventsBasedObjectVariant
|
||||
? {
|
||||
left: eventsBasedObject.getAreaMinX(),
|
||||
top: eventsBasedObject.getAreaMinY(),
|
||||
right: eventsBasedObject.getAreaMaxX(),
|
||||
bottom: eventsBasedObject.getAreaMaxY(),
|
||||
left: eventsBasedObjectVariant.getAreaMinX(),
|
||||
top: eventsBasedObjectVariant.getAreaMinY(),
|
||||
right: eventsBasedObjectVariant.getAreaMaxX(),
|
||||
bottom: eventsBasedObjectVariant.getAreaMaxY(),
|
||||
}
|
||||
: {
|
||||
left: 0,
|
||||
@@ -88,7 +88,7 @@ export default class WindowBorder {
|
||||
displayedRectangle.width(),
|
||||
displayedRectangle.height()
|
||||
);
|
||||
if (eventsBasedObject) {
|
||||
if (eventsBasedObjectVariant) {
|
||||
const origin = this.toCanvasCoordinates(0, 0);
|
||||
this.pixiRectangle.drawRect(origin[0] - 8, origin[1] - 1, 16, 2);
|
||||
this.pixiRectangle.drawRect(origin[0] - 1, origin[1] - 8, 2, 16);
|
||||
|
@@ -91,6 +91,7 @@ export type InstancesEditorPropsWithoutSizeAndScroll = {|
|
||||
project: gdProject,
|
||||
layout: gdLayout | null,
|
||||
eventsBasedObject: gdEventsBasedObject | null,
|
||||
eventsBasedObjectVariant: gdEventsBasedObjectVariant | null,
|
||||
layersContainer: gdLayersContainer,
|
||||
globalObjectsContainer: gdObjectsContainer | null,
|
||||
objectsContainer: gdObjectsContainer,
|
||||
@@ -564,7 +565,7 @@ export default class InstancesEditor extends Component<Props, State> {
|
||||
this.windowBorder = new WindowBorder({
|
||||
project: props.project,
|
||||
layout: props.layout,
|
||||
eventsBasedObject: props.eventsBasedObject,
|
||||
eventsBasedObjectVariant: props.eventsBasedObjectVariant,
|
||||
toCanvasCoordinates: this.viewPosition.toCanvasCoordinates,
|
||||
});
|
||||
this.windowMask = new WindowMask({
|
||||
@@ -1477,13 +1478,13 @@ export default class InstancesEditor extends Component<Props, State> {
|
||||
};
|
||||
|
||||
_getAreaRectangle = (): Rectangle => {
|
||||
const { eventsBasedObject, project } = this.props;
|
||||
return eventsBasedObject
|
||||
const { eventsBasedObjectVariant, project } = this.props;
|
||||
return eventsBasedObjectVariant
|
||||
? new Rectangle(
|
||||
eventsBasedObject.getAreaMinX(),
|
||||
eventsBasedObject.getAreaMinY(),
|
||||
eventsBasedObject.getAreaMaxX(),
|
||||
eventsBasedObject.getAreaMaxY()
|
||||
eventsBasedObjectVariant.getAreaMinX(),
|
||||
eventsBasedObjectVariant.getAreaMinY(),
|
||||
eventsBasedObjectVariant.getAreaMaxX(),
|
||||
eventsBasedObjectVariant.getAreaMaxY()
|
||||
)
|
||||
: new Rectangle(
|
||||
0,
|
||||
|
@@ -63,7 +63,8 @@ export type RenderEditorContainerProps = {|
|
||||
) => void,
|
||||
onOpenCustomObjectEditor: (
|
||||
gdEventsFunctionsExtension,
|
||||
gdEventsBasedObject
|
||||
gdEventsBasedObject,
|
||||
variantName: string
|
||||
) => void,
|
||||
openObjectEvents: (extensionName: string, objectName: string) => void,
|
||||
|
||||
@@ -143,7 +144,9 @@ export type RenderEditorContainerProps = {|
|
||||
|
||||
// Object editing
|
||||
openBehaviorEvents: (extensionName: string, behaviorName: string) => void,
|
||||
onEventsBasedObjectChildrenEdited: () => void,
|
||||
onEventsBasedObjectChildrenEdited: (
|
||||
eventsBasedObject: gdEventsBasedObject
|
||||
) => void,
|
||||
onSceneObjectEdited: (
|
||||
scene: gdLayout,
|
||||
objectWithContext: ObjectWithContext
|
||||
@@ -158,7 +161,17 @@ export type RenderEditorContainerProps = {|
|
||||
extensionName: string,
|
||||
eventsBasedObjectName: string
|
||||
) => void,
|
||||
onOpenEventBasedObjectVariantEditor: (
|
||||
extensionName: string,
|
||||
eventsBasedObjectName: string,
|
||||
variantName: string
|
||||
) => void,
|
||||
onExtensionInstalled: (extensionName: string) => void,
|
||||
onDeleteEventsBasedObjectVariant: (
|
||||
eventsFunctionsExtension: gdEventsFunctionsExtension,
|
||||
eventBasedObject: gdEventsBasedObject,
|
||||
variant: gdEventsBasedObjectVariant
|
||||
) => void,
|
||||
|};
|
||||
|
||||
export type RenderEditorContainerPropsWithRef = {|
|
||||
|
@@ -120,9 +120,7 @@ export class CustomObjectEditorContainer extends React.Component<RenderEditorCon
|
||||
getEventsFunctionsExtension(): ?gdEventsFunctionsExtension {
|
||||
const { project, projectItemName } = this.props;
|
||||
if (!project || !projectItemName) return null;
|
||||
const extensionName = gd.PlatformExtension.getExtensionFromFullObjectType(
|
||||
projectItemName
|
||||
);
|
||||
const extensionName = projectItemName.split('::')[0] || '';
|
||||
|
||||
if (!project.hasEventsFunctionsExtensionNamed(extensionName)) {
|
||||
return null;
|
||||
@@ -144,9 +142,7 @@ export class CustomObjectEditorContainer extends React.Component<RenderEditorCon
|
||||
const extension = this.getEventsFunctionsExtension();
|
||||
if (!extension) return null;
|
||||
|
||||
const eventsBasedObjectName = gd.PlatformExtension.getObjectNameFromFullObjectType(
|
||||
projectItemName
|
||||
);
|
||||
const eventsBasedObjectName = projectItemName.split('::')[1] || '';
|
||||
|
||||
if (!extension.getEventsBasedObjects().has(eventsBasedObjectName)) {
|
||||
return null;
|
||||
@@ -154,6 +150,24 @@ export class CustomObjectEditorContainer extends React.Component<RenderEditorCon
|
||||
return extension.getEventsBasedObjects().get(eventsBasedObjectName);
|
||||
}
|
||||
|
||||
getVariantName(): string {
|
||||
const { projectItemName } = this.props;
|
||||
return (projectItemName && projectItemName.split('::')[2]) || '';
|
||||
}
|
||||
|
||||
getVariant(): ?gdEventsBasedObjectVariant {
|
||||
const { project, projectItemName } = this.props;
|
||||
if (!project || !projectItemName) return null;
|
||||
|
||||
const eventsBasedObject = this.getEventsBasedObject();
|
||||
if (!eventsBasedObject) return null;
|
||||
|
||||
const variantName = projectItemName.split('::')[2] || '';
|
||||
return eventsBasedObject.getVariants().hasVariantNamed(variantName)
|
||||
? eventsBasedObject.getVariants().getVariant(variantName)
|
||||
: eventsBasedObject.getDefaultVariant();
|
||||
}
|
||||
|
||||
getEventsBasedObjectName(): ?string {
|
||||
const { project, projectItemName } = this.props;
|
||||
if (!project || !projectItemName) return null;
|
||||
@@ -176,6 +190,9 @@ export class CustomObjectEditorContainer extends React.Component<RenderEditorCon
|
||||
const eventsBasedObject = this.getEventsBasedObject();
|
||||
if (!eventsBasedObject) return null;
|
||||
|
||||
const variant = this.getVariant();
|
||||
if (!variant) return null;
|
||||
|
||||
const projectScopedContainersAccessor = new ProjectScopedContainersAccessor(
|
||||
{
|
||||
project,
|
||||
@@ -197,10 +214,11 @@ export class CustomObjectEditorContainer extends React.Component<RenderEditorCon
|
||||
layout={null}
|
||||
eventsFunctionsExtension={eventsFunctionsExtension}
|
||||
eventsBasedObject={eventsBasedObject}
|
||||
eventsBasedObjectVariant={variant}
|
||||
globalObjectsContainer={null}
|
||||
objectsContainer={eventsBasedObject.getObjects()}
|
||||
layersContainer={eventsBasedObject.getLayers()}
|
||||
initialInstances={eventsBasedObject.getInitialInstances()}
|
||||
objectsContainer={variant.getObjects()}
|
||||
layersContainer={variant.getLayers()}
|
||||
initialInstances={variant.getInitialInstances()}
|
||||
getInitialInstancesEditorSettings={() =>
|
||||
prepareInstancesEditorSettings(
|
||||
{}, // TODO
|
||||
@@ -216,13 +234,24 @@ export class CustomObjectEditorContainer extends React.Component<RenderEditorCon
|
||||
isActive={isActive}
|
||||
hotReloadPreviewButtonProps={this.props.hotReloadPreviewButtonProps}
|
||||
openBehaviorEvents={this.props.openBehaviorEvents}
|
||||
onObjectEdited={this.props.onEventsBasedObjectChildrenEdited}
|
||||
onObjectEdited={() =>
|
||||
this.props.onEventsBasedObjectChildrenEdited(eventsBasedObject)
|
||||
}
|
||||
onObjectGroupEdited={() =>
|
||||
this.props.onEventsBasedObjectChildrenEdited(eventsBasedObject)
|
||||
}
|
||||
onEventsBasedObjectChildrenEdited={
|
||||
this.props.onEventsBasedObjectChildrenEdited
|
||||
}
|
||||
onExtractAsEventBasedObject={this.props.onExtractAsEventBasedObject}
|
||||
onOpenEventBasedObjectEditor={this.props.onOpenEventBasedObjectEditor}
|
||||
onOpenEventBasedObjectVariantEditor={
|
||||
this.props.onOpenEventBasedObjectVariantEditor
|
||||
}
|
||||
onExtensionInstalled={this.props.onExtensionInstalled}
|
||||
onDeleteEventsBasedObjectVariant={
|
||||
this.props.onDeleteEventsBasedObjectVariant
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@@ -169,7 +169,8 @@ export class EventsFunctionsExtensionEditorContainer extends React.Component<Ren
|
||||
onOpenCustomObjectEditor={eventsBasedObject => {
|
||||
this.props.onOpenCustomObjectEditor(
|
||||
eventsFunctionsExtension,
|
||||
eventsBasedObject
|
||||
eventsBasedObject,
|
||||
''
|
||||
);
|
||||
}}
|
||||
hotReloadPreviewButtonProps={this.props.hotReloadPreviewButtonProps}
|
||||
|
@@ -235,6 +235,7 @@ export class ExternalLayoutEditorContainer extends React.Component<
|
||||
layout={layout}
|
||||
eventsFunctionsExtension={null}
|
||||
eventsBasedObject={null}
|
||||
eventsBasedObjectVariant={null}
|
||||
globalObjectsContainer={project.getObjects()}
|
||||
objectsContainer={layout.getObjects()}
|
||||
layersContainer={layout.getLayers()}
|
||||
@@ -259,12 +260,20 @@ export class ExternalLayoutEditorContainer extends React.Component<
|
||||
onOpenEventBasedObjectEditor={
|
||||
this.props.onOpenEventBasedObjectEditor
|
||||
}
|
||||
onOpenEventBasedObjectVariantEditor={
|
||||
this.props.onOpenEventBasedObjectVariantEditor
|
||||
}
|
||||
onObjectEdited={objectWithContext =>
|
||||
this.props.onSceneObjectEdited(layout, objectWithContext)
|
||||
}
|
||||
// It's only used to refresh events-based object variants.
|
||||
onObjectGroupEdited={() => {}}
|
||||
// Nothing to do as events-based objects can't have external layout.
|
||||
onEventsBasedObjectChildrenEdited={() => {}}
|
||||
onExtensionInstalled={this.props.onExtensionInstalled}
|
||||
onDeleteEventsBasedObjectVariant={
|
||||
this.props.onDeleteEventsBasedObjectVariant
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{!layout && (
|
||||
|
@@ -139,7 +139,7 @@ const GuidedLessons = ({ selectInAppTutorial, lessonsIds }: Props) => {
|
||||
id: CAMERA_PARALLAX_IN_APP_TUTORIAL_ID,
|
||||
title: t`Background and cameras`,
|
||||
shortDescription: t`Follow a character with scrolling background.`,
|
||||
description: t`Follow this Castlevania-type chraracter with the camera, while the background scrolls.`,
|
||||
description: t`Follow this Castlevania-type character with the camera, while the background scrolls.`,
|
||||
durationInMinutes: 2,
|
||||
renderImage: props => <Parallax {...props} />,
|
||||
},
|
||||
|
@@ -129,6 +129,7 @@ export class SceneEditorContainer extends React.Component<RenderEditorContainerP
|
||||
layout={layout}
|
||||
eventsFunctionsExtension={null}
|
||||
eventsBasedObject={null}
|
||||
eventsBasedObjectVariant={null}
|
||||
globalObjectsContainer={project.getObjects()}
|
||||
objectsContainer={layout.getObjects()}
|
||||
layersContainer={layout.getLayers()}
|
||||
@@ -149,10 +150,18 @@ export class SceneEditorContainer extends React.Component<RenderEditorContainerP
|
||||
onExtractAsExternalLayout={this.props.onExtractAsExternalLayout}
|
||||
onExtractAsEventBasedObject={this.props.onExtractAsEventBasedObject}
|
||||
onOpenEventBasedObjectEditor={this.props.onOpenEventBasedObjectEditor}
|
||||
onOpenEventBasedObjectVariantEditor={
|
||||
this.props.onOpenEventBasedObjectVariantEditor
|
||||
}
|
||||
onExtensionInstalled={this.props.onExtensionInstalled}
|
||||
onDeleteEventsBasedObjectVariant={
|
||||
this.props.onDeleteEventsBasedObjectVariant
|
||||
}
|
||||
onObjectEdited={objectWithContext =>
|
||||
this.props.onSceneObjectEdited(layout, objectWithContext)
|
||||
}
|
||||
// It's only used to refresh events-based object variants.
|
||||
onObjectGroupEdited={() => {}}
|
||||
// Nothing to do as scenes are not events-based objects.
|
||||
onEventsBasedObjectChildrenEdited={() => {}}
|
||||
/>
|
||||
|
@@ -379,6 +379,29 @@ export const closeCustomObjectTab = (
|
||||
});
|
||||
};
|
||||
|
||||
export const closeEventsBasedObjectVariantTab = (
|
||||
state: EditorTabsState,
|
||||
eventsFunctionsExtensionName: string,
|
||||
eventsBasedObjectName: string,
|
||||
eventsBasedObjectVariantName: string
|
||||
) => {
|
||||
return closeTabsExceptIf(state, editorTab => {
|
||||
const editor = editorTab.editorRef;
|
||||
if (editor instanceof CustomObjectEditorContainer) {
|
||||
return (
|
||||
(!editor.getEventsFunctionsExtensionName() ||
|
||||
editor.getEventsFunctionsExtensionName() !==
|
||||
eventsFunctionsExtensionName) &&
|
||||
(!editor.getEventsBasedObjectName() ||
|
||||
editor.getEventsBasedObjectName() !== eventsBasedObjectName) &&
|
||||
(!editor.getVariantName() ||
|
||||
editor.getVariantName() !== eventsBasedObjectVariantName)
|
||||
);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
};
|
||||
|
||||
export const getEventsFunctionsExtensionEditor = (
|
||||
state: EditorTabsState,
|
||||
eventsFunctionsExtension: gdEventsFunctionsExtension
|
||||
@@ -399,14 +422,16 @@ export const getEventsFunctionsExtensionEditor = (
|
||||
export const getCustomObjectEditor = (
|
||||
state: EditorTabsState,
|
||||
eventsFunctionsExtension: gdEventsFunctionsExtension,
|
||||
eventsBasedObject: gdEventsBasedObject
|
||||
eventsBasedObject: gdEventsBasedObject,
|
||||
variantName: string
|
||||
): ?{| editor: CustomObjectEditorContainer, tabIndex: number |} => {
|
||||
for (let tabIndex = 0; tabIndex < state.editors.length; ++tabIndex) {
|
||||
const editor = state.editors[tabIndex].editorRef;
|
||||
if (
|
||||
editor instanceof CustomObjectEditorContainer &&
|
||||
editor.getEventsFunctionsExtension() === eventsFunctionsExtension &&
|
||||
editor.getEventsBasedObject() === eventsBasedObject
|
||||
editor.getEventsBasedObject() === eventsBasedObject &&
|
||||
editor.getVariantName() === variantName
|
||||
) {
|
||||
return { editor, tabIndex };
|
||||
}
|
||||
|
@@ -48,7 +48,17 @@ const projectHasItem = ({
|
||||
case 'external events':
|
||||
return project.hasExternalEventsNamed(name);
|
||||
case 'custom object':
|
||||
return project.hasEventsBasedObject(name);
|
||||
const nameElements = name.split('::');
|
||||
const objectType = nameElements[0] + '::' + nameElements[1];
|
||||
const variantName = nameElements[2];
|
||||
return (
|
||||
project.hasEventsBasedObject(objectType) &&
|
||||
(!variantName ||
|
||||
project
|
||||
.getEventsBasedObject(objectType)
|
||||
.getVariants()
|
||||
.getVariant(variantName))
|
||||
);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
@@ -39,6 +39,7 @@ import {
|
||||
closeExternalEventsTabs,
|
||||
closeEventsFunctionsExtensionTabs,
|
||||
closeCustomObjectTab,
|
||||
closeEventsBasedObjectVariantTab,
|
||||
saveUiSettings,
|
||||
type EditorTabsState,
|
||||
type EditorTab,
|
||||
@@ -137,7 +138,6 @@ import { type HotReloadPreviewButtonProps } from '../HotReload/HotReloadPreviewB
|
||||
import HotReloadLogsDialog from '../HotReload/HotReloadLogsDialog';
|
||||
import { useDiscordRichPresence } from '../Utils/UpdateDiscordRichPresence';
|
||||
import { delay } from '../Utils/Delay';
|
||||
import { type ExtensionShortHeader } from '../Utils/GDevelopServices/Extension';
|
||||
import useNewProjectDialog from './UseNewProjectDialog';
|
||||
import { findAndLogProjectPreviewErrors } from '../Utils/ProjectErrorsChecker';
|
||||
import { renameResourcesInProject } from '../ResourcesList/ResourceUtils';
|
||||
@@ -589,7 +589,8 @@ const MainFrame = (props: Props) => {
|
||||
: kind === 'layout events'
|
||||
? name + ` ${i18n._(t`(Events)`)}`
|
||||
: kind === 'custom object'
|
||||
? name.split('::')[1] + ` ${i18n._(t`(Object)`)}`
|
||||
? name.split('::')[2] ||
|
||||
name.split('::')[1] + ` ${i18n._(t`(Object)`)}`
|
||||
: name;
|
||||
const tabOptions =
|
||||
kind === 'layout'
|
||||
@@ -1271,12 +1272,12 @@ const MainFrame = (props: Props) => {
|
||||
toolbar.current.setEditorToolbar(editorToolbar);
|
||||
};
|
||||
|
||||
const onInstallExtension = (extensionShortHeader: ExtensionShortHeader) => {
|
||||
const onInstallExtension = (extensionName: string) => {
|
||||
const { currentProject } = state;
|
||||
if (!currentProject) return;
|
||||
|
||||
// Close the extension tab before updating/reinstalling the extension.
|
||||
const eventsFunctionsExtensionName = extensionShortHeader.name;
|
||||
const eventsFunctionsExtensionName = extensionName;
|
||||
|
||||
if (
|
||||
currentProject.hasEventsFunctionsExtensionNamed(
|
||||
@@ -1400,6 +1401,20 @@ const MainFrame = (props: Props) => {
|
||||
};
|
||||
|
||||
const onExtensionInstalled = (extensionName: string) => {
|
||||
const { currentProject } = state;
|
||||
if (!currentProject) {
|
||||
return;
|
||||
}
|
||||
const eventsBasedObjects = currentProject
|
||||
.getEventsFunctionsExtension(extensionName)
|
||||
.getEventsBasedObjects();
|
||||
for (let index = 0; index < eventsBasedObjects.getCount(); index++) {
|
||||
const eventsBasedObject = eventsBasedObjects.getAt(index);
|
||||
gd.EventsBasedObjectVariantHelper.complyVariantsToEventsBasedObject(
|
||||
currentProject,
|
||||
eventsBasedObject
|
||||
);
|
||||
}
|
||||
// TODO Open the closed tabs back
|
||||
// It would be safer to close the tabs before the extension is installed
|
||||
// but it would make opening them back more complicated.
|
||||
@@ -1585,6 +1600,29 @@ const MainFrame = (props: Props) => {
|
||||
}));
|
||||
};
|
||||
|
||||
const deleteEventsBasedObjectVariant = (
|
||||
eventsFunctionsExtension: gdEventsFunctionsExtension,
|
||||
eventBasedObject: gdEventsBasedObject,
|
||||
variant: gdEventsBasedObjectVariant
|
||||
): void => {
|
||||
const variants = eventBasedObject.getVariants();
|
||||
const variantName = variant.getName();
|
||||
if (!variants.hasVariantNamed(variantName)) {
|
||||
return;
|
||||
}
|
||||
variants.removeVariant(variantName);
|
||||
|
||||
setState(state => ({
|
||||
...state,
|
||||
editorTabs: closeEventsBasedObjectVariantTab(
|
||||
state.editorTabs,
|
||||
eventsFunctionsExtension.getName(),
|
||||
eventBasedObject.getName(),
|
||||
variantName
|
||||
),
|
||||
}));
|
||||
};
|
||||
|
||||
const setPreviewedLayout = (
|
||||
previewLayoutName: ?string,
|
||||
previewExternalLayoutName?: ?string
|
||||
@@ -2108,7 +2146,8 @@ const MainFrame = (props: Props) => {
|
||||
const openCustomObjectEditor = React.useCallback(
|
||||
(
|
||||
eventsFunctionsExtension: gdEventsFunctionsExtension,
|
||||
eventsBasedObject: gdEventsBasedObject
|
||||
eventsBasedObject: gdEventsBasedObject,
|
||||
variantName: string
|
||||
) => {
|
||||
const { currentProject, editorTabs } = state;
|
||||
if (!currentProject) return;
|
||||
@@ -2116,7 +2155,8 @@ const MainFrame = (props: Props) => {
|
||||
const foundTab = getCustomObjectEditor(
|
||||
editorTabs,
|
||||
eventsFunctionsExtension,
|
||||
eventsBasedObject
|
||||
eventsBasedObject,
|
||||
variantName
|
||||
);
|
||||
if (foundTab) {
|
||||
setState(state => ({
|
||||
@@ -2133,7 +2173,10 @@ const MainFrame = (props: Props) => {
|
||||
name:
|
||||
eventsFunctionsExtension.getName() +
|
||||
'::' +
|
||||
eventsBasedObject.getName(),
|
||||
eventsBasedObject.getName() +
|
||||
(eventsBasedObject.getVariants().hasVariantNamed(variantName)
|
||||
? '::' + variantName
|
||||
: ''),
|
||||
project: currentProject,
|
||||
}),
|
||||
}),
|
||||
@@ -2217,13 +2260,41 @@ const MainFrame = (props: Props) => {
|
||||
extensionName: string,
|
||||
eventsBasedObjectName: string
|
||||
) => {
|
||||
if (!currentProject) return;
|
||||
if (
|
||||
!currentProject ||
|
||||
!currentProject.hasEventsFunctionsExtensionNamed(extensionName)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
openEventsFunctionsExtension(
|
||||
extensionName,
|
||||
null,
|
||||
null,
|
||||
eventsBasedObjectName
|
||||
);
|
||||
const eventsFunctionsExtension = currentProject.getEventsFunctionsExtension(
|
||||
extensionName
|
||||
);
|
||||
const eventsBasedObjects = eventsFunctionsExtension.getEventsBasedObjects();
|
||||
if (!eventsBasedObjects.has(eventsBasedObjectName)) {
|
||||
return;
|
||||
}
|
||||
const eventsBasedObject = eventsBasedObjects.get(eventsBasedObjectName);
|
||||
openCustomObjectEditor(eventsFunctionsExtension, eventsBasedObject, '');
|
||||
|
||||
// Trigger reloading of extensions as an extension was modified (or even added)
|
||||
// to create the custom object.
|
||||
eventsFunctionsExtensionsState.loadProjectEventsFunctionsExtensions(
|
||||
currentProject
|
||||
);
|
||||
};
|
||||
|
||||
const onOpenEventBasedObjectVariantEditor = (
|
||||
extensionName: string,
|
||||
eventsBasedObjectName: string,
|
||||
variantName: string
|
||||
) => {
|
||||
if (!currentProject) return;
|
||||
if (!currentProject.hasEventsFunctionsExtensionNamed(extensionName)) {
|
||||
return;
|
||||
}
|
||||
@@ -2235,7 +2306,11 @@ const MainFrame = (props: Props) => {
|
||||
return;
|
||||
}
|
||||
const eventsBasedObject = eventsBasedObjects.get(eventsBasedObjectName);
|
||||
openCustomObjectEditor(eventsFunctionsExtension, eventsBasedObject);
|
||||
openCustomObjectEditor(
|
||||
eventsFunctionsExtension,
|
||||
eventsBasedObject,
|
||||
variantName
|
||||
);
|
||||
|
||||
// Trigger reloading of extensions as an extension was modified (or even added)
|
||||
// to create the custom object.
|
||||
@@ -2245,7 +2320,16 @@ const MainFrame = (props: Props) => {
|
||||
};
|
||||
|
||||
const onEventsBasedObjectChildrenEdited = React.useCallback(
|
||||
() => {
|
||||
(eventsBasedObject: gdEventsBasedObject) => {
|
||||
const project = state.currentProject;
|
||||
if (!project) {
|
||||
return;
|
||||
}
|
||||
gd.EventsBasedObjectVariantHelper.complyVariantsToEventsBasedObject(
|
||||
project,
|
||||
eventsBasedObject
|
||||
);
|
||||
|
||||
for (const editor of state.editorTabs.editors) {
|
||||
const { editorRef } = editor;
|
||||
if (editorRef) {
|
||||
@@ -2253,7 +2337,7 @@ const MainFrame = (props: Props) => {
|
||||
}
|
||||
}
|
||||
},
|
||||
[state.editorTabs]
|
||||
[state.editorTabs, state.currentProject]
|
||||
);
|
||||
|
||||
const onSceneObjectEdited = React.useCallback(
|
||||
@@ -3947,6 +4031,8 @@ const MainFrame = (props: Props) => {
|
||||
onExtractAsExternalLayout: onExtractAsExternalLayout,
|
||||
onExtractAsEventBasedObject: onOpenEventBasedObjectEditor,
|
||||
onOpenEventBasedObjectEditor: onOpenEventBasedObjectEditor,
|
||||
onOpenEventBasedObjectVariantEditor: onOpenEventBasedObjectVariantEditor,
|
||||
onDeleteEventsBasedObjectVariant: deleteEventsBasedObjectVariant,
|
||||
onEventsBasedObjectChildrenEdited: onEventsBasedObjectChildrenEdited,
|
||||
onSceneObjectEdited: onSceneObjectEdited,
|
||||
onExtensionInstalled: onExtensionInstalled,
|
||||
|
@@ -157,7 +157,7 @@ const TopLevelCollapsibleSection = ({
|
||||
renderContentAsHiddenWhenFolded?: boolean,
|
||||
noContentMargin?: boolean,
|
||||
onOpenFullEditor: () => void,
|
||||
onAdd?: () => void,
|
||||
onAdd?: (() => void) | null,
|
||||
|}) => (
|
||||
<>
|
||||
<Separator />
|
||||
@@ -216,6 +216,8 @@ type Props = {|
|
||||
objects: Array<gdObject>,
|
||||
onEditObject: (object: gdObject, initialTab: ?ObjectEditorTab) => void,
|
||||
onExtensionInstalled: (extensionName: string) => void,
|
||||
isVariableListLocked: boolean,
|
||||
isBehaviorListLocked: boolean,
|
||||
|};
|
||||
|
||||
export const CompactObjectPropertiesEditor = ({
|
||||
@@ -234,6 +236,8 @@ export const CompactObjectPropertiesEditor = ({
|
||||
objects,
|
||||
onEditObject,
|
||||
onExtensionInstalled,
|
||||
isVariableListLocked,
|
||||
isBehaviorListLocked,
|
||||
}: Props) => {
|
||||
const forceUpdate = useForceUpdate();
|
||||
const [
|
||||
@@ -545,7 +549,7 @@ export const CompactObjectPropertiesEditor = ({
|
||||
isFolded={isBehaviorsFolded}
|
||||
toggleFolded={() => setIsBehaviorsFolded(!isBehaviorsFolded)}
|
||||
onOpenFullEditor={() => onEditObject(object, 'behaviors')}
|
||||
onAdd={openNewBehaviorDialog}
|
||||
onAdd={isBehaviorListLocked ? null : openNewBehaviorDialog}
|
||||
renderContent={() => (
|
||||
<ColumnStackLayout noMargin>
|
||||
{!allVisibleBehaviors.length && (
|
||||
@@ -618,12 +622,16 @@ export const CompactObjectPropertiesEditor = ({
|
||||
isFolded={isVariablesFolded}
|
||||
toggleFolded={() => setIsVariablesFolded(!isVariablesFolded)}
|
||||
onOpenFullEditor={() => onEditObject(object, 'variables')}
|
||||
onAdd={() => {
|
||||
if (variablesListRef.current) {
|
||||
variablesListRef.current.addVariable();
|
||||
}
|
||||
setIsVariablesFolded(false);
|
||||
}}
|
||||
onAdd={
|
||||
isVariableListLocked
|
||||
? null
|
||||
: () => {
|
||||
if (variablesListRef.current) {
|
||||
variablesListRef.current.addVariable();
|
||||
}
|
||||
setIsVariablesFolded(false);
|
||||
}
|
||||
}
|
||||
renderContentAsHiddenWhenFolded={
|
||||
true /* Allows to keep a ref to the variables list for add button to work. */
|
||||
}
|
||||
@@ -664,6 +672,7 @@ export const CompactObjectPropertiesEditor = ({
|
||||
on this object.
|
||||
</Trans>
|
||||
}
|
||||
isListLocked={isVariableListLocked}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
@@ -1,534 +0,0 @@
|
||||
// @flow
|
||||
import { Trans } from '@lingui/macro';
|
||||
import { t } from '@lingui/macro';
|
||||
import { I18n } from '@lingui/react';
|
||||
|
||||
import * as React from 'react';
|
||||
import PropertiesEditor from '../../PropertiesEditor';
|
||||
import propertiesMapToSchema from '../../PropertiesEditor/PropertiesMapToSchema';
|
||||
import EmptyMessage from '../../UI/EmptyMessage';
|
||||
import { type EditorProps } from './EditorProps.flow';
|
||||
import { Column, Line } from '../../UI/Grid';
|
||||
import { getExtraObjectsInformation } from '../../Hints';
|
||||
import { getObjectTutorialIds } from '../../Utils/GDevelopServices/Tutorial';
|
||||
import AlertMessage from '../../UI/AlertMessage';
|
||||
import { ColumnStackLayout } from '../../UI/Layout';
|
||||
import DismissableTutorialMessage from '../../Hints/DismissableTutorialMessage';
|
||||
import { mapFor } from '../../Utils/MapFor';
|
||||
import ObjectsEditorService from '../ObjectsEditorService';
|
||||
import Text from '../../UI/Text';
|
||||
import useForceUpdate from '../../Utils/UseForceUpdate';
|
||||
import { Accordion, AccordionHeader, AccordionBody } from '../../UI/Accordion';
|
||||
import { IconContainer } from '../../UI/IconContainer';
|
||||
import PreferencesContext from '../../MainFrame/Preferences/PreferencesContext';
|
||||
import AnimationList, {
|
||||
type AnimationListInterface,
|
||||
} from './SpriteEditor/AnimationList';
|
||||
import PointsEditor from './SpriteEditor/PointsEditor';
|
||||
import CollisionMasksEditor from './SpriteEditor/CollisionMasksEditor';
|
||||
import {
|
||||
hasAnyFrame,
|
||||
getFirstAnimationFrame,
|
||||
setCollisionMaskOnAllFrames,
|
||||
} from './SpriteEditor/Utils/SpriteObjectHelper';
|
||||
import { getMatchingCollisionMask } from './SpriteEditor/CollisionMasksEditor/CollisionMaskHelper';
|
||||
import ResourcesLoader from '../../ResourcesLoader';
|
||||
import ScrollView, { type ScrollViewInterface } from '../../UI/ScrollView';
|
||||
import FlatButton from '../../UI/FlatButton';
|
||||
import RaisedButton from '../../UI/RaisedButton';
|
||||
import FlatButtonWithSplitMenu from '../../UI/FlatButtonWithSplitMenu';
|
||||
import { ResponsiveLineStackLayout } from '../../UI/Layout';
|
||||
import { useResponsiveWindowSize } from '../../UI/Responsive/ResponsiveWindowMeasurer';
|
||||
import Add from '../../UI/CustomSvgIcons/Add';
|
||||
import Dialog from '../../UI/Dialog';
|
||||
import HelpButton from '../../UI/HelpButton';
|
||||
import RestoreIcon from '../../UI/CustomSvgIcons/Restore';
|
||||
|
||||
const gd: libGDevelop = global.gd;
|
||||
|
||||
const styles = {
|
||||
icon: { width: 16, height: 16 },
|
||||
};
|
||||
|
||||
type Props = EditorProps;
|
||||
|
||||
const CustomObjectPropertiesEditor = (props: Props) => {
|
||||
const forceUpdate = useForceUpdate();
|
||||
|
||||
const {
|
||||
objectConfiguration,
|
||||
project,
|
||||
layout,
|
||||
eventsFunctionsExtension,
|
||||
eventsBasedObject,
|
||||
object,
|
||||
objectName,
|
||||
resourceManagementProps,
|
||||
onSizeUpdated,
|
||||
onObjectUpdated,
|
||||
unsavedChanges,
|
||||
renderObjectNameField,
|
||||
isChildObject,
|
||||
} = props;
|
||||
|
||||
const { isMobile } = useResponsiveWindowSize();
|
||||
|
||||
const customObjectConfiguration = gd.asCustomObjectConfiguration(
|
||||
objectConfiguration
|
||||
);
|
||||
const properties = customObjectConfiguration.getProperties();
|
||||
|
||||
const propertiesSchema = propertiesMapToSchema(
|
||||
properties,
|
||||
object => object.getProperties(),
|
||||
(object, name, value) => object.updateProperty(name, value)
|
||||
);
|
||||
|
||||
const extraInformation = getExtraObjectsInformation()[
|
||||
customObjectConfiguration.getType()
|
||||
];
|
||||
|
||||
const { values } = React.useContext(PreferencesContext);
|
||||
const tutorialIds = getObjectTutorialIds(customObjectConfiguration.getType());
|
||||
|
||||
const eventBasedObject = project.hasEventsBasedObject(
|
||||
customObjectConfiguration.getType()
|
||||
)
|
||||
? project.getEventsBasedObject(customObjectConfiguration.getType())
|
||||
: null;
|
||||
|
||||
const animations = customObjectConfiguration.getAnimations();
|
||||
|
||||
// The matching collision mask only takes the first sprite of the first
|
||||
// animation of the object. We consider this is enough to start with, and
|
||||
// the user can then edit the collision mask for further needs.
|
||||
const onCreateMatchingSpriteCollisionMask = React.useCallback(
|
||||
async () => {
|
||||
const firstSprite = getFirstAnimationFrame(animations);
|
||||
if (!firstSprite) {
|
||||
return;
|
||||
}
|
||||
const firstSpriteResourceName = firstSprite.getImageName();
|
||||
const firstAnimationResourceSource = ResourcesLoader.getResourceFullUrl(
|
||||
project,
|
||||
firstSpriteResourceName,
|
||||
{}
|
||||
);
|
||||
let matchingCollisionMask = null;
|
||||
try {
|
||||
matchingCollisionMask = await getMatchingCollisionMask(
|
||||
firstAnimationResourceSource
|
||||
);
|
||||
} catch (e) {
|
||||
console.error(
|
||||
'Unable to create a matching collision mask for the sprite, fallback to full image collision mask.',
|
||||
e
|
||||
);
|
||||
}
|
||||
setCollisionMaskOnAllFrames(animations, matchingCollisionMask);
|
||||
forceUpdate();
|
||||
},
|
||||
[animations, project, forceUpdate]
|
||||
);
|
||||
|
||||
const scrollView = React.useRef<?ScrollViewInterface>(null);
|
||||
const animationList = React.useRef<?AnimationListInterface>(null);
|
||||
|
||||
const [
|
||||
justAddedAnimationName,
|
||||
setJustAddedAnimationName,
|
||||
] = React.useState<?string>(null);
|
||||
const justAddedAnimationElement = React.useRef<?any>(null);
|
||||
|
||||
React.useEffect(
|
||||
() => {
|
||||
if (
|
||||
scrollView.current &&
|
||||
justAddedAnimationElement.current &&
|
||||
justAddedAnimationName
|
||||
) {
|
||||
scrollView.current.scrollTo(justAddedAnimationElement.current);
|
||||
setJustAddedAnimationName(null);
|
||||
justAddedAnimationElement.current = null;
|
||||
}
|
||||
},
|
||||
[justAddedAnimationName]
|
||||
);
|
||||
|
||||
const [pointsEditorOpen, setPointsEditorOpen] = React.useState(false);
|
||||
const [
|
||||
collisionMasksEditorOpen,
|
||||
setCollisionMasksEditorOpen,
|
||||
] = React.useState(false);
|
||||
|
||||
return (
|
||||
<I18n>
|
||||
{({ i18n }) => (
|
||||
<>
|
||||
<ScrollView ref={scrollView}>
|
||||
<ColumnStackLayout noMargin>
|
||||
{renderObjectNameField && renderObjectNameField()}
|
||||
{tutorialIds.map(tutorialId => (
|
||||
<DismissableTutorialMessage
|
||||
key={tutorialId}
|
||||
tutorialId={tutorialId}
|
||||
/>
|
||||
))}
|
||||
{propertiesSchema.length ||
|
||||
(eventBasedObject &&
|
||||
(eventBasedObject.getObjects().getObjectsCount() ||
|
||||
eventBasedObject.isAnimatable())) ? (
|
||||
<React.Fragment>
|
||||
{extraInformation ? (
|
||||
<Line>
|
||||
<ColumnStackLayout noMargin>
|
||||
{extraInformation.map(({ kind, message }, index) => (
|
||||
<AlertMessage kind={kind} key={index}>
|
||||
{i18n._(message)}
|
||||
</AlertMessage>
|
||||
))}
|
||||
</ColumnStackLayout>
|
||||
</Line>
|
||||
) : null}
|
||||
<PropertiesEditor
|
||||
unsavedChanges={unsavedChanges}
|
||||
schema={propertiesSchema}
|
||||
instances={[customObjectConfiguration]}
|
||||
project={project}
|
||||
resourceManagementProps={resourceManagementProps}
|
||||
/>
|
||||
{eventBasedObject &&
|
||||
(!customObjectConfiguration.isForcedToOverrideEventsBasedObjectChildrenConfiguration() &&
|
||||
!customObjectConfiguration.isMarkedAsOverridingEventsBasedObjectChildrenConfiguration() ? (
|
||||
<Line alignItems="center">
|
||||
<Column expand noMargin>
|
||||
<Text size="block-title">Children objects</Text>
|
||||
</Column>
|
||||
<Column alignItems="right">
|
||||
<FlatButton
|
||||
label={
|
||||
<Trans>Override children configuration</Trans>
|
||||
}
|
||||
onClick={() => {
|
||||
customObjectConfiguration.setMarkedAsOverridingEventsBasedObjectChildrenConfiguration(
|
||||
true
|
||||
);
|
||||
customObjectConfiguration.clearChildrenConfiguration();
|
||||
if (onObjectUpdated) {
|
||||
onObjectUpdated();
|
||||
}
|
||||
forceUpdate();
|
||||
}}
|
||||
/>
|
||||
</Column>
|
||||
</Line>
|
||||
) : (
|
||||
<>
|
||||
{!customObjectConfiguration.isForcedToOverrideEventsBasedObjectChildrenConfiguration() && (
|
||||
<Line alignItems="center">
|
||||
<Column expand noMargin>
|
||||
<Text size="block-title">Children objects</Text>
|
||||
</Column>
|
||||
<Column alignItems="right">
|
||||
<FlatButton
|
||||
leftIcon={<RestoreIcon style={styles.icon} />}
|
||||
label={
|
||||
<Trans>
|
||||
Reset and hide children configuration
|
||||
</Trans>
|
||||
}
|
||||
onClick={() => {
|
||||
customObjectConfiguration.setMarkedAsOverridingEventsBasedObjectChildrenConfiguration(
|
||||
false
|
||||
);
|
||||
customObjectConfiguration.clearChildrenConfiguration();
|
||||
if (onObjectUpdated) {
|
||||
onObjectUpdated();
|
||||
}
|
||||
forceUpdate();
|
||||
}}
|
||||
/>
|
||||
</Column>
|
||||
</Line>
|
||||
)}
|
||||
{mapFor(
|
||||
0,
|
||||
eventBasedObject.getObjects().getObjectsCount(),
|
||||
i => {
|
||||
const childObject = eventBasedObject
|
||||
.getObjects()
|
||||
.getObjectAt(i);
|
||||
const childObjectConfiguration = customObjectConfiguration.getChildObjectConfiguration(
|
||||
childObject.getName()
|
||||
);
|
||||
const editorConfiguration = ObjectsEditorService.getEditorConfiguration(
|
||||
project,
|
||||
childObjectConfiguration.getType()
|
||||
);
|
||||
const EditorComponent =
|
||||
editorConfiguration.component;
|
||||
|
||||
const objectMetadata = gd.MetadataProvider.getObjectMetadata(
|
||||
gd.JsPlatform.get(),
|
||||
childObjectConfiguration.getType()
|
||||
);
|
||||
const iconUrl = objectMetadata.getIconFilename();
|
||||
const tutorialIds = getObjectTutorialIds(
|
||||
childObjectConfiguration.getType()
|
||||
);
|
||||
const enabledTutorialIds = tutorialIds.filter(
|
||||
tutorialId =>
|
||||
!values.hiddenTutorialHints[tutorialId]
|
||||
);
|
||||
// TODO EBO: Add a protection against infinite loops in case
|
||||
// of object cycles (thought it should be forbidden).
|
||||
return (
|
||||
<Accordion
|
||||
key={childObject.getName()}
|
||||
defaultExpanded
|
||||
>
|
||||
<AccordionHeader>
|
||||
{iconUrl ? (
|
||||
<IconContainer
|
||||
src={iconUrl}
|
||||
alt={childObject.getName()}
|
||||
size={20}
|
||||
/>
|
||||
) : null}
|
||||
<Column expand>
|
||||
<Text size="block-title">
|
||||
{childObject.getName()}
|
||||
</Text>
|
||||
</Column>
|
||||
</AccordionHeader>
|
||||
<AccordionBody>
|
||||
<Column expand noMargin noOverflowParent>
|
||||
{enabledTutorialIds.length ? (
|
||||
<Line>
|
||||
<ColumnStackLayout expand>
|
||||
{tutorialIds.map(tutorialId => (
|
||||
<DismissableTutorialMessage
|
||||
key={tutorialId}
|
||||
tutorialId={tutorialId}
|
||||
/>
|
||||
))}
|
||||
</ColumnStackLayout>
|
||||
</Line>
|
||||
) : null}
|
||||
<Line noMargin>
|
||||
<Column expand>
|
||||
<EditorComponent
|
||||
isChildObject
|
||||
objectConfiguration={
|
||||
childObjectConfiguration
|
||||
}
|
||||
project={project}
|
||||
layout={layout}
|
||||
eventsFunctionsExtension={
|
||||
eventsFunctionsExtension
|
||||
}
|
||||
eventsBasedObject={eventsBasedObject}
|
||||
resourceManagementProps={
|
||||
resourceManagementProps
|
||||
}
|
||||
onSizeUpdated={
|
||||
forceUpdate /*Force update to ensure dialog is properly positioned*/
|
||||
}
|
||||
objectName={
|
||||
objectName +
|
||||
' ' +
|
||||
childObject.getName()
|
||||
}
|
||||
onObjectUpdated={onObjectUpdated}
|
||||
unsavedChanges={unsavedChanges}
|
||||
/>
|
||||
</Column>
|
||||
</Line>
|
||||
</Column>
|
||||
</AccordionBody>
|
||||
</Accordion>
|
||||
);
|
||||
}
|
||||
)}
|
||||
</>
|
||||
))}
|
||||
{eventBasedObject && eventBasedObject.isAnimatable() && (
|
||||
<Column expand>
|
||||
<Text size="block-title">
|
||||
<Trans>Animations</Trans>
|
||||
</Text>
|
||||
<AnimationList
|
||||
ref={animationList}
|
||||
animations={animations}
|
||||
project={project}
|
||||
layout={layout}
|
||||
eventsFunctionsExtension={eventsFunctionsExtension}
|
||||
eventsBasedObject={eventsBasedObject}
|
||||
object={object}
|
||||
objectName={objectName}
|
||||
resourceManagementProps={resourceManagementProps}
|
||||
onSizeUpdated={onSizeUpdated}
|
||||
onObjectUpdated={onObjectUpdated}
|
||||
isAnimationListLocked={false}
|
||||
scrollView={scrollView}
|
||||
onCreateMatchingSpriteCollisionMask={
|
||||
onCreateMatchingSpriteCollisionMask
|
||||
}
|
||||
/>
|
||||
</Column>
|
||||
)}
|
||||
</React.Fragment>
|
||||
) : (
|
||||
<EmptyMessage>
|
||||
<Trans>
|
||||
There is nothing to configure for this object. You can still
|
||||
use events to interact with the object.
|
||||
</Trans>
|
||||
</EmptyMessage>
|
||||
)}
|
||||
</ColumnStackLayout>
|
||||
</ScrollView>
|
||||
{eventBasedObject &&
|
||||
eventBasedObject.isAnimatable() &&
|
||||
!isChildObject && (
|
||||
<Column noMargin>
|
||||
<ResponsiveLineStackLayout
|
||||
justifyContent="space-between"
|
||||
noColumnMargin
|
||||
>
|
||||
{!isMobile ? ( // On mobile, use only 1 button to gain space.
|
||||
<ResponsiveLineStackLayout noMargin noColumnMargin>
|
||||
<FlatButton
|
||||
label={<Trans>Edit collision masks</Trans>}
|
||||
onClick={() => setCollisionMasksEditorOpen(true)}
|
||||
disabled={!hasAnyFrame(animations)}
|
||||
/>
|
||||
<FlatButton
|
||||
label={<Trans>Edit points</Trans>}
|
||||
onClick={() => setPointsEditorOpen(true)}
|
||||
disabled={!hasAnyFrame(animations)}
|
||||
/>
|
||||
</ResponsiveLineStackLayout>
|
||||
) : (
|
||||
<FlatButtonWithSplitMenu
|
||||
label={<Trans>Edit collision masks</Trans>}
|
||||
onClick={() => setCollisionMasksEditorOpen(true)}
|
||||
disabled={!hasAnyFrame(animations)}
|
||||
buildMenuTemplate={i18n => [
|
||||
{
|
||||
label: i18n._(t`Edit points`),
|
||||
disabled: !hasAnyFrame(animations),
|
||||
click: () => setPointsEditorOpen(true),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
<RaisedButton
|
||||
label={<Trans>Add an animation</Trans>}
|
||||
primary
|
||||
onClick={() => {
|
||||
if (!animationList.current) {
|
||||
return;
|
||||
}
|
||||
animationList.current.addAnimation();
|
||||
}}
|
||||
icon={<Add />}
|
||||
/>
|
||||
</ResponsiveLineStackLayout>
|
||||
</Column>
|
||||
)}
|
||||
{pointsEditorOpen && (
|
||||
<Dialog
|
||||
title={<Trans>Edit points</Trans>}
|
||||
actions={[
|
||||
<RaisedButton
|
||||
key="apply"
|
||||
label={<Trans>Apply</Trans>}
|
||||
primary
|
||||
onClick={() => setPointsEditorOpen(false)}
|
||||
/>,
|
||||
]}
|
||||
secondaryActions={[
|
||||
<HelpButton
|
||||
helpPagePath="/objects/sprite/edit-points"
|
||||
key="help"
|
||||
/>,
|
||||
]}
|
||||
onRequestClose={() => setPointsEditorOpen(false)}
|
||||
maxWidth="lg"
|
||||
flexBody
|
||||
fullHeight
|
||||
open={pointsEditorOpen}
|
||||
>
|
||||
<PointsEditor
|
||||
animations={animations}
|
||||
resourcesLoader={ResourcesLoader}
|
||||
project={project}
|
||||
onPointsUpdated={onObjectUpdated}
|
||||
onRenamedPoint={(oldName, newName) => {
|
||||
if (!object) {
|
||||
return;
|
||||
}
|
||||
if (layout) {
|
||||
gd.WholeProjectRefactorer.renameObjectPointInScene(
|
||||
project,
|
||||
layout,
|
||||
object,
|
||||
oldName,
|
||||
newName
|
||||
);
|
||||
} else if (eventsFunctionsExtension && eventsBasedObject) {
|
||||
gd.WholeProjectRefactorer.renameObjectPointInEventsBasedObject(
|
||||
project,
|
||||
eventsFunctionsExtension,
|
||||
eventsBasedObject,
|
||||
object,
|
||||
oldName,
|
||||
newName
|
||||
);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Dialog>
|
||||
)}
|
||||
{collisionMasksEditorOpen && (
|
||||
<Dialog
|
||||
title={<Trans>Edit collision masks</Trans>}
|
||||
actions={[
|
||||
<RaisedButton
|
||||
key="apply"
|
||||
label={<Trans>Apply</Trans>}
|
||||
primary
|
||||
onClick={() => setCollisionMasksEditorOpen(false)}
|
||||
/>,
|
||||
]}
|
||||
secondaryActions={[
|
||||
<HelpButton
|
||||
helpPagePath="/objects/sprite/collision-mask"
|
||||
key="help"
|
||||
/>,
|
||||
]}
|
||||
maxWidth="lg"
|
||||
flexBody
|
||||
fullHeight
|
||||
onRequestClose={() => setCollisionMasksEditorOpen(false)}
|
||||
open={collisionMasksEditorOpen}
|
||||
>
|
||||
<CollisionMasksEditor
|
||||
animations={animations}
|
||||
resourcesLoader={ResourcesLoader}
|
||||
project={project}
|
||||
onMasksUpdated={onObjectUpdated}
|
||||
onCreateMatchingSpriteCollisionMask={
|
||||
onCreateMatchingSpriteCollisionMask
|
||||
}
|
||||
/>
|
||||
</Dialog>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</I18n>
|
||||
);
|
||||
};
|
||||
|
||||
export default CustomObjectPropertiesEditor;
|
@@ -0,0 +1,68 @@
|
||||
// @flow
|
||||
import { t, Trans } from '@lingui/macro';
|
||||
import React from 'react';
|
||||
import FlatButton from '../../../UI/FlatButton';
|
||||
import Dialog, { DialogPrimaryButton } from '../../../UI/Dialog';
|
||||
import SemiControlledTextField from '../../../UI/SemiControlledTextField';
|
||||
import HelpButton from '../../../UI/HelpButton';
|
||||
|
||||
type Props = {|
|
||||
initialName: string,
|
||||
onApply: (variantName: string) => void,
|
||||
onCancel: () => void,
|
||||
|};
|
||||
|
||||
const NewVariantDialog = ({ initialName, onApply, onCancel }: Props) => {
|
||||
const [variantName, setVariantName] = React.useState<string>(initialName);
|
||||
|
||||
const apply = React.useCallback(
|
||||
() => {
|
||||
onApply(variantName);
|
||||
},
|
||||
[onApply, variantName]
|
||||
);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
title={<Trans>Create a new variant</Trans>}
|
||||
id="create-variant-dialog"
|
||||
actions={[
|
||||
<FlatButton
|
||||
key="cancel"
|
||||
label={<Trans>Cancel</Trans>}
|
||||
onClick={onCancel}
|
||||
/>,
|
||||
<DialogPrimaryButton
|
||||
key="apply"
|
||||
label={<Trans>Create</Trans>}
|
||||
primary
|
||||
onClick={apply}
|
||||
/>,
|
||||
]}
|
||||
secondaryActions={[
|
||||
<HelpButton
|
||||
key="help-button"
|
||||
helpPagePath="/objects/custom-objects-prefab-template"
|
||||
/>,
|
||||
]}
|
||||
onRequestClose={onCancel}
|
||||
onApply={apply}
|
||||
open
|
||||
maxWidth="sm"
|
||||
>
|
||||
<SemiControlledTextField
|
||||
fullWidth
|
||||
id="variant-name"
|
||||
commitOnBlur
|
||||
floatingLabelText={<Trans>Variant name</Trans>}
|
||||
floatingLabelFixed
|
||||
value={variantName}
|
||||
translatableHintText={t`Variant name`}
|
||||
onChange={setVariantName}
|
||||
autoFocus="desktop"
|
||||
/>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default NewVariantDialog;
|
@@ -0,0 +1,756 @@
|
||||
// @flow
|
||||
import { Trans } from '@lingui/macro';
|
||||
import { t } from '@lingui/macro';
|
||||
import { I18n } from '@lingui/react';
|
||||
import { type I18n as I18nType } from '@lingui/core';
|
||||
|
||||
import * as React from 'react';
|
||||
import PropertiesEditor from '../../../PropertiesEditor';
|
||||
import propertiesMapToSchema from '../../../PropertiesEditor/PropertiesMapToSchema';
|
||||
import EmptyMessage from '../../../UI/EmptyMessage';
|
||||
import { type EditorProps } from '../EditorProps.flow';
|
||||
import { Column, Line } from '../../../UI/Grid';
|
||||
import { getExtraObjectsInformation } from '../../../Hints';
|
||||
import { getObjectTutorialIds } from '../../../Utils/GDevelopServices/Tutorial';
|
||||
import AlertMessage from '../../../UI/AlertMessage';
|
||||
import DismissableTutorialMessage from '../../../Hints/DismissableTutorialMessage';
|
||||
import { mapFor } from '../../../Utils/MapFor';
|
||||
import ObjectsEditorService from '../../ObjectsEditorService';
|
||||
import Text from '../../../UI/Text';
|
||||
import useForceUpdate from '../../../Utils/UseForceUpdate';
|
||||
import {
|
||||
Accordion,
|
||||
AccordionHeader,
|
||||
AccordionBody,
|
||||
} from '../../../UI/Accordion';
|
||||
import { IconContainer } from '../../../UI/IconContainer';
|
||||
import PreferencesContext from '../../../MainFrame/Preferences/PreferencesContext';
|
||||
import AnimationList, {
|
||||
type AnimationListInterface,
|
||||
} from '../SpriteEditor/AnimationList';
|
||||
import PointsEditor from '../SpriteEditor/PointsEditor';
|
||||
import CollisionMasksEditor from '../SpriteEditor/CollisionMasksEditor';
|
||||
import {
|
||||
hasAnyFrame,
|
||||
getFirstAnimationFrame,
|
||||
setCollisionMaskOnAllFrames,
|
||||
} from '../SpriteEditor/Utils/SpriteObjectHelper';
|
||||
import { getMatchingCollisionMask } from '../SpriteEditor/CollisionMasksEditor/CollisionMaskHelper';
|
||||
import ResourcesLoader from '../../../ResourcesLoader';
|
||||
import ScrollView, { type ScrollViewInterface } from '../../../UI/ScrollView';
|
||||
import FlatButton from '../../../UI/FlatButton';
|
||||
import RaisedButton from '../../../UI/RaisedButton';
|
||||
import FlatButtonWithSplitMenu from '../../../UI/FlatButtonWithSplitMenu';
|
||||
import {
|
||||
ResponsiveLineStackLayout,
|
||||
LineStackLayout,
|
||||
ColumnStackLayout,
|
||||
} from '../../../UI/Layout';
|
||||
import { useResponsiveWindowSize } from '../../../UI/Responsive/ResponsiveWindowMeasurer';
|
||||
import Add from '../../../UI/CustomSvgIcons/Add';
|
||||
import Trash from '../../../UI/CustomSvgIcons/Trash';
|
||||
import Edit from '../../../UI/CustomSvgIcons/ShareExternal';
|
||||
import Dialog from '../../../UI/Dialog';
|
||||
import HelpButton from '../../../UI/HelpButton';
|
||||
import RestoreIcon from '../../../UI/CustomSvgIcons/Restore';
|
||||
import SelectField from '../../../UI/SelectField';
|
||||
import SelectOption from '../../../UI/SelectOption';
|
||||
import NewVariantDialog from './NewVariantDialog';
|
||||
import newNameGenerator from '../../../Utils/NewNameGenerator';
|
||||
import {
|
||||
serializeToJSObject,
|
||||
unserializeFromJSObject,
|
||||
} from '../../../Utils/Serializer';
|
||||
|
||||
const gd: libGDevelop = global.gd;
|
||||
|
||||
const styles = {
|
||||
icon: { width: 16, height: 16 },
|
||||
};
|
||||
|
||||
const getVariantName = (
|
||||
eventBasedObject: gdEventsBasedObject | null,
|
||||
customObjectConfiguration: gdCustomObjectConfiguration
|
||||
): string =>
|
||||
eventBasedObject &&
|
||||
eventBasedObject
|
||||
.getVariants()
|
||||
.hasVariantNamed(customObjectConfiguration.getVariantName())
|
||||
? customObjectConfiguration.getVariantName()
|
||||
: '';
|
||||
|
||||
const getVariant = (
|
||||
eventBasedObject: gdEventsBasedObject,
|
||||
customObjectConfiguration: gdCustomObjectConfiguration
|
||||
): gdEventsBasedObjectVariant => {
|
||||
const variantName = getVariantName(
|
||||
eventBasedObject,
|
||||
customObjectConfiguration
|
||||
);
|
||||
const variants = eventBasedObject.getVariants();
|
||||
return variantName
|
||||
? variants.getVariant(variantName)
|
||||
: eventBasedObject.getDefaultVariant();
|
||||
};
|
||||
|
||||
type Props = EditorProps;
|
||||
|
||||
const CustomObjectPropertiesEditor = (props: Props) => {
|
||||
const forceUpdate = useForceUpdate();
|
||||
|
||||
const {
|
||||
objectConfiguration,
|
||||
project,
|
||||
layout,
|
||||
eventsFunctionsExtension,
|
||||
eventsBasedObject,
|
||||
object,
|
||||
objectName,
|
||||
resourceManagementProps,
|
||||
onSizeUpdated,
|
||||
onObjectUpdated,
|
||||
unsavedChanges,
|
||||
renderObjectNameField,
|
||||
isChildObject,
|
||||
onOpenEventBasedObjectVariantEditor,
|
||||
onDeleteEventsBasedObjectVariant,
|
||||
} = props;
|
||||
|
||||
const { isMobile } = useResponsiveWindowSize();
|
||||
|
||||
const customObjectConfiguration = gd.asCustomObjectConfiguration(
|
||||
objectConfiguration
|
||||
);
|
||||
const properties = customObjectConfiguration.getProperties();
|
||||
|
||||
const propertiesSchema = propertiesMapToSchema(
|
||||
properties,
|
||||
object => object.getProperties(),
|
||||
(object, name, value) => object.updateProperty(name, value)
|
||||
);
|
||||
|
||||
const extraInformation = getExtraObjectsInformation()[
|
||||
customObjectConfiguration.getType()
|
||||
];
|
||||
|
||||
const { values } = React.useContext(PreferencesContext);
|
||||
const tutorialIds = getObjectTutorialIds(customObjectConfiguration.getType());
|
||||
|
||||
const eventBasedObject = project.hasEventsBasedObject(
|
||||
customObjectConfiguration.getType()
|
||||
)
|
||||
? project.getEventsBasedObject(customObjectConfiguration.getType())
|
||||
: null;
|
||||
|
||||
const animations = customObjectConfiguration.getAnimations();
|
||||
|
||||
// The matching collision mask only takes the first sprite of the first
|
||||
// animation of the object. We consider this is enough to start with, and
|
||||
// the user can then edit the collision mask for further needs.
|
||||
const onCreateMatchingSpriteCollisionMask = React.useCallback(
|
||||
async () => {
|
||||
const firstSprite = getFirstAnimationFrame(animations);
|
||||
if (!firstSprite) {
|
||||
return;
|
||||
}
|
||||
const firstSpriteResourceName = firstSprite.getImageName();
|
||||
const firstAnimationResourceSource = ResourcesLoader.getResourceFullUrl(
|
||||
project,
|
||||
firstSpriteResourceName,
|
||||
{}
|
||||
);
|
||||
let matchingCollisionMask = null;
|
||||
try {
|
||||
matchingCollisionMask = await getMatchingCollisionMask(
|
||||
firstAnimationResourceSource
|
||||
);
|
||||
} catch (e) {
|
||||
console.error(
|
||||
'Unable to create a matching collision mask for the sprite, fallback to full image collision mask.',
|
||||
e
|
||||
);
|
||||
}
|
||||
setCollisionMaskOnAllFrames(animations, matchingCollisionMask);
|
||||
forceUpdate();
|
||||
},
|
||||
[animations, project, forceUpdate]
|
||||
);
|
||||
|
||||
const scrollView = React.useRef<?ScrollViewInterface>(null);
|
||||
const animationList = React.useRef<?AnimationListInterface>(null);
|
||||
|
||||
const [
|
||||
justAddedAnimationName,
|
||||
setJustAddedAnimationName,
|
||||
] = React.useState<?string>(null);
|
||||
const justAddedAnimationElement = React.useRef<?any>(null);
|
||||
|
||||
React.useEffect(
|
||||
() => {
|
||||
if (
|
||||
scrollView.current &&
|
||||
justAddedAnimationElement.current &&
|
||||
justAddedAnimationName
|
||||
) {
|
||||
scrollView.current.scrollTo(justAddedAnimationElement.current);
|
||||
setJustAddedAnimationName(null);
|
||||
justAddedAnimationElement.current = null;
|
||||
}
|
||||
},
|
||||
[justAddedAnimationName]
|
||||
);
|
||||
|
||||
const [pointsEditorOpen, setPointsEditorOpen] = React.useState(false);
|
||||
const [
|
||||
collisionMasksEditorOpen,
|
||||
setCollisionMasksEditorOpen,
|
||||
] = React.useState(false);
|
||||
const [newVariantDialogOpen, setNewVariantDialogOpen] = React.useState(false);
|
||||
|
||||
const editVariant = React.useCallback(
|
||||
() => {
|
||||
onOpenEventBasedObjectVariantEditor &&
|
||||
onOpenEventBasedObjectVariantEditor(
|
||||
gd.PlatformExtension.getExtensionFromFullObjectType(
|
||||
customObjectConfiguration.getType()
|
||||
),
|
||||
gd.PlatformExtension.getObjectNameFromFullObjectType(
|
||||
customObjectConfiguration.getType()
|
||||
),
|
||||
customObjectConfiguration.getVariantName()
|
||||
);
|
||||
},
|
||||
[customObjectConfiguration, onOpenEventBasedObjectVariantEditor]
|
||||
);
|
||||
|
||||
const duplicateVariant = React.useCallback(
|
||||
(i18n: I18nType, newName: string) => {
|
||||
if (!eventBasedObject) {
|
||||
return;
|
||||
}
|
||||
const variants = eventBasedObject.getVariants();
|
||||
// TODO Forbid name with `::`
|
||||
const uniqueNewName = newNameGenerator(
|
||||
newName || i18n._(t`New variant`),
|
||||
tentativeNewName => variants.hasVariantNamed(tentativeNewName)
|
||||
);
|
||||
const oldVariantName = getVariantName(
|
||||
eventBasedObject,
|
||||
customObjectConfiguration
|
||||
);
|
||||
const oldVariant = oldVariantName
|
||||
? variants.getVariant(oldVariantName)
|
||||
: eventBasedObject.getDefaultVariant();
|
||||
const newVariant = variants.insertNewVariant(uniqueNewName, 0);
|
||||
unserializeFromJSObject(
|
||||
newVariant,
|
||||
serializeToJSObject(oldVariant),
|
||||
'unserializeFrom',
|
||||
project
|
||||
);
|
||||
newVariant.setName(uniqueNewName);
|
||||
newVariant.setAssetStoreAssetId('');
|
||||
newVariant.setAssetStoreOriginalName('');
|
||||
customObjectConfiguration.setVariantName(uniqueNewName);
|
||||
setNewVariantDialogOpen(false);
|
||||
forceUpdate();
|
||||
},
|
||||
[customObjectConfiguration, eventBasedObject, forceUpdate, project]
|
||||
);
|
||||
|
||||
const deleteVariant = React.useCallback(
|
||||
() => {
|
||||
if (!eventBasedObject || !onDeleteEventsBasedObjectVariant) {
|
||||
return;
|
||||
}
|
||||
const variants = eventBasedObject.getVariants();
|
||||
const selectedVariantName = customObjectConfiguration.getVariantName();
|
||||
if (variants.hasVariantNamed(selectedVariantName)) {
|
||||
customObjectConfiguration.setVariantName('');
|
||||
const extensionName = gd.PlatformExtension.getExtensionFromFullObjectType(
|
||||
customObjectConfiguration.getType()
|
||||
);
|
||||
if (!project.hasEventsFunctionsExtensionNamed(extensionName)) {
|
||||
return;
|
||||
}
|
||||
const eventBasedExtension = project.getEventsFunctionsExtension(
|
||||
extensionName
|
||||
);
|
||||
onDeleteEventsBasedObjectVariant(
|
||||
eventBasedExtension,
|
||||
eventBasedObject,
|
||||
variants.getVariant(selectedVariantName)
|
||||
);
|
||||
forceUpdate();
|
||||
}
|
||||
},
|
||||
[
|
||||
customObjectConfiguration,
|
||||
eventBasedObject,
|
||||
forceUpdate,
|
||||
onDeleteEventsBasedObjectVariant,
|
||||
project,
|
||||
]
|
||||
);
|
||||
|
||||
return (
|
||||
<I18n>
|
||||
{({ i18n }) => (
|
||||
<>
|
||||
<ScrollView ref={scrollView}>
|
||||
<ColumnStackLayout noMargin>
|
||||
{renderObjectNameField && renderObjectNameField()}
|
||||
{tutorialIds.map(tutorialId => (
|
||||
<DismissableTutorialMessage
|
||||
key={tutorialId}
|
||||
tutorialId={tutorialId}
|
||||
/>
|
||||
))}
|
||||
{propertiesSchema.length ||
|
||||
(eventBasedObject &&
|
||||
(eventBasedObject.getObjects().getObjectsCount() ||
|
||||
eventBasedObject.isAnimatable())) ? (
|
||||
<React.Fragment>
|
||||
{extraInformation ? (
|
||||
<Line>
|
||||
<ColumnStackLayout noMargin>
|
||||
{extraInformation.map(({ kind, message }, index) => (
|
||||
<AlertMessage kind={kind} key={index}>
|
||||
{i18n._(message)}
|
||||
</AlertMessage>
|
||||
))}
|
||||
</ColumnStackLayout>
|
||||
</Line>
|
||||
) : null}
|
||||
<PropertiesEditor
|
||||
unsavedChanges={unsavedChanges}
|
||||
schema={propertiesSchema}
|
||||
instances={[customObjectConfiguration]}
|
||||
project={project}
|
||||
resourceManagementProps={resourceManagementProps}
|
||||
/>
|
||||
{!customObjectConfiguration.isForcedToOverrideEventsBasedObjectChildrenConfiguration() && (
|
||||
<>
|
||||
<Line>
|
||||
<Column expand noMargin>
|
||||
<Text size="block-title">Variant</Text>
|
||||
</Column>
|
||||
</Line>
|
||||
<ColumnStackLayout expand noMargin>
|
||||
<LineStackLayout>
|
||||
<FlatButton
|
||||
label={<Trans>Edit</Trans>}
|
||||
leftIcon={<Edit />}
|
||||
onClick={editVariant}
|
||||
disabled={
|
||||
!eventBasedObject ||
|
||||
getVariant(
|
||||
eventBasedObject,
|
||||
customObjectConfiguration
|
||||
).getAssetStoreAssetId() !== ''
|
||||
}
|
||||
/>
|
||||
<FlatButton
|
||||
label={<Trans>Duplicate</Trans>}
|
||||
leftIcon={<Add />}
|
||||
onClick={() => setNewVariantDialogOpen(true)}
|
||||
/>
|
||||
<FlatButton
|
||||
label={<Trans>Delete</Trans>}
|
||||
leftIcon={<Trash />}
|
||||
onClick={deleteVariant}
|
||||
/>
|
||||
</LineStackLayout>
|
||||
<SelectField
|
||||
floatingLabelText={<Trans>Variant</Trans>}
|
||||
value={getVariantName(
|
||||
eventBasedObject,
|
||||
customObjectConfiguration
|
||||
)}
|
||||
onChange={(e, i, value: string) => {
|
||||
customObjectConfiguration.setVariantName(value);
|
||||
forceUpdate();
|
||||
}}
|
||||
>
|
||||
<SelectOption
|
||||
key="default-variant"
|
||||
value=""
|
||||
label={t`Default`}
|
||||
/>
|
||||
{eventBasedObject &&
|
||||
mapFor(
|
||||
0,
|
||||
eventBasedObject.getVariants().getVariantsCount(),
|
||||
i => {
|
||||
if (!eventBasedObject) {
|
||||
return null;
|
||||
}
|
||||
const variant = eventBasedObject
|
||||
.getVariants()
|
||||
.getVariantAt(i);
|
||||
return (
|
||||
<SelectOption
|
||||
key={'variant-' + variant.getName()}
|
||||
value={variant.getName()}
|
||||
label={variant.getName()}
|
||||
/>
|
||||
);
|
||||
}
|
||||
)}
|
||||
</SelectField>
|
||||
</ColumnStackLayout>
|
||||
</>
|
||||
)}
|
||||
{!getVariantName(
|
||||
eventBasedObject,
|
||||
customObjectConfiguration
|
||||
) &&
|
||||
(eventBasedObject &&
|
||||
(!customObjectConfiguration.isForcedToOverrideEventsBasedObjectChildrenConfiguration() &&
|
||||
!customObjectConfiguration.isMarkedAsOverridingEventsBasedObjectChildrenConfiguration() ? (
|
||||
<Line alignItems="center">
|
||||
<Column expand noMargin>
|
||||
<Text size="block-title">Children objects</Text>
|
||||
</Column>
|
||||
<Column alignItems="right">
|
||||
<FlatButton
|
||||
label={
|
||||
<Trans>Override children configuration</Trans>
|
||||
}
|
||||
onClick={() => {
|
||||
customObjectConfiguration.setMarkedAsOverridingEventsBasedObjectChildrenConfiguration(
|
||||
true
|
||||
);
|
||||
customObjectConfiguration.clearChildrenConfiguration();
|
||||
if (onObjectUpdated) {
|
||||
onObjectUpdated();
|
||||
}
|
||||
forceUpdate();
|
||||
}}
|
||||
/>
|
||||
</Column>
|
||||
</Line>
|
||||
) : (
|
||||
<>
|
||||
<Line alignItems="center">
|
||||
<Column expand noMargin>
|
||||
<Text size="block-title">Children objects</Text>
|
||||
</Column>
|
||||
{!customObjectConfiguration.isForcedToOverrideEventsBasedObjectChildrenConfiguration() && (
|
||||
<Column alignItems="right">
|
||||
<FlatButton
|
||||
leftIcon={<RestoreIcon style={styles.icon} />}
|
||||
label={
|
||||
<Trans>
|
||||
Reset and hide children configuration
|
||||
</Trans>
|
||||
}
|
||||
onClick={() => {
|
||||
customObjectConfiguration.setMarkedAsOverridingEventsBasedObjectChildrenConfiguration(
|
||||
false
|
||||
);
|
||||
customObjectConfiguration.clearChildrenConfiguration();
|
||||
if (onObjectUpdated) {
|
||||
onObjectUpdated();
|
||||
}
|
||||
forceUpdate();
|
||||
}}
|
||||
/>
|
||||
</Column>
|
||||
)}
|
||||
</Line>
|
||||
{mapFor(
|
||||
0,
|
||||
eventBasedObject.getObjects().getObjectsCount(),
|
||||
i => {
|
||||
const childObject = eventBasedObject
|
||||
.getObjects()
|
||||
.getObjectAt(i);
|
||||
const childObjectConfiguration = customObjectConfiguration.getChildObjectConfiguration(
|
||||
childObject.getName()
|
||||
);
|
||||
const editorConfiguration = ObjectsEditorService.getEditorConfiguration(
|
||||
project,
|
||||
childObjectConfiguration.getType()
|
||||
);
|
||||
const EditorComponent =
|
||||
editorConfiguration.component;
|
||||
|
||||
const objectMetadata = gd.MetadataProvider.getObjectMetadata(
|
||||
gd.JsPlatform.get(),
|
||||
childObjectConfiguration.getType()
|
||||
);
|
||||
const iconUrl = objectMetadata.getIconFilename();
|
||||
const tutorialIds = getObjectTutorialIds(
|
||||
childObjectConfiguration.getType()
|
||||
);
|
||||
const enabledTutorialIds = tutorialIds.filter(
|
||||
tutorialId =>
|
||||
!values.hiddenTutorialHints[tutorialId]
|
||||
);
|
||||
// TODO EBO: Add a protection against infinite loops in case
|
||||
// of object cycles (thought it should be forbidden).
|
||||
return (
|
||||
<Accordion
|
||||
key={childObject.getName()}
|
||||
defaultExpanded
|
||||
>
|
||||
<AccordionHeader>
|
||||
{iconUrl ? (
|
||||
<IconContainer
|
||||
src={iconUrl}
|
||||
alt={childObject.getName()}
|
||||
size={20}
|
||||
/>
|
||||
) : null}
|
||||
<Column expand>
|
||||
<Text size="block-title">
|
||||
{childObject.getName()}
|
||||
</Text>
|
||||
</Column>
|
||||
</AccordionHeader>
|
||||
<AccordionBody>
|
||||
<Column expand noMargin noOverflowParent>
|
||||
{enabledTutorialIds.length ? (
|
||||
<Line>
|
||||
<ColumnStackLayout expand>
|
||||
{tutorialIds.map(tutorialId => (
|
||||
<DismissableTutorialMessage
|
||||
key={tutorialId}
|
||||
tutorialId={tutorialId}
|
||||
/>
|
||||
))}
|
||||
</ColumnStackLayout>
|
||||
</Line>
|
||||
) : null}
|
||||
<Line noMargin>
|
||||
<Column expand>
|
||||
<EditorComponent
|
||||
isChildObject
|
||||
objectConfiguration={
|
||||
childObjectConfiguration
|
||||
}
|
||||
project={project}
|
||||
layout={layout}
|
||||
eventsFunctionsExtension={
|
||||
eventsFunctionsExtension
|
||||
}
|
||||
eventsBasedObject={
|
||||
eventsBasedObject
|
||||
}
|
||||
resourceManagementProps={
|
||||
resourceManagementProps
|
||||
}
|
||||
onSizeUpdated={
|
||||
forceUpdate /*Force update to ensure dialog is properly positioned*/
|
||||
}
|
||||
objectName={
|
||||
objectName +
|
||||
' ' +
|
||||
childObject.getName()
|
||||
}
|
||||
onObjectUpdated={onObjectUpdated}
|
||||
unsavedChanges={unsavedChanges}
|
||||
/>
|
||||
</Column>
|
||||
</Line>
|
||||
</Column>
|
||||
</AccordionBody>
|
||||
</Accordion>
|
||||
);
|
||||
}
|
||||
)}
|
||||
</>
|
||||
)))}
|
||||
{eventBasedObject && eventBasedObject.isAnimatable() && (
|
||||
<Column expand>
|
||||
<Text size="block-title">
|
||||
<Trans>Animations</Trans>
|
||||
</Text>
|
||||
<AnimationList
|
||||
ref={animationList}
|
||||
animations={animations}
|
||||
project={project}
|
||||
layout={layout}
|
||||
eventsFunctionsExtension={eventsFunctionsExtension}
|
||||
eventsBasedObject={eventsBasedObject}
|
||||
object={object}
|
||||
objectName={objectName}
|
||||
resourceManagementProps={resourceManagementProps}
|
||||
onSizeUpdated={onSizeUpdated}
|
||||
onObjectUpdated={onObjectUpdated}
|
||||
isAnimationListLocked={false}
|
||||
scrollView={scrollView}
|
||||
onCreateMatchingSpriteCollisionMask={
|
||||
onCreateMatchingSpriteCollisionMask
|
||||
}
|
||||
/>
|
||||
</Column>
|
||||
)}
|
||||
</React.Fragment>
|
||||
) : (
|
||||
<EmptyMessage>
|
||||
<Trans>
|
||||
There is nothing to configure for this object. You can still
|
||||
use events to interact with the object.
|
||||
</Trans>
|
||||
</EmptyMessage>
|
||||
)}
|
||||
</ColumnStackLayout>
|
||||
</ScrollView>
|
||||
{eventBasedObject &&
|
||||
eventBasedObject.isAnimatable() &&
|
||||
!isChildObject && (
|
||||
<Column noMargin>
|
||||
<ResponsiveLineStackLayout
|
||||
justifyContent="space-between"
|
||||
noColumnMargin
|
||||
>
|
||||
{!isMobile ? ( // On mobile, use only 1 button to gain space.
|
||||
<ResponsiveLineStackLayout noMargin noColumnMargin>
|
||||
<FlatButton
|
||||
label={<Trans>Edit collision masks</Trans>}
|
||||
onClick={() => setCollisionMasksEditorOpen(true)}
|
||||
disabled={!hasAnyFrame(animations)}
|
||||
/>
|
||||
<FlatButton
|
||||
label={<Trans>Edit points</Trans>}
|
||||
onClick={() => setPointsEditorOpen(true)}
|
||||
disabled={!hasAnyFrame(animations)}
|
||||
/>
|
||||
</ResponsiveLineStackLayout>
|
||||
) : (
|
||||
<FlatButtonWithSplitMenu
|
||||
label={<Trans>Edit collision masks</Trans>}
|
||||
onClick={() => setCollisionMasksEditorOpen(true)}
|
||||
disabled={!hasAnyFrame(animations)}
|
||||
buildMenuTemplate={i18n => [
|
||||
{
|
||||
label: i18n._(t`Edit points`),
|
||||
disabled: !hasAnyFrame(animations),
|
||||
click: () => setPointsEditorOpen(true),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
<RaisedButton
|
||||
label={<Trans>Add an animation</Trans>}
|
||||
primary
|
||||
onClick={() => {
|
||||
if (!animationList.current) {
|
||||
return;
|
||||
}
|
||||
animationList.current.addAnimation();
|
||||
}}
|
||||
icon={<Add />}
|
||||
/>
|
||||
</ResponsiveLineStackLayout>
|
||||
</Column>
|
||||
)}
|
||||
{pointsEditorOpen && (
|
||||
<Dialog
|
||||
title={<Trans>Edit points</Trans>}
|
||||
actions={[
|
||||
<RaisedButton
|
||||
key="apply"
|
||||
label={<Trans>Apply</Trans>}
|
||||
primary
|
||||
onClick={() => setPointsEditorOpen(false)}
|
||||
/>,
|
||||
]}
|
||||
secondaryActions={[
|
||||
<HelpButton
|
||||
helpPagePath="/objects/sprite/edit-points"
|
||||
key="help"
|
||||
/>,
|
||||
]}
|
||||
onRequestClose={() => setPointsEditorOpen(false)}
|
||||
maxWidth="lg"
|
||||
flexBody
|
||||
fullHeight
|
||||
open={pointsEditorOpen}
|
||||
>
|
||||
<PointsEditor
|
||||
animations={animations}
|
||||
resourcesLoader={ResourcesLoader}
|
||||
project={project}
|
||||
onPointsUpdated={onObjectUpdated}
|
||||
onRenamedPoint={(oldName, newName) => {
|
||||
if (!object) {
|
||||
return;
|
||||
}
|
||||
if (layout) {
|
||||
gd.WholeProjectRefactorer.renameObjectPointInScene(
|
||||
project,
|
||||
layout,
|
||||
object,
|
||||
oldName,
|
||||
newName
|
||||
);
|
||||
} else if (eventsFunctionsExtension && eventsBasedObject) {
|
||||
gd.WholeProjectRefactorer.renameObjectPointInEventsBasedObject(
|
||||
project,
|
||||
eventsFunctionsExtension,
|
||||
eventsBasedObject,
|
||||
object,
|
||||
oldName,
|
||||
newName
|
||||
);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Dialog>
|
||||
)}
|
||||
{collisionMasksEditorOpen && (
|
||||
<Dialog
|
||||
title={<Trans>Edit collision masks</Trans>}
|
||||
actions={[
|
||||
<RaisedButton
|
||||
key="apply"
|
||||
label={<Trans>Apply</Trans>}
|
||||
primary
|
||||
onClick={() => setCollisionMasksEditorOpen(false)}
|
||||
/>,
|
||||
]}
|
||||
secondaryActions={[
|
||||
<HelpButton
|
||||
helpPagePath="/objects/sprite/collision-mask"
|
||||
key="help"
|
||||
/>,
|
||||
]}
|
||||
maxWidth="lg"
|
||||
flexBody
|
||||
fullHeight
|
||||
onRequestClose={() => setCollisionMasksEditorOpen(false)}
|
||||
open={collisionMasksEditorOpen}
|
||||
>
|
||||
<CollisionMasksEditor
|
||||
animations={animations}
|
||||
resourcesLoader={ResourcesLoader}
|
||||
project={project}
|
||||
onMasksUpdated={onObjectUpdated}
|
||||
onCreateMatchingSpriteCollisionMask={
|
||||
onCreateMatchingSpriteCollisionMask
|
||||
}
|
||||
/>
|
||||
</Dialog>
|
||||
)}
|
||||
{newVariantDialogOpen && eventBasedObject && (
|
||||
<NewVariantDialog
|
||||
initialName={
|
||||
getVariantName(eventBasedObject, customObjectConfiguration) ||
|
||||
i18n._(t`New variant`)
|
||||
}
|
||||
onApply={name => duplicateVariant(i18n, name)}
|
||||
onCancel={() => {
|
||||
setNewVariantDialogOpen(false);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</I18n>
|
||||
);
|
||||
};
|
||||
|
||||
export default CustomObjectPropertiesEditor;
|
@@ -37,4 +37,18 @@ export type EditorProps = {|
|
||||
scrollView?: ScrollViewInterface,
|
||||
renderObjectNameField?: () => React.Node,
|
||||
isChildObject?: boolean,
|
||||
onOpenEventBasedObjectEditor?: (
|
||||
extensionName: string,
|
||||
eventsBasedObjectName: string
|
||||
) => void,
|
||||
onOpenEventBasedObjectVariantEditor?: (
|
||||
extensionName: string,
|
||||
eventsBasedObjectName: string,
|
||||
variantName: string
|
||||
) => void,
|
||||
onDeleteEventsBasedObjectVariant?: (
|
||||
eventsFunctionsExtension: gdEventsFunctionsExtension,
|
||||
eventBasedObject: gdEventsBasedObject,
|
||||
variant: gdEventsBasedObjectVariant
|
||||
) => void,
|
||||
|};
|
||||
|
@@ -61,6 +61,22 @@ type Props = {|
|
||||
hotReloadPreviewButtonProps: HotReloadPreviewButtonProps,
|
||||
openBehaviorEvents: (extensionName: string, behaviorName: string) => void,
|
||||
onExtensionInstalled: (extensionName: string) => void,
|
||||
onOpenEventBasedObjectEditor: (
|
||||
extensionName: string,
|
||||
eventsBasedObjectName: string
|
||||
) => void,
|
||||
onOpenEventBasedObjectVariantEditor: (
|
||||
extensionName: string,
|
||||
eventsBasedObjectName: string,
|
||||
variantName: string
|
||||
) => void,
|
||||
onDeleteEventsBasedObjectVariant: (
|
||||
eventsFunctionsExtension: gdEventsFunctionsExtension,
|
||||
eventBasedObject: gdEventsBasedObject,
|
||||
variant: gdEventsBasedObjectVariant
|
||||
) => void,
|
||||
isBehaviorListLocked: boolean,
|
||||
isVariableListLocked: boolean,
|
||||
|};
|
||||
|
||||
type InnerDialogProps = {|
|
||||
@@ -90,6 +106,11 @@ const InnerDialog = (props: InnerDialogProps) => {
|
||||
onUpdateBehaviorsSharedData,
|
||||
onComputeAllVariableNames,
|
||||
onExtensionInstalled,
|
||||
onOpenEventBasedObjectEditor,
|
||||
onOpenEventBasedObjectVariantEditor,
|
||||
onDeleteEventsBasedObjectVariant,
|
||||
isBehaviorListLocked,
|
||||
isVariableListLocked,
|
||||
} = props;
|
||||
const [currentTab, setCurrentTab] = React.useState<ObjectEditorTab>(
|
||||
initialTab || 'properties'
|
||||
@@ -146,6 +167,13 @@ const InnerDialog = (props: InnerDialogProps) => {
|
||||
changeset,
|
||||
originalSerializedVariables
|
||||
);
|
||||
if (eventsBasedObject) {
|
||||
gd.ObjectVariableHelper.applyChangesToVariants(
|
||||
eventsBasedObject,
|
||||
object.getName(),
|
||||
changeset
|
||||
);
|
||||
}
|
||||
object.clearPersistentUuid();
|
||||
|
||||
// Do the renaming *after* applying changes, as "withSerializableObject"
|
||||
@@ -287,6 +315,11 @@ const InnerDialog = (props: InnerDialogProps) => {
|
||||
autoFocus="desktop"
|
||||
/>
|
||||
)}
|
||||
onOpenEventBasedObjectEditor={onOpenEventBasedObjectEditor}
|
||||
onOpenEventBasedObjectVariantEditor={
|
||||
onOpenEventBasedObjectVariantEditor
|
||||
}
|
||||
onDeleteEventsBasedObjectVariant={onDeleteEventsBasedObjectVariant}
|
||||
/>
|
||||
</Column>
|
||||
) : null}
|
||||
@@ -303,6 +336,7 @@ const InnerDialog = (props: InnerDialogProps) => {
|
||||
onBehaviorsUpdated={notifyOfChange}
|
||||
openBehaviorEvents={askConfirmationAndOpenBehaviorEvents}
|
||||
onExtensionInstalled={onExtensionInstalled}
|
||||
isListLocked={isBehaviorListLocked}
|
||||
/>
|
||||
)}
|
||||
{currentTab === 'variables' && (
|
||||
@@ -329,6 +363,7 @@ const InnerDialog = (props: InnerDialogProps) => {
|
||||
helpPagePath={'/all-features/variables/object-variables'}
|
||||
onComputeAllVariableNames={onComputeAllVariableNames}
|
||||
onVariablesUpdated={notifyOfChange}
|
||||
isListLocked={isVariableListLocked}
|
||||
/>
|
||||
</Column>
|
||||
)}
|
||||
|
@@ -13,6 +13,7 @@ import useDismissableTutorialMessage from '../Hints/useDismissableTutorialMessag
|
||||
import VariablesList from '../VariablesList/VariablesList';
|
||||
import HelpButton from '../UI/HelpButton';
|
||||
import useValueWithInit from '../Utils/UseRefInitHook';
|
||||
import Text from '../UI/Text';
|
||||
|
||||
const gd: libGDevelop = global.gd;
|
||||
|
||||
@@ -29,6 +30,8 @@ type Props = {|
|
||||
initialInstances: gdInitialInstancesContainer | null,
|
||||
initialTab: ?ObjectGroupEditorTab,
|
||||
onComputeAllVariableNames?: () => Array<string>,
|
||||
isVariableListLocked: boolean,
|
||||
isObjectListLocked: boolean,
|
||||
|};
|
||||
|
||||
const EditedObjectGroupEditorDialog = ({
|
||||
@@ -42,6 +45,8 @@ const EditedObjectGroupEditorDialog = ({
|
||||
initialInstances,
|
||||
initialTab,
|
||||
onComputeAllVariableNames,
|
||||
isVariableListLocked,
|
||||
isObjectListLocked,
|
||||
}: Props) => {
|
||||
const forceUpdate = useForceUpdate();
|
||||
const {
|
||||
@@ -99,6 +104,16 @@ const EditedObjectGroupEditorDialog = ({
|
||||
changeset,
|
||||
originalSerializedVariables
|
||||
);
|
||||
const { eventsBasedObject } = projectScopedContainersAccessor._scope;
|
||||
if (eventsBasedObject) {
|
||||
for (const objectName of group.getAllObjectsNames().toJSArray()) {
|
||||
gd.ObjectVariableHelper.applyChangesToVariants(
|
||||
eventsBasedObject,
|
||||
objectName,
|
||||
changeset
|
||||
);
|
||||
}
|
||||
}
|
||||
groupVariablesContainer.clearPersistentUuid();
|
||||
};
|
||||
|
||||
@@ -169,17 +184,28 @@ const EditedObjectGroupEditorDialog = ({
|
||||
/>
|
||||
}
|
||||
>
|
||||
{currentTab === 'objects' && (
|
||||
<ObjectGroupEditor
|
||||
project={project}
|
||||
projectScopedContainersAccessor={projectScopedContainersAccessor}
|
||||
globalObjectsContainer={globalObjectsContainer}
|
||||
objectsContainer={objectsContainer}
|
||||
groupObjectNames={group.getAllObjectsNames().toJSArray()}
|
||||
onObjectAdded={addObject}
|
||||
onObjectRemoved={removeObject}
|
||||
/>
|
||||
)}
|
||||
{currentTab === 'objects' &&
|
||||
(isObjectListLocked && group.getAllObjectsNames().size() === 0 ? (
|
||||
<Column noMargin expand justifyContent="center">
|
||||
<Text size="block-title" align="center">
|
||||
{<Trans>Empty group</Trans>}
|
||||
</Text>
|
||||
<Text align="center" noMargin>
|
||||
{<Trans>This object group is empty and locked.</Trans>}
|
||||
</Text>
|
||||
</Column>
|
||||
) : (
|
||||
<ObjectGroupEditor
|
||||
project={project}
|
||||
projectScopedContainersAccessor={projectScopedContainersAccessor}
|
||||
globalObjectsContainer={globalObjectsContainer}
|
||||
objectsContainer={objectsContainer}
|
||||
groupObjectNames={group.getAllObjectsNames().toJSArray()}
|
||||
onObjectAdded={addObject}
|
||||
onObjectRemoved={removeObject}
|
||||
isObjectListLocked={isObjectListLocked}
|
||||
/>
|
||||
))}
|
||||
{currentTab === 'variables' && (
|
||||
<Column expand noMargin>
|
||||
{groupVariablesContainer.count() > 0 && DismissableTutorialMessage && (
|
||||
@@ -205,6 +231,7 @@ const EditedObjectGroupEditorDialog = ({
|
||||
helpPagePath={'/all-features/variables/object-variables'}
|
||||
onComputeAllVariableNames={onComputeAllVariableNames}
|
||||
onVariablesUpdated={notifyOfVariableChange}
|
||||
isListLocked={isVariableListLocked}
|
||||
/>
|
||||
</Column>
|
||||
)}
|
||||
|
@@ -134,6 +134,7 @@ const NewObjectGroupEditorDialog = ({
|
||||
groupObjectNames={groupObjectNames}
|
||||
onObjectAdded={addObject}
|
||||
onObjectRemoved={removeObject}
|
||||
isObjectListLocked={false}
|
||||
/>
|
||||
</Dialog>
|
||||
);
|
||||
|
@@ -30,6 +30,8 @@ type Props = {|
|
||||
bypassedObjectGroupsContainer?: ?gdObjectGroupsContainer,
|
||||
initialTab?: ?ObjectGroupEditorTab,
|
||||
onComputeAllVariableNames?: () => Array<string>,
|
||||
isVariableListLocked: boolean,
|
||||
isObjectListLocked: boolean,
|
||||
|};
|
||||
|
||||
const ObjectGroupEditorDialog = ({
|
||||
@@ -45,6 +47,8 @@ const ObjectGroupEditorDialog = ({
|
||||
bypassedObjectGroupsContainer,
|
||||
initialTab,
|
||||
onComputeAllVariableNames,
|
||||
isVariableListLocked,
|
||||
isObjectListLocked,
|
||||
}: Props) => {
|
||||
const [
|
||||
editedObjectGroup,
|
||||
@@ -108,7 +112,8 @@ const ObjectGroupEditorDialog = ({
|
||||
);
|
||||
|
||||
return !editedObjectGroup ||
|
||||
editedObjectGroup.getAllObjectsNames().size() === 0 ? (
|
||||
(editedObjectGroup.getAllObjectsNames().size() === 0 &&
|
||||
!isObjectListLocked) ? (
|
||||
<NewObjectGroupEditorDialog
|
||||
project={project}
|
||||
projectScopedContainersAccessor={projectScopedContainersAccessor}
|
||||
@@ -130,6 +135,8 @@ const ObjectGroupEditorDialog = ({
|
||||
initialInstances={initialInstances}
|
||||
initialTab={selectedTab}
|
||||
onComputeAllVariableNames={onComputeAllVariableNames}
|
||||
isVariableListLocked={isVariableListLocked}
|
||||
isObjectListLocked={isObjectListLocked}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@@ -27,6 +27,7 @@ type Props = {|
|
||||
onObjectGroupUpdated?: () => void,
|
||||
onObjectAdded: (objectName: string) => void,
|
||||
onObjectRemoved: (objectName: string) => void,
|
||||
isObjectListLocked: boolean,
|
||||
|};
|
||||
|
||||
const ObjectGroupEditor = ({
|
||||
@@ -37,6 +38,7 @@ const ObjectGroupEditor = ({
|
||||
groupObjectNames,
|
||||
onObjectAdded,
|
||||
onObjectRemoved,
|
||||
isObjectListLocked,
|
||||
}: Props) => {
|
||||
const [objectName, setObjectName] = React.useState<string>('');
|
||||
|
||||
@@ -111,7 +113,13 @@ const ObjectGroupEditor = ({
|
||||
)}
|
||||
/>
|
||||
) : null;
|
||||
return (
|
||||
return isObjectListLocked ? (
|
||||
<ListItem
|
||||
key={objectName}
|
||||
primaryText={objectName}
|
||||
leftIcon={icon}
|
||||
/>
|
||||
) : (
|
||||
<ListItem
|
||||
key={objectName}
|
||||
primaryText={objectName}
|
||||
@@ -135,6 +143,7 @@ const ObjectGroupEditor = ({
|
||||
noGroups
|
||||
hintText={t`Choose an object to add to the group`}
|
||||
fullWidth
|
||||
disabled={isObjectListLocked}
|
||||
/>
|
||||
</Column>
|
||||
</Paper>
|
||||
|
@@ -71,6 +71,7 @@ const ObjectGroupsListWithObjectGroupEditor = ({
|
||||
onGroupRenamed={onGroupsUpdated}
|
||||
canSetAsGlobalGroup={canSetAsGlobalGroup}
|
||||
unsavedChanges={unsavedChanges}
|
||||
isListLocked={false}
|
||||
/>
|
||||
{(editedGroup || isCreatingNewGroup) && (
|
||||
<ObjectGroupEditorDialog
|
||||
@@ -102,6 +103,8 @@ const ObjectGroupsListWithObjectGroupEditor = ({
|
||||
}
|
||||
}}
|
||||
initialTab={'objects'}
|
||||
isVariableListLocked={false}
|
||||
isObjectListLocked={false}
|
||||
/>
|
||||
)}
|
||||
</React.Fragment>
|
||||
|
@@ -109,6 +109,7 @@ type Props = {|
|
||||
onGroupRenamed?: () => void,
|
||||
canSetAsGlobalGroup?: boolean,
|
||||
unsavedChanges?: ?UnsavedChanges,
|
||||
isListLocked: boolean,
|
||||
|};
|
||||
|
||||
const ObjectGroupsList = React.forwardRef<Props, ObjectGroupsListInterface>(
|
||||
@@ -127,6 +128,7 @@ const ObjectGroupsList = React.forwardRef<Props, ObjectGroupsListInterface>(
|
||||
unsavedChanges,
|
||||
onEditGroup,
|
||||
canSetAsGlobalGroup,
|
||||
isListLocked,
|
||||
} = props;
|
||||
const [
|
||||
selectedGroupWithContext,
|
||||
@@ -472,6 +474,8 @@ const ObjectGroupsList = React.forwardRef<Props, ObjectGroupsListInterface>(
|
||||
{
|
||||
label: i18n._(t`Duplicate`),
|
||||
click: () => onDuplicate(item),
|
||||
accelerator: 'CmdOrCtrl+D',
|
||||
enabled: !isListLocked,
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
@@ -482,11 +486,13 @@ const ObjectGroupsList = React.forwardRef<Props, ObjectGroupsListInterface>(
|
||||
{
|
||||
label: i18n._(t`Rename`),
|
||||
click: () => onEditName(item),
|
||||
accelerator: 'F2',
|
||||
enabled: !isListLocked,
|
||||
},
|
||||
globalObjectGroups
|
||||
? {
|
||||
label: i18n._(t`Set as global group`),
|
||||
enabled: !isGroupWithContextGlobal(item),
|
||||
enabled: !isGroupWithContextGlobal(item) && !isListLocked,
|
||||
click: () => setAsGlobalGroup(item),
|
||||
visible: canSetAsGlobalGroup !== false,
|
||||
}
|
||||
@@ -494,22 +500,26 @@ const ObjectGroupsList = React.forwardRef<Props, ObjectGroupsListInterface>(
|
||||
{
|
||||
label: i18n._(t`Delete`),
|
||||
click: () => onDelete(item),
|
||||
accelerator: 'Backspace',
|
||||
enabled: !isListLocked,
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: i18n._(t`Add a new group...`),
|
||||
click: onCreateGroup,
|
||||
enabled: !isListLocked,
|
||||
},
|
||||
].filter(Boolean),
|
||||
[
|
||||
onCreateGroup,
|
||||
onEditName,
|
||||
editItem,
|
||||
onDelete,
|
||||
onDuplicate,
|
||||
canSetAsGlobalGroup,
|
||||
setAsGlobalGroup,
|
||||
isListLocked,
|
||||
globalObjectGroups,
|
||||
canSetAsGlobalGroup,
|
||||
onCreateGroup,
|
||||
onDuplicate,
|
||||
editItem,
|
||||
onEditName,
|
||||
setAsGlobalGroup,
|
||||
onDelete,
|
||||
]
|
||||
);
|
||||
|
||||
@@ -521,9 +531,10 @@ const ObjectGroupsList = React.forwardRef<Props, ObjectGroupsListInterface>(
|
||||
label: i18n._(t`Add a new group`),
|
||||
click: onCreateGroup,
|
||||
id: 'add-new-group-top-button',
|
||||
enabled: !isListLocked,
|
||||
}
|
||||
: null,
|
||||
[onCreateGroup]
|
||||
[isListLocked, onCreateGroup]
|
||||
);
|
||||
|
||||
const labels = React.useMemo(
|
||||
@@ -578,23 +589,29 @@ const ObjectGroupsList = React.forwardRef<Props, ObjectGroupsListInterface>(
|
||||
() => {
|
||||
if (keyboardShortcutsRef.current) {
|
||||
keyboardShortcutsRef.current.setShortcutCallback('onDelete', () => {
|
||||
if (!selectedGroupWithContext) return;
|
||||
if (!selectedGroupWithContext || isListLocked) return;
|
||||
onDelete(selectedGroupWithContext);
|
||||
});
|
||||
keyboardShortcutsRef.current.setShortcutCallback(
|
||||
'onDuplicate',
|
||||
() => {
|
||||
if (!selectedGroupWithContext) return;
|
||||
if (!selectedGroupWithContext || isListLocked) return;
|
||||
onDuplicate(selectedGroupWithContext);
|
||||
}
|
||||
);
|
||||
keyboardShortcutsRef.current.setShortcutCallback('onRename', () => {
|
||||
if (!selectedGroupWithContext) return;
|
||||
if (!selectedGroupWithContext || isListLocked) return;
|
||||
onEditName(selectedGroupWithContext);
|
||||
});
|
||||
}
|
||||
},
|
||||
[selectedGroupWithContext, onDelete, onDuplicate, onEditName]
|
||||
[
|
||||
selectedGroupWithContext,
|
||||
onDelete,
|
||||
onDuplicate,
|
||||
onEditName,
|
||||
isListLocked,
|
||||
]
|
||||
);
|
||||
|
||||
// Force List component to be mounted again if globalObjectGroups or objectGroups
|
||||
@@ -687,6 +704,7 @@ const ObjectGroupsList = React.forwardRef<Props, ObjectGroupsListInterface>(
|
||||
onClick={onCreateGroup}
|
||||
id="add-new-group-button"
|
||||
icon={<Add />}
|
||||
disabled={isListLocked}
|
||||
/>
|
||||
</Column>
|
||||
</Line>
|
||||
|
@@ -72,6 +72,7 @@ export type ObjectFolderTreeViewItemProps = {|
|
||||
) => void,
|
||||
forceUpdateList: () => void,
|
||||
forceUpdate: () => void,
|
||||
isListLocked: boolean,
|
||||
|};
|
||||
|
||||
export const getObjectFolderTreeViewItemId = (
|
||||
@@ -197,6 +198,7 @@ export class ObjectFolderTreeViewItemContent implements TreeViewItemContent {
|
||||
onAddNewObject,
|
||||
onMovedObjectFolderOrObjectToAnotherFolderInSameContainer,
|
||||
forceUpdate,
|
||||
isListLocked,
|
||||
} = this.props;
|
||||
|
||||
const container = this._isGlobal
|
||||
@@ -222,54 +224,61 @@ export class ObjectFolderTreeViewItemContent implements TreeViewItemContent {
|
||||
isGlobalObject: this._isGlobal,
|
||||
isFolder: true,
|
||||
}),
|
||||
enabled: Clipboard.has(OBJECT_CLIPBOARD_KIND),
|
||||
enabled: Clipboard.has(OBJECT_CLIPBOARD_KIND) && !isListLocked,
|
||||
click: () => this.paste(),
|
||||
},
|
||||
{
|
||||
label: i18n._(t`Rename`),
|
||||
click: () => this.props.editName(this.getId()),
|
||||
accelerator: 'F2',
|
||||
enabled: !isListLocked,
|
||||
},
|
||||
{
|
||||
label: i18n._(t`Delete`),
|
||||
click: () => this.delete(),
|
||||
accelerator: 'Backspace',
|
||||
enabled: !isListLocked,
|
||||
},
|
||||
{
|
||||
label: i18n._('Move to folder'),
|
||||
submenu: [
|
||||
...filteredFolderAndPathsInContainer.map(({ folder, path }) => ({
|
||||
label: path,
|
||||
enabled: folder !== this.objectFolder.getParent(),
|
||||
click: () => {
|
||||
if (folder === this.objectFolder.getParent()) return;
|
||||
this.objectFolder
|
||||
.getParent()
|
||||
.moveObjectFolderOrObjectToAnotherFolder(
|
||||
this.objectFolder,
|
||||
folder,
|
||||
0
|
||||
);
|
||||
onMovedObjectFolderOrObjectToAnotherFolderInSameContainer({
|
||||
objectFolderOrObject: folder,
|
||||
global: this._isGlobal,
|
||||
});
|
||||
},
|
||||
})),
|
||||
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: i18n._(t`Create new folder...`),
|
||||
click: () =>
|
||||
addFolder([
|
||||
{
|
||||
objectFolderOrObject: this.objectFolder.getParent(),
|
||||
global: this._isGlobal,
|
||||
isListLocked
|
||||
? {
|
||||
label: i18n._('Move to folder'),
|
||||
enabled: false,
|
||||
}
|
||||
: {
|
||||
label: i18n._('Move to folder'),
|
||||
submenu: [
|
||||
...filteredFolderAndPathsInContainer.map(({ folder, path }) => ({
|
||||
label: path,
|
||||
enabled: folder !== this.objectFolder.getParent(),
|
||||
click: () => {
|
||||
if (folder === this.objectFolder.getParent()) return;
|
||||
this.objectFolder
|
||||
.getParent()
|
||||
.moveObjectFolderOrObjectToAnotherFolder(
|
||||
this.objectFolder,
|
||||
folder,
|
||||
0
|
||||
);
|
||||
onMovedObjectFolderOrObjectToAnotherFolderInSameContainer({
|
||||
objectFolderOrObject: folder,
|
||||
global: this._isGlobal,
|
||||
});
|
||||
},
|
||||
]),
|
||||
})),
|
||||
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: i18n._(t`Create new folder...`),
|
||||
click: () =>
|
||||
addFolder([
|
||||
{
|
||||
objectFolderOrObject: this.objectFolder.getParent(),
|
||||
global: this._isGlobal,
|
||||
},
|
||||
]),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
...renderQuickCustomizationMenuItems({
|
||||
i18n,
|
||||
visibility: this.objectFolder.getQuickCustomizationVisibility(),
|
||||
@@ -286,6 +295,7 @@ export class ObjectFolderTreeViewItemContent implements TreeViewItemContent {
|
||||
objectFolderOrObject: this.objectFolder,
|
||||
global: this._isGlobal,
|
||||
}),
|
||||
enabled: !isListLocked,
|
||||
},
|
||||
{
|
||||
label: i18n._(t`Add a new folder`),
|
||||
@@ -293,6 +303,7 @@ export class ObjectFolderTreeViewItemContent implements TreeViewItemContent {
|
||||
addFolder([
|
||||
{ objectFolderOrObject: this.objectFolder, global: this._isGlobal },
|
||||
]),
|
||||
enabled: !isListLocked,
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
|
@@ -45,6 +45,7 @@ type Props = {|
|
||||
onApply?: () => void,
|
||||
value: string,
|
||||
errorTextIfInvalid?: React.Node,
|
||||
disabled?: boolean,
|
||||
|
||||
fullWidth?: boolean,
|
||||
floatingLabelText?: React.Node,
|
||||
@@ -189,6 +190,7 @@ const ObjectSelector = React.forwardRef<Props, ObjectSelectorInterface>(
|
||||
hintText,
|
||||
requiredCapabilitiesBehaviorTypes,
|
||||
requiredVisibleBehaviorTypes,
|
||||
disabled,
|
||||
...otherProps
|
||||
} = props;
|
||||
|
||||
@@ -241,7 +243,7 @@ const ObjectSelector = React.forwardRef<Props, ObjectSelectorInterface>(
|
||||
undefined
|
||||
);
|
||||
|
||||
return shouldAutofocusInput ? (
|
||||
return disabled ? null : shouldAutofocusInput ? (
|
||||
<SemiControlledAutoComplete
|
||||
margin={margin}
|
||||
hintText={hintText || t`Choose an object`}
|
||||
|
@@ -44,6 +44,11 @@ export type ObjectTreeViewItemCallbacks = {|
|
||||
extensionName: string,
|
||||
eventsBasedObjectName: string
|
||||
) => void,
|
||||
onOpenEventBasedObjectVariantEditor: (
|
||||
extensionName: string,
|
||||
eventsBasedObjectName: string,
|
||||
variantName: string
|
||||
) => void,
|
||||
onRenameObjectFolderOrObjectWithContextFinish: (
|
||||
objectFolderOrObjectWithContext: ObjectFolderOrObjectWithContext,
|
||||
newName: string,
|
||||
@@ -82,6 +87,7 @@ export type ObjectTreeViewItemProps = {|
|
||||
addFolder: (items: Array<ObjectFolderOrObjectWithContext>) => void,
|
||||
forceUpdateList: () => void,
|
||||
forceUpdate: () => void,
|
||||
isListLocked: boolean,
|
||||
|};
|
||||
|
||||
export const addSerializedObjectToObjectsContainer = ({
|
||||
@@ -279,9 +285,10 @@ export class ObjectTreeViewItemContent implements TreeViewItemContent {
|
||||
swapObjectAsset,
|
||||
canSetAsGlobalObject,
|
||||
setAsGlobalObject,
|
||||
onOpenEventBasedObjectEditor,
|
||||
onOpenEventBasedObjectVariantEditor,
|
||||
selectObjectFolderOrObjectWithContext,
|
||||
addFolder,
|
||||
isListLocked,
|
||||
} = this.props;
|
||||
|
||||
const container = this._isGlobal
|
||||
@@ -312,29 +319,33 @@ export class ObjectTreeViewItemContent implements TreeViewItemContent {
|
||||
{
|
||||
label: i18n._(t`Cut`),
|
||||
click: () => this.cut(),
|
||||
enabled: !isListLocked,
|
||||
},
|
||||
{
|
||||
label: this._getPasteLabel(i18n, {
|
||||
isGlobalObject: this._isGlobal,
|
||||
isFolder: false,
|
||||
}),
|
||||
enabled: Clipboard.has(OBJECT_CLIPBOARD_KIND),
|
||||
enabled: Clipboard.has(OBJECT_CLIPBOARD_KIND) && !isListLocked,
|
||||
click: () => this.paste(),
|
||||
},
|
||||
{
|
||||
label: i18n._(t`Duplicate`),
|
||||
click: () => this.duplicate(),
|
||||
accelerator: 'CmdOrCtrl+D',
|
||||
enabled: !isListLocked,
|
||||
},
|
||||
{
|
||||
label: i18n._(t`Rename`),
|
||||
click: () => this.props.editName(this.getId()),
|
||||
accelerator: 'F2',
|
||||
enabled: !isListLocked,
|
||||
},
|
||||
{
|
||||
label: i18n._(t`Delete`),
|
||||
click: () => this.delete(),
|
||||
accelerator: 'Backspace',
|
||||
enabled: !isListLocked,
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
@@ -359,15 +370,20 @@ export class ObjectTreeViewItemContent implements TreeViewItemContent {
|
||||
project.hasEventsBasedObject(object.getType())
|
||||
? {
|
||||
label: i18n._(t`Edit children`),
|
||||
click: () =>
|
||||
onOpenEventBasedObjectEditor(
|
||||
click: () => {
|
||||
const customObjectConfiguration = gd.asCustomObjectConfiguration(
|
||||
object.getConfiguration()
|
||||
);
|
||||
onOpenEventBasedObjectVariantEditor(
|
||||
gd.PlatformExtension.getExtensionFromFullObjectType(
|
||||
object.getType()
|
||||
),
|
||||
gd.PlatformExtension.getObjectNameFromFullObjectType(
|
||||
object.getType()
|
||||
)
|
||||
),
|
||||
),
|
||||
customObjectConfiguration.getVariantName()
|
||||
);
|
||||
},
|
||||
}
|
||||
: null,
|
||||
{ type: 'separator' },
|
||||
@@ -383,46 +399,51 @@ export class ObjectTreeViewItemContent implements TreeViewItemContent {
|
||||
{ type: 'separator' },
|
||||
globalObjectsContainer && {
|
||||
label: i18n._(t`Set as global object`),
|
||||
enabled: !this._isGlobal,
|
||||
enabled: !this._isGlobal && !isListLocked,
|
||||
click: () => {
|
||||
selectObjectFolderOrObjectWithContext(null);
|
||||
setAsGlobalObject({ i18n, objectFolderOrObject: this.object });
|
||||
},
|
||||
visible: canSetAsGlobalObject !== false,
|
||||
},
|
||||
{
|
||||
label: i18n._('Move to folder'),
|
||||
submenu: [
|
||||
...folderAndPathsInContainer.map(({ folder, path }) => ({
|
||||
label: path,
|
||||
enabled: folder !== this.object.getParent(),
|
||||
click: () => {
|
||||
this.object
|
||||
.getParent()
|
||||
.moveObjectFolderOrObjectToAnotherFolder(
|
||||
this.object,
|
||||
folder,
|
||||
0
|
||||
);
|
||||
onMovedObjectFolderOrObjectToAnotherFolderInSameContainer({
|
||||
objectFolderOrObject: folder,
|
||||
global: this._isGlobal,
|
||||
});
|
||||
},
|
||||
})),
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: i18n._(t`Create new folder...`),
|
||||
click: () =>
|
||||
addFolder([
|
||||
{
|
||||
objectFolderOrObject: this.object.getParent(),
|
||||
global: this._isGlobal,
|
||||
isListLocked
|
||||
? {
|
||||
label: i18n._('Move to folder'),
|
||||
enabled: false,
|
||||
}
|
||||
: {
|
||||
label: i18n._('Move to folder'),
|
||||
submenu: [
|
||||
...folderAndPathsInContainer.map(({ folder, path }) => ({
|
||||
label: path,
|
||||
enabled: folder !== this.object.getParent(),
|
||||
click: () => {
|
||||
this.object
|
||||
.getParent()
|
||||
.moveObjectFolderOrObjectToAnotherFolder(
|
||||
this.object,
|
||||
folder,
|
||||
0
|
||||
);
|
||||
onMovedObjectFolderOrObjectToAnotherFolderInSameContainer({
|
||||
objectFolderOrObject: folder,
|
||||
global: this._isGlobal,
|
||||
});
|
||||
},
|
||||
]),
|
||||
})),
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: i18n._(t`Create new folder...`),
|
||||
click: () =>
|
||||
addFolder([
|
||||
{
|
||||
objectFolderOrObject: this.object.getParent(),
|
||||
global: this._isGlobal,
|
||||
},
|
||||
]),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: i18n._(t`Add instance to the scene`),
|
||||
|
@@ -280,6 +280,7 @@ class LabelTreeViewItemContent implements TreeViewItemContent {
|
||||
id: rightButton.id,
|
||||
label: i18n._(rightButton.label),
|
||||
click: rightButton.click,
|
||||
enabled: rightButton.enabled,
|
||||
}
|
||||
: null,
|
||||
...(buildMenuTemplateFunction ? buildMenuTemplateFunction() : []),
|
||||
@@ -463,6 +464,11 @@ type Props = {|
|
||||
extensionName: string,
|
||||
eventsBasedObjectName: string
|
||||
) => void,
|
||||
onOpenEventBasedObjectVariantEditor: (
|
||||
extensionName: string,
|
||||
eventsBasedObjectName: string,
|
||||
variantName: string
|
||||
) => void,
|
||||
onExportAssets: () => void,
|
||||
onObjectCreated: gdObject => void,
|
||||
onObjectEdited: ObjectWithContext => void,
|
||||
@@ -479,6 +485,7 @@ type Props = {|
|
||||
) => string,
|
||||
unsavedChanges?: ?UnsavedChanges,
|
||||
hotReloadPreviewButtonProps: HotReloadPreviewButtonProps,
|
||||
isListLocked: boolean,
|
||||
|};
|
||||
|
||||
const ObjectsList = React.forwardRef<Props, ObjectsListInterface>(
|
||||
@@ -502,6 +509,7 @@ const ObjectsList = React.forwardRef<Props, ObjectsListInterface>(
|
||||
|
||||
onEditObject,
|
||||
onOpenEventBasedObjectEditor,
|
||||
onOpenEventBasedObjectVariantEditor,
|
||||
onExportAssets,
|
||||
onObjectCreated,
|
||||
onObjectEdited,
|
||||
@@ -513,6 +521,7 @@ const ObjectsList = React.forwardRef<Props, ObjectsListInterface>(
|
||||
getThumbnail,
|
||||
unsavedChanges,
|
||||
hotReloadPreviewButtonProps,
|
||||
isListLocked,
|
||||
}: Props,
|
||||
ref
|
||||
) => {
|
||||
@@ -981,6 +990,7 @@ const ObjectsList = React.forwardRef<Props, ObjectsListInterface>(
|
||||
onAddObjectInstance,
|
||||
initialInstances,
|
||||
onOpenEventBasedObjectEditor,
|
||||
onOpenEventBasedObjectVariantEditor,
|
||||
getValidatedObjectOrGroupName,
|
||||
onRenameObjectFolderOrObjectWithContextFinish,
|
||||
onObjectModified,
|
||||
@@ -994,6 +1004,7 @@ const ObjectsList = React.forwardRef<Props, ObjectsListInterface>(
|
||||
addFolder,
|
||||
forceUpdateList,
|
||||
forceUpdate,
|
||||
isListLocked,
|
||||
}),
|
||||
[
|
||||
project,
|
||||
@@ -1007,6 +1018,7 @@ const ObjectsList = React.forwardRef<Props, ObjectsListInterface>(
|
||||
onAddObjectInstance,
|
||||
initialInstances,
|
||||
onOpenEventBasedObjectEditor,
|
||||
onOpenEventBasedObjectVariantEditor,
|
||||
getValidatedObjectOrGroupName,
|
||||
onRenameObjectFolderOrObjectWithContextFinish,
|
||||
onObjectModified,
|
||||
@@ -1020,6 +1032,7 @@ const ObjectsList = React.forwardRef<Props, ObjectsListInterface>(
|
||||
addFolder,
|
||||
forceUpdateList,
|
||||
forceUpdate,
|
||||
isListLocked,
|
||||
]
|
||||
);
|
||||
|
||||
@@ -1041,6 +1054,7 @@ const ObjectsList = React.forwardRef<Props, ObjectsListInterface>(
|
||||
showDeleteConfirmation,
|
||||
forceUpdateList,
|
||||
forceUpdate,
|
||||
isListLocked,
|
||||
}),
|
||||
[
|
||||
project,
|
||||
@@ -1059,6 +1073,7 @@ const ObjectsList = React.forwardRef<Props, ObjectsListInterface>(
|
||||
showDeleteConfirmation,
|
||||
forceUpdateList,
|
||||
forceUpdate,
|
||||
isListLocked,
|
||||
]
|
||||
);
|
||||
|
||||
@@ -1143,6 +1158,7 @@ const ObjectsList = React.forwardRef<Props, ObjectsListInterface>(
|
||||
onAddNewObject(selectedObjectFolderOrObjectsWithContext[0]);
|
||||
},
|
||||
id: 'add-new-object-top-button',
|
||||
enabled: !isListLocked,
|
||||
},
|
||||
() => [
|
||||
{
|
||||
@@ -1154,6 +1170,7 @@ const ObjectsList = React.forwardRef<Props, ObjectsListInterface>(
|
||||
global: false,
|
||||
},
|
||||
]),
|
||||
enabled: !isListLocked,
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
@@ -1184,16 +1201,18 @@ const ObjectsList = React.forwardRef<Props, ObjectsListInterface>(
|
||||
return treeViewItems;
|
||||
},
|
||||
[
|
||||
globalObjectsRootFolder,
|
||||
labels.higherScopeObjectsTitle,
|
||||
labels.localScopeObjectsTitle,
|
||||
objectTreeViewItemProps,
|
||||
objectFolderTreeViewItemProps,
|
||||
objectsRootFolder,
|
||||
isListLocked,
|
||||
addFolder,
|
||||
expandFolders,
|
||||
globalObjectsRootFolder,
|
||||
objectFolderTreeViewItemProps,
|
||||
objectTreeViewItemProps,
|
||||
objectsRootFolder,
|
||||
onAddNewObject,
|
||||
onExportAssets,
|
||||
selectedObjectFolderOrObjectsWithContext,
|
||||
labels,
|
||||
onExportAssets,
|
||||
]
|
||||
);
|
||||
|
||||
@@ -1221,20 +1240,31 @@ const ObjectsList = React.forwardRef<Props, ObjectsListInterface>(
|
||||
() => {
|
||||
if (keyboardShortcutsRef.current) {
|
||||
keyboardShortcutsRef.current.setShortcutCallback('onDelete', () => {
|
||||
deleteItem(selectedItems[0]);
|
||||
if (!isListLocked) {
|
||||
deleteItem(selectedItems[0]);
|
||||
}
|
||||
});
|
||||
keyboardShortcutsRef.current.setShortcutCallback(
|
||||
'onDuplicate',
|
||||
() => {
|
||||
duplicateItem(selectedItems[0]);
|
||||
if (!isListLocked) {
|
||||
duplicateItem(selectedItems[0]);
|
||||
}
|
||||
}
|
||||
);
|
||||
keyboardShortcutsRef.current.setShortcutCallback('onRename', () => {
|
||||
editName(selectedItems[0].content.getId());
|
||||
if (!isListLocked) {
|
||||
editName(selectedItems[0].content.getId());
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
[selectedObjectFolderOrObjectsWithContext, editName, selectedItems]
|
||||
[
|
||||
selectedObjectFolderOrObjectsWithContext,
|
||||
editName,
|
||||
selectedItems,
|
||||
isListLocked,
|
||||
]
|
||||
);
|
||||
|
||||
const canMoveSelectionTo = React.useCallback(
|
||||
@@ -1549,6 +1579,7 @@ const ObjectsList = React.forwardRef<Props, ObjectsListInterface>(
|
||||
}
|
||||
id="add-new-object-button"
|
||||
icon={<Add />}
|
||||
disabled={isListLocked}
|
||||
/>
|
||||
</Column>
|
||||
</Line>
|
||||
|
@@ -83,7 +83,8 @@ const ObjectsRenderingService = {
|
||||
instance: gdInitialInstance,
|
||||
associatedObjectConfiguration: gdObjectConfiguration,
|
||||
pixiContainer: PIXI.Container,
|
||||
threeGroup: THREE.Group | null
|
||||
threeGroup: THREE.Group | null,
|
||||
propertyOverridings: Map<string, string> = new Map<string, string>()
|
||||
): RenderedInstance | Rendered3DInstance {
|
||||
const objectType = associatedObjectConfiguration.getType();
|
||||
if (threeGroup && this.renderers3D.hasOwnProperty(objectType)) {
|
||||
@@ -101,7 +102,8 @@ const ObjectsRenderingService = {
|
||||
instance,
|
||||
associatedObjectConfiguration,
|
||||
pixiContainer,
|
||||
PixiResourcesLoader
|
||||
PixiResourcesLoader,
|
||||
propertyOverridings
|
||||
);
|
||||
else {
|
||||
if (project.hasEventsBasedObject(objectType)) {
|
||||
@@ -135,7 +137,8 @@ const ObjectsRenderingService = {
|
||||
associatedObjectConfiguration,
|
||||
pixiContainer,
|
||||
threeGroup,
|
||||
PixiResourcesLoader
|
||||
PixiResourcesLoader,
|
||||
propertyOverridings
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user