mirror of
https://github.com/Heretek-AI/GDevelop.git
synced 2026-08-25 12:23:58 -04:00
e91e071805
Fix #8805
315 lines
12 KiB
C++
315 lines
12 KiB
C++
/*
|
|
* 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/InitialInstance.h"
|
|
|
|
#include "GDCore/Project/Layout.h"
|
|
#include "GDCore/Project/Object.h"
|
|
#include "GDCore/Project/ObjectsContainer.h"
|
|
#include "GDCore/Project/Project.h"
|
|
#include "GDCore/Project/PropertyDescriptor.h"
|
|
#include "GDCore/Serialization/SerializerElement.h"
|
|
#include "GDCore/Tools/UUID/UUID.h"
|
|
#include "GDCore/Extensions/Metadata/BehaviorMetadata.h"
|
|
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
|
|
#include "GDCore/Project/CustomBehavior.h"
|
|
#include "GDCore/Tools/Log.h"
|
|
|
|
namespace gd {
|
|
|
|
gd::String* InitialInstance::badStringPropertyValue = NULL;
|
|
|
|
InitialInstance::InitialInstance()
|
|
: objectName(""),
|
|
layer(""),
|
|
persistentUuid(UUID::MakeUuid4()),
|
|
behaviorOverridings(true) {}
|
|
|
|
void InitialInstance::UnserializeFrom(gd::Project &project,
|
|
const SerializerElement &element) {
|
|
SetObjectName(element.GetStringAttribute("name", "", "nom"));
|
|
SetX(element.GetDoubleAttribute("x"));
|
|
SetY(element.GetDoubleAttribute("y"));
|
|
SetZ(element.GetDoubleAttribute("z", 0));
|
|
SetAngle(element.GetDoubleAttribute("angle"));
|
|
SetRotationX(element.GetDoubleAttribute("rotationX", 0));
|
|
SetRotationY(element.GetDoubleAttribute("rotationY", 0));
|
|
SetHasCustomSize(
|
|
element.GetBoolAttribute("customSize", false, "personalizedSize"));
|
|
SetCustomWidth(element.GetDoubleAttribute("width"));
|
|
SetCustomHeight(element.GetDoubleAttribute("height"));
|
|
if (element.HasChild("depth") || element.HasAttribute("depth")) {
|
|
SetHasCustomDepth(true);
|
|
SetCustomDepth(element.GetDoubleAttribute("depth"));
|
|
} else {
|
|
SetHasCustomDepth(false);
|
|
}
|
|
if (element.HasChild("defaultWidth") ||
|
|
element.HasAttribute("defaultWidth")) {
|
|
defaultWidth = element.GetDoubleAttribute("defaultWidth");
|
|
}
|
|
if (element.HasChild("defaultHeight") ||
|
|
element.HasAttribute("defaultHeight")) {
|
|
defaultHeight = element.GetDoubleAttribute("defaultHeight");
|
|
}
|
|
if (element.HasChild("defaultDepth") ||
|
|
element.HasAttribute("defaultDepth")) {
|
|
defaultDepth = element.GetDoubleAttribute("defaultDepth");
|
|
}
|
|
SetZOrder(element.GetIntAttribute("zOrder", 0, "plan"));
|
|
SetOpacity(element.GetIntAttribute("opacity", 255));
|
|
SetLayer(element.GetStringAttribute("layer"));
|
|
SetFlippedX(element.GetBoolAttribute("flippedX", false));
|
|
SetFlippedY(element.GetBoolAttribute("flippedY", false));
|
|
SetFlippedZ(element.GetBoolAttribute("flippedZ", false));
|
|
SetLocked(element.GetBoolAttribute("locked", false));
|
|
SetSealed(element.GetBoolAttribute("sealed", false));
|
|
SetShouldKeepRatio(element.GetBoolAttribute("keepRatio", false));
|
|
|
|
persistentUuid = element.GetStringAttribute("persistentUuid");
|
|
if (persistentUuid.empty()) ResetPersistentUuid();
|
|
|
|
numberProperties.clear();
|
|
if (element.HasChild("numberProperties", "floatInfos")) {
|
|
const SerializerElement& numberPropertiesElement =
|
|
element.GetChild("numberProperties", 0, "floatInfos");
|
|
numberPropertiesElement.ConsiderAsArrayOf("property", "Info");
|
|
for (std::size_t j = 0; j < numberPropertiesElement.GetChildrenCount(); ++j) {
|
|
gd::String name =
|
|
numberPropertiesElement.GetChild(j).GetStringAttribute("name");
|
|
double value =
|
|
numberPropertiesElement.GetChild(j).GetDoubleAttribute("value");
|
|
|
|
// Compatibility with GD <= 5.1.164
|
|
if (name == "z") {
|
|
SetZ(value);
|
|
} else if (name == "rotationX") {
|
|
SetRotationX(value);
|
|
} else if (name == "rotationY") {
|
|
SetRotationY(value);
|
|
} else if (name == "depth") {
|
|
SetHasCustomDepth(true);
|
|
SetCustomDepth(value);
|
|
}
|
|
// end of compatibility code
|
|
else {
|
|
numberProperties[name] = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
stringProperties.clear();
|
|
if (element.HasChild("stringProperties", "stringInfos")) {
|
|
const SerializerElement& stringPropElement =
|
|
element.GetChild("stringProperties", 0, "stringInfos");
|
|
stringPropElement.ConsiderAsArrayOf("property", "Info");
|
|
for (std::size_t j = 0; j < stringPropElement.GetChildrenCount(); ++j) {
|
|
gd::String name = stringPropElement.GetChild(j).GetStringAttribute("name");
|
|
gd::String value =
|
|
stringPropElement.GetChild(j).GetStringAttribute("value");
|
|
stringProperties[name] = value;
|
|
}
|
|
}
|
|
|
|
if (element.HasChild("initialVariables", "InitialVariables")) {
|
|
GetVariables().UnserializeFrom(
|
|
element.GetChild("initialVariables", 0, "InitialVariables"));
|
|
// The "mixed values" marker is an editor-only, display state used when
|
|
// showing the variables of several objects or instances at once. It must
|
|
// never be kept on the variables of an instance - clean it up in case it
|
|
// was wrongly persisted in the project by a previous version of the
|
|
// editor.
|
|
GetVariables().ClearMixedValues();
|
|
}
|
|
|
|
if (element.HasChild("behaviorOverridings")) {
|
|
behaviorOverridings.UnserializeFrom(
|
|
project, element.GetChild("behaviorOverridings"));
|
|
}
|
|
}
|
|
|
|
void InitialInstance::SerializeTo(SerializerElement& element) const {
|
|
element.SetAttribute("name", GetObjectName());
|
|
element.SetAttribute("x", GetX());
|
|
element.SetAttribute("y", GetY());
|
|
if (GetZ() != 0) element.SetAttribute("z", GetZ());
|
|
element.SetAttribute("zOrder", GetZOrder());
|
|
if (GetOpacity() != 255) element.SetAttribute("opacity", GetOpacity());
|
|
if (IsFlippedX()) element.SetAttribute("flippedX", IsFlippedX());
|
|
if (IsFlippedY()) element.SetAttribute("flippedY", IsFlippedY());
|
|
if (IsFlippedZ()) element.SetAttribute("flippedZ", IsFlippedZ());
|
|
element.SetAttribute("layer", GetLayer());
|
|
element.SetAttribute("angle", GetAngle());
|
|
if (GetRotationX() != 0) element.SetAttribute("rotationX", GetRotationX());
|
|
if (GetRotationY() != 0) element.SetAttribute("rotationY", GetRotationY());
|
|
element.SetAttribute("customSize", HasCustomSize());
|
|
element.SetAttribute("width", GetCustomWidth());
|
|
element.SetAttribute("height", GetCustomHeight());
|
|
if (HasCustomDepth()) element.SetAttribute("depth", GetCustomDepth());
|
|
// defaultWidth, defaultHeight and defaultDepth are not serialized
|
|
// because they are evaluated by InGameEditor.
|
|
if (IsLocked()) element.SetAttribute("locked", IsLocked());
|
|
if (IsSealed()) element.SetAttribute("sealed", IsSealed());
|
|
if (ShouldKeepRatio()) element.SetAttribute("keepRatio", ShouldKeepRatio());
|
|
|
|
if (persistentUuid.empty()) persistentUuid = UUID::MakeUuid4();
|
|
element.SetStringAttribute("persistentUuid", persistentUuid);
|
|
|
|
SerializerElement& numberPropertiesElement =
|
|
element.AddChild("numberProperties");
|
|
numberPropertiesElement.ConsiderAsArrayOf("property");
|
|
for (const auto& property : numberProperties) {
|
|
numberPropertiesElement.AddChild("property")
|
|
.SetAttribute("name", property.first)
|
|
.SetAttribute("value", property.second);
|
|
}
|
|
|
|
SerializerElement& stringPropElement = element.AddChild("stringProperties");
|
|
stringPropElement.ConsiderAsArrayOf("property");
|
|
for (const auto& property : stringProperties) {
|
|
stringPropElement.AddChild("property")
|
|
.SetAttribute("name", property.first)
|
|
.SetAttribute("value", property.second);
|
|
}
|
|
|
|
GetVariables().SerializeTo(element.AddChild("initialVariables"));
|
|
|
|
if (!behaviorOverridings.GetAllBehaviorContents().empty()) {
|
|
behaviorOverridings.SerializeTo(element.AddChild("behaviorOverridings"));
|
|
}
|
|
}
|
|
|
|
InitialInstance& InitialInstance::ResetPersistentUuid() {
|
|
persistentUuid = UUID::MakeUuid4();
|
|
return *this;
|
|
}
|
|
|
|
std::map<gd::String, gd::PropertyDescriptor>
|
|
InitialInstance::GetCustomProperties(
|
|
gd::ObjectsContainer& globalObjectsContainer,
|
|
gd::ObjectsContainer& objectsContainer) {
|
|
// Find an object
|
|
if (objectsContainer.HasObjectNamed(GetObjectName()))
|
|
return objectsContainer.GetObject(GetObjectName())
|
|
.GetConfiguration()
|
|
.GetInitialInstanceProperties(*this);
|
|
else if (globalObjectsContainer.HasObjectNamed(GetObjectName()))
|
|
return globalObjectsContainer.GetObject(GetObjectName())
|
|
.GetConfiguration()
|
|
.GetInitialInstanceProperties(*this);
|
|
|
|
std::map<gd::String, gd::PropertyDescriptor> nothing;
|
|
return nothing;
|
|
}
|
|
|
|
bool InitialInstance::UpdateCustomProperty(
|
|
const gd::String& name,
|
|
const gd::String& value,
|
|
gd::ObjectsContainer& globalObjectsContainer,
|
|
gd::ObjectsContainer& objectsContainer) {
|
|
if (objectsContainer.HasObjectNamed(GetObjectName()))
|
|
return objectsContainer.GetObject(GetObjectName())
|
|
.GetConfiguration()
|
|
.UpdateInitialInstanceProperty(*this, name, value);
|
|
else if (globalObjectsContainer.HasObjectNamed(GetObjectName()))
|
|
return globalObjectsContainer.GetObject(GetObjectName())
|
|
.GetConfiguration()
|
|
.UpdateInitialInstanceProperty(*this, name, value);
|
|
|
|
return false;
|
|
}
|
|
|
|
double InitialInstance::GetRawDoubleProperty(const gd::String& name) const {
|
|
const auto& it = numberProperties.find(name);
|
|
return it != numberProperties.end() ? it->second : 0;
|
|
}
|
|
|
|
const gd::String& InitialInstance::GetRawStringProperty(
|
|
const gd::String& name) const {
|
|
if (!badStringPropertyValue) badStringPropertyValue = new gd::String("");
|
|
|
|
const auto& it = stringProperties.find(name);
|
|
return it != stringProperties.end() ? it->second : *badStringPropertyValue;
|
|
}
|
|
|
|
void InitialInstance::SetRawDoubleProperty(const gd::String& name,
|
|
double value) {
|
|
numberProperties[name] = value;
|
|
}
|
|
|
|
void InitialInstance::SetRawStringProperty(const gd::String& name,
|
|
const gd::String& value) {
|
|
stringProperties[name] = value;
|
|
}
|
|
|
|
bool InitialInstance::HasAnyOverriddenProperty(const gd::Object &object) {
|
|
for (auto &behaviorOverridingPair : behaviorOverridings.GetAllBehaviorContents()) {
|
|
auto &behaviorName = behaviorOverridingPair.first;
|
|
if (HasAnyOverriddenPropertyForBehavior(object.GetBehavior(behaviorName))) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool InitialInstance::HasAnyOverriddenPropertyForBehavior(
|
|
const gd::Behavior &behavior) {
|
|
auto &behaviorName = behavior.GetName();
|
|
if (!HasBehaviorOverridingNamed(behaviorName)) {
|
|
return false;
|
|
}
|
|
auto &behaviorOverriding = GetBehaviorOverriding(behaviorName);
|
|
if (behaviorOverriding.GetContent().IsEmpty()) {
|
|
return false;
|
|
}
|
|
const auto &overridingProperties = behaviorOverriding.GetProperties();
|
|
for (auto &propertyPair : behavior.GetProperties()) {
|
|
auto &propertyName = propertyPair.first;
|
|
auto &behaviorProperty = propertyPair.second;
|
|
|
|
if (behaviorProperty.GetType() != "Behavior" &&
|
|
behaviorOverriding.HasPropertyValue(propertyName) &&
|
|
overridingProperties.find(propertyName) !=
|
|
overridingProperties.end() &&
|
|
overridingProperties.at(propertyName).GetValue() !=
|
|
behaviorProperty.GetValue()) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
gd::Behavior &InitialInstance::GetBehaviorOverriding(const gd::String &name) {
|
|
return behaviorOverridings.GetBehavior(name);
|
|
}
|
|
|
|
const gd::Behavior &
|
|
InitialInstance::GetBehaviorOverriding(const gd::String &name) const {
|
|
return behaviorOverridings.GetBehavior(name);
|
|
}
|
|
|
|
bool InitialInstance::HasBehaviorOverridingNamed(const gd::String &name) const {
|
|
return behaviorOverridings.HasBehaviorNamed(name);
|
|
}
|
|
|
|
void InitialInstance::RemoveBehaviorOverriding(const gd::String &name) {
|
|
behaviorOverridings.RemoveBehavior(name);
|
|
}
|
|
|
|
bool InitialInstance::RenameBehaviorOverriding(const gd::String &name,
|
|
const gd::String &newName) {
|
|
return behaviorOverridings.RenameBehavior(name, newName);
|
|
}
|
|
|
|
gd::Behavior *
|
|
InitialInstance::AddNewBehaviorOverriding(const gd::Project &project,
|
|
const gd::String &type,
|
|
const gd::String &name) {
|
|
return behaviorOverridings.AddNewBehavior(project, type, name);
|
|
}
|
|
|
|
} // namespace gd
|