mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00

- Variants allows to restyle custom objects - They can be customized with the graphical editor - The asset store will progressively use them notably for UI elements (buttons, sliders)
305 lines
13 KiB
C++
305 lines
13 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 "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"
|
|
#include "GDCore/Project/ObjectsContainer.h"
|
|
#include "GDCore/Project/ObjectsContainersList.h"
|
|
#include "GDCore/Project/Variable.h"
|
|
#include "GDCore/Project/VariablesContainer.h"
|
|
#include "GDCore/String.h"
|
|
|
|
namespace gd {
|
|
|
|
void ObjectVariableHelper::FillAnyVariableBetweenObjects(
|
|
gd::ObjectsContainer &globalObjectsContainer,
|
|
gd::ObjectsContainer &objectsContainer,
|
|
const gd::ObjectGroup &objectGroup) {
|
|
const auto &objectNames = objectGroup.GetAllObjectsNames();
|
|
for (const gd::String &sourceObjectName : objectNames) {
|
|
const bool hasObject = objectsContainer.HasObjectNamed(sourceObjectName);
|
|
if (!hasObject &&
|
|
!globalObjectsContainer.HasObjectNamed(sourceObjectName)) {
|
|
continue;
|
|
}
|
|
const auto &sourceObject =
|
|
hasObject ? objectsContainer.GetObject(sourceObjectName)
|
|
: globalObjectsContainer.GetObject(sourceObjectName);
|
|
const auto &sourceVariablesContainer = sourceObject.GetVariables();
|
|
|
|
for (const gd::String &destinationObjectName : objectNames) {
|
|
if (sourceObjectName == destinationObjectName) {
|
|
continue;
|
|
}
|
|
const bool hasObject =
|
|
objectsContainer.HasObjectNamed(destinationObjectName);
|
|
if (!hasObject &&
|
|
!globalObjectsContainer.HasObjectNamed(destinationObjectName)) {
|
|
continue;
|
|
}
|
|
auto &destinationObject =
|
|
hasObject ? objectsContainer.GetObject(destinationObjectName)
|
|
: globalObjectsContainer.GetObject(destinationObjectName);
|
|
auto &destinationVariablesContainer = destinationObject.GetVariables();
|
|
|
|
for (std::size_t sourceVariableIndex = 0;
|
|
sourceVariableIndex < sourceVariablesContainer.Count();
|
|
++sourceVariableIndex) {
|
|
auto &sourceVariable =
|
|
sourceVariablesContainer.Get(sourceVariableIndex);
|
|
const auto &variableName =
|
|
sourceVariablesContainer.GetNameAt(sourceVariableIndex);
|
|
|
|
if (!destinationVariablesContainer.Has(variableName)) {
|
|
destinationVariablesContainer.Insert(
|
|
variableName, sourceVariable,
|
|
destinationVariablesContainer.Count());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
gd::VariablesContainer ObjectVariableHelper::MergeVariableContainers(
|
|
const gd::ObjectsContainersList &objectsContainersList,
|
|
const gd::ObjectGroup &objectGroup) {
|
|
gd::VariablesContainer mergedVariablesContainer;
|
|
|
|
const auto &objectNames = objectGroup.GetAllObjectsNames();
|
|
std::size_t objectIndex = 0;
|
|
bool isFirstObjectFound = false;
|
|
for (; objectIndex < objectNames.size() && !isFirstObjectFound;
|
|
objectIndex++) {
|
|
const gd::String &objectName = objectNames[objectIndex];
|
|
if (!objectsContainersList.HasObjectOrGroupNamed(objectName)) {
|
|
continue;
|
|
}
|
|
isFirstObjectFound = true;
|
|
mergedVariablesContainer =
|
|
*objectsContainersList.GetObjectOrGroupVariablesContainer(objectName);
|
|
}
|
|
for (; objectIndex < objectNames.size(); objectIndex++) {
|
|
const gd::String &objectName = objectNames[objectIndex];
|
|
if (!objectsContainersList.HasObjectOrGroupNamed(objectName)) {
|
|
continue;
|
|
}
|
|
const auto &variablesContainer =
|
|
*objectsContainersList.GetObjectOrGroupVariablesContainer(objectName);
|
|
|
|
for (std::size_t variableIndex = 0;
|
|
variableIndex < mergedVariablesContainer.Count(); ++variableIndex) {
|
|
auto &mergedVariable = mergedVariablesContainer.Get(variableIndex);
|
|
const auto &variableName =
|
|
mergedVariablesContainer.GetNameAt(variableIndex);
|
|
|
|
if (variablesContainer.Has(variableName)) {
|
|
auto &variable = variablesContainer.Get(variableName);
|
|
if (mergedVariable.GetType() != variable.GetType()) {
|
|
mergedVariable.CastTo(gd::Variable::Type::MixedTypes);
|
|
} else if (mergedVariable != variable) {
|
|
mergedVariable.MarkAsMixedValues();
|
|
}
|
|
} else {
|
|
mergedVariablesContainer.Remove(variableName);
|
|
variableIndex--;
|
|
}
|
|
}
|
|
}
|
|
return mergedVariablesContainer;
|
|
}
|
|
|
|
void ObjectVariableHelper::FillMissingGroupVariablesToObjects(
|
|
gd::ObjectsContainer &globalObjectsContainer,
|
|
gd::ObjectsContainer &objectsContainer, const gd::ObjectGroup &objectGroup,
|
|
const gd::SerializerElement &originalSerializedVariables) {
|
|
gd::VariablesContainer groupVariablesContainer;
|
|
groupVariablesContainer.UnserializeFrom(originalSerializedVariables);
|
|
// Add missing variables to objects added in the group.
|
|
for (const gd::String &objectName : objectGroup.GetAllObjectsNames()) {
|
|
const bool hasObject = objectsContainer.HasObjectNamed(objectName);
|
|
if (!hasObject && !globalObjectsContainer.HasObjectNamed(objectName)) {
|
|
continue;
|
|
}
|
|
auto &object = hasObject ? objectsContainer.GetObject(objectName)
|
|
: globalObjectsContainer.GetObject(objectName);
|
|
auto &variablesContainer = object.GetVariables();
|
|
for (std::size_t variableIndex = 0;
|
|
variableIndex < groupVariablesContainer.Count(); ++variableIndex) {
|
|
auto &groupVariable = groupVariablesContainer.Get(variableIndex);
|
|
const auto &variableName =
|
|
groupVariablesContainer.GetNameAt(variableIndex);
|
|
|
|
if (!variablesContainer.Has(variableName)) {
|
|
variablesContainer.Insert(variableName, groupVariable,
|
|
variablesContainer.Count());
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
// TODO Handle position changes for group variables.
|
|
// We could try to change the order of object variables in a way that the next
|
|
// call to MergeVariableContainers rebuild them in the same order.
|
|
void ObjectVariableHelper::ApplyChangesToObjects(
|
|
gd::ObjectsContainer &globalObjectsContainer,
|
|
gd::ObjectsContainer &objectsContainer,
|
|
const gd::VariablesContainer &groupVariablesContainer,
|
|
const gd::ObjectGroup &objectGroup,
|
|
const gd::VariablesChangeset &changeset) {
|
|
for (const gd::String &objectName : objectGroup.GetAllObjectsNames()) {
|
|
const bool hasObject = objectsContainer.HasObjectNamed(objectName);
|
|
if (!hasObject && !globalObjectsContainer.HasObjectNamed(objectName)) {
|
|
continue;
|
|
}
|
|
auto &object = hasObject ? objectsContainer.GetObject(objectName)
|
|
: globalObjectsContainer.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 an object already had the variable but it was not
|
|
// shared by other object of the group.
|
|
continue;
|
|
}
|
|
variablesContainer.Insert(variableName,
|
|
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;
|
|
if (variablesContainer.Has(newVariableName)) {
|
|
// It can happens if an object already had the variable but it was not
|
|
// shared by other object of the group.
|
|
variablesContainer.Remove(oldVariableName);
|
|
} else {
|
|
variablesContainer.Rename(oldVariableName, newVariableName);
|
|
}
|
|
}
|
|
// Apply type and value changes
|
|
for (const gd::String &variableName : changeset.valueChangedVariableNames) {
|
|
size_t index = variablesContainer.GetPosition(variableName);
|
|
variablesContainer.Remove(variableName);
|
|
variablesContainer.Insert(
|
|
variableName, groupVariablesContainer.Get(variableName), index);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ObjectVariableHelper::ApplyChangesToObjectInstances(
|
|
gd::VariablesContainer &objectVariablesContainer,
|
|
gd::InitialInstancesContainer &initialInstancesContainer,
|
|
const gd::String &objectName, const gd::VariablesChangeset &changeset) {
|
|
initialInstancesContainer.IterateOverInstances(
|
|
[&objectVariablesContainer, &objectName,
|
|
&changeset](gd::InitialInstance &instance) {
|
|
if (instance.GetObjectName() == objectName) {
|
|
auto &destinationVariablesContainer = instance.GetVariables();
|
|
for (const gd::String &variableName :
|
|
changeset.removedVariableNames) {
|
|
destinationVariablesContainer.Remove(variableName);
|
|
}
|
|
for (const gd::String &variableName : changeset.addedVariableNames) {
|
|
// Instance variables may already exist with another type.
|
|
if (destinationVariablesContainer.Has(variableName) &&
|
|
destinationVariablesContainer.Get(variableName).GetType() !=
|
|
objectVariablesContainer.Get(variableName).GetType()) {
|
|
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;
|
|
if (destinationVariablesContainer.Has(newVariableName)) {
|
|
// It can happens if an instance already had the variable.
|
|
destinationVariablesContainer.Remove(oldVariableName);
|
|
} else {
|
|
destinationVariablesContainer.Rename(oldVariableName,
|
|
newVariableName);
|
|
}
|
|
}
|
|
// Apply type changes
|
|
for (const gd::String &variableName :
|
|
changeset.valueChangedVariableNames) {
|
|
if (destinationVariablesContainer.Has(variableName) &&
|
|
destinationVariablesContainer.Get(variableName).GetType() !=
|
|
objectVariablesContainer.Get(variableName).GetType()) {
|
|
destinationVariablesContainer.Remove(variableName);
|
|
}
|
|
}
|
|
}
|
|
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
|