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

* In the future, there will be other actions where you can optionally wait for their tasks to be finished before running the rest of the actions. It makes logic easier and more straightforward to express in events. * This is similar to the Wait action, except that it's optional.
371 lines
15 KiB
C++
371 lines
15 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/Events/Serialization.h"
|
|
|
|
#include "GDCore/CommonTools.h"
|
|
#include "GDCore/Events/Event.h"
|
|
#include "GDCore/Events/EventsList.h"
|
|
#include "GDCore/Events/Instruction.h"
|
|
#include "GDCore/Events/InstructionsList.h"
|
|
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
|
|
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
|
|
#include "GDCore/Extensions/Platform.h"
|
|
#include "GDCore/Project/Project.h"
|
|
#include "GDCore/Serialization/Serializer.h"
|
|
#include "GDCore/Serialization/SerializerElement.h"
|
|
#include "GDCore/Tools/Log.h"
|
|
#include "GDCore/Tools/VersionWrapper.h"
|
|
|
|
using namespace std;
|
|
|
|
namespace {
|
|
bool AddQuotesToFunctionCall(gd::String& expressionStr,
|
|
const gd::String& functionName) {
|
|
bool changedSomething = false;
|
|
size_t functionCallPos = expressionStr.find(functionName + "(");
|
|
while (functionCallPos != gd::String::npos) {
|
|
size_t pos = functionCallPos + functionName.size() + 1;
|
|
|
|
// Skip whitespace
|
|
while (pos < expressionStr.size() && expressionStr[pos] == ' ') pos++;
|
|
|
|
if (pos < expressionStr.size()) {
|
|
changedSomething = true;
|
|
|
|
// Insert the first quote
|
|
expressionStr.insert(pos, "\"");
|
|
pos++;
|
|
|
|
// Escape the argument
|
|
while (pos < expressionStr.size() && expressionStr[pos] != ')') {
|
|
if (expressionStr[pos] == '"') {
|
|
expressionStr.insert(pos,
|
|
"\\"); // Insert a backslash to escape the quote
|
|
pos++;
|
|
}
|
|
pos++;
|
|
}
|
|
|
|
// Insert the last quote
|
|
if (pos < expressionStr.size() && expressionStr[pos] == ')') {
|
|
expressionStr.insert(pos, "\"");
|
|
pos++;
|
|
}
|
|
}
|
|
|
|
functionCallPos = expressionStr.find(functionName + "(", pos + 1);
|
|
}
|
|
|
|
return changedSomething;
|
|
}
|
|
} // namespace
|
|
|
|
namespace gd {
|
|
|
|
void EventsListSerialization::UpdateInstructionsFromGD4097(
|
|
gd::Project& project, gd::InstructionsList& list) {
|
|
for (std::size_t i = 0; i < list.size(); ++i) {
|
|
gd::Instruction& instr = list[i];
|
|
|
|
for (std::size_t j = 0; j < instr.GetParametersCount(); ++j) {
|
|
gd::String expressionStr = instr.GetParameter(j).GetPlainString();
|
|
bool changedSomething = false;
|
|
changedSomething |= AddQuotesToFunctionCall(expressionStr, "PointX");
|
|
changedSomething |= AddQuotesToFunctionCall(expressionStr, "PointY");
|
|
|
|
if (changedSomething) {
|
|
std::cout << "(Debug) Converted \""
|
|
<< instr.GetParameter(j).GetPlainString() << "\" to \""
|
|
<< expressionStr << "\"" << std::endl;
|
|
instr.SetParameter(j, gd::Expression(expressionStr));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void EventsListSerialization::UpdateInstructionsFromGD31x(
|
|
gd::Project& project, gd::InstructionsList& list) {
|
|
for (std::size_t i = 0; i < list.size(); ++i) {
|
|
gd::Instruction& instr = list[i];
|
|
|
|
if (instr.GetType() == "VarScene" || instr.GetType() == "VarSceneTxt" ||
|
|
instr.GetType() == "VarGlobal" || instr.GetType() == "VarGlobalTxt" ||
|
|
instr.GetType() == "ModVarScene" ||
|
|
instr.GetType() == "ModVarSceneTxt" ||
|
|
instr.GetType() == "ModVarGlobal" ||
|
|
instr.GetType() == "ModVarGlobalTxt") {
|
|
std::vector<gd::Expression> parameters = instr.GetParameters();
|
|
if (parameters.size() >= 1) parameters.erase(parameters.begin() + 0);
|
|
instr.SetParameters(parameters);
|
|
}
|
|
|
|
if (instr.GetType() == "VarSceneDef" || instr.GetType() == "VarGlobalDef" ||
|
|
instr.GetType() == "VarObjetDef") {
|
|
instr.SetParameter(
|
|
1,
|
|
gd::Expression("\"" + instr.GetParameter(1).GetPlainString() + "\""));
|
|
}
|
|
}
|
|
}
|
|
|
|
void EventsListSerialization::UpdateInstructionsFromGD2x(
|
|
gd::Project& project,
|
|
gd::InstructionsList& list,
|
|
bool instructionsAreActions) {
|
|
for (std::size_t i = 0; i < list.size(); ++i) {
|
|
gd::Instruction& instr = list[i];
|
|
|
|
const gd::InstructionMetadata& metadata =
|
|
instructionsAreActions
|
|
? MetadataProvider::GetActionMetadata(project.GetCurrentPlatform(),
|
|
instr.GetType())
|
|
: MetadataProvider::GetConditionMetadata(
|
|
project.GetCurrentPlatform(), instr.GetType());
|
|
|
|
// Specific updates for some instructions
|
|
if (instr.GetType() == "LinkedObjects::LinkObjects" ||
|
|
instr.GetType() == "LinkedObjects::RemoveLinkBetween") {
|
|
instr.SetParameter(1, instr.GetParameter(3));
|
|
instr.SetParameter(2, instr.GetParameter(4));
|
|
} else if (instr.GetType() == "LinkedObjects::RemoveAllLinksOf") {
|
|
instr.SetParameter(1, instr.GetParameter(2));
|
|
} else if (instr.GetType() == "LinkedObjects::PickObjectsLinkedTo") {
|
|
instr.SetParameter(1, instr.GetParameter(5));
|
|
instr.SetParameter(2, instr.GetParameter(3));
|
|
} else if (instr.GetType() ==
|
|
"PhysicsBehavior::AddRevoluteJointBetweenObjects") {
|
|
instr.SetParameter(4, instr.GetParameter(5));
|
|
instr.SetParameter(5, instr.GetParameter(6));
|
|
} else if (instr.GetType() == "FixCamera" ||
|
|
instr.GetType() == "CentreCamera") {
|
|
std::vector<gd::Expression> parameters = instr.GetParameters();
|
|
if (parameters.size() >= 3) parameters.erase(parameters.begin() + 2);
|
|
instr.SetParameters(parameters);
|
|
} else if (instr.GetType() == "AjoutObjConcern" ||
|
|
instr.GetType() == "AjoutHasard") {
|
|
instr.SetParameter(1, instr.GetParameter(3));
|
|
} else if (instr.GetType() == "SeDirige" ||
|
|
instr.GetType() == "EstTourne") {
|
|
std::vector<gd::Expression> parameters = instr.GetParameters();
|
|
if (parameters.size() >= 3) parameters.erase(parameters.begin() + 2);
|
|
if (parameters.size() >= 3) parameters.erase(parameters.begin() + 2);
|
|
instr.SetParameters(parameters);
|
|
} else if (instr.GetType() == "Create") {
|
|
std::vector<gd::Expression> parameters = instr.GetParameters();
|
|
if (parameters.size() >= 2) parameters.erase(parameters.begin() + 1);
|
|
if (parameters.size() >= 2) parameters.erase(parameters.begin() + 1);
|
|
instr.SetParameters(parameters);
|
|
} else if (instr.GetType() == "CreateByName") {
|
|
std::vector<gd::Expression> parameters = instr.GetParameters();
|
|
if (parameters.size() >= 2) parameters.erase(parameters.begin() + 1);
|
|
instr.SetParameters(parameters);
|
|
} else if (instr.GetType() == "NbObjet") {
|
|
std::vector<gd::Expression> parameters = instr.GetParameters();
|
|
if (parameters.size() >= 2) parameters.erase(parameters.begin() + 1);
|
|
instr.SetParameters(parameters);
|
|
} else if (instr.GetType() == "Distance") {
|
|
std::vector<gd::Expression> parameters = instr.GetParameters();
|
|
if (parameters.size() >= 3) parameters.erase(parameters.begin() + 2);
|
|
if (parameters.size() >= 3) parameters.erase(parameters.begin() + 2);
|
|
if (parameters.size() >= 4 && (parameters[3].GetPlainString() == ">=" ||
|
|
parameters[3].GetPlainString() == ">")) {
|
|
instr.SetInverted(true);
|
|
} else {
|
|
instr.SetInverted(false);
|
|
}
|
|
instr.SetParameters(parameters);
|
|
}
|
|
|
|
// Common updates for some parameters
|
|
const std::vector<gd::Expression>& parameters = instr.GetParameters();
|
|
for (std::size_t j = 0;
|
|
j < parameters.size() && j < metadata.parameters.size();
|
|
++j) {
|
|
if (metadata.parameters[j].type == "relationalOperator" ||
|
|
metadata.parameters[j].type == "operator") {
|
|
if (j == parameters.size() - 1) {
|
|
std::cout << "ERROR: No more parameters after a [relational]operator "
|
|
"when trying to update an instruction from GD2.x";
|
|
} else {
|
|
// Exchange parameters
|
|
gd::String op = parameters[j + 1].GetPlainString();
|
|
instr.SetParameter(j + 1, parameters[j]);
|
|
instr.SetParameter(j, gd::Expression(op));
|
|
}
|
|
}
|
|
}
|
|
|
|
// UpdateInstructionsFromGD2x(project, instr.GetSubInstructions(),
|
|
// instructionsAreActions);
|
|
}
|
|
}
|
|
|
|
void EventsListSerialization::UnserializeEventsFrom(
|
|
gd::Project& project, EventsList& list, const SerializerElement& events) {
|
|
list.Clear();
|
|
events.ConsiderAsArrayOf("event", "Event");
|
|
for (std::size_t i = 0; i < events.GetChildrenCount(); ++i) {
|
|
SerializerElement& eventElem = events.GetChild(i);
|
|
gd::String type =
|
|
eventElem.GetChild("type", 0, "Type").GetValue().GetString();
|
|
gd::BaseEventSPtr event = project.CreateEvent(type);
|
|
if (event != std::shared_ptr<gd::BaseEvent>())
|
|
event->UnserializeFrom(project, eventElem);
|
|
else {
|
|
std::cout << "WARNING: Unknown event of type " << type << std::endl;
|
|
event = std::make_shared<EmptyEvent>();
|
|
}
|
|
|
|
event->SetDisabled(eventElem.GetBoolAttribute("disabled", false));
|
|
event->SetFolded(eventElem.GetBoolAttribute("folded", false));
|
|
|
|
list.InsertEvent(event, list.GetEventsCount());
|
|
}
|
|
}
|
|
|
|
void EventsListSerialization::SerializeEventsTo(const EventsList& list,
|
|
SerializerElement& events) {
|
|
events.ConsiderAsArrayOf("event");
|
|
for (std::size_t j = 0; j < list.size(); j++) {
|
|
const gd::BaseEvent& event = list.GetEvent(j);
|
|
SerializerElement& eventElem = events.AddChild("event");
|
|
|
|
if (event.IsDisabled())
|
|
eventElem.SetAttribute("disabled", event.IsDisabled());
|
|
if (event.IsFolded()) eventElem.SetAttribute("folded", event.IsFolded());
|
|
eventElem.AddChild("type").SetValue(event.GetType());
|
|
|
|
event.SerializeTo(eventElem);
|
|
}
|
|
}
|
|
|
|
using namespace std;
|
|
|
|
void gd::EventsListSerialization::UnserializeInstructionsFrom(
|
|
gd::Project& project,
|
|
gd::InstructionsList& instructions,
|
|
const SerializerElement& elem) {
|
|
elem.ConsiderAsArrayOf("instruction");
|
|
// Compatibility with GD <= 4.0.95
|
|
if (elem.HasChild("condition", "Condition"))
|
|
elem.ConsiderAsArrayOf("condition", "Condition");
|
|
else if (elem.HasChild("action", "Action"))
|
|
elem.ConsiderAsArrayOf("action", "Action");
|
|
// end of compatibility code
|
|
|
|
for (std::size_t i = 0; i < elem.GetChildrenCount(); ++i) {
|
|
gd::Instruction instruction;
|
|
const SerializerElement& instrElement = elem.GetChild(i);
|
|
|
|
instruction.SetType(
|
|
instrElement.GetChild("type", 0, "Type")
|
|
.GetStringAttribute("value")
|
|
.FindAndReplace("Automatism",
|
|
"Behavior")); // Compatibility with GD <= 4
|
|
instruction.SetInverted(
|
|
instrElement.GetChild("type", 0, "Type")
|
|
.GetBoolAttribute("inverted", false, "Contraire"));
|
|
|
|
instruction.SetAwaited(
|
|
instrElement.GetChild("type", 0, "Type").GetBoolAttribute("await"));
|
|
|
|
// Read parameters
|
|
vector<gd::Expression> parameters;
|
|
|
|
// Compatibility with GD <= 3.3
|
|
if (instrElement.HasChild("Parametre")) {
|
|
for (std::size_t j = 0; j < instrElement.GetChildrenCount("Parametre");
|
|
++j)
|
|
parameters.push_back(gd::Expression(
|
|
instrElement.GetChild("Parametre", j).GetValue().GetString()));
|
|
|
|
}
|
|
// end of compatibility code
|
|
else {
|
|
const SerializerElement& parametersElem =
|
|
instrElement.GetChild("parameters");
|
|
parametersElem.ConsiderAsArrayOf("parameter");
|
|
for (std::size_t j = 0; j < parametersElem.GetChildrenCount(); ++j)
|
|
parameters.push_back(
|
|
gd::Expression(parametersElem.GetChild(j).GetValue().GetString()));
|
|
}
|
|
|
|
instruction.SetParameters(parameters);
|
|
|
|
// Read sub instructions
|
|
if (instrElement.HasChild("subInstructions"))
|
|
UnserializeInstructionsFrom(project,
|
|
instruction.GetSubInstructions(),
|
|
instrElement.GetChild("subInstructions"));
|
|
// Compatibility with GD <= 4.0.95
|
|
if (instrElement.HasChild("subConditions", "SubConditions"))
|
|
UnserializeInstructionsFrom(
|
|
project,
|
|
instruction.GetSubInstructions(),
|
|
instrElement.GetChild("subConditions", 0, "SubConditions"));
|
|
if (instrElement.HasChild("subActions", "SubActions"))
|
|
UnserializeInstructionsFrom(
|
|
project,
|
|
instruction.GetSubInstructions(),
|
|
instrElement.GetChild("subActions", 0, "SubActions"));
|
|
// end of compatibility code
|
|
|
|
instructions.Insert(instruction);
|
|
}
|
|
|
|
// Compatibility with GD <= 3.1
|
|
if (project.GetLastSaveGDMajorVersion() < 3 ||
|
|
(project.GetLastSaveGDMajorVersion() == 3 &&
|
|
project.GetLastSaveGDMinorVersion() <= 1))
|
|
UpdateInstructionsFromGD31x(project, instructions);
|
|
|
|
if (project.GetLastSaveGDMajorVersion() < 3)
|
|
UpdateInstructionsFromGD2x(
|
|
project, instructions, elem.HasChild("action", "Action"));
|
|
|
|
// Compatibility with GD <= 4.0.97
|
|
if (VersionWrapper::IsOlderOrEqual(project.GetLastSaveGDMajorVersion(),
|
|
project.GetLastSaveGDMinorVersion(),
|
|
project.GetLastSaveGDBuildVersion(),
|
|
0,
|
|
4,
|
|
0,
|
|
97,
|
|
0)) {
|
|
UpdateInstructionsFromGD4097(project, instructions);
|
|
}
|
|
// end of compatibility code
|
|
}
|
|
|
|
void gd::EventsListSerialization::SerializeInstructionsTo(
|
|
const gd::InstructionsList& list, SerializerElement& instructions) {
|
|
instructions.ConsiderAsArrayOf("instruction");
|
|
for (std::size_t k = 0; k < list.size(); k++) {
|
|
SerializerElement& instruction = instructions.AddChild("instruction");
|
|
instruction.AddChild("type").SetAttribute("value", list[k].GetType());
|
|
|
|
if (list[k].IsInverted())
|
|
instruction.GetChild("type").SetAttribute("inverted", true);
|
|
if (list[k].IsAwaited())
|
|
instruction.GetChild("type").SetAttribute("await", true);
|
|
|
|
// Parameters
|
|
SerializerElement& parameters = instruction.AddChild("parameters");
|
|
parameters.ConsiderAsArrayOf("parameter");
|
|
for (std::size_t l = 0; l < list[k].GetParameters().size(); l++)
|
|
parameters.AddChild("parameter")
|
|
.SetValue(list[k].GetParameter(l).GetPlainString());
|
|
|
|
// Sub instructions
|
|
if (!list[k].GetSubInstructions().empty()) {
|
|
SerializeInstructionsTo(list[k].GetSubInstructions(),
|
|
instruction.AddChild("subInstructions"));
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace gd
|