Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Core/GDCore/Project/BehaviorConfigurationContainer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ BehaviorConfigurationContainer::~BehaviorConfigurationContainer(){};


std::map<gd::String, gd::PropertyDescriptor> BehaviorConfigurationContainer::GetProperties() const {
return GetProperties(content);
};
return GetProperties(content);
}

std::map<gd::String, gd::PropertyDescriptor> BehaviorConfigurationContainer::GetProperties(
const gd::SerializerElement& behaviorContent) const {
Expand Down
35 changes: 32 additions & 3 deletions Core/GDCore/Project/BehaviorConfigurationContainer.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,17 +81,47 @@ class GD_CORE_API BehaviorConfigurationContainer {
* behavior
*
* \return false if the new value cannot be set
* \see gd::InitialInstance
*/
bool UpdateProperty(const gd::String& name, const gd::String& value) {
return UpdateProperty(content, name, value);
};

/**
* \brief Called when the IDE wants to remove a custom property from the
* behavior
*
* \see gd::InitialInstance
*/
void RemoveProperty(const gd::String &name) {
content.RemoveChild(name);
content.RemoveAttribute(name);
};

/**
* \brief Called to check if a property is overriden.
*
* \see gd::InitialInstance
*/
bool HasPropertyValue(const gd::String name) const {
return content.HasChild(name) || content.HasAttribute(name);
}

/**
* \brief Called to initialize the content with the default properties
* for the behavior.
*/
virtual void InitializeContent() { InitializeContent(content); };
virtual void InitializeContent() {
InitializeContent(content);
};

/**
* \brief Called to remove all properties values for the behavior.
*
* \see gd::InitialInstance
*/
void ClearContent() {
content.Clear();
};

/**
* \brief Serialize the behavior content.
Expand Down Expand Up @@ -191,7 +221,6 @@ class GD_CORE_API BehaviorConfigurationContainer {
* behavior
*
* \return false if the new value cannot be set
* \see gd::InitialInstance
*/
virtual bool UpdateProperty(gd::SerializerElement& behaviorContent,
const gd::String& name,
Expand Down
205 changes: 205 additions & 0 deletions Core/GDCore/Project/BehaviorsContainer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "GDCore/Project/BehaviorsContainer.h"

#include "GDCore/Extensions/Metadata/BehaviorMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Project/Behavior.h"
#include "GDCore/Project/CustomBehavior.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/PropertyDescriptor.h"
#include "GDCore/Project/QuickCustomization.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/Tools/Log.h"
#include "GDCore/Tools/UUID/UUID.h"

namespace gd {

BehaviorsContainer::~BehaviorsContainer() {}

BehaviorsContainer::BehaviorsContainer(bool isOverriding_)
: isOverriding(isOverriding_) {}

void BehaviorsContainer::Init(const gd::BehaviorsContainer &behaviorsContainer) {
isOverriding = behaviorsContainer.isOverriding;
behaviors = gd::Clone(behaviorsContainer.behaviors);
}

std::vector<gd::String> BehaviorsContainer::GetAllBehaviorNames() const {
std::vector<gd::String> allNameIdentifiers;

for (auto &it : behaviors)
allNameIdentifiers.push_back(it.first);

return allNameIdentifiers;
}

void BehaviorsContainer::RemoveBehavior(const gd::String &name) {
behaviors.erase(name);
}

bool BehaviorsContainer::RenameBehavior(const gd::String &name,
const gd::String &newName) {
if (behaviors.find(name) == behaviors.end() ||
behaviors.find(newName) != behaviors.end())
return false;

std::unique_ptr<Behavior> aut = std::move(behaviors.find(name)->second);
behaviors.erase(name);
behaviors[newName] = std::move(aut);
behaviors[newName]->SetName(newName);

return true;
}

gd::Behavior &BehaviorsContainer::GetBehavior(const gd::String &name) {
return *behaviors.find(name)->second;
}

const gd::Behavior &
BehaviorsContainer::GetBehavior(const gd::String &name) const {
return *behaviors.find(name)->second;
}

bool BehaviorsContainer::HasBehaviorNamed(const gd::String &name) const {
return behaviors.find(name) != behaviors.end();
}

gd::Behavior *BehaviorsContainer::AddNewBehavior(const gd::Project &project,
const gd::String &type,
const gd::String &name) {
auto initializeAndAdd = [this,
&name](std::unique_ptr<gd::Behavior> behavior) {
if (isOverriding) {
// We don't call the Initialize method for behavior overridings because they
// should have no property value initially.
behavior->ClearContent();
}
else {
behavior->InitializeContent();
}
this->behaviors[name] = std::move(behavior);
return this->behaviors[name].get();
};

if (project.HasEventsBasedBehavior(type)) {
return initializeAndAdd(
gd::make_unique<CustomBehavior>(name, project, type));
} else {
const gd::BehaviorMetadata &behaviorMetadata =
gd::MetadataProvider::GetBehaviorMetadata(project.GetCurrentPlatform(),
type);
if (gd::MetadataProvider::IsBadBehaviorMetadata(behaviorMetadata)) {
gd::LogWarning("Tried to create a behavior with an unknown type: " +
type);
// It's probably an events-based behavior that was removed.
// Create a custom behavior to preserve the properties values.
return initializeAndAdd(
gd::make_unique<CustomBehavior>(name, project, type));
}
std::unique_ptr<gd::Behavior> behavior(behaviorMetadata.Get().Clone());
behavior->SetName(name);
return initializeAndAdd(std::move(behavior));
}
}

void BehaviorsContainer::UnserializeFrom(gd::Project &project,
const SerializerElement &element) {
element.ConsiderAsArrayOf("behavior", "automatism");
for (std::size_t i = 0; i < element.GetChildrenCount(); ++i) {
SerializerElement &behaviorElement = element.GetChild(i);

gd::String type = behaviorElement.GetStringAttribute("type").FindAndReplace(
"Automatism", "Behavior"); // Compatibility with GD <= 4
gd::String name = behaviorElement.GetStringAttribute("name");

auto behavior = gd::BehaviorsContainer::AddNewBehavior(project, type, name);
// Compatibility with GD <= 4.0.98
// If there is only one child called "content" (in addition to "type" and
// "name"), it's the content of a JavaScript behavior. Move the content
// out of the "content" object (to put it directly at the root of the
// behavior element).
if (behaviorElement.HasChild("content") &&
behaviorElement.GetAllChildren().size() == 3) {
SerializerElement &contentElement = behaviorElement.GetChild("content");

// Physics2 Behavior was using "type" for the type of the body. The name
// conflicts with the behavior "type". Rename it.
if (contentElement.HasChild("type")) {
contentElement.AddChild("bodyType")
.SetValue(contentElement.GetChild("type").GetStringValue());
contentElement.RemoveChild("type");
}

behavior->UnserializeFrom(contentElement);
}
// end of compatibility code
else {
behavior->UnserializeFrom(behaviorElement);
}

bool isFolded = behaviorElement.GetBoolAttribute("isFolded", false);
behavior->SetFolded(isFolded);

// Handle Quick Customization info.
if (behaviorElement.HasChild("propertiesQuickCustomizationVisibilities")) {
behavior->GetPropertiesQuickCustomizationVisibilities().UnserializeFrom(
behaviorElement.GetChild("propertiesQuickCustomizationVisibilities"));
}
if (behaviorElement.HasChild("quickCustomizationVisibility")) {
behavior->SetQuickCustomizationVisibility(
QuickCustomization::StringAsVisibility(
behaviorElement.GetStringAttribute(
"quickCustomizationVisibility")));
}
}
}

void BehaviorsContainer::SerializeTo(SerializerElement &element) const {
element.ConsiderAsArrayOf("behavior");
std::vector<gd::String> allBehaviors = GetAllBehaviorNames();
for (std::size_t i = 0; i < allBehaviors.size(); ++i) {
const gd::Behavior &behavior = GetBehavior(allBehaviors[i]);
// Default behaviors are added at the object creation according to
// metadata. They don't need to be serialized.
// During the export, all behaviors are set as not default by
// `BehaviorDefaultFlagClearer` because the Runtime needs all the behaviors.
if (behavior.IsDefaultBehavior()) {
continue;
}
SerializerElement &behaviorElement = element.AddChild("behavior");

behavior.SerializeTo(behaviorElement);
behaviorElement.RemoveChild("type"); // The content can contain type or
// name properties, remove them.
behaviorElement.RemoveChild("name");
behaviorElement.RemoveChild("isFolded");
behaviorElement.SetAttribute("type", behavior.GetTypeName());
behaviorElement.SetAttribute("name", behavior.GetName());
if (behavior.IsFolded())
behaviorElement.SetAttribute("isFolded", true);

// Handle Quick Customization info.
behaviorElement.RemoveChild("propertiesQuickCustomizationVisibilities");
const QuickCustomizationVisibilitiesContainer
&propertiesQuickCustomizationVisibilities =
behavior.GetPropertiesQuickCustomizationVisibilities();
if (!propertiesQuickCustomizationVisibilities.IsEmpty()) {
propertiesQuickCustomizationVisibilities.SerializeTo(
behaviorElement.AddChild("propertiesQuickCustomizationVisibilities"));
}
const QuickCustomization::Visibility visibility =
behavior.GetQuickCustomizationVisibility();
if (visibility != QuickCustomization::Visibility::Default) {
behaviorElement.SetAttribute(
"quickCustomizationVisibility",
QuickCustomization::VisibilityAsString(visibility));
}
}
}

} // namespace gd
Loading