Compare commits

..

28 Commits

Author SHA1 Message Date
Davy Hélard
55fc686a5b Fix expression default type. 2022-10-29 22:02:37 +02:00
Davy Hélard
5580189f88 Remove unused imports. 2022-10-29 20:49:29 +02:00
Davy Hélard
9a6d1d6d32 Remove useless lines added by mistake. 2022-10-29 19:42:54 +02:00
Davy Hélard
6c9739c01d Fix object parameter extraInfo declaration. 2022-10-29 19:41:07 +02:00
Davy Hélard
cd3c997b28 Review changes: more renaming 2022-10-29 17:53:15 +02:00
Davy Hélard
8064c4de57 Review changes: renaming 2022-10-29 14:54:09 +02:00
Davy Hélard
8666851f54 Documentation. 2022-10-29 13:40:53 +02:00
Davy Hélard
6d568b2f2c Fix type conversion to expressionType. 2022-10-29 13:40:52 +02:00
Davy Hélard
180d4318aa Add ValueTypeEditor to edit type definitions. 2022-10-29 13:40:52 +02:00
Davy Hélard
65a57f86da Allow to define type extraInfo for operands. 2022-10-29 13:40:52 +02:00
Davy Hélard
6a3e7f9c58 Define expression types with ValueTypeMetadata. 2022-10-29 13:40:52 +02:00
Davy Hélard
74f1d571ba Extract ValueTypeMetadata from ParameterMetadata. 2022-10-29 13:40:51 +02:00
Davy Hélard
a7cb3fc5a2 Add some comment about parameter types. 2022-10-29 13:40:51 +02:00
Davy Hélard
08d3c3323a Move out changes to mapFor. 2022-10-29 13:40:51 +02:00
Davy Hélard
fec603b811 Format 2022-10-29 13:40:51 +02:00
Davy Hélard
d68affc117 Add tests for shiftSentenceParamIndexes 2022-10-29 13:40:50 +02:00
Davy Hélard
77cd6c44d6 Review change: memory leak in test. 2022-10-29 13:40:50 +02:00
Davy Hélard
2b4c8813e4 Review changes 2022-10-29 13:40:50 +02:00
Davy Hélard
2ef9266ec4 Disable the "Add parameter" button for ActionWithOperator. 2022-10-29 13:40:49 +02:00
Davy Hélard
ceba6cf739 Split function types and returned types in 2 drop-down lists. 2022-10-29 13:40:49 +02:00
Davy Hélard
18f2085de7 Better disable parameters in UI. 2022-10-29 13:40:49 +02:00
Davy Hélard
b586fb87ed Show the right parameters in the editor. 2022-10-29 13:40:49 +02:00
Davy Hélard
8aed02ab17 Allow event extensions to define an action with an operator. 2022-10-29 13:40:48 +02:00
Davy Hélard
8c383fc448 Cleanup stories imports. 2022-10-29 13:40:48 +02:00
Davy Hélard
af3a2016f2 Fix flow. 2022-10-29 13:40:48 +02:00
Davy Hélard
7a20161794 Use the icon of expressions. 2022-10-29 13:40:47 +02:00
Davy Hélard
0feb4ef321 Put operator and operand first. 2022-10-29 13:40:47 +02:00
Davy Hélard
5cbcd16523 Allow event extensions to define a condition with an operator from an expression. 2022-10-29 13:40:47 +02:00
1650 changed files with 98738 additions and 35271 deletions

View File

@@ -670,18 +670,6 @@ gd::String EventsCodeGenerator::GenerateActionsListCode(
return outputCode;
}
const gd::String EventsCodeGenerator::GenerateRelationalOperatorCodes(const gd::String &operatorString) {
if (operatorString == "=") {
return "==";
}
if (operatorString != "<" && operatorString != ">" &&
operatorString != "<=" && operatorString != ">=" && operatorString != "!=") {
cout << "Warning: Bad relational operator: Set to == by default." << endl;
return "==";
}
return operatorString;
}
gd::String EventsCodeGenerator::GenerateParameterCodes(
const gd::Expression& parameter,
const gd::ParameterMetadata& metadata,
@@ -706,7 +694,14 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
argOutput =
GenerateObject(parameter.GetPlainString(), metadata.GetType(), context);
} else if (metadata.GetType() == "relationalOperator") {
argOutput += GenerateRelationalOperatorCodes(parameter.GetPlainString());
auto parameterString = parameter.GetPlainString();
argOutput += parameterString == "=" ? "==" : parameterString;
if (argOutput != "==" && argOutput != "<" && argOutput != ">" &&
argOutput != "<=" && argOutput != ">=" && argOutput != "!=") {
cout << "Warning: Bad relational operator: Set to == by default." << endl;
argOutput = "==";
}
argOutput = "\"" + argOutput + "\"";
} else if (metadata.GetType() == "operator") {
argOutput += parameter.GetPlainString();

View File

@@ -481,9 +481,6 @@ class GD_CORE_API EventsCodeGenerator {
*/
size_t GenerateSingleUsageUniqueIdForEventsList();
virtual const gd::String GenerateRelationalOperatorCodes(
const gd::String& operatorString);
protected:
/**
* \brief Generate the code for a single parameter.

View File

@@ -55,10 +55,6 @@ void Instruction::SetParameter(std::size_t nb, const gd::Expression& val) {
parameters[nb] = val;
}
void Instruction::AddParameter(const gd::Expression& val) {
parameters.push_back(val);
}
std::shared_ptr<Instruction> GD_CORE_API
CloneRememberingOriginalElement(std::shared_ptr<Instruction> instruction) {
std::shared_ptr<Instruction> copy =

View File

@@ -123,11 +123,6 @@ class GD_CORE_API Instruction {
*/
void SetParameter(std::size_t nb, const gd::Expression& val);
/** Add a parameter at the end
* \param val The new value of the parameter
*/
void AddParameter(const gd::Expression& val);
/** \brief Get a reference to the std::vector containing the parameters.
* \return A std::vector containing the parameters
*/

View File

@@ -81,7 +81,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAdvancedExtension(
.AddExpression(
"GetArgumentAsNumber",
_("Get function parameter value"),
_("Get function parameter (also called \"argument\") value."),
_("Get function parameter (also called \"argument\") value"),
"",
"res/function16.png")
.AddParameter("functionParameterName", "Parameter name");
@@ -90,34 +90,10 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAdvancedExtension(
.AddStrExpression(
"GetArgumentAsString",
_("Get function parameter text"),
_("Get function parameter (also called \"argument\") text."),
_("Get function parameter (also called \"argument\") text "),
"",
"res/function16.png")
.AddParameter("functionParameterName", "Parameter name");
extension
.AddCondition(
"CompareArgumentAsNumber",
_("Compare function parameter value"),
_("Compare function parameter (also called \"argument\") value."),
_("Parameter _PARAM0_"),
"",
"res/function32.png",
"res/function16.png")
.AddParameter("functionParameterName", "Parameter name")
.UseStandardRelationalOperatorParameters("number");
extension
.AddCondition(
"CompareArgumentAsString",
_("Compare function parameter text"),
_("Compare function parameter (also called \"argument\") text."),
_("Parameter _PARAM0_"),
"",
"res/function32.png",
"res/function16.png")
.AddParameter("functionParameterName", "Parameter name")
.UseStandardRelationalOperatorParameters("string");
}
} // namespace gd

View File

@@ -96,7 +96,7 @@ BuiltinExtensionsImplementer::ImplementsCommonConversionsExtension(
"JSONToVariableStructure",
_("Convert JSON to a scene variable"),
_("Parse a JSON object and store it into a scene variable"),
_("Convert JSON string _PARAM0_ and store it into variable _PARAM1_"),
_("Parse JSON string _PARAM0_ and store it into variable _PARAM1_"),
"",
"res/actions/net24.png",
"res/actions/net.png")
@@ -108,7 +108,7 @@ BuiltinExtensionsImplementer::ImplementsCommonConversionsExtension(
.AddAction("JSONToGlobalVariableStructure",
_("Convert JSON to global variable"),
_("Parse a JSON object and store it into a global variable"),
_("Convert JSON string _PARAM0_ and store it into global "
_("Parse JSON string _PARAM0_ and store it into global "
"variable _PARAM1_"),
"",
"res/actions/net24.png",

View File

@@ -492,16 +492,6 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
"",
"res/mathfunction.png")
.SetHelpPath("/all-features/expressions");
extension
.AddExpression("lerpAngle",
_("Lerp (Linear interpolation) between two angles"),
_("Linearly interpolates between two angles (in degrees) by taking the shortest direction around the circle."),
"",
"res/mathfunction.png")
.AddParameter("expression", _("Starting angle, in degrees"))
.AddParameter("expression", _("Destination angle, in degrees"))
.AddParameter("expression", _("Interpolation value between 0 and 1."));
}
} // namespace gd

View File

@@ -324,19 +324,6 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
"res/conditions/animation24.png",
"res/conditions/animation.png")
.AddParameter("object", _("Object"), "Sprite")
.MarkAsSimple()
.SetHidden();
obj.AddCondition("AnimationEnded2",
_("Animation finished"),
_("Check if the animation being played by the Sprite object "
"is finished."),
_("The animation of _PARAM0_ is finished"),
_("Animations and images"),
"res/conditions/animation24.png",
"res/conditions/animation.png")
.AddParameter("object", _("Object"), "Sprite")
.MarkAsSimple();

View File

@@ -155,7 +155,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
"",
"res/timer_black.svg",
"res/timer_black.svg")
.AddParameter("expression", _("Time to wait in seconds"))
.AddParameter("expression", "Time to wait in seconds")
.SetHelpPath("/all-features/timers-and-time/wait-action");
extension

View File

@@ -236,6 +236,7 @@ class GD_CORE_API BehaviorMetadata {
}
const gd::String& GetName() const;
#if defined(GD_IDE_ONLY)
const gd::String& GetFullName() const { return fullname; }
const gd::String& GetDefaultName() const { return defaultName; }
const gd::String& GetDescription() const { return description; }
@@ -256,21 +257,7 @@ class GD_CORE_API BehaviorMetadata {
* \note An empty string means the base object, so any object.
*/
const gd::String& GetObjectType() const { return objectType; }
/**
* Check if the behavior is private - it can't be used outside of its
* extension.
*/
bool IsPrivate() const { return isPrivate; }
/**
* Set that the behavior is private - it can't be used outside of its
* extension.
*/
BehaviorMetadata &SetPrivate() {
isPrivate = true;
return *this;
}
#endif
/**
* \brief Return the associated gd::Behavior, handling behavior contents.
@@ -328,7 +315,6 @@ class GD_CORE_API BehaviorMetadata {
gd::String group;
gd::String iconFilename;
gd::String objectType;
bool isPrivate = false;
// TODO: Nitpicking: convert these to std::unique_ptr to clarify ownership.
std::shared_ptr<gd::Behavior> instance;

View File

@@ -179,7 +179,8 @@ class GD_CORE_API MultipleInstructionMetadata {
}
/**
* \see gd::InstructionMetadata::SetPrivate
* Set that the instruction is private - it can't be used outside of the
* object/ behavior that it is attached too.
*/
MultipleInstructionMetadata &SetPrivate() {
if (expression)

View File

@@ -15,12 +15,9 @@ ParameterMetadata::ParameterMetadata() : codeOnly(false) {}
void ParameterMetadata::SerializeTo(SerializerElement& element) const {
valueTypeMetadata.SerializeTo(element);
element.SetAttribute("description", description);
if (!longDescription.empty()) {
element.SetAttribute("longDescription", longDescription);
}
if (codeOnly) {
element.SetAttribute("codeOnly", codeOnly);
}
element.SetAttribute("longDescription", longDescription);
element.SetAttribute("codeOnly", codeOnly);
element.SetAttribute("defaultValue", defaultValue);
element.SetAttribute("name", name);
}
@@ -29,6 +26,7 @@ void ParameterMetadata::UnserializeFrom(const SerializerElement& element) {
description = element.GetStringAttribute("description");
longDescription = element.GetStringAttribute("longDescription");
codeOnly = element.GetBoolAttribute("codeOnly");
defaultValue = element.GetStringAttribute("defaultValue");
name = element.GetStringAttribute("name");
}

View File

@@ -144,15 +144,13 @@ class GD_CORE_API ParameterMetadata {
/**
* \brief Get the default value for the parameter.
*/
const gd::String &GetDefaultValue() const {
return valueTypeMetadata.GetDefaultValue();
}
const gd::String &GetDefaultValue() const { return defaultValue; }
/**
* \brief Set the default value, if the parameter is optional.
*/
ParameterMetadata &SetDefaultValue(const gd::String &defaultValue_) {
valueTypeMetadata.SetDefaultValue(defaultValue_);
defaultValue = defaultValue_;
return *this;
}
@@ -238,6 +236,8 @@ class GD_CORE_API ParameterMetadata {
private:
gd::ValueTypeMetadata valueTypeMetadata; ///< Parameter type
gd::String longDescription; ///< Long description shown in the editor.
gd::String defaultValue; ///< Used as a default value in editor or if an
///< optional parameter is empty.
gd::String name; ///< The name of the parameter to be used in code
///< generation. Optional.
};

View File

@@ -46,25 +46,4 @@ const gd::String &ValueTypeMetadata::GetPrimitiveValueType(const gd::String &par
return parameterType;
}
const gd::String ValueTypeMetadata::numberValueType = "number";
const gd::String ValueTypeMetadata::booleanValueType = "boolean";
const gd::String ValueTypeMetadata::colorValueType = "color";
const gd::String ValueTypeMetadata::choiceValueType = "stringWithSelector";
const gd::String ValueTypeMetadata::stringValueType = "string";
const gd::String &ValueTypeMetadata::ConvertPropertyTypeToValueType(
const gd::String &propertyType) {
if (propertyType == "Number") {
return numberValueType;
} else if (propertyType == "Boolean") {
return booleanValueType;
} else if (propertyType == "Color") {
return colorValueType;
} else if (propertyType == "Choice") {
return choiceValueType;
}
// For "String" or default
return stringValueType;
};
} // namespace gd

View File

@@ -192,12 +192,6 @@ class GD_CORE_API ValueTypeMetadata {
static const gd::String numberType;
static const gd::String stringType;
/**
* \brief Return the ValueTypeMetadata name for a property type.
* \see gd::PropertyDescriptor
*/
static const gd::String &ConvertPropertyTypeToValueType(const gd::String &propertyType);
/** \name Serialization
*/
///@{
@@ -218,12 +212,6 @@ class GD_CORE_API ValueTypeMetadata {
bool optional; ///< True if the parameter is optional
gd::String defaultValue; ///< Used as a default value in editor or if an
///< optional parameter is empty.
static const gd::String numberValueType;
static const gd::String booleanValueType;
static const gd::String colorValueType;
static const gd::String choiceValueType;
static const gd::String stringValueType;
};
} // namespace gd

View File

@@ -24,12 +24,14 @@
namespace gd {
#if defined(GD_IDE_ONLY)
std::map<gd::String, gd::InstructionMetadata>
PlatformExtension::badConditionsMetadata;
std::map<gd::String, gd::InstructionMetadata>
PlatformExtension::badActionsMetadata;
std::map<gd::String, gd::ExpressionMetadata>
PlatformExtension::badExpressionsMetadata;
#endif
gd::InstructionMetadata& PlatformExtension::AddCondition(
const gd::String& name,
@@ -39,6 +41,7 @@ gd::InstructionMetadata& PlatformExtension::AddCondition(
const gd::String& group,
const gd::String& icon,
const gd::String& smallicon) {
#if defined(GD_IDE_ONLY)
gd::String nameWithNamespace = GetNameSpace() + name;
conditionsInfos[nameWithNamespace] = InstructionMetadata(GetNameSpace(),
nameWithNamespace,
@@ -50,6 +53,7 @@ gd::InstructionMetadata& PlatformExtension::AddCondition(
smallicon)
.SetHelpPath(GetHelpPath());
return conditionsInfos[nameWithNamespace];
#endif
}
gd::InstructionMetadata& PlatformExtension::AddAction(
@@ -60,6 +64,7 @@ gd::InstructionMetadata& PlatformExtension::AddAction(
const gd::String& group,
const gd::String& icon,
const gd::String& smallicon) {
#if defined(GD_IDE_ONLY)
gd::String nameWithNamespace = GetNameSpace() + name;
actionsInfos[nameWithNamespace] = InstructionMetadata(GetNameSpace(),
nameWithNamespace,
@@ -71,6 +76,7 @@ gd::InstructionMetadata& PlatformExtension::AddAction(
smallicon)
.SetHelpPath(GetHelpPath());
return actionsInfos[nameWithNamespace];
#endif
}
gd::ExpressionMetadata& PlatformExtension::AddExpression(
@@ -79,6 +85,7 @@ gd::ExpressionMetadata& PlatformExtension::AddExpression(
const gd::String& description,
const gd::String& group,
const gd::String& smallicon) {
#if defined(GD_IDE_ONLY)
gd::String nameWithNamespace = GetNameSpace() + name;
expressionsInfos[nameWithNamespace] = ExpressionMetadata("number",
GetNameSpace(),
@@ -89,6 +96,7 @@ gd::ExpressionMetadata& PlatformExtension::AddExpression(
smallicon)
.SetHelpPath(GetHelpPath());
return expressionsInfos[nameWithNamespace];
#endif
}
gd::ExpressionMetadata& PlatformExtension::AddStrExpression(
@@ -97,6 +105,7 @@ gd::ExpressionMetadata& PlatformExtension::AddStrExpression(
const gd::String& description,
const gd::String& group,
const gd::String& smallicon) {
#if defined(GD_IDE_ONLY)
gd::String nameWithNamespace = GetNameSpace() + name;
strExpressionsInfos[nameWithNamespace] = ExpressionMetadata("string",
GetNameSpace(),
@@ -107,6 +116,7 @@ gd::ExpressionMetadata& PlatformExtension::AddStrExpression(
smallicon)
.SetHelpPath(GetHelpPath());
return strExpressionsInfos[nameWithNamespace];
#endif
}
gd::MultipleInstructionMetadata PlatformExtension::AddExpressionAndCondition(
@@ -210,10 +220,12 @@ PlatformExtension::AddExpressionAndConditionAndAction(
expression, condition, action);
}
#if defined(GD_IDE_ONLY)
gd::DependencyMetadata& PlatformExtension::AddDependency() {
extensionDependenciesMetadata.push_back(DependencyMetadata());
return extensionDependenciesMetadata.back();
}
#endif
gd::ObjectMetadata& PlatformExtension::AddObject(
const gd::String& name,
@@ -305,6 +317,7 @@ gd::EventMetadata& PlatformExtension::AddEvent(
const gd::String& group_,
const gd::String& smallicon_,
std::shared_ptr<gd::BaseEvent> instance_) {
#if defined(GD_IDE_ONLY)
gd::String nameWithNamespace = GetNameSpace() + name_;
eventsInfos[nameWithNamespace] = gd::EventMetadata(nameWithNamespace,
fullname_,
@@ -313,6 +326,7 @@ gd::EventMetadata& PlatformExtension::AddEvent(
smallicon_,
instance_);
return eventsInfos[nameWithNamespace];
#endif
}
PlatformExtension& PlatformExtension::SetExtensionInformation(
@@ -392,6 +406,8 @@ std::vector<gd::String> PlatformExtension::GetBehaviorsTypes() const {
return behaviors;
}
#if defined(GD_IDE_ONLY)
gd::InstructionMetadata& PlatformExtension::AddDuplicatedAction(
const gd::String& newActionName, const gd::String& copiedActionName) {
gd::String newNameWithNamespace = GetNameSpace() + newActionName;
@@ -570,6 +586,7 @@ gd::BaseEventSPtr PlatformExtension::CreateEvent(
return std::shared_ptr<gd::BaseEvent>();
}
#endif
CreateFunPtr PlatformExtension::GetObjectCreationFunctionPtr(
const gd::String& objectType) const {
@@ -648,6 +665,7 @@ bool PlatformExtension::IsBuiltin() const {
builtinExtensions.end();
}
#if defined(GD_IDE_ONLY)
void PlatformExtension::StripUnimplementedInstructionsAndExpressions() {
for (std::map<gd::String, gd::InstructionMetadata>::iterator it =
GetAllActions().begin();
@@ -792,40 +810,7 @@ void PlatformExtension::StripUnimplementedInstructionsAndExpressions() {
++it;
}
}
gd::String
PlatformExtension::GetEventsFunctionFullType(const gd::String &extensionName,
const gd::String &functionName) {
const auto &separator = GetNamespaceSeparator();
return extensionName + separator + functionName;
}
gd::String PlatformExtension::GetBehaviorEventsFunctionFullType(
const gd::String &extensionName, const gd::String &behaviorName,
const gd::String &functionName) {
const auto &separator = GetNamespaceSeparator();
return extensionName + separator + behaviorName + separator + functionName;
}
gd::String
PlatformExtension::GetBehaviorFullType(const gd::String &extensionName,
const gd::String &behaviorName) {
const auto &separator = GetNamespaceSeparator();
return extensionName + separator + behaviorName;
}
gd::String PlatformExtension::GetObjectEventsFunctionFullType(
const gd::String &extensionName, const gd::String &objectName,
const gd::String &functionName) {
const auto &separator = GetNamespaceSeparator();
return extensionName + separator + objectName + separator + functionName;
}
gd::String PlatformExtension::GetObjectFullType(const gd::String &extensionName,
const gd::String &objectName) {
const auto &separator = GetNamespaceSeparator();
return extensionName + separator + objectName;
}
#endif
PlatformExtension::PlatformExtension()
: deprecated(false), category(_("General")) {}

View File

@@ -620,26 +620,7 @@ class GD_CORE_API PlatformExtension {
*/
static gd::String GetNamespaceSeparator() { return "::"; }
static gd::String GetEventsFunctionFullType(const gd::String &extensionName,
const gd::String &functionName);
static gd::String
GetBehaviorEventsFunctionFullType(const gd::String &extensionName,
const gd::String &behaviorName,
const gd::String &functionName);
static gd::String GetBehaviorFullType(const gd::String &extensionName,
const gd::String &behaviorName);
static gd::String
GetObjectEventsFunctionFullType(const gd::String &extensionName,
const gd::String &objectName,
const gd::String &functionName);
static gd::String GetObjectFullType(const gd::String &extensionName,
const gd::String &objectName);
private:
private:
/**
* Set the namespace (the string all actions/conditions/expressions start
* with).

View File

@@ -3,6 +3,7 @@
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#if defined(GD_IDE_ONLY)
#ifndef SCENECANVASSETTINGS_H
#define SCENECANVASSETTINGS_H
#include "GDCore/String.h"
@@ -42,3 +43,4 @@ private:
} // namespace gd
#endif // SCENECANVASSETTINGS_H
#endif

View File

@@ -773,18 +773,15 @@ vector<EventsSearchResult> EventsRefactorer::SearchInEvents(
const gd::String& ignored_characters =
EventsRefactorer::searchIgnoredCharacters;
if (inEventSentences) {
// Remove ignored characters only when searching in event sentences.
search.replace_if(
search.begin(),
search.end(),
[ignored_characters](const char& c) {
return ignored_characters.find(c) != gd::String::npos;
},
"");
search = search.LeftTrim().RightTrim();
search.RemoveConsecutiveOccurrences(search.begin(), search.end(), ' ');
}
search.replace_if(
search.begin(),
search.end(),
[ignored_characters](const char& c) {
return ignored_characters.find(c) != gd::String::npos;
},
"");
search = search.LeftTrim().RightTrim();
search.RemoveConsecutiveOccurrences(search.begin(), search.end(), ' ');
for (std::size_t i = 0; i < events.size(); ++i) {
bool eventAddedInResults = false;

View File

@@ -1,301 +0,0 @@
/*
* GDevelop Core
* Copyright 2008-2022 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "PropertyFunctionGenerator.h"
#include "GDCore/Events/Builtin/StandardEvent.h"
#include "GDCore/Events/Event.h"
#include "GDCore/Extensions/Metadata/ValueTypeMetadata.h"
#include "GDCore/Extensions/PlatformExtension.h"
#include "GDCore/Project/EventsBasedBehavior.h"
#include "GDCore/Project/EventsBasedObject.h"
#include "GDCore/Project/EventsFunctionsExtension.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/PropertyDescriptor.h"
#include "GDCore/String.h"
namespace gd {
void PropertyFunctionGenerator::GenerateBehaviorGetterAndSetter(
gd::Project &project, gd::EventsFunctionsExtension &extension,
gd::EventsBasedBehavior &eventsBasedBehavior,
const gd::NamedPropertyDescriptor &property, bool isSharedProperties) {
GenerateGetterAndSetter(project, extension, eventsBasedBehavior, property,
eventsBasedBehavior.GetObjectType(), true,
isSharedProperties);
}
void PropertyFunctionGenerator::GenerateObjectGetterAndSetter(
gd::Project &project, gd::EventsFunctionsExtension &extension,
gd::EventsBasedObject &eventsBasedObject,
const gd::NamedPropertyDescriptor &property) {
GenerateGetterAndSetter(project, extension, eventsBasedObject, property, "",
false, false);
}
void PropertyFunctionGenerator::GenerateGetterAndSetter(
gd::Project &project, gd::EventsFunctionsExtension &extension,
gd::AbstractEventsBasedEntity &eventsBasedEntity,
const gd::NamedPropertyDescriptor &property, const gd::String &objectType,
bool isBehavior, bool isSharedProperties) {
auto &propertyName = property.GetName();
auto &functionsContainer = eventsBasedEntity.GetEventsFunctions();
gd::String capitalizedName = CapitalizeFirstLetter(property.GetName());
gd::String setterName = "Set" + capitalizedName;
gd::String functionGroupName =
(eventsBasedEntity.GetFullName().empty()
? eventsBasedEntity.GetName()
: eventsBasedEntity.GetFullName()) +
(property.GetGroup().empty()
? ""
: " " + UnCapitalizeFirstLetter(property.GetGroup())) +
" configuration";
gd::String propertyLabel =
property.GetLabel().empty() ? property.GetName() : property.GetLabel();
gd::String descriptionSubject =
(property.GetType() == "Boolean" ? "if " : "the ") +
UnCapitalizeFirstLetter(propertyLabel) +
(isSharedProperties || property.GetType() == "Boolean"
? "."
: " of the object.") +
(property.GetDescription().empty() ? ""
: " " + property.GetDescription()) +
(isSharedProperties
? " While an object is needed, this will apply to all "
"objects using the behavior."
: "");
gd::String propertyGetterName =
(isSharedProperties ? "SharedProperty" : "Property") + property.GetName();
gd::String getterType =
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
extension.GetName(), eventsBasedEntity.GetName(), propertyGetterName);
gd::String setterType =
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
extension.GetName(), eventsBasedEntity.GetName(),
"Set" + propertyGetterName);
gd::String getterName = capitalizedName;
gd::String numberOrString =
property.GetType() == "Number" ? "Number" : "String";
if (!functionsContainer.HasEventsFunctionNamed(getterName)) {
auto &getter = functionsContainer.InsertNewEventsFunction(
getterName, functionsContainer.GetEventsFunctionsCount());
auto &expressionType =
gd::ValueTypeMetadata::ConvertPropertyTypeToValueType(
property.GetType());
// TODO Stop replacing number by expression when it"s handled by the UI
// and released.
auto &legacyExpressionType =
expressionType == "number" ? "expression" : expressionType;
getter.GetExpressionType()
.SetName(legacyExpressionType)
.SetExtraInfo(GetStringifiedExtraInfo(property));
getter.SetFullName(propertyLabel).SetGroup(functionGroupName);
if (property.GetType() == "Boolean") {
getter.SetFunctionType(gd::EventsFunction::Condition)
.SetDescription("Check " + descriptionSubject)
.SetSentence("_PARAM0_ " + UnCapitalizeFirstLetter(propertyLabel));
} else {
getter.SetFunctionType(gd::EventsFunction::ExpressionAndCondition)
.SetDescription(descriptionSubject)
.SetSentence("the " + UnCapitalizeFirstLetter(propertyLabel));
}
auto &event =
dynamic_cast<gd::StandardEvent &>(getter.GetEvents().InsertNewEvent(
project, "BuiltinCommonInstructions::Standard", 0));
if (property.GetType() == "Boolean") {
gd::Instruction condition;
condition.SetType(getterType);
condition.AddParameter("Object");
if (isBehavior) {
condition.AddParameter("Behavior");
}
event.GetConditions().Insert(condition, 0);
gd::Instruction action;
action.SetType("SetReturnBoolean");
action.AddParameter("True");
event.GetActions().Insert(action, 0);
} else {
gd::Instruction action;
action.SetType("SetReturn" + numberOrString);
gd::String receiver = isBehavior ? "Object.Behavior::" : "Object.";
gd::String propertyPrefix =
(isSharedProperties ? "SharedProperty" : "Property");
action.AddParameter(receiver + propertyPrefix + property.GetName() +
"()");
event.GetActions().Insert(action, 0);
}
}
if (!functionsContainer.HasEventsFunctionNamed(setterName)) {
auto &setter = functionsContainer.InsertNewEventsFunction(
setterName, functionsContainer.GetEventsFunctionsCount());
if (property.GetType() == "Boolean") {
setter.SetFunctionType(gd::EventsFunction::Action)
.SetFullName(propertyLabel)
.SetGroup(functionGroupName)
.SetDescription("Change " + descriptionSubject)
.SetSentence("_PARAM0_ " + UnCapitalizeFirstLetter(propertyLabel) +
(isBehavior ? ": _PARAM2_" : ": _PARAM1_"));
gd::ParameterMetadata objectParameter;
objectParameter.SetType("object")
.SetName("Object")
.SetDescription("Object")
.SetExtraInfo(objectType);
if (!isBehavior) {
gd::String objectFullType = gd::PlatformExtension::GetObjectFullType(
extension.GetName(), eventsBasedEntity.GetName());
objectParameter.SetExtraInfo(objectFullType);
}
setter.GetParameters().push_back(objectParameter);
if (isBehavior) {
gd::ParameterMetadata behaviorParameter;
gd::String behaviorFullType =
gd::PlatformExtension::GetBehaviorFullType(
extension.GetName(), eventsBasedEntity.GetName());
behaviorParameter.SetType("behavior")
.SetName("Behavior")
.SetDescription("Behavior")
.SetExtraInfo(behaviorFullType);
setter.GetParameters().push_back(behaviorParameter);
}
gd::ParameterMetadata valueParameter;
valueParameter.SetType("yesorno")
.SetName("Value")
.SetDescription(capitalizedName)
.SetOptional(true)
.SetDefaultValue("yes");
setter.GetParameters().push_back(valueParameter);
} else {
setter.SetFunctionType(gd::EventsFunction::ActionWithOperator);
setter.SetGetterName(getterName);
}
if (property.GetType() == "Boolean") {
{
auto &event =
dynamic_cast<gd::StandardEvent &>(setter.GetEvents().InsertNewEvent(
project, "BuiltinCommonInstructions::Standard", 0));
gd::Instruction condition;
condition.SetType("GetArgumentAsBoolean");
condition.AddParameter("\"Value\"");
event.GetConditions().Insert(condition, 0);
gd::Instruction action;
action.SetType(setterType);
action.AddParameter("Object");
if (isBehavior) {
action.AddParameter("Behavior");
action.AddParameter("yes");
} else {
action.AddParameter("yes");
}
event.GetActions().Insert(action, 0);
}
{
auto &event =
dynamic_cast<gd::StandardEvent &>(setter.GetEvents().InsertNewEvent(
project, "BuiltinCommonInstructions::Standard", 0));
gd::Instruction condition;
condition.SetType("GetArgumentAsBoolean");
condition.AddParameter("\"Value\"");
condition.SetInverted(true);
event.GetConditions().Insert(condition, 0);
gd::Instruction action;
action.SetType(setterType);
action.AddParameter("Object");
if (isBehavior) {
action.AddParameter("Behavior");
action.AddParameter("no");
} else {
action.AddParameter("no");
}
event.GetActions().Insert(action, 0);
}
} else {
auto &event =
dynamic_cast<gd::StandardEvent &>(setter.GetEvents().InsertNewEvent(
project, "BuiltinCommonInstructions::Standard", 0));
gd::Instruction action;
action.SetType(setterType);
action.AddParameter("Object");
gd::String parameterGetterCall =
"GetArgumentAs" + numberOrString + "(\"Value\")";
if (isBehavior) {
action.AddParameter("Behavior");
action.AddParameter("=");
action.AddParameter(parameterGetterCall);
} else {
action.AddParameter("=");
action.AddParameter(parameterGetterCall);
}
event.GetActions().Insert(action, 0);
}
}
}
bool PropertyFunctionGenerator::CanGenerateGetterAndSetter(
const gd::AbstractEventsBasedEntity &eventsBasedEntity,
const gd::NamedPropertyDescriptor &property) {
auto &type = property.GetType();
if (type != "Boolean" && type != "Number" && type != "String" &&
type != "Choice" && type != "Color") {
return false;
}
auto &functionsContainer = eventsBasedEntity.GetEventsFunctions();
auto getterName = CapitalizeFirstLetter(property.GetName());
auto setterName = "Set" + getterName;
return !functionsContainer.HasEventsFunctionNamed(setterName) &&
!functionsContainer.HasEventsFunctionNamed(getterName);
};
gd::String PropertyFunctionGenerator::GetStringifiedExtraInfo(
const gd::PropertyDescriptor &property) {
if (property.GetType() == "Choice") {
gd::String arrayString;
arrayString += "[";
bool isFirst = true;
for (const gd::String &choice : property.GetExtraInfo()) {
if (!isFirst) {
arrayString += ",";
}
isFirst = false;
arrayString += "\"" + choice + "\"";
}
arrayString += "]";
return arrayString;
}
return "";
}
gd::String
PropertyFunctionGenerator::CapitalizeFirstLetter(const gd::String &string) {
if (string.empty()) {
return string;
}
return string.substr(0, 1).UpperCase() + string.substr(1);
}
gd::String
PropertyFunctionGenerator::UnCapitalizeFirstLetter(const gd::String &string) {
if (string.empty()) {
return string;
}
return string.substr(0, 1).LowerCase() + string.substr(1);
}
} // namespace gd

View File

@@ -1,65 +0,0 @@
/*
* GDevelop Core
* Copyright 2008-2022 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_PROPERTYFUNCTIONGENERATOR_H
#define GDCORE_PROPERTYFUNCTIONGENERATOR_H
namespace gd {
class String;
class Project;
class EventsFunctionsExtension;
class EventsBasedBehavior;
class EventsBasedObject;
class AbstractEventsBasedEntity;
class PropertyDescriptor;
class NamedPropertyDescriptor;
} // namespace gd
namespace gd {
/**
* \brief Generate a getter and a setter functions for properties.
*/
class GD_CORE_API PropertyFunctionGenerator {
public:
/**
* \brief Generate a getter and a setter for the given behavior property.
*/
static void GenerateBehaviorGetterAndSetter(
gd::Project &project, gd::EventsFunctionsExtension &extension,
gd::EventsBasedBehavior &eventsBasedBehavior,
const gd::NamedPropertyDescriptor &property, bool isSharedProperties);
/**
* \brief Generate a getter and a setter for the given object property.
*/
static void
GenerateObjectGetterAndSetter(gd::Project &project,
gd::EventsFunctionsExtension &extension,
gd::EventsBasedObject &eventsBasedObject,
const gd::NamedPropertyDescriptor &property);
static bool CanGenerateGetterAndSetter(
const gd::AbstractEventsBasedEntity &eventsBasedEntity,
const gd::NamedPropertyDescriptor &property);
~PropertyFunctionGenerator();
private:
static void GenerateGetterAndSetter(
gd::Project &project, gd::EventsFunctionsExtension &extension,
gd::AbstractEventsBasedEntity &eventsBasedEntity,
const gd::NamedPropertyDescriptor &property, const gd::String &objectType,
bool isBehavior, bool isSharedProperties);
static gd::String CapitalizeFirstLetter(const gd::String &string);
static gd::String UnCapitalizeFirstLetter(const gd::String &string);
static gd::String
GetStringifiedExtraInfo(const gd::PropertyDescriptor &property);
PropertyFunctionGenerator();
};
} // namespace gd
#endif // GDCORE_PROPERTYFUNCTIONGENERATOR_H

View File

@@ -35,6 +35,40 @@
#include "GDCore/String.h"
#include "GDCore/Tools/Log.h"
namespace {
// These functions are doing the reverse of what is done when adding
// instructions/expression to extension/behaviors. If needed, they could be
// moved to gd::PlatformExtension to colocate the usage of the namespace
// separator?
gd::String GetEventsFunctionFullType(const gd::String& extensionName,
const gd::String& functionName) {
const auto& separator = gd::PlatformExtension::GetNamespaceSeparator();
return extensionName + separator + functionName;
}
gd::String GetBehaviorEventsFunctionFullType(const gd::String& extensionName,
const gd::String& behaviorName,
const gd::String& functionName) {
const auto& separator = gd::PlatformExtension::GetNamespaceSeparator();
return extensionName + separator + behaviorName + separator + functionName;
}
gd::String GetBehaviorFullType(const gd::String& extensionName,
const gd::String& behaviorName) {
const auto& separator = gd::PlatformExtension::GetNamespaceSeparator();
return extensionName + separator + behaviorName;
}
gd::String GetObjectEventsFunctionFullType(const gd::String& extensionName,
const gd::String& objectName,
const gd::String& functionName) {
const auto& separator = gd::PlatformExtension::GetNamespaceSeparator();
return extensionName + separator + objectName + separator + functionName;
}
gd::String GetObjectFullType(const gd::String& extensionName,
const gd::String& objectName) {
const auto& separator = gd::PlatformExtension::GetNamespaceSeparator();
return extensionName + separator + objectName;
}
} // namespace
namespace gd {
// By convention, the first parameter of an events based behavior method is
@@ -218,7 +252,7 @@ WholeProjectRefactorer::GetAllObjectTypesUsingEventsBasedBehavior(
const gd::EventsFunctionsExtension& eventsFunctionsExtension,
const gd::EventsBasedBehavior& eventsBasedBehavior) {
std::set<gd::String> allTypes;
const gd::String behaviorType = gd::PlatformExtension::GetBehaviorFullType(
const gd::String behaviorType = GetBehaviorFullType(
eventsFunctionsExtension.GetName(), eventsBasedBehavior.GetName());
auto addTypesOfObjectsIn =
@@ -261,7 +295,7 @@ void WholeProjectRefactorer::EnsureBehaviorEventsFunctionsProperParameters(
.SetType("behavior")
.SetName("Behavior")
.SetDescription("Behavior")
.SetExtraInfo(gd::PlatformExtension::GetBehaviorFullType(eventsFunctionsExtension.GetName(),
.SetExtraInfo(GetBehaviorFullType(eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName()));
}
}
@@ -281,7 +315,7 @@ void WholeProjectRefactorer::EnsureObjectEventsFunctionsProperParameters(
.SetType("object")
.SetName(parentObjectParameterName)
.SetDescription("Object")
.SetExtraInfo(gd::PlatformExtension::GetObjectFullType(eventsFunctionsExtension.GetName(),
.SetExtraInfo(GetObjectFullType(eventsFunctionsExtension.GetName(),
eventsBasedObject.GetName()));
}
}
@@ -296,8 +330,8 @@ void WholeProjectRefactorer::RenameEventsFunctionsExtension(
DoRenameEventsFunction(
project,
eventsFunction,
gd::PlatformExtension::GetEventsFunctionFullType(oldName, eventsFunction.GetName()),
gd::PlatformExtension::GetEventsFunctionFullType(newName, eventsFunction.GetName()));
GetEventsFunctionFullType(oldName, eventsFunction.GetName()),
GetEventsFunctionFullType(newName, eventsFunction.GetName()));
};
auto renameBehaviorEventsFunction =
@@ -310,10 +344,10 @@ void WholeProjectRefactorer::RenameEventsFunctionsExtension(
if (eventsFunction.IsAction() || eventsFunction.IsCondition()) {
gd::InstructionsTypeRenamer renamer = gd::InstructionsTypeRenamer(
project,
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(oldName,
GetBehaviorEventsFunctionFullType(oldName,
eventsBasedBehavior.GetName(),
eventsFunction.GetName()),
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(newName,
GetBehaviorEventsFunctionFullType(newName,
eventsBasedBehavior.GetName(),
eventsFunction.GetName()));
ExposeProjectEvents(project, renamer);
@@ -326,12 +360,12 @@ void WholeProjectRefactorer::RenameEventsFunctionsExtension(
const gd::NamedPropertyDescriptor& property) {
gd::InstructionsTypeRenamer actionRenamer = gd::InstructionsTypeRenamer(
project,
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
GetBehaviorEventsFunctionFullType(
oldName,
eventsBasedBehavior.GetName(),
gd::EventsBasedBehavior::GetPropertyActionName(
property.GetName())),
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
GetBehaviorEventsFunctionFullType(
newName,
eventsBasedBehavior.GetName(),
gd::EventsBasedBehavior::GetPropertyActionName(
@@ -341,12 +375,12 @@ void WholeProjectRefactorer::RenameEventsFunctionsExtension(
gd::InstructionsTypeRenamer conditionRenamer =
gd::InstructionsTypeRenamer(
project,
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
GetBehaviorEventsFunctionFullType(
oldName,
eventsBasedBehavior.GetName(),
gd::EventsBasedBehavior::GetPropertyConditionName(
property.GetName())),
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
GetBehaviorEventsFunctionFullType(
newName,
eventsBasedBehavior.GetName(),
gd::EventsBasedBehavior::GetPropertyConditionName(
@@ -357,43 +391,6 @@ void WholeProjectRefactorer::RenameEventsFunctionsExtension(
// extension name
};
auto renameBehaviorSharedPropertyFunctions =
[&project, &oldName, &newName](
const gd::EventsBasedBehavior& eventsBasedBehavior,
const gd::NamedPropertyDescriptor& property) {
gd::InstructionsTypeRenamer actionRenamer = gd::InstructionsTypeRenamer(
project,
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
oldName,
eventsBasedBehavior.GetName(),
gd::EventsBasedBehavior::GetSharedPropertyActionName(
property.GetName())),
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
newName,
eventsBasedBehavior.GetName(),
gd::EventsBasedBehavior::GetSharedPropertyActionName(
property.GetName())));
ExposeProjectEvents(project, actionRenamer);
gd::InstructionsTypeRenamer conditionRenamer =
gd::InstructionsTypeRenamer(
project,
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
oldName,
eventsBasedBehavior.GetName(),
gd::EventsBasedBehavior::GetSharedPropertyConditionName(
property.GetName())),
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
newName,
eventsBasedBehavior.GetName(),
gd::EventsBasedBehavior::GetSharedPropertyConditionName(
property.GetName())));
ExposeProjectEvents(project, conditionRenamer);
// Nothing to do for expressions, expressions are not including the
// extension name
};
auto renameObjectEventsFunction =
[&project, &oldName, &newName](
const gd::EventsBasedObject& eventsBasedObject,
@@ -404,10 +401,10 @@ void WholeProjectRefactorer::RenameEventsFunctionsExtension(
if (eventsFunction.IsAction() || eventsFunction.IsCondition()) {
gd::InstructionsTypeRenamer renamer = gd::InstructionsTypeRenamer(
project,
gd::PlatformExtension::GetObjectEventsFunctionFullType(oldName,
GetObjectEventsFunctionFullType(oldName,
eventsBasedObject.GetName(),
eventsFunction.GetName()),
gd::PlatformExtension::GetObjectEventsFunctionFullType(newName,
GetObjectEventsFunctionFullType(newName,
eventsBasedObject.GetName(),
eventsFunction.GetName()));
ExposeProjectEvents(project, renamer);
@@ -420,12 +417,12 @@ void WholeProjectRefactorer::RenameEventsFunctionsExtension(
const gd::NamedPropertyDescriptor& property) {
gd::InstructionsTypeRenamer actionRenamer = gd::InstructionsTypeRenamer(
project,
gd::PlatformExtension::GetObjectEventsFunctionFullType(
GetObjectEventsFunctionFullType(
oldName,
eventsBasedObject.GetName(),
gd::EventsBasedObject::GetPropertyActionName(
property.GetName())),
gd::PlatformExtension::GetObjectEventsFunctionFullType(
GetObjectEventsFunctionFullType(
newName,
eventsBasedObject.GetName(),
gd::EventsBasedObject::GetPropertyActionName(
@@ -435,12 +432,12 @@ void WholeProjectRefactorer::RenameEventsFunctionsExtension(
gd::InstructionsTypeRenamer conditionRenamer =
gd::InstructionsTypeRenamer(
project,
gd::PlatformExtension::GetObjectEventsFunctionFullType(
GetObjectEventsFunctionFullType(
oldName,
eventsBasedObject.GetName(),
gd::EventsBasedObject::GetPropertyConditionName(
property.GetName())),
gd::PlatformExtension::GetObjectEventsFunctionFullType(
GetObjectEventsFunctionFullType(
newName,
eventsBasedObject.GetName(),
gd::EventsBasedObject::GetPropertyConditionName(
@@ -491,19 +488,13 @@ void WholeProjectRefactorer::RenameEventsFunctionsExtension(
}
// Behavior properties
for (auto &&eventsBasedBehavior :
for (auto&& eventsBasedBehavior :
eventsFunctionsExtension.GetEventsBasedBehaviors().GetInternalVector()) {
for (auto &&propertyDescriptor :
eventsBasedBehavior->GetPropertyDescriptors().GetInternalVector()) {
auto& behaviorProperties = eventsBasedBehavior->GetPropertyDescriptors();
for (auto&& propertyDescriptor : behaviorProperties.GetInternalVector()) {
renameBehaviorPropertyFunctions(*eventsBasedBehavior,
*propertyDescriptor);
}
for (auto &&propertyDescriptor :
eventsBasedBehavior->GetSharedPropertyDescriptors()
.GetInternalVector()) {
renameBehaviorSharedPropertyFunctions(*eventsBasedBehavior,
*propertyDescriptor);
}
}
// Object instructions
@@ -532,8 +523,8 @@ void WholeProjectRefactorer::RenameEventsFunctionsExtension(
eventsFunctionsExtension.GetEventsBasedBehaviors().GetInternalVector()) {
DoRenameBehavior(
project,
gd::PlatformExtension::GetBehaviorFullType(oldName, eventsBasedBehavior->GetName()),
gd::PlatformExtension::GetBehaviorFullType(newName, eventsBasedBehavior->GetName()));
GetBehaviorFullType(oldName, eventsBasedBehavior->GetName()),
GetBehaviorFullType(newName, eventsBasedBehavior->GetName()));
}
// Finally, rename custom objects type
@@ -541,8 +532,8 @@ void WholeProjectRefactorer::RenameEventsFunctionsExtension(
eventsFunctionsExtension.GetEventsBasedObjects().GetInternalVector()) {
DoRenameObject(
project,
gd::PlatformExtension::GetObjectFullType(oldName, eventsBasedObject->GetName()),
gd::PlatformExtension::GetObjectFullType(newName, eventsBasedObject->GetName()));
GetObjectFullType(oldName, eventsBasedObject->GetName()),
GetObjectFullType(newName, eventsBasedObject->GetName()));
}
}
@@ -559,9 +550,9 @@ void WholeProjectRefactorer::RenameEventsFunction(
DoRenameEventsFunction(
project,
eventsFunction,
gd::PlatformExtension::GetEventsFunctionFullType(eventsFunctionsExtension.GetName(),
GetEventsFunctionFullType(eventsFunctionsExtension.GetName(),
oldFunctionName),
gd::PlatformExtension::GetEventsFunctionFullType(eventsFunctionsExtension.GetName(),
GetEventsFunctionFullType(eventsFunctionsExtension.GetName(),
newFunctionName));
if (eventsFunction.GetFunctionType() == gd::EventsFunction::ExpressionAndCondition) {
@@ -594,7 +585,7 @@ void WholeProjectRefactorer::RenameBehaviorEventsFunction(
gd::ExpressionsRenamer renamer =
gd::ExpressionsRenamer(project.GetCurrentPlatform());
renamer.SetReplacedBehaviorExpression(
gd::PlatformExtension::GetBehaviorFullType(eventsFunctionsExtension.GetName(),
GetBehaviorFullType(eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName()),
oldFunctionName,
newFunctionName);
@@ -603,10 +594,10 @@ void WholeProjectRefactorer::RenameBehaviorEventsFunction(
if (eventsFunction.IsAction() || eventsFunction.IsCondition()) {
gd::InstructionsTypeRenamer renamer = gd::InstructionsTypeRenamer(
project,
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(eventsFunctionsExtension.GetName(),
GetBehaviorEventsFunctionFullType(eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName(),
oldFunctionName),
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(eventsFunctionsExtension.GetName(),
GetBehaviorEventsFunctionFullType(eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName(),
newFunctionName));
ExposeProjectEvents(project, renamer);
@@ -638,7 +629,7 @@ void WholeProjectRefactorer::RenameObjectEventsFunction(
gd::ExpressionsRenamer renamer =
gd::ExpressionsRenamer(project.GetCurrentPlatform());
renamer.SetReplacedObjectExpression(
gd::PlatformExtension::GetObjectFullType(eventsFunctionsExtension.GetName(),
GetObjectFullType(eventsFunctionsExtension.GetName(),
eventsBasedObject.GetName()),
oldFunctionName,
newFunctionName);
@@ -647,10 +638,10 @@ void WholeProjectRefactorer::RenameObjectEventsFunction(
if (eventsFunction.IsAction() || eventsFunction.IsCondition()) {
gd::InstructionsTypeRenamer renamer = gd::InstructionsTypeRenamer(
project,
gd::PlatformExtension::GetObjectEventsFunctionFullType(eventsFunctionsExtension.GetName(),
GetObjectEventsFunctionFullType(eventsFunctionsExtension.GetName(),
eventsBasedObject.GetName(),
oldFunctionName),
gd::PlatformExtension::GetObjectEventsFunctionFullType(eventsFunctionsExtension.GetName(),
GetObjectEventsFunctionFullType(eventsFunctionsExtension.GetName(),
eventsBasedObject.GetName(),
newFunctionName));
ExposeProjectEvents(project, renamer);
@@ -677,7 +668,7 @@ void WholeProjectRefactorer::MoveEventsFunctionParameter(
const gd::EventsFunction& eventsFunction =
eventsFunctionsExtension.GetEventsFunction(functionName);
const gd::String& eventsFunctionType = gd::PlatformExtension::GetEventsFunctionFullType(
const gd::String& eventsFunctionType = GetEventsFunctionFullType(
eventsFunctionsExtension.GetName(), functionName);
if (eventsFunction.IsExpression()) {
@@ -712,7 +703,7 @@ void WholeProjectRefactorer::MoveBehaviorEventsFunctionParameter(
eventsFunctions.GetEventsFunction(functionName);
const gd::String& eventsFunctionType =
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(eventsFunctionsExtension.GetName(),
GetBehaviorEventsFunctionFullType(eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName(),
functionName);
@@ -720,7 +711,7 @@ void WholeProjectRefactorer::MoveBehaviorEventsFunctionParameter(
gd::ExpressionsParameterMover mover =
gd::ExpressionsParameterMover(project.GetCurrentPlatform());
mover.SetBehaviorExpressionMovedParameter(
gd::PlatformExtension::GetBehaviorFullType(eventsFunctionsExtension.GetName(),
GetBehaviorFullType(eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName()),
functionName,
oldIndex,
@@ -752,7 +743,7 @@ void WholeProjectRefactorer::MoveObjectEventsFunctionParameter(
eventsFunctions.GetEventsFunction(functionName);
const gd::String& eventsFunctionType =
gd::PlatformExtension::GetObjectEventsFunctionFullType(eventsFunctionsExtension.GetName(),
GetObjectEventsFunctionFullType(eventsFunctionsExtension.GetName(),
eventsBasedObject.GetName(),
functionName);
@@ -760,7 +751,7 @@ void WholeProjectRefactorer::MoveObjectEventsFunctionParameter(
gd::ExpressionsParameterMover mover =
gd::ExpressionsParameterMover(project.GetCurrentPlatform());
mover.SetObjectExpressionMovedParameter(
gd::PlatformExtension::GetObjectFullType(eventsFunctionsExtension.GetName(),
GetObjectFullType(eventsFunctionsExtension.GetName(),
eventsBasedObject.GetName()),
functionName,
oldIndex,
@@ -812,7 +803,7 @@ void WholeProjectRefactorer::RenameEventsBasedBehaviorProperty(
gd::ExpressionsRenamer expressionRenamer =
gd::ExpressionsRenamer(project.GetCurrentPlatform());
expressionRenamer.SetReplacedBehaviorExpression(
gd::PlatformExtension::GetBehaviorFullType(eventsFunctionsExtension.GetName(),
GetBehaviorFullType(eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName()),
EventsBasedBehavior::GetPropertyExpressionName(oldPropertyName),
EventsBasedBehavior::GetPropertyExpressionName(newPropertyName));
@@ -820,11 +811,11 @@ void WholeProjectRefactorer::RenameEventsBasedBehaviorProperty(
gd::InstructionsTypeRenamer actionRenamer = gd::InstructionsTypeRenamer(
project,
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName(),
EventsBasedBehavior::GetPropertyActionName(oldPropertyName)),
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName(),
EventsBasedBehavior::GetPropertyActionName(newPropertyName)));
@@ -832,11 +823,11 @@ void WholeProjectRefactorer::RenameEventsBasedBehaviorProperty(
gd::InstructionsTypeRenamer conditionRenamer = gd::InstructionsTypeRenamer(
project,
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName(),
EventsBasedBehavior::GetPropertyConditionName(oldPropertyName)),
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName(),
EventsBasedBehavior::GetPropertyConditionName(newPropertyName)));
@@ -844,72 +835,6 @@ void WholeProjectRefactorer::RenameEventsBasedBehaviorProperty(
}
}
void WholeProjectRefactorer::RenameEventsBasedBehaviorSharedProperty(
gd::Project& project,
const gd::EventsFunctionsExtension& eventsFunctionsExtension,
const gd::EventsBasedBehavior& eventsBasedBehavior,
const gd::String& oldPropertyName,
const gd::String& newPropertyName) {
auto& properties = eventsBasedBehavior.GetPropertyDescriptors();
if (!properties.Has(oldPropertyName)) return;
if (properties.Get(oldPropertyName).GetType() == "Behavior") {
// This is a property representing another behavior that must exist on the
// object.
// This other "required behavior" uses the property name, that is about to
// change, as its name.
// So we must change all reference to this name in the events of the
// behavior functions.
gd::EventsBehaviorRenamer behaviorRenamer(project.GetCurrentPlatform(),
behaviorObjectParameterName,
oldPropertyName,
newPropertyName);
ExposeEventsBasedBehaviorEvents(
project, eventsBasedBehavior, behaviorRenamer);
} else {
// Properties that represent primitive values will be used through
// their related actions/conditions/expressions. Rename these.
// Order is important: we first rename the expressions then the
// instructions, to avoid being unable to fetch the metadata (the types of
// parameters) of instructions after they are renamed.
gd::ExpressionsRenamer expressionRenamer =
gd::ExpressionsRenamer(project.GetCurrentPlatform());
expressionRenamer.SetReplacedBehaviorExpression(
gd::PlatformExtension::GetBehaviorFullType(eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName()),
EventsBasedBehavior::GetSharedPropertyExpressionName(oldPropertyName),
EventsBasedBehavior::GetSharedPropertyExpressionName(newPropertyName));
ExposeProjectEvents(project, expressionRenamer);
gd::InstructionsTypeRenamer actionRenamer = gd::InstructionsTypeRenamer(
project,
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName(),
EventsBasedBehavior::GetSharedPropertyActionName(oldPropertyName)),
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName(),
EventsBasedBehavior::GetSharedPropertyActionName(newPropertyName)));
ExposeProjectEvents(project, actionRenamer);
gd::InstructionsTypeRenamer conditionRenamer = gd::InstructionsTypeRenamer(
project,
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName(),
EventsBasedBehavior::GetSharedPropertyConditionName(oldPropertyName)),
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName(),
EventsBasedBehavior::GetSharedPropertyConditionName(newPropertyName)));
ExposeProjectEvents(project, conditionRenamer);
}
}
void WholeProjectRefactorer::RenameEventsBasedObjectProperty(
gd::Project& project,
const gd::EventsFunctionsExtension& eventsFunctionsExtension,
@@ -928,7 +853,7 @@ void WholeProjectRefactorer::RenameEventsBasedObjectProperty(
gd::ExpressionsRenamer expressionRenamer =
gd::ExpressionsRenamer(project.GetCurrentPlatform());
expressionRenamer.SetReplacedObjectExpression(
gd::PlatformExtension::GetObjectFullType(eventsFunctionsExtension.GetName(),
GetObjectFullType(eventsFunctionsExtension.GetName(),
eventsBasedObject.GetName()),
EventsBasedObject::GetPropertyExpressionName(oldPropertyName),
EventsBasedObject::GetPropertyExpressionName(newPropertyName));
@@ -936,11 +861,11 @@ void WholeProjectRefactorer::RenameEventsBasedObjectProperty(
gd::InstructionsTypeRenamer actionRenamer = gd::InstructionsTypeRenamer(
project,
gd::PlatformExtension::GetObjectEventsFunctionFullType(
GetObjectEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
eventsBasedObject.GetName(),
EventsBasedObject::GetPropertyActionName(oldPropertyName)),
gd::PlatformExtension::GetObjectEventsFunctionFullType(
GetObjectEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
eventsBasedObject.GetName(),
EventsBasedObject::GetPropertyActionName(newPropertyName)));
@@ -948,11 +873,11 @@ void WholeProjectRefactorer::RenameEventsBasedObjectProperty(
gd::InstructionsTypeRenamer conditionRenamer = gd::InstructionsTypeRenamer(
project,
gd::PlatformExtension::GetObjectEventsFunctionFullType(
GetObjectEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
eventsBasedObject.GetName(),
EventsBasedObject::GetPropertyConditionName(oldPropertyName)),
gd::PlatformExtension::GetObjectEventsFunctionFullType(
GetObjectEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
eventsBasedObject.GetName(),
EventsBasedObject::GetPropertyConditionName(newPropertyName)));
@@ -1189,11 +1114,11 @@ void WholeProjectRefactorer::RenameEventsBasedBehavior(
if (eventsFunction.IsAction() || eventsFunction.IsCondition()) {
gd::InstructionsTypeRenamer renamer = gd::InstructionsTypeRenamer(
project,
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
oldBehaviorName,
eventsFunction.GetName()),
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
newBehaviorName,
eventsFunction.GetName()));
@@ -1209,11 +1134,11 @@ void WholeProjectRefactorer::RenameEventsBasedBehavior(
property) {
gd::InstructionsTypeRenamer actionRenamer = gd::InstructionsTypeRenamer(
project,
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
oldBehaviorName,
EventsBasedBehavior::GetPropertyActionName(property.GetName())),
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
newBehaviorName,
EventsBasedBehavior::GetPropertyActionName(property.GetName())));
@@ -1221,11 +1146,11 @@ void WholeProjectRefactorer::RenameEventsBasedBehavior(
gd::InstructionsTypeRenamer conditionRenamer = gd::InstructionsTypeRenamer(
project,
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
oldBehaviorName,
EventsBasedBehavior::GetPropertyConditionName(property.GetName())),
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
newBehaviorName,
EventsBasedBehavior::GetPropertyConditionName(property.GetName())));
@@ -1235,40 +1160,6 @@ void WholeProjectRefactorer::RenameEventsBasedBehavior(
// the behavior
};
auto renameBehaviorSharedProperty = [&project,
&eventsFunctionsExtension,
&oldBehaviorName,
&newBehaviorName](
const gd::NamedPropertyDescriptor&
property) {
gd::InstructionsTypeRenamer actionRenamer = gd::InstructionsTypeRenamer(
project,
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
oldBehaviorName,
EventsBasedBehavior::GetSharedPropertyActionName(property.GetName())),
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
newBehaviorName,
EventsBasedBehavior::GetSharedPropertyActionName(property.GetName())));
ExposeProjectEvents(project, actionRenamer);
gd::InstructionsTypeRenamer conditionRenamer = gd::InstructionsTypeRenamer(
project,
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
oldBehaviorName,
EventsBasedBehavior::GetSharedPropertyConditionName(property.GetName())),
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
newBehaviorName,
EventsBasedBehavior::GetSharedPropertyConditionName(property.GetName())));
ExposeProjectEvents(project, conditionRenamer);
// Nothing to do for expression, expressions are not including the name of
// the behavior
};
// Order is important: we first rename the expressions then the instructions,
// to avoid being unable to fetch the metadata (the types of parameters) of
// instructions after they are renamed.
@@ -1289,17 +1180,15 @@ void WholeProjectRefactorer::RenameEventsBasedBehavior(
}
// Behavior properties
for (auto&& property : eventsBasedBehavior.GetPropertyDescriptors().GetInternalVector()) {
auto& properties = eventsBasedBehavior.GetPropertyDescriptors();
for (auto&& property : properties.GetInternalVector()) {
renameBehaviorProperty(*property);
}
for (auto&& property : eventsBasedBehavior.GetSharedPropertyDescriptors().GetInternalVector()) {
renameBehaviorSharedProperty(*property);
}
DoRenameBehavior(
project,
gd::PlatformExtension::GetBehaviorFullType(eventsFunctionsExtension.GetName(), oldBehaviorName),
gd::PlatformExtension::GetBehaviorFullType(eventsFunctionsExtension.GetName(), newBehaviorName));
GetBehaviorFullType(eventsFunctionsExtension.GetName(), oldBehaviorName),
GetBehaviorFullType(eventsFunctionsExtension.GetName(), newBehaviorName));
}
void WholeProjectRefactorer::RenameEventsBasedObject(
@@ -1328,11 +1217,11 @@ void WholeProjectRefactorer::RenameEventsBasedObject(
if (eventsFunction.IsAction() || eventsFunction.IsCondition()) {
gd::InstructionsTypeRenamer renamer = gd::InstructionsTypeRenamer(
project,
gd::PlatformExtension::GetObjectEventsFunctionFullType(
GetObjectEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
oldObjectName,
eventsFunction.GetName()),
gd::PlatformExtension::GetObjectEventsFunctionFullType(
GetObjectEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
newObjectName,
eventsFunction.GetName()));
@@ -1348,11 +1237,11 @@ void WholeProjectRefactorer::RenameEventsBasedObject(
property) {
gd::InstructionsTypeRenamer actionRenamer = gd::InstructionsTypeRenamer(
project,
gd::PlatformExtension::GetObjectEventsFunctionFullType(
GetObjectEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
oldObjectName,
EventsBasedObject::GetPropertyActionName(property.GetName())),
gd::PlatformExtension::GetObjectEventsFunctionFullType(
GetObjectEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
newObjectName,
EventsBasedObject::GetPropertyActionName(property.GetName())));
@@ -1360,11 +1249,11 @@ void WholeProjectRefactorer::RenameEventsBasedObject(
gd::InstructionsTypeRenamer conditionRenamer = gd::InstructionsTypeRenamer(
project,
gd::PlatformExtension::GetObjectEventsFunctionFullType(
GetObjectEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
oldObjectName,
EventsBasedObject::GetPropertyConditionName(property.GetName())),
gd::PlatformExtension::GetObjectEventsFunctionFullType(
GetObjectEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
newObjectName,
EventsBasedObject::GetPropertyConditionName(property.GetName())));
@@ -1401,8 +1290,8 @@ void WholeProjectRefactorer::RenameEventsBasedObject(
DoRenameObject(
project,
gd::PlatformExtension::GetObjectFullType(eventsFunctionsExtension.GetName(), oldObjectName),
gd::PlatformExtension::GetObjectFullType(eventsFunctionsExtension.GetName(), newObjectName));
GetObjectFullType(eventsFunctionsExtension.GetName(), oldObjectName),
GetObjectFullType(eventsFunctionsExtension.GetName(), newObjectName));
}
void WholeProjectRefactorer::DoRenameEventsFunction(

View File

@@ -207,21 +207,6 @@ class GD_CORE_API WholeProjectRefactorer {
const gd::String& oldPropertyName,
const gd::String& newPropertyName);
/**
* \brief Refactor the project **before** a shared property of a behavior is
* renamed.
*
* \warning Do the renaming of the specified shared property after calling
* this. This is because the shared property is expected to have its old name
* for the refactoring.
*/
static void RenameEventsBasedBehaviorSharedProperty(
gd::Project& project,
const gd::EventsFunctionsExtension& eventsFunctionsExtension,
const gd::EventsBasedBehavior& eventsBasedBehavior,
const gd::String& oldPropertyName,
const gd::String& newPropertyName);
/**
* \brief Refactor the project **before** a property of an object is
* renamed.

View File

@@ -13,8 +13,7 @@ namespace gd {
/**
* \brief Base class used to represents a behavior that can be applied to an
* object. It stores the content (i.e: the properties) of a behavior of an object
* and forward the properties related functions to Javascript with Emscripten.
* object. It stores the content (i.e: the properties) of a behavior of an object.
*
* \see gd::BehaviorsSharedData
* \see gd::Object

View File

@@ -11,7 +11,6 @@
#include "GDCore/Project/PropertyDescriptor.h"
#include "GDCore/Serialization/Serializer.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/Project/CustomConfigurationHelper.h"
#include <map>
@@ -22,26 +21,52 @@ CustomBehavior *CustomBehavior::Clone() const {
return clone;
}
void CustomBehavior::InitializeContent(gd::SerializerElement &behaviorContent) {
if (!project.HasEventsBasedBehavior(GetTypeName())) {
return;
}
const auto &eventsBasedBehavior = project.GetEventsBasedBehavior(GetTypeName());
const auto &properties = eventsBasedBehavior.GetPropertyDescriptors();
gd::CustomConfigurationHelper::InitializeContent(properties, behaviorContent);
}
std::map<gd::String, gd::PropertyDescriptor> CustomBehavior::GetProperties(
const gd::SerializerElement &behaviorContent) const {
auto behaviorProperties = std::map<gd::String, gd::PropertyDescriptor>();
if (!project.HasEventsBasedBehavior(GetTypeName())) {
auto behaviorProperties = std::map<gd::String, gd::PropertyDescriptor>();
return behaviorProperties;
}
const auto &eventsBasedBehavior = project.GetEventsBasedBehavior(GetTypeName());
const auto &properties = eventsBasedBehavior.GetPropertyDescriptors();
return gd::CustomConfigurationHelper::GetProperties(properties, behaviorContent);
for (auto &property : properties.GetInternalVector()) {
const auto &propertyName = property->GetName();
const auto &propertyType = property->GetType();
// TODO Move this into a PropertyDescriptor copy method.
auto &newProperty = behaviorProperties[propertyName]
.SetType(property->GetType())
.SetDescription(property->GetDescription())
.SetGroup(property->GetGroup())
.SetLabel(property->GetLabel())
.SetValue(property->GetValue())
.SetHidden(property->IsHidden());
for (auto &extraInfo : property->GetExtraInfo()) {
newProperty.AddExtraInfo(extraInfo);
}
if (behaviorContent.HasChild(propertyName)) {
if (propertyType == "String" || propertyType == "Choice" ||
propertyType == "Color" || propertyType == "Behavior") {
newProperty.SetValue(
behaviorContent.GetChild(propertyName).GetStringValue());
} else if (propertyType == "Number") {
newProperty.SetValue(gd::String::From(
behaviorContent.GetChild(propertyName).GetDoubleValue()));
} else if (propertyType == "Boolean") {
newProperty.SetValue(
behaviorContent.GetChild(propertyName).GetBoolValue() ? "true"
: "false");
}
} else {
// No value was serialized for this property. `newProperty`
// will have the default value coming from `enumeratedProperty`.
}
}
return behaviorProperties;
}
bool CustomBehavior::UpdateProperty(gd::SerializerElement &behaviorContent,
@@ -52,10 +77,43 @@ bool CustomBehavior::UpdateProperty(gd::SerializerElement &behaviorContent,
}
const auto &eventsBasedBehavior = project.GetEventsBasedBehavior(GetTypeName());
const auto &properties = eventsBasedBehavior.GetPropertyDescriptors();
if (!properties.Has(propertyName)) {
return false;
}
const auto &property = properties.Get(propertyName);
return gd::CustomConfigurationHelper::UpdateProperty(
properties,
behaviorContent,
propertyName,
newValue);
auto &element = behaviorContent.AddChild(propertyName);
const gd::String &propertyType = property.GetType();
if (propertyType == "String" || propertyType == "Choice" ||
propertyType == "Color" || propertyType == "Behavior") {
element.SetStringValue(newValue);
} else if (propertyType == "Number") {
element.SetDoubleValue(newValue.To<double>());
} else if (propertyType == "Boolean") {
element.SetBoolValue(newValue == "1");
}
return true;
}
void CustomBehavior::InitializeContent(gd::SerializerElement &behaviorContent) {
if (!project.HasEventsBasedBehavior(GetTypeName())) {
return;
}
const auto &eventsBasedBehavior = project.GetEventsBasedBehavior(GetTypeName());
const auto &properties = eventsBasedBehavior.GetPropertyDescriptors();
for (auto &&property : properties.GetInternalVector()) {
auto &element = behaviorContent.AddChild(property->GetName());
auto propertyType = property->GetType();
if (propertyType == "String" || propertyType == "Choice" ||
propertyType == "Color" || propertyType == "Behavior") {
element.SetStringValue(property->GetValue());
} else if (propertyType == "Number") {
element.SetDoubleValue(property->GetValue().To<double>());
} else if (propertyType == "Boolean") {
element.SetBoolValue(property->GetValue() == "true");
}
}
}

View File

@@ -17,7 +17,8 @@ using namespace gd;
namespace gd {
/**
* \brief A gd::Behavior that stores its content in JSON.
* \brief A gd::Behavior that stores its content in JSON and forward the
* properties related functions to Javascript with Emscripten.
*/
class CustomBehavior : public gd::Behavior {
public:
@@ -33,11 +34,13 @@ public:
using Behavior::UpdateProperty;
protected:
std::map<gd::String, gd::PropertyDescriptor>
virtual std::map<gd::String, gd::PropertyDescriptor>
GetProperties(const gd::SerializerElement &behaviorContent) const override;
bool UpdateProperty(gd::SerializerElement &behaviorContent,
const gd::String &name, const gd::String &value) override;
void InitializeContent(gd::SerializerElement &behaviorContent) override;
virtual bool UpdateProperty(gd::SerializerElement &behaviorContent,
const gd::String &name,
const gd::String &value) override;
virtual void
InitializeContent(gd::SerializerElement &behaviorContent) override;
private:
const Project &project; ///< The project is used to get the

View File

@@ -1,61 +0,0 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "CustomBehaviorsSharedData.h"
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
#include "GDCore/Project/Behavior.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/PropertyDescriptor.h"
#include "GDCore/Serialization/Serializer.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/Project/CustomConfigurationHelper.h"
#include <map>
using namespace gd;
CustomBehaviorsSharedData *CustomBehaviorsSharedData::Clone() const {
CustomBehaviorsSharedData *clone = new CustomBehaviorsSharedData(*this);
return clone;
}
void CustomBehaviorsSharedData::InitializeContent(gd::SerializerElement &behaviorContent) {
if (!project.HasEventsBasedBehavior(GetTypeName())) {
return;
}
const auto &eventsBasedBehavior = project.GetEventsBasedBehavior(GetTypeName());
const auto &properties = eventsBasedBehavior.GetSharedPropertyDescriptors();
gd::CustomConfigurationHelper::InitializeContent(properties, behaviorContent);
}
std::map<gd::String, gd::PropertyDescriptor> CustomBehaviorsSharedData::GetProperties(
const gd::SerializerElement &behaviorContent) const {
if (!project.HasEventsBasedBehavior(GetTypeName())) {
auto behaviorProperties = std::map<gd::String, gd::PropertyDescriptor>();
return behaviorProperties;
}
const auto &eventsBasedBehavior = project.GetEventsBasedBehavior(GetTypeName());
const auto &properties = eventsBasedBehavior.GetSharedPropertyDescriptors();
return gd::CustomConfigurationHelper::GetProperties(properties, behaviorContent);
}
bool CustomBehaviorsSharedData::UpdateProperty(gd::SerializerElement &behaviorContent,
const gd::String &propertyName,
const gd::String &newValue) {
if (!project.HasEventsBasedBehavior(GetTypeName())) {
return false;
}
const auto &eventsBasedBehavior = project.GetEventsBasedBehavior(GetTypeName());
const auto &properties = eventsBasedBehavior.GetSharedPropertyDescriptors();
return gd::CustomConfigurationHelper::UpdateProperty(
properties,
behaviorContent,
propertyName,
newValue);
}

View File

@@ -1,46 +0,0 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_CUSTOMBEHAVIORSSHAREDDATA_H
#define GDCORE_CUSTOMBEHAVIORSSHAREDDATA_H
#include "GDCore/Project/BehaviorsSharedData.h"
#include "GDCore/Project/EventsBasedBehavior.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/PropertyDescriptor.h"
#include "GDCore/Serialization/Serializer.h"
#include "GDCore/Serialization/SerializerElement.h"
using namespace gd;
namespace gd {
/**
* \brief A gd::BehaviorsSharedData that stores its content in JSON.
*/
class CustomBehaviorsSharedData : public gd::BehaviorsSharedData {
public:
CustomBehaviorsSharedData(const gd::String &name, const Project &project_,
const gd::String &fullType)
: BehaviorsSharedData(name, fullType), project(project_) {}
CustomBehaviorsSharedData *Clone() const override;
using BehaviorsSharedData::GetProperties;
using BehaviorsSharedData::InitializeContent;
using BehaviorsSharedData::UpdateProperty;
protected:
std::map<gd::String, gd::PropertyDescriptor>
GetProperties(const gd::SerializerElement &behaviorContent) const override;
bool UpdateProperty(gd::SerializerElement &behaviorContent,
const gd::String &name, const gd::String &value) override;
void InitializeContent(gd::SerializerElement &behaviorContent) override;
private:
const Project &project; ///< The project is used to get the
///< EventBasedBehavior from the fullType.
};
} // namespace gd
#endif // GDCORE_CUSTOMBEHAVIORSSHAREDDATA_H

View File

@@ -1,104 +0,0 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "CustomConfigurationHelper.h"
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
#include "GDCore/Project/Behavior.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/PropertyDescriptor.h"
#include "GDCore/Serialization/Serializer.h"
#include "GDCore/Serialization/SerializerElement.h"
#include <map>
using namespace gd;
void CustomConfigurationHelper::InitializeContent(
const gd::SerializableWithNameList<gd::NamedPropertyDescriptor> &properties,
gd::SerializerElement &configurationContent) {
for (auto &&property : properties.GetInternalVector()) {
auto &element = configurationContent.AddChild(property->GetName());
auto propertyType = property->GetType();
if (propertyType == "String" || propertyType == "Choice" ||
propertyType == "Color" || propertyType == "Behavior") {
element.SetStringValue(property->GetValue());
} else if (propertyType == "Number") {
element.SetDoubleValue(property->GetValue().To<double>());
} else if (propertyType == "Boolean") {
element.SetBoolValue(property->GetValue() == "true");
}
}
}
std::map<gd::String, gd::PropertyDescriptor> CustomConfigurationHelper::GetProperties(
const gd::SerializableWithNameList<gd::NamedPropertyDescriptor> &properties,
const gd::SerializerElement &configurationContent) {
auto behaviorProperties = std::map<gd::String, gd::PropertyDescriptor>();
for (auto &property : properties.GetInternalVector()) {
const auto &propertyName = property->GetName();
const auto &propertyType = property->GetType();
// TODO Move this into a PropertyDescriptor copy method.
auto &newProperty = behaviorProperties[propertyName]
.SetType(property->GetType())
.SetDescription(property->GetDescription())
.SetGroup(property->GetGroup())
.SetLabel(property->GetLabel())
.SetValue(property->GetValue())
.SetHidden(property->IsHidden());
for (auto &extraInfo : property->GetExtraInfo()) {
newProperty.AddExtraInfo(extraInfo);
}
if (configurationContent.HasChild(propertyName)) {
if (propertyType == "String" || propertyType == "Choice" ||
propertyType == "Color" || propertyType == "Behavior") {
newProperty.SetValue(
configurationContent.GetChild(propertyName).GetStringValue());
} else if (propertyType == "Number") {
newProperty.SetValue(gd::String::From(
configurationContent.GetChild(propertyName).GetDoubleValue()));
} else if (propertyType == "Boolean") {
newProperty.SetValue(
configurationContent.GetChild(propertyName).GetBoolValue() ? "true"
: "false");
}
} else {
// No value was serialized for this property. `newProperty`
// will have the default value coming from `enumeratedProperty`.
}
}
return behaviorProperties;
}
bool CustomConfigurationHelper::UpdateProperty(
const gd::SerializableWithNameList<gd::NamedPropertyDescriptor> &properties,
gd::SerializerElement &configurationContent,
const gd::String &propertyName,
const gd::String &newValue) {
if (!properties.Has(propertyName)) {
return false;
}
const auto &property = properties.Get(propertyName);
auto &element = configurationContent.AddChild(propertyName);
const gd::String &propertyType = property.GetType();
if (propertyType == "String" || propertyType == "Choice" ||
propertyType == "Color" || propertyType == "Behavior") {
element.SetStringValue(newValue);
} else if (propertyType == "Number") {
element.SetDoubleValue(newValue.To<double>());
} else if (propertyType == "Boolean") {
element.SetBoolValue(newValue == "1");
}
return true;
}

View File

@@ -1,43 +0,0 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_CUSTOMCONFIGURATIONHELPER_H
#define GDCORE_CUSTOMCONFIGURATIONHELPER_H
#include "GDCore/Project/Behavior.h"
#include "GDCore/Project/EventsBasedBehavior.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/PropertyDescriptor.h"
#include "GDCore/Serialization/Serializer.h"
#include "GDCore/Serialization/SerializerElement.h"
using namespace gd;
namespace gd {
/**
* \brief Helper functions that gd::CustomBehavior and gd::CustomBehaviorsSharedData use to
* store their content in JSON.
*/
class CustomConfigurationHelper {
public:
CustomConfigurationHelper() {}
static void InitializeContent(
const gd::SerializableWithNameList<gd::NamedPropertyDescriptor> &properties,
gd::SerializerElement &behaviorContent);
static std::map<gd::String, gd::PropertyDescriptor> GetProperties(
const gd::SerializableWithNameList<gd::NamedPropertyDescriptor> &properties,
const gd::SerializerElement &behaviorContent);
static bool UpdateProperty(
const gd::SerializableWithNameList<gd::NamedPropertyDescriptor> &properties,
gd::SerializerElement &behaviorContent,
const gd::String &name,
const gd::String &value);
};
} // namespace gd
#endif // GDCORE_CUSTOMCONFIGURATIONHELPER_H

View File

@@ -12,7 +12,6 @@
#include "GDCore/Serialization/Serializer.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/Tools/Log.h"
#include "GDCore/Project/CustomConfigurationHelper.h"
using namespace gd;
@@ -68,7 +67,50 @@ std::map<gd::String, gd::PropertyDescriptor> CustomObjectConfiguration::GetPrope
const auto &eventsBasedObject = project->GetEventsBasedObject(GetType());
const auto &properties = eventsBasedObject.GetPropertyDescriptors();
return gd::CustomConfigurationHelper::GetProperties(properties, objectContent);
for (auto &property : properties.GetInternalVector()) {
const auto &propertyName = property->GetName();
const auto &propertyType = property->GetType();
// TODO Move this into a PropertyDescriptor copy method.
auto &newProperty = objectProperties[propertyName]
.SetType(property->GetType())
.SetDescription(property->GetDescription())
.SetGroup(property->GetGroup())
.SetLabel(property->GetLabel())
.SetValue(property->GetValue())
.SetHidden(property->IsHidden());
for (auto &extraInfo : property->GetExtraInfo()) {
newProperty.AddExtraInfo(extraInfo);
}
if (objectContent.HasChild(propertyName)) {
if (
propertyType == "String" ||
propertyType == "Choice" ||
propertyType == "Color"
) {
newProperty.SetValue(
objectContent.GetChild(propertyName).GetStringValue()
);
} else if (propertyType == "Number") {
newProperty.SetValue(
gd::String::From(objectContent.GetChild(propertyName).GetDoubleValue())
);
} else if (propertyType == "Boolean") {
newProperty.SetValue(
objectContent.GetChild(propertyName).GetBoolValue()
? "true"
: "false"
);
}
} else {
// No value was serialized for this property. `newProperty`
// will have the default value coming from `enumeratedProperty`.
}
}
return objectProperties;
}
bool CustomObjectConfiguration::UpdateProperty(const gd::String& propertyName,
@@ -78,12 +120,27 @@ bool CustomObjectConfiguration::UpdateProperty(const gd::String& propertyName,
}
const auto &eventsBasedObject = project->GetEventsBasedObject(GetType());
const auto &properties = eventsBasedObject.GetPropertyDescriptors();
return gd::CustomConfigurationHelper::UpdateProperty(
properties,
objectContent,
propertyName,
newValue);
if (!properties.Has(propertyName)) {
return false;
}
const auto &property = properties.Get(propertyName);
auto &element = objectContent.AddChild(propertyName);
const gd::String &propertyType = property.GetType();
if (
propertyType == "String" ||
propertyType == "Choice" ||
propertyType == "Color"
) {
element.SetStringValue(newValue);
} else if (propertyType == "Number") {
element.SetDoubleValue(newValue.To<double>());
} else if (propertyType == "Boolean") {
element.SetBoolValue(newValue == "1");
}
return true;
}
std::map<gd::String, gd::PropertyDescriptor>

View File

@@ -18,20 +18,12 @@ EventsBasedBehavior::EventsBasedBehavior()
void EventsBasedBehavior::SerializeTo(SerializerElement& element) const {
AbstractEventsBasedEntity::SerializeTo(element);
element.SetAttribute("objectType", objectType);
if (isPrivate) {
element.SetBoolAttribute("private", isPrivate);
}
sharedPropertyDescriptors.SerializeElementsTo(
"propertyDescriptor", element.AddChild("sharedPropertyDescriptors"));
}
void EventsBasedBehavior::UnserializeFrom(gd::Project& project,
const SerializerElement& element) {
AbstractEventsBasedEntity::UnserializeFrom(project, element);
objectType = element.GetStringAttribute("objectType");
isPrivate = element.GetBoolAttribute("private");
sharedPropertyDescriptors.UnserializeElementsFrom(
"propertyDescriptor", element.GetChild("sharedPropertyDescriptors"));
}
} // namespace gd

View File

@@ -73,58 +73,6 @@ class GD_CORE_API EventsBasedBehavior: public AbstractEventsBasedEntity {
return *this;
}
/**
* \brief Check if the behavior is private - it can't be used outside of its
* extension.
*/
bool IsPrivate() { return isPrivate; }
/**
* \brief Set that the behavior is private - it can't be used outside of its
* extension.
*/
EventsBasedBehavior& SetPrivate(bool _isPrivate) {
isPrivate = _isPrivate;
return *this;
}
/**
* \brief Return a reference to the list of shared properties.
*/
SerializableWithNameList<NamedPropertyDescriptor>& GetSharedPropertyDescriptors() {
return sharedPropertyDescriptors;
}
/**
* \brief Return a const reference to the list of shared properties.
*/
const SerializableWithNameList<NamedPropertyDescriptor>& GetSharedPropertyDescriptors()
const {
return sharedPropertyDescriptors;
}
/**
* \brief Get the name of the action to change a shared property.
*/
static gd::String GetSharedPropertyActionName(const gd::String &propertyName) {
return "SetSharedProperty" + propertyName;
};
/**
* \brief Get the name of the condition to compare a shared property.
*/
static gd::String GetSharedPropertyConditionName(const gd::String &propertyName) {
return "SharedProperty" + propertyName;
};
/**
* \brief Get the name of the expression to get a shared property.
*/
static gd::String
GetSharedPropertyExpressionName(const gd::String &propertyName) {
return "SharedProperty" + propertyName;
};
void SerializeTo(SerializerElement& element) const override;
void UnserializeFrom(gd::Project& project,
@@ -132,8 +80,6 @@ class GD_CORE_API EventsBasedBehavior: public AbstractEventsBasedEntity {
private:
gd::String objectType;
bool isPrivate = false;
SerializableWithNameList<NamedPropertyDescriptor> sharedPropertyDescriptors;
};
} // namespace gd

View File

@@ -59,19 +59,11 @@ const std::vector<gd::ParameterMetadata>& EventsFunction::GetParametersForEvents
void EventsFunction::SerializeTo(SerializerElement& element) const {
element.SetAttribute("name", name);
element.SetAttribute("fullName", fullName);
if (!description.empty()) {
element.SetAttribute("description", description);
}
element.SetAttribute("description", description);
element.SetAttribute("sentence", sentence);
if (!group.empty()) {
element.SetAttribute("group", group);
}
if (!getterName.empty()) {
element.SetAttribute("getterName", getterName);
}
if (isPrivate) {
element.SetBoolAttribute("private", isPrivate);
}
element.SetAttribute("group", group);
element.SetAttribute("getterName", getterName);
element.SetBoolAttribute("private", isPrivate);
events.SerializeTo(element.AddChild("events"));
gd::String functionTypeStr = "Action";

View File

@@ -145,11 +145,6 @@ class GD_CORE_API EventsFunction {
*/
const gd::ValueTypeMetadata& GetExpressionType() const { return expressionType; }
/**
* \brief Get the type of the expression
*/
gd::ValueTypeMetadata& GetExpressionType() { return expressionType; }
enum FunctionType {
Action,
Condition,

View File

@@ -54,7 +54,7 @@ void EventsFunctionsExtension::SerializeTo(SerializerElement& element) const {
element.SetAttribute("version", version);
element.SetAttribute("extensionNamespace", extensionNamespace);
element.SetAttribute("shortDescription", shortDescription);
element.AddChild("description").SetMultilineStringValue(description);
element.SetAttribute("description", description);
element.SetAttribute("name", name);
element.SetAttribute("fullName", fullName);
element.SetAttribute("category", category);
@@ -100,7 +100,7 @@ void EventsFunctionsExtension::UnserializeExtensionDeclarationFrom(
version = element.GetStringAttribute("version");
extensionNamespace = element.GetStringAttribute("extensionNamespace");
shortDescription = element.GetStringAttribute("shortDescription");
description = element.GetChild("description").GetMultilineStringValue();
description = element.GetStringAttribute("description");
name = element.GetStringAttribute("name");
fullName = element.GetStringAttribute("fullName");
category = element.GetStringAttribute("category");

View File

@@ -60,6 +60,7 @@ void InitialInstancesContainer::IterateOverInstancesWithZOrdering(
for (auto& instance : sortedInstances) func(instance);
}
#if defined(GD_IDE_ONLY)
gd::InitialInstance& InitialInstancesContainer::InsertNewInitialInstance() {
gd::InitialInstance newInstance;
initialInstances.push_back(newInstance);
@@ -172,6 +173,7 @@ void InitialInstancesContainer::SerializeTo(SerializerElement& element) const {
}
void InitialInstancesContainer::Clear() { initialInstances.clear(); }
#endif
InitialInstanceFunctor::~InitialInstanceFunctor(){};

View File

@@ -17,7 +17,6 @@
#include "GDCore/IDE/SceneNameMangler.h"
#include "GDCore/Project/Behavior.h"
#include "GDCore/Project/BehaviorsSharedData.h"
#include "GDCore/Project/CustomBehaviorsSharedData.h"
#include "GDCore/Project/InitialInstance.h"
#include "GDCore/Project/Layer.h"
#include "GDCore/Project/Object.h"
@@ -27,7 +26,6 @@
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/String.h"
#include "GDCore/Tools/PolymorphicClone.h"
#include "GDCore/Tools/Log.h"
using namespace std;
@@ -242,11 +240,11 @@ void Layout::UpdateBehaviorsSharedData(gd::Project& project) {
}
}
std::unique_ptr<gd::BehaviorsSharedData> Layout::CreateBehaviorsSharedData(
gd::Project& project, const gd::String& name, const gd::String& behaviorsType) {
std::unique_ptr<gd::BehaviorsSharedData> Layout::CreateBehaviorsSharedData(gd::Project& project, const gd::String& name, const gd::String& behaviorsType) {
if (project.HasEventsBasedBehavior(behaviorsType)) {
// Events based behaviors don't have shared data yet.
auto sharedData =
gd::make_unique<gd::CustomBehaviorsSharedData>(name, project, behaviorsType);
gd::make_unique<gd::BehaviorsSharedData>(name, behaviorsType);
sharedData->InitializeContent();
return std::move(sharedData);
}
@@ -255,14 +253,7 @@ std::unique_ptr<gd::BehaviorsSharedData> Layout::CreateBehaviorsSharedData(
project.GetCurrentPlatform(),
behaviorsType);
if (gd::MetadataProvider::IsBadBehaviorMetadata(behaviorMetadata)) {
gd::LogWarning("Tried to create a behavior shared data with an unknown type: " +
behaviorsType + " on object " + GetName() + "!");
// It's probably an events-based behavior that was removed.
// Create a custom behavior shared data to preserve the properties values.
auto sharedData =
gd::make_unique<gd::CustomBehaviorsSharedData>(name, project, behaviorsType);
sharedData->InitializeContent();
return std::move(sharedData);
return nullptr;
}
gd::BehaviorsSharedData* behaviorsSharedDataBluePrint =

View File

@@ -1,27 +0,0 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "MeasurementBaseUnit.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/String.h"
#include "GDCore/Tools/Localization.h"
#include <vector>
namespace gd {
MeasurementBaseUnit::~MeasurementBaseUnit() {}
const gd::MeasurementBaseUnit MeasurementBaseUnit::degreeAngle =
MeasurementBaseUnit("degree", "deg", "");
const gd::MeasurementBaseUnit MeasurementBaseUnit::pixel =
MeasurementBaseUnit("pixel", "px", "distance");
const gd::MeasurementBaseUnit MeasurementBaseUnit::meter =
MeasurementBaseUnit("meter", "m", "distance");
const gd::MeasurementBaseUnit MeasurementBaseUnit::second =
MeasurementBaseUnit("second", "s", "time");
const gd::MeasurementBaseUnit MeasurementBaseUnit::kilogram =
MeasurementBaseUnit("kilogram", "Kg", "mass");
} // namespace gd

View File

@@ -1,57 +0,0 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_MEASUREMENTBASEUNIT
#define GDCORE_MEASUREMENTBASEUNIT
#include <vector>
#include "GDCore/String.h"
namespace gd {
class SerializerElement;
}
namespace gd {
/**
* \brief An atomic unit of measurement.
*/
class GD_CORE_API MeasurementBaseUnit {
public:
MeasurementBaseUnit(gd::String name_, gd::String symbol_,
gd::String quantity_)
: name(name_), symbol(symbol_), quantity(quantity_) {}
virtual ~MeasurementBaseUnit();
/**
* \brief Return the unit name.
*/
const gd::String &GetName() const { return name; }
/**
* \brief Return the unit symbol.
*/
const gd::String &GetSymbol() const { return symbol; }
/**
* \brief Return the physical quantity.
*/
const gd::String &GetQuantity() const { return quantity; }
static const gd::MeasurementBaseUnit degreeAngle;
static const gd::MeasurementBaseUnit pixel;
static const gd::MeasurementBaseUnit meter;
static const gd::MeasurementBaseUnit second;
static const gd::MeasurementBaseUnit kilogram;
private:
gd::String name; ///< The unit name
gd::String symbol; ///< The unit symbol
gd::String quantity; ///< The physical quantity
};
} // namespace gd
#endif // GDCORE_MEASUREMENTBASEUNIT

View File

@@ -1,38 +0,0 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "MeasurementUnit.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/String.h"
#include <vector>
namespace gd {
MeasurementUnit::~MeasurementUnit() {}
gd::MeasurementUnit MeasurementUnit::undefined = CreateUndefined();
gd::MeasurementUnit MeasurementUnit::dimensionless = CreateDimensionless();
gd::MeasurementUnit MeasurementUnit::degreeAngle = CreateDegreeAngle();
gd::MeasurementUnit MeasurementUnit::second = CreateSecond();
gd::MeasurementUnit MeasurementUnit::pixel = CreatePixel();
gd::MeasurementUnit MeasurementUnit::pixelSpeed = CreatePixelSpeed();
gd::MeasurementUnit MeasurementUnit::pixelAcceleration =
CreatePixelAcceleration();
gd::MeasurementUnit MeasurementUnit::newton = CreateNewton();
gd::MeasurementUnit MeasurementUnit::angularSpeed = CreateAngularSpeed();
void MeasurementUnit::ApplyTranslation() {
undefined = CreateUndefined();
dimensionless = CreateDimensionless();
degreeAngle = CreateDegreeAngle();
second = CreateSecond();
pixel = CreatePixel();
pixelSpeed = CreatePixelSpeed();
pixelAcceleration = CreatePixelAcceleration();
newton = CreateNewton();
angularSpeed = CreateAngularSpeed();
}
} // namespace gd

View File

@@ -1,197 +0,0 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_MEASUREMENTUNIT
#define GDCORE_MEASUREMENTUNIT
#include <vector>
#include "GDCore/Project/MeasurementUnitElement.h"
#include "GDCore/String.h"
#include "GDCore/Tools/Localization.h"
namespace gd {
class SerializerElement;
class MeasurementBaseUnit;
} // namespace gd
namespace gd {
/**
* \brief A unit of measurement.
*/
class GD_CORE_API MeasurementUnit {
public:
MeasurementUnit(const std::vector<gd::MeasurementUnitElement> &elements_,
gd::String name_, gd::String label_,
gd::String elementsWithWords_, gd::String description_ = "")
: elements(elements_), name(name_), label(label_),
description(description_), elementsWithWords(elementsWithWords_) {}
MeasurementUnit(gd::String name_, gd::String label_,
gd::String elementsWithWords_, gd::String description_ = "")
: name(name_), label(label_), description(description_),
elementsWithWords(elementsWithWords_) {}
virtual ~MeasurementUnit();
/**
* \brief Return the unit name.
*/
const gd::String &GetName() const { return name; }
/**
* \brief Return the unit label.
*/
const gd::String &GetLabel() const { return label; }
/**
* \brief Return the unit description.
*/
const gd::String &GetDescription() const { return description; }
/**
* \brief Return the unit description.
*/
const gd::String &GetElementsWithWords() const { return elementsWithWords; }
/**
* \brief Return the unit elements.
*/
const std::vector<gd::MeasurementUnitElement> &GetElements() const {
return elements;
}
std::size_t GetElementsCount() const { return elements.size(); }
int GetElementPower(std::size_t elementIndex) const {
return elements.at(elementIndex).GetPower();
}
const gd::MeasurementBaseUnit &
GetElementBaseUnit(std::size_t elementIndex) const {
return elements.at(elementIndex).GetBaseUnit();
}
bool IsUndefined() const { return this == &gd::MeasurementUnit::undefined; }
static void ApplyTranslation();
static gd::MeasurementUnit &GetUndefined() { return undefined; }
static gd::MeasurementUnit &GetDimensionless() { return dimensionless; }
static gd::MeasurementUnit &GetDegreeAngle() { return degreeAngle; }
static gd::MeasurementUnit &GetSecond() { return second; }
static gd::MeasurementUnit &GetPixel() { return pixel; }
static gd::MeasurementUnit &GetPixelSpeed() { return pixelSpeed; }
static gd::MeasurementUnit &GetPixelAcceleration() {
return pixelAcceleration;
}
static gd::MeasurementUnit &GetAngularSpeed() { return angularSpeed; }
static gd::MeasurementUnit &GetNewton() { return newton; }
private:
static gd::MeasurementUnit undefined;
static gd::MeasurementUnit dimensionless;
static gd::MeasurementUnit degreeAngle;
static gd::MeasurementUnit second;
static gd::MeasurementUnit pixel;
static gd::MeasurementUnit pixelSpeed;
static gd::MeasurementUnit pixelAcceleration;
static gd::MeasurementUnit newton;
static gd::MeasurementUnit angularSpeed;
static gd::MeasurementUnit CreateUndefined() {
return MeasurementUnit("Undefined", _("Undefined"), "");
}
static gd::MeasurementUnit CreateDimensionless() {
return MeasurementUnit("Dimensionless", _("Dimensionless"), "");
}
static gd::MeasurementUnit CreateDegreeAngle() {
std::vector<gd::MeasurementUnitElement> elements;
elements.push_back(
MeasurementUnitElement(gd::MeasurementBaseUnit::degreeAngle, 1));
return MeasurementUnit(elements, "DegreeAngle", _("Angle"), _("degree"));
}
static gd::MeasurementUnit CreateSecond() {
std::vector<gd::MeasurementUnitElement> elements;
elements.push_back(
MeasurementUnitElement(gd::MeasurementBaseUnit::second, 1));
return MeasurementUnit(elements, "Second", _("Duration"), _("second"));
}
static gd::MeasurementUnit CreatePixel() {
std::vector<gd::MeasurementUnitElement> elements;
elements.push_back(
MeasurementUnitElement(gd::MeasurementBaseUnit::pixel, 1));
return MeasurementUnit(elements, "Pixel", _("Distance"), _("pixel"));
}
static gd::MeasurementUnit CreatePixelSpeed() {
std::vector<gd::MeasurementUnitElement> elements;
elements.push_back(
MeasurementUnitElement(gd::MeasurementBaseUnit::pixel, 1));
elements.push_back(
MeasurementUnitElement(gd::MeasurementBaseUnit::second, -1));
return MeasurementUnit(elements, "PixelSpeed", _("Speed"),
_("pixel per second"),
_("How much distance is covered per second."));
}
static gd::MeasurementUnit CreatePixelAcceleration() {
std::vector<gd::MeasurementUnitElement> elements;
elements.push_back(
MeasurementUnitElement(gd::MeasurementBaseUnit::pixel, 1));
elements.push_back(
MeasurementUnitElement(gd::MeasurementBaseUnit::second, -2));
return MeasurementUnit(elements, "PixelAcceleration", _("Acceleration"),
_("pixel per second, per second"),
_("How much speed is gained (or lost) per second."));
}
static gd::MeasurementUnit CreateNewton() {
std::vector<gd::MeasurementUnitElement> elements;
elements.push_back(
MeasurementUnitElement(gd::MeasurementBaseUnit::meter, 1));
elements.push_back(
MeasurementUnitElement(gd::MeasurementBaseUnit::kilogram, 1));
elements.push_back(
MeasurementUnitElement(gd::MeasurementBaseUnit::second, -2));
return MeasurementUnit(
elements, "Newton",
_("Force (in Newton)"), _("meter kilogram per second, per second"),
_("A unit to measure forces."));
}
static gd::MeasurementUnit CreateAngularSpeed() {
std::vector<gd::MeasurementUnitElement> elements;
elements.push_back(
MeasurementUnitElement(gd::MeasurementBaseUnit::degreeAngle, 1));
elements.push_back(
MeasurementUnitElement(gd::MeasurementBaseUnit::second, -1));
return MeasurementUnit(elements, "AngularSpeed", _("Angular speed"),
_("degree per second"),
_("How much angle is covered per second."));
}
gd::String name; ///< The unit name.
gd::String label; ///< The unit label.
gd::String description; ///< The unit description.
gd::String elementsWithWords; ///< The unit elements put in words.
std::vector<gd::MeasurementUnitElement> elements; ///< The unit elements.
};
} // namespace gd
#endif // GDCORE_MEASUREMENTUNIT

View File

@@ -1,15 +0,0 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "MeasurementUnitElement.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/String.h"
#include <vector>
namespace gd {
MeasurementUnitElement::~MeasurementUnitElement() {}
} // namespace gd

View File

@@ -1,46 +0,0 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_MEASUREMENTUNITELEMENT
#define GDCORE_MEASUREMENTUNITELEMENT
#include <vector>
#include "GDCore/Project/MeasurementBaseUnit.h"
#include "GDCore/String.h"
namespace gd {
class SerializerElement;
}
namespace gd {
/**
* \brief A couple of an atomic unit of measurement and its power.
*/
class GD_CORE_API MeasurementUnitElement {
public:
MeasurementUnitElement(const gd::MeasurementBaseUnit &baseUnit_, int power_)
: baseUnit(baseUnit_), power(power_) {}
virtual ~MeasurementUnitElement();
/**
* \brief Return the base unit.
*/
const gd::MeasurementBaseUnit &GetBaseUnit() const { return baseUnit; }
/**
* \brief Return the power on the base unit.
*/
int GetPower() const { return power; }
private:
gd::MeasurementBaseUnit baseUnit; ///< The base unit.
int power; ///< The power on the base unit.
};
} // namespace gd
#endif // GDCORE_MEASUREMENTUNITELEMENT

View File

@@ -114,7 +114,7 @@ gd::Behavior* Object::AddNewBehavior(const gd::Project& project,
if (gd::MetadataProvider::IsBadBehaviorMetadata(behaviorMetadata)) {
gd::LogWarning("Tried to create a behavior with an unknown type: " + type
+ " on object " + GetName() + "!");
// It's probably an events-based behavior that was removed.
// It's probably an events-based object that was removed.
// Create a custom behavior to preserve the properties values.
return initializeAndAdd(
gd::make_unique<CustomBehavior>(name, project, type));

View File

@@ -8,8 +8,6 @@
#include <vector>
#include "GDCore/String.h"
#include "GDCore/Project/MeasurementUnit.h"
namespace gd {
class SerializerElement;
}
@@ -30,12 +28,12 @@ class GD_CORE_API PropertyDescriptor {
* \param propertyValue The value of the property.
*/
PropertyDescriptor(gd::String propertyValue)
: currentValue(propertyValue), type("string"), label(""), hidden(false), measurementUnit(gd::MeasurementUnit::GetUndefined()) {}
: currentValue(propertyValue), type("string"), label(""), hidden(false) {}
/**
* \brief Empty constructor creating an empty property to be displayed.
*/
PropertyDescriptor() : hidden(false), measurementUnit(gd::MeasurementUnit::GetUndefined()) {};
PropertyDescriptor() : hidden(false){};
/**
* \brief Destructor
@@ -105,21 +103,12 @@ class GD_CORE_API PropertyDescriptor {
extraInformation.push_back(info);
return *this;
}
/**
* \brief Change the unit of measurement of the property value.
*/
PropertyDescriptor& SetMeasurementUnit(const gd::MeasurementUnit &measurementUnit_) {
measurementUnit = measurementUnit_;
return *this;
}
const gd::String& GetValue() const { return currentValue; }
const gd::String& GetType() const { return type; }
const gd::String& GetLabel() const { return label; }
const gd::String& GetDescription() const { return description; }
const gd::String& GetGroup() const { return group; }
const gd::MeasurementUnit& GetMeasurementUnit() const { return measurementUnit; }
const std::vector<gd::String>& GetExtraInfo() const {
return extraInformation;
@@ -179,7 +168,6 @@ class GD_CORE_API PropertyDescriptor {
///< choices, if a property is a displayed as a combo
///< box.
bool hidden;
gd::MeasurementUnit measurementUnit; //< The unit of measurement of the property vale.
};
} // namespace gd

View File

@@ -208,7 +208,8 @@ SerializerElement& SerializerElement::GetChild(
for (size_t i = 0; i < children.size(); ++i) {
if (children[i].second == std::shared_ptr<SerializerElement>()) continue;
if (children[i].first == name || (isArray && children[i].first.empty()) ||
if (children[i].first == name ||
(isArray && children[i].first.empty()) ||
(!deprecatedName.empty() && children[i].first == deprecatedName)) {
if (index == currentIndex)
return *children[i].second;
@@ -241,7 +242,8 @@ std::size_t SerializerElement::GetChildrenCount(
for (size_t i = 0; i < children.size(); ++i) {
if (children[i].second == std::shared_ptr<SerializerElement>()) continue;
if (children[i].first == name || (isArray && children[i].first.empty()) ||
if (children[i].first == name ||
(isArray && children[i].first.empty()) ||
(!deprecatedName.empty() && children[i].first == deprecatedName))
currentIndex++;
}
@@ -289,31 +291,4 @@ void SerializerElement::Init(const gd::SerializerElement& other) {
deprecatedArrayOf = other.deprecatedArrayOf;
}
void SerializerElement::SetMultilineStringValue(const gd::String& value) {
if (value.find('\n') == gd::String::npos) {
SetStringValue(value);
return;
}
std::vector<gd::String> lines = value.Split('\n');
children.clear();
ConsiderAsArrayOf("");
for (const auto& line : lines) {
AddChild("").SetStringValue(line);
}
}
gd::String SerializerElement::GetMultilineStringValue() {
if (!ConsideredAsArray()) {
return GetValue().GetString();
}
gd::String value;
for (const auto& child : children) {
if (!value.empty()) value += "\n";
value += child.second->GetStringValue();
}
return value;
}
} // namespace gd

View File

@@ -10,7 +10,6 @@
#include <memory>
#include <string>
#include <vector>
#include "GDCore/Serialization/SerializerValue.h"
#include "GDCore/String.h"
@@ -178,18 +177,6 @@ class GD_CORE_API SerializerElement {
* \brief Return true if no value was set for the element.
*/
bool IsValueUndefined() const { return valueUndefined; }
/**
* \brief Save the value either as a string or as an array of strings if it
* has line breaks.
*/
void SetMultilineStringValue(const gd::String &value);
/**
* \brief Read the value, either represented as a string or as an array of strings,
* into a string.
*/
gd::String GetMultilineStringValue();
///@}
/** \name Attributes
@@ -453,7 +440,7 @@ class GD_CORE_API SerializerElement {
* Initialize element using another element. Used by copy-ctor and assign-op.
* Don't forget to update me if members were changed!
*/
void Init(const gd::SerializerElement &other);
void Init(const gd::SerializerElement& other);
bool valueUndefined; ///< If true, the element does not have a value.
SerializerValue elementValue;

View File

@@ -12,7 +12,6 @@
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Tools/Localization.h"
#include "GDCore/Events/Builtin/StandardEvent.h"
#include "catch.hpp"
// TODO Remove these 2 classes and write the test with events based behaviors.
@@ -95,13 +94,6 @@ void SetupProjectWithDummyPlatform(gd::Project& project,
// Don't show extension loading logs for tests (too verbose).
platform.EnableExtensionLoadingLogs(false);
// Required for tests on event generation.
std::shared_ptr<gd::PlatformExtension> commonInstructionsExtension =
std::shared_ptr<gd::PlatformExtension>(new gd::PlatformExtension);
commonInstructionsExtension->SetExtensionInformation(
"BuiltinCommonInstructions", "instruction extension", "", "", "");
commonInstructionsExtension->AddEvent("Standard", "Standard event", "", "", "", std::make_shared<gd::StandardEvent>());
std::shared_ptr<gd::PlatformExtension> baseObjectExtension =
std::shared_ptr<gd::PlatformExtension>(new gd::PlatformExtension);
@@ -381,7 +373,6 @@ void SetupProjectWithDummyPlatform(gd::Project& project,
.AddUnsupportedBaseObjectCapability("effect");
}
platform.AddExtension(commonInstructionsExtension);
platform.AddExtension(baseObjectExtension);
platform.AddExtension(extension);
project.AddPlatform(platform);

View File

@@ -1,739 +0,0 @@
/*
* 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 common features of GDevelop Core.
*/
#include "GDCore/IDE/PropertyFunctionGenerator.h"
#include "DummyPlatform.h"
#include "GDCore/Events/Builtin/StandardEvent.h"
#include "GDCore/Extensions/Metadata/ValueTypeMetadata.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Project/EventsBasedBehavior.h"
#include "GDCore/Project/EventsFunctionsExtension.h"
#include "GDCore/Project/Project.h"
#include "GDCore/String.h"
#include "catch.hpp"
namespace {
gd::EventsBasedBehavior &
CreateBehavior(gd::EventsFunctionsExtension &eventsExtension) {
auto &eventsBasedBehavior =
eventsExtension.GetEventsBasedBehaviors().InsertNew(
"MyEventsBasedBehavior", 0);
eventsBasedBehavior.SetFullName("My events based behavior");
eventsBasedBehavior.SetDescription("An events based behavior for test");
eventsBasedBehavior.SetObjectType("");
return eventsBasedBehavior;
};
gd::EventsBasedObject &
CreateObject(gd::EventsFunctionsExtension &eventsExtension) {
auto &eventsBasedObject = eventsExtension.GetEventsBasedObjects().InsertNew(
"MyEventsBasedObject", 0);
eventsBasedObject.SetFullName("My events based object");
eventsBasedObject.SetDescription("An events based object for test");
return eventsBasedObject;
};
} // namespace
TEST_CASE("PropertyFunctionGenerator", "[common]") {
SECTION("Can generate functions for a number property in a behavior") {
gd::Platform platform;
gd::Project project;
SetupProjectWithDummyPlatform(project, platform);
auto &extension =
project.InsertNewEventsFunctionsExtension("MyEventsExtension", 0);
auto &behavior = CreateBehavior(extension);
auto &property =
behavior.GetPropertyDescriptors().InsertNew("MovementAngle", 0);
property.SetType("Number")
.SetLabel("Movement angle")
.SetDescription("The angle of the trajectory direction.")
.SetGroup("Movement");
gd::PropertyFunctionGenerator::GenerateBehaviorGetterAndSetter(
project, extension, behavior, property, false);
REQUIRE(
behavior.GetEventsFunctions().HasEventsFunctionNamed("MovementAngle"));
REQUIRE(behavior.GetEventsFunctions().HasEventsFunctionNamed(
"SetMovementAngle"));
{
auto &getter =
behavior.GetEventsFunctions().GetEventsFunction("MovementAngle");
REQUIRE(getter.GetFunctionType() ==
gd::EventsFunction::ExpressionAndCondition);
REQUIRE(getter.GetExpressionType().GetName() == "expression");
REQUIRE(getter.GetFullName() == "Movement angle");
REQUIRE(getter.GetGroup() ==
"My events based behavior movement configuration");
REQUIRE(getter.GetDescription() ==
"the movement angle of the object. The "
"angle of the trajectory direction.");
REQUIRE(getter.GetSentence() == "the movement angle");
// Object and behavior parameters are added automatically.
REQUIRE(getter.GetParameters().size() == 0);
REQUIRE(getter.GetEvents().GetEventsCount() == 1);
REQUIRE(getter.GetEvents().GetEvent(0).GetType() ==
"BuiltinCommonInstructions::Standard");
auto &getterEvent =
dynamic_cast<gd::StandardEvent &>(getter.GetEvents().GetEvent(0));
REQUIRE(getterEvent.GetConditions().size() == 0);
REQUIRE(getterEvent.GetActions().size() == 1);
auto &getterAction = getterEvent.GetActions().at(0);
REQUIRE(getterAction.GetType() == "SetReturnNumber");
REQUIRE(getterAction.GetParametersCount() == 1);
REQUIRE(getterAction.GetParameter(0).GetPlainString() ==
"Object.Behavior::PropertyMovementAngle()");
}
{
auto &setter =
behavior.GetEventsFunctions().GetEventsFunction("SetMovementAngle");
REQUIRE(setter.GetFunctionType() ==
gd::EventsFunction::ActionWithOperator);
REQUIRE(setter.GetGetterName() == "MovementAngle");
// These fields are deducted from the getter.
REQUIRE(setter.GetFullName() == "");
REQUIRE(setter.GetGroup() == "");
REQUIRE(setter.GetDescription() == "");
REQUIRE(setter.GetSentence() == "");
// Object and behavior parameters are added automatically.
REQUIRE(setter.GetParameters().size() == 0);
REQUIRE(setter.GetEvents().GetEventsCount() == 1);
REQUIRE(setter.GetEvents().GetEvent(0).GetType() ==
"BuiltinCommonInstructions::Standard");
auto &setterEvent =
dynamic_cast<gd::StandardEvent &>(setter.GetEvents().GetEvent(0));
REQUIRE(setterEvent.GetConditions().size() == 0);
REQUIRE(setterEvent.GetActions().size() == 1);
auto &setterAction = setterEvent.GetActions().at(0);
REQUIRE(
setterAction.GetType() ==
"MyEventsExtension::MyEventsBasedBehavior::SetPropertyMovementAngle");
REQUIRE(setterAction.GetParametersCount() == 4);
REQUIRE(setterAction.GetParameter(0).GetPlainString() == "Object");
REQUIRE(setterAction.GetParameter(1).GetPlainString() == "Behavior");
REQUIRE(setterAction.GetParameter(2).GetPlainString() == "=");
REQUIRE(setterAction.GetParameter(3).GetPlainString() ==
"GetArgumentAsNumber(\"Value\")");
}
}
SECTION("Can generate functions for a choice property in a behavior") {
gd::Platform platform;
gd::Project project;
SetupProjectWithDummyPlatform(project, platform);
auto &extension =
project.InsertNewEventsFunctionsExtension("MyEventsExtension", 0);
auto &behavior = CreateBehavior(extension);
auto &property =
behavior.GetPropertyDescriptors().InsertNew("CollisionShape", 0);
property.SetType("Choice")
.SetLabel("Collision shape")
.SetLabel("Dot shape")
.SetDescription("The shape is used for collision.")
.SetGroup("Movement");
property.GetExtraInfo().push_back("Dot shape");
property.GetExtraInfo().push_back("Bounding disk");
gd::PropertyFunctionGenerator::GenerateBehaviorGetterAndSetter(
project, extension, behavior, property, false);
REQUIRE(
behavior.GetEventsFunctions().HasEventsFunctionNamed("CollisionShape"));
REQUIRE(behavior.GetEventsFunctions().HasEventsFunctionNamed(
"SetCollisionShape"));
auto &getter =
behavior.GetEventsFunctions().GetEventsFunction("CollisionShape");
REQUIRE(getter.GetFunctionType() ==
gd::EventsFunction::ExpressionAndCondition);
REQUIRE(getter.GetExpressionType().GetName() == "stringWithSelector");
REQUIRE(getter.GetExpressionType().GetExtraInfo() ==
"[\"Dot shape\",\"Bounding disk\"]");
}
SECTION("Can generate functions for a boolean property in a behavior") {
gd::Platform platform;
gd::Project project;
SetupProjectWithDummyPlatform(project, platform);
auto &extension =
project.InsertNewEventsFunctionsExtension("MyEventsExtension", 0);
auto &behavior = CreateBehavior(extension);
auto &property = behavior.GetPropertyDescriptors().InsertNew("Rotate", 0);
property.SetType("Boolean")
.SetLabel("Rotate object")
.SetDescription(
"The rotation follows movements done by this behavior only.")
.SetGroup("Movement");
gd::PropertyFunctionGenerator::GenerateBehaviorGetterAndSetter(
project, extension, behavior, property, false);
REQUIRE(behavior.GetEventsFunctions().HasEventsFunctionNamed("Rotate"));
REQUIRE(behavior.GetEventsFunctions().HasEventsFunctionNamed("SetRotate"));
{
auto &getter = behavior.GetEventsFunctions().GetEventsFunction("Rotate");
REQUIRE(getter.GetFunctionType() == gd::EventsFunction::Condition);
REQUIRE(getter.GetExpressionType().GetName() == "boolean");
REQUIRE(getter.GetFullName() == "Rotate object");
REQUIRE(getter.GetGroup() ==
"My events based behavior movement configuration");
REQUIRE(getter.GetDescription() ==
"Check if rotate object. The rotation follows movements done by "
"this behavior only.");
REQUIRE(getter.GetSentence() == "_PARAM0_ rotate object");
// Object and behavior parameters are added automatically.
REQUIRE(getter.GetParameters().size() == 0);
REQUIRE(getter.GetEvents().GetEventsCount() == 1);
REQUIRE(getter.GetEvents().GetEvent(0).GetType() ==
"BuiltinCommonInstructions::Standard");
auto &getterEvent =
dynamic_cast<gd::StandardEvent &>(getter.GetEvents().GetEvent(0));
REQUIRE(getterEvent.GetConditions().size() == 1);
REQUIRE(getterEvent.GetActions().size() == 1);
auto &getterCondition = getterEvent.GetConditions().at(0);
REQUIRE(getterCondition.GetType() ==
"MyEventsExtension::MyEventsBasedBehavior::PropertyRotate");
REQUIRE(!getterCondition.IsInverted());
REQUIRE(getterCondition.GetParametersCount() == 2);
REQUIRE(getterCondition.GetParameter(0).GetPlainString() == "Object");
REQUIRE(getterCondition.GetParameter(1).GetPlainString() == "Behavior");
auto &getterAction = getterEvent.GetActions().at(0);
REQUIRE(getterAction.GetType() == "SetReturnBoolean");
REQUIRE(getterAction.GetParametersCount() == 1);
REQUIRE(getterAction.GetParameter(0).GetPlainString() == "True");
}
{
auto &setter =
behavior.GetEventsFunctions().GetEventsFunction("SetRotate");
REQUIRE(setter.GetFunctionType() == gd::EventsFunction::Action);
REQUIRE(setter.GetFullName() == "Rotate object");
REQUIRE(setter.GetGroup() ==
"My events based behavior movement configuration");
REQUIRE(setter.GetDescription() ==
"Change if rotate object. The rotation follows movements done by "
"this behavior only.");
REQUIRE(setter.GetSentence() == "_PARAM0_ rotate object: _PARAM2_");
// To generate the value parameter, object and behavior parameters has to
// be declared too.
REQUIRE(setter.GetParameters().size() == 3);
auto &objectParameter = setter.GetParameters().at(0);
REQUIRE(objectParameter.GetName() == "Object");
REQUIRE(objectParameter.GetType() == "object");
auto &behaviorParameter = setter.GetParameters().at(1);
REQUIRE(behaviorParameter.GetName() == "Behavior");
REQUIRE(behaviorParameter.GetType() == "behavior");
REQUIRE(behaviorParameter.GetExtraInfo() ==
"MyEventsExtension::MyEventsBasedBehavior");
auto &valueParameter = setter.GetParameters().at(2);
REQUIRE(valueParameter.GetName() == "Value");
REQUIRE(valueParameter.GetType() == "yesorno");
REQUIRE(setter.GetEvents().GetEventsCount() == 2);
REQUIRE(setter.GetEvents().GetEvent(0).GetType() ==
"BuiltinCommonInstructions::Standard");
REQUIRE(setter.GetEvents().GetEvent(1).GetType() ==
"BuiltinCommonInstructions::Standard");
auto &setterNoEvent =
dynamic_cast<gd::StandardEvent &>(setter.GetEvents().GetEvent(0));
REQUIRE(setterNoEvent.GetConditions().size() == 1);
REQUIRE(setterNoEvent.GetActions().size() == 1);
auto &setterNoCondition = setterNoEvent.GetConditions().at(0);
REQUIRE(setterNoCondition.GetType() == "GetArgumentAsBoolean");
REQUIRE(setterNoCondition.IsInverted());
REQUIRE(setterNoCondition.GetParametersCount() == 1);
REQUIRE(setterNoCondition.GetParameter(0).GetPlainString() ==
"\"Value\"");
auto &setterNoAction = setterNoEvent.GetActions().at(0);
REQUIRE(setterNoAction.GetType() ==
"MyEventsExtension::MyEventsBasedBehavior::SetPropertyRotate");
REQUIRE(setterNoAction.GetParametersCount() == 3);
REQUIRE(setterNoAction.GetParameter(0).GetPlainString() == "Object");
REQUIRE(setterNoAction.GetParameter(1).GetPlainString() == "Behavior");
REQUIRE(setterNoAction.GetParameter(2).GetPlainString() == "no");
auto &setterYesEvent =
dynamic_cast<gd::StandardEvent &>(setter.GetEvents().GetEvent(1));
REQUIRE(setterYesEvent.GetConditions().size() == 1);
REQUIRE(setterYesEvent.GetActions().size() == 1);
auto &setterYesCondition = setterYesEvent.GetConditions().at(0);
REQUIRE(setterYesCondition.GetType() == "GetArgumentAsBoolean");
REQUIRE(!setterYesCondition.IsInverted());
REQUIRE(setterYesCondition.GetParametersCount() == 1);
REQUIRE(setterYesCondition.GetParameter(0).GetPlainString() ==
"\"Value\"");
auto &setterYesAction = setterYesEvent.GetActions().at(0);
REQUIRE(setterYesAction.GetType() ==
"MyEventsExtension::MyEventsBasedBehavior::SetPropertyRotate");
REQUIRE(setterYesAction.GetParametersCount() == 3);
REQUIRE(setterYesAction.GetParameter(0).GetPlainString() == "Object");
REQUIRE(setterYesAction.GetParameter(1).GetPlainString() == "Behavior");
REQUIRE(setterYesAction.GetParameter(2).GetPlainString() == "yes");
}
}
SECTION("Can generate functions for a number property in an object") {
gd::Platform platform;
gd::Project project;
SetupProjectWithDummyPlatform(project, platform);
auto &extension =
project.InsertNewEventsFunctionsExtension("MyEventsExtension", 0);
auto &object = CreateObject(extension);
auto &property =
object.GetPropertyDescriptors().InsertNew("MovementAngle", 0);
property.SetType("Number")
.SetLabel("Movement angle")
.SetDescription("The angle of the trajectory direction.")
.SetGroup("Movement");
gd::PropertyFunctionGenerator::GenerateObjectGetterAndSetter(
project, extension, object, property);
REQUIRE(
object.GetEventsFunctions().HasEventsFunctionNamed("MovementAngle"));
REQUIRE(
object.GetEventsFunctions().HasEventsFunctionNamed("SetMovementAngle"));
{
auto &getter =
object.GetEventsFunctions().GetEventsFunction("MovementAngle");
REQUIRE(getter.GetFunctionType() ==
gd::EventsFunction::ExpressionAndCondition);
REQUIRE(getter.GetExpressionType().GetName() == "expression");
REQUIRE(getter.GetFullName() == "Movement angle");
REQUIRE(getter.GetGroup() ==
"My events based object movement configuration");
REQUIRE(getter.GetDescription() ==
"the movement angle of the object. The "
"angle of the trajectory direction.");
REQUIRE(getter.GetSentence() == "the movement angle");
// Object parameter is added automatically.
REQUIRE(getter.GetParameters().size() == 0);
REQUIRE(getter.GetEvents().GetEventsCount() == 1);
REQUIRE(getter.GetEvents().GetEvent(0).GetType() ==
"BuiltinCommonInstructions::Standard");
auto &getterEvent =
dynamic_cast<gd::StandardEvent &>(getter.GetEvents().GetEvent(0));
REQUIRE(getterEvent.GetConditions().size() == 0);
REQUIRE(getterEvent.GetActions().size() == 1);
auto &getterAction = getterEvent.GetActions().at(0);
REQUIRE(getterAction.GetType() == "SetReturnNumber");
REQUIRE(getterAction.GetParametersCount() == 1);
REQUIRE(getterAction.GetParameter(0).GetPlainString() ==
"Object.PropertyMovementAngle()");
}
{
auto &setter =
object.GetEventsFunctions().GetEventsFunction("SetMovementAngle");
REQUIRE(setter.GetFunctionType() ==
gd::EventsFunction::ActionWithOperator);
REQUIRE(setter.GetGetterName() == "MovementAngle");
// These fields are deducted from the getter.
REQUIRE(setter.GetFullName() == "");
REQUIRE(setter.GetGroup() == "");
REQUIRE(setter.GetDescription() == "");
REQUIRE(setter.GetSentence() == "");
// Object parameter is added automatically.
REQUIRE(setter.GetParameters().size() == 0);
REQUIRE(setter.GetEvents().GetEventsCount() == 1);
REQUIRE(setter.GetEvents().GetEvent(0).GetType() ==
"BuiltinCommonInstructions::Standard");
auto &setterEvent =
dynamic_cast<gd::StandardEvent &>(setter.GetEvents().GetEvent(0));
REQUIRE(setterEvent.GetConditions().size() == 0);
REQUIRE(setterEvent.GetActions().size() == 1);
auto &setterAction = setterEvent.GetActions().at(0);
REQUIRE(
setterAction.GetType() ==
"MyEventsExtension::MyEventsBasedObject::SetPropertyMovementAngle");
REQUIRE(setterAction.GetParametersCount() == 3);
REQUIRE(setterAction.GetParameter(0).GetPlainString() == "Object");
REQUIRE(setterAction.GetParameter(1).GetPlainString() == "=");
REQUIRE(setterAction.GetParameter(2).GetPlainString() ==
"GetArgumentAsNumber(\"Value\")");
}
}
SECTION("Can generate functions for a choice property in an object") {
gd::Platform platform;
gd::Project project;
SetupProjectWithDummyPlatform(project, platform);
auto &extension =
project.InsertNewEventsFunctionsExtension("MyEventsExtension", 0);
auto &object = CreateObject(extension);
auto &property =
object.GetPropertyDescriptors().InsertNew("CollisionShape", 0);
property.SetType("Choice")
.SetLabel("Collision shape")
.SetLabel("Dot shape")
.SetDescription("The shape is used for collision.")
.SetGroup("Movement");
property.GetExtraInfo().push_back("Dot shape");
property.GetExtraInfo().push_back("Bounding disk");
gd::PropertyFunctionGenerator::GenerateObjectGetterAndSetter(
project, extension, object, property);
REQUIRE(
object.GetEventsFunctions().HasEventsFunctionNamed("CollisionShape"));
REQUIRE(object.GetEventsFunctions().HasEventsFunctionNamed(
"SetCollisionShape"));
auto &getter =
object.GetEventsFunctions().GetEventsFunction("CollisionShape");
REQUIRE(getter.GetFunctionType() ==
gd::EventsFunction::ExpressionAndCondition);
REQUIRE(getter.GetExpressionType().GetName() == "stringWithSelector");
REQUIRE(getter.GetExpressionType().GetExtraInfo() ==
"[\"Dot shape\",\"Bounding disk\"]");
}
SECTION("Can generate functions for a boolean property in an object") {
gd::Platform platform;
gd::Project project;
SetupProjectWithDummyPlatform(project, platform);
auto &extension =
project.InsertNewEventsFunctionsExtension("MyEventsExtension", 0);
auto &object = CreateObject(extension);
auto &property = object.GetPropertyDescriptors().InsertNew("Rotate", 0);
property.SetType("Boolean")
.SetLabel("Rotate object")
.SetDescription("The rotation follows movements done by this object.")
.SetGroup("Movement");
gd::PropertyFunctionGenerator::GenerateObjectGetterAndSetter(
project, extension, object, property);
REQUIRE(object.GetEventsFunctions().HasEventsFunctionNamed("Rotate"));
REQUIRE(object.GetEventsFunctions().HasEventsFunctionNamed("SetRotate"));
{
auto &getter = object.GetEventsFunctions().GetEventsFunction("Rotate");
REQUIRE(getter.GetFunctionType() == gd::EventsFunction::Condition);
REQUIRE(getter.GetExpressionType().GetName() == "boolean");
REQUIRE(getter.GetFullName() == "Rotate object");
REQUIRE(getter.GetGroup() ==
"My events based object movement configuration");
REQUIRE(getter.GetDescription() ==
"Check if rotate object. The rotation follows movements done by "
"this object.");
REQUIRE(getter.GetSentence() == "_PARAM0_ rotate object");
// The Object parameter is added automatically.
REQUIRE(getter.GetParameters().size() == 0);
REQUIRE(getter.GetEvents().GetEventsCount() == 1);
REQUIRE(getter.GetEvents().GetEvent(0).GetType() ==
"BuiltinCommonInstructions::Standard");
auto &getterEvent =
dynamic_cast<gd::StandardEvent &>(getter.GetEvents().GetEvent(0));
REQUIRE(getterEvent.GetConditions().size() == 1);
REQUIRE(getterEvent.GetActions().size() == 1);
auto &getterCondition = getterEvent.GetConditions().at(0);
REQUIRE(getterCondition.GetType() ==
"MyEventsExtension::MyEventsBasedObject::PropertyRotate");
REQUIRE(!getterCondition.IsInverted());
REQUIRE(getterCondition.GetParametersCount() == 1);
REQUIRE(getterCondition.GetParameter(0).GetPlainString() == "Object");
auto &getterAction = getterEvent.GetActions().at(0);
REQUIRE(getterAction.GetType() == "SetReturnBoolean");
REQUIRE(getterAction.GetParametersCount() == 1);
REQUIRE(getterAction.GetParameter(0).GetPlainString() == "True");
}
{
auto &setter = object.GetEventsFunctions().GetEventsFunction("SetRotate");
REQUIRE(setter.GetFunctionType() == gd::EventsFunction::Action);
REQUIRE(setter.GetFullName() == "Rotate object");
REQUIRE(setter.GetGroup() ==
"My events based object movement configuration");
REQUIRE(setter.GetDescription() ==
"Change if rotate object. The rotation follows movements done by "
"this object.");
REQUIRE(setter.GetSentence() == "_PARAM0_ rotate object: _PARAM1_");
// To generate the value parameter, the object parameter has to
// be declared too.
REQUIRE(setter.GetParameters().size() == 2);
auto &objectParameter = setter.GetParameters().at(0);
REQUIRE(objectParameter.GetName() == "Object");
REQUIRE(objectParameter.GetType() == "object");
REQUIRE(objectParameter.GetExtraInfo() ==
"MyEventsExtension::MyEventsBasedObject");
auto &valueParameter = setter.GetParameters().at(1);
REQUIRE(valueParameter.GetName() == "Value");
REQUIRE(valueParameter.GetType() == "yesorno");
REQUIRE(setter.GetEvents().GetEventsCount() == 2);
REQUIRE(setter.GetEvents().GetEvent(0).GetType() ==
"BuiltinCommonInstructions::Standard");
REQUIRE(setter.GetEvents().GetEvent(1).GetType() ==
"BuiltinCommonInstructions::Standard");
auto &setterNoEvent =
dynamic_cast<gd::StandardEvent &>(setter.GetEvents().GetEvent(0));
REQUIRE(setterNoEvent.GetConditions().size() == 1);
REQUIRE(setterNoEvent.GetActions().size() == 1);
auto &setterNoCondition = setterNoEvent.GetConditions().at(0);
REQUIRE(setterNoCondition.GetType() == "GetArgumentAsBoolean");
REQUIRE(setterNoCondition.IsInverted());
REQUIRE(setterNoCondition.GetParametersCount() == 1);
REQUIRE(setterNoCondition.GetParameter(0).GetPlainString() ==
"\"Value\"");
auto &setterNoAction = setterNoEvent.GetActions().at(0);
REQUIRE(setterNoAction.GetType() ==
"MyEventsExtension::MyEventsBasedObject::SetPropertyRotate");
REQUIRE(setterNoAction.GetParametersCount() == 2);
REQUIRE(setterNoAction.GetParameter(0).GetPlainString() == "Object");
REQUIRE(setterNoAction.GetParameter(1).GetPlainString() == "no");
auto &setterYesEvent =
dynamic_cast<gd::StandardEvent &>(setter.GetEvents().GetEvent(1));
REQUIRE(setterYesEvent.GetConditions().size() == 1);
REQUIRE(setterYesEvent.GetActions().size() == 1);
auto &setterYesCondition = setterYesEvent.GetConditions().at(0);
REQUIRE(setterYesCondition.GetType() == "GetArgumentAsBoolean");
REQUIRE(!setterYesCondition.IsInverted());
REQUIRE(setterYesCondition.GetParametersCount() == 1);
REQUIRE(setterYesCondition.GetParameter(0).GetPlainString() ==
"\"Value\"");
auto &setterYesAction = setterYesEvent.GetActions().at(0);
REQUIRE(setterYesAction.GetType() ==
"MyEventsExtension::MyEventsBasedObject::SetPropertyRotate");
REQUIRE(setterYesAction.GetParametersCount() == 2);
REQUIRE(setterYesAction.GetParameter(0).GetPlainString() == "Object");
REQUIRE(setterYesAction.GetParameter(1).GetPlainString() == "yes");
}
}
SECTION("Can generate functions for a shared property") {
gd::Platform platform;
gd::Project project;
SetupProjectWithDummyPlatform(project, platform);
auto &extension =
project.InsertNewEventsFunctionsExtension("MyEventsExtension", 0);
auto &behavior = CreateBehavior(extension);
auto &property =
behavior.GetSharedPropertyDescriptors().InsertNew("MovementAngle", 0);
property.SetType("Number")
.SetLabel("Movement angle")
.SetDescription("The angle of the trajectory direction.")
.SetGroup("Movement");
gd::PropertyFunctionGenerator::GenerateBehaviorGetterAndSetter(
project, extension, behavior, property, true);
REQUIRE(
behavior.GetEventsFunctions().HasEventsFunctionNamed("MovementAngle"));
REQUIRE(behavior.GetEventsFunctions().HasEventsFunctionNamed(
"SetMovementAngle"));
{
auto &getter =
behavior.GetEventsFunctions().GetEventsFunction("MovementAngle");
REQUIRE(getter.GetDescription() ==
"the movement angle. The angle of the trajectory direction. "
"While an object is needed, this will apply to all objects using "
"the behavior.");
REQUIRE(getter.GetEvents().GetEventsCount() == 1);
REQUIRE(getter.GetEvents().GetEvent(0).GetType() ==
"BuiltinCommonInstructions::Standard");
auto &getterEvent =
dynamic_cast<gd::StandardEvent &>(getter.GetEvents().GetEvent(0));
REQUIRE(getterEvent.GetConditions().size() == 0);
REQUIRE(getterEvent.GetActions().size() == 1);
auto &getterAction = getterEvent.GetActions().at(0);
REQUIRE(getterAction.GetType() == "SetReturnNumber");
REQUIRE(getterAction.GetParametersCount() == 1);
REQUIRE(getterAction.GetParameter(0).GetPlainString() ==
"Object.Behavior::SharedPropertyMovementAngle()");
}
{
auto &setter =
behavior.GetEventsFunctions().GetEventsFunction("SetMovementAngle");
REQUIRE(setter.GetEvents().GetEventsCount() == 1);
REQUIRE(setter.GetEvents().GetEvent(0).GetType() ==
"BuiltinCommonInstructions::Standard");
auto &setterEvent =
dynamic_cast<gd::StandardEvent &>(setter.GetEvents().GetEvent(0));
REQUIRE(setterEvent.GetConditions().size() == 0);
REQUIRE(setterEvent.GetActions().size() == 1);
auto &setterAction = setterEvent.GetActions().at(0);
REQUIRE(setterAction.GetType() ==
"MyEventsExtension::MyEventsBasedBehavior::"
"SetSharedPropertyMovementAngle");
}
}
SECTION("Allow functions generation when there is no setter") {
gd::Platform platform;
gd::Project project;
SetupProjectWithDummyPlatform(project, platform);
auto &extension =
project.InsertNewEventsFunctionsExtension("MyEventsExtension", 0);
auto &behavior = CreateBehavior(extension);
auto &property =
behavior.GetPropertyDescriptors().InsertNew("MovementAngle", 0);
property.SetType("Number")
.SetLabel("Movement angle")
.SetDescription("The angle of the trajectory direction.")
.SetGroup("Movement");
REQUIRE(gd::PropertyFunctionGenerator::CanGenerateGetterAndSetter(
behavior, property));
}
SECTION("Forbid functions generation when a getter exists") {
gd::Platform platform;
gd::Project project;
SetupProjectWithDummyPlatform(project, platform);
auto &extension =
project.InsertNewEventsFunctionsExtension("MyEventsExtension", 0);
auto &behavior = CreateBehavior(extension);
auto &property =
behavior.GetPropertyDescriptors().InsertNew("MovementAngle", 0);
property.SetType("Number")
.SetLabel("Movement angle")
.SetDescription("The angle of the trajectory direction.")
.SetGroup("Movement");
behavior.GetEventsFunctions().InsertNewEventsFunction("MovementAngle", 0);
REQUIRE(!gd::PropertyFunctionGenerator::CanGenerateGetterAndSetter(
behavior, property));
}
SECTION("Forbid functions generation when a setter exists") {
gd::Platform platform;
gd::Project project;
SetupProjectWithDummyPlatform(project, platform);
auto &extension =
project.InsertNewEventsFunctionsExtension("MyEventsExtension", 0);
auto &behavior = CreateBehavior(extension);
auto &property =
behavior.GetPropertyDescriptors().InsertNew("MovementAngle", 0);
property.SetType("Number")
.SetLabel("Movement angle")
.SetDescription("The angle of the trajectory direction.")
.SetGroup("Movement");
behavior.GetEventsFunctions().InsertNewEventsFunction("SetMovementAngle",
0);
REQUIRE(!gd::PropertyFunctionGenerator::CanGenerateGetterAndSetter(
behavior, property));
}
SECTION("Forbid functions generation when both setter and getter exist") {
gd::Platform platform;
gd::Project project;
SetupProjectWithDummyPlatform(project, platform);
auto &extension =
project.InsertNewEventsFunctionsExtension("MyEventsExtension", 0);
auto &behavior = CreateBehavior(extension);
auto &property =
behavior.GetPropertyDescriptors().InsertNew("MovementAngle", 0);
property.SetType("Number")
.SetLabel("Movement angle")
.SetDescription("The angle of the trajectory direction.")
.SetGroup("Movement");
behavior.GetEventsFunctions().InsertNewEventsFunction("MovementAngle", 0);
behavior.GetEventsFunctions().InsertNewEventsFunction("SetMovementAngle",
0);
REQUIRE(!gd::PropertyFunctionGenerator::CanGenerateGetterAndSetter(
behavior, property));
}
SECTION("Forbid functions generation for required behavior properties") {
gd::Platform platform;
gd::Project project;
SetupProjectWithDummyPlatform(project, platform);
auto &extension =
project.InsertNewEventsFunctionsExtension("MyEventsExtension", 0);
auto &behavior = CreateBehavior(extension);
auto &property =
behavior.GetPropertyDescriptors().InsertNew("MovementAngle", 0);
property.SetType("Behavior")
.SetLabel("Pathfinding behavior")
.SetDescription("A required behavior.")
.SetGroup("Movement")
.GetExtraInfo()
.push_back("PlatformBehavior::PlatformerObjectBehavior");
REQUIRE(!gd::PropertyFunctionGenerator::CanGenerateGetterAndSetter(
behavior, property));
}
SECTION("Can generate functions when only the property name is filled") {
gd::Platform platform;
gd::Project project;
SetupProjectWithDummyPlatform(project, platform);
auto &extension =
project.InsertNewEventsFunctionsExtension("MyEventsExtension", 0);
auto &behavior = CreateBehavior(extension);
auto &property =
behavior.GetPropertyDescriptors().InsertNew("MovementAngle", 0);
property.SetType("Number");
gd::PropertyFunctionGenerator::GenerateBehaviorGetterAndSetter(
project, extension, behavior, property, false);
REQUIRE(
behavior.GetEventsFunctions().HasEventsFunctionNamed("MovementAngle"));
REQUIRE(behavior.GetEventsFunctions().HasEventsFunctionNamed(
"SetMovementAngle"));
auto &getter =
behavior.GetEventsFunctions().GetEventsFunction("MovementAngle");
REQUIRE(getter.GetFunctionType() ==
gd::EventsFunction::ExpressionAndCondition);
REQUIRE(getter.GetExpressionType().GetName() == "expression");
REQUIRE(getter.GetFullName() == "MovementAngle");
REQUIRE(getter.GetGroup() == "My events based behavior configuration");
REQUIRE(getter.GetDescription() == "the movementAngle of the object.");
REQUIRE(getter.GetSentence() == "the movementAngle");
}
}

View File

@@ -81,30 +81,6 @@ TEST_CASE("SerializerElement", "[common]") {
REQUIRE(element.GetChild(2).GetDoubleValue() == 45.6);
}
SECTION("Multiline strings") {
SerializerElement element;
// A single line is saved as a string.
element.SetMultilineStringValue("test");
REQUIRE(element.GetMultilineStringValue() == "test");
REQUIRE(element.GetStringValue() == "test");
// A string can be read.
element.SetStringValue("test of\nsomething\nsaved as a string");
REQUIRE(element.GetMultilineStringValue() == "test of\nsomething\nsaved as a string");
// A multi lines string is saved as an array.
element.SetMultilineStringValue("test\nwith\nmultiple lines.");
REQUIRE(element.ConsideredAsArray() == true);
REQUIRE(element.GetChildrenCount() == 3);
REQUIRE(element.GetMultilineStringValue() == "test\nwith\nmultiple lines.");
element.SetMultilineStringValue("test\n\nwith\n\nmultiple lines.\n");
REQUIRE(element.ConsideredAsArray() == true);
REQUIRE(element.GetChildrenCount() == 6);
REQUIRE(element.GetMultilineStringValue() == "test\n\nwith\n\nmultiple lines.\n");
}
SECTION("(Deprecated) attributes") {
SerializerElement element;
element.AddChild("child1").SetStringValue("value123");

View File

@@ -80,9 +80,6 @@ enum TestEvent {
BehaviorPropertyAction,
BehaviorPropertyCondition,
BehaviorPropertyExpression,
BehaviorSharedPropertyAction,
BehaviorSharedPropertyCondition,
BehaviorSharedPropertyExpression,
BehaviorExpression,
IllNamedBehaviorExpression,
NoParameterBehaviorExpression,
@@ -341,52 +338,6 @@ const void SetupEvents(gd::EventsList &eventList) {
eventList.InsertEvent(event);
}
if (eventList.GetEventsCount() != BehaviorSharedPropertyAction) {
throw std::logic_error("Invalid events setup: " + std::to_string(eventList.GetEventsCount()));
}
// Create an event in the layout using "MyProperty" action
{
gd::StandardEvent event;
gd::Instruction instruction;
instruction.SetType(
"MyEventsExtension::MyEventsBasedBehavior::" +
gd::EventsBasedBehavior::GetSharedPropertyActionName("MyProperty"));
event.GetActions().Insert(instruction);
eventList.InsertEvent(event);
}
if (eventList.GetEventsCount() != BehaviorSharedPropertyCondition) {
throw std::logic_error("Invalid events setup: " + std::to_string(eventList.GetEventsCount()));
}
// Create an event in the layout using "MyProperty" condition
{
gd::StandardEvent event;
gd::Instruction instruction;
instruction.SetType(
"MyEventsExtension::MyEventsBasedBehavior::" +
gd::EventsBasedBehavior::GetSharedPropertyConditionName("MyProperty"));
event.GetConditions().Insert(instruction);
eventList.InsertEvent(event);
}
if (eventList.GetEventsCount() != BehaviorSharedPropertyExpression) {
throw std::logic_error("Invalid events setup: " + std::to_string(eventList.GetEventsCount()));
}
// Create an event in the layout using "MyProperty" expression
{
gd::StandardEvent event;
gd::Instruction instruction;
instruction.SetType("MyExtension::DoSomething");
instruction.SetParametersCount(1);
instruction.SetParameter(
0, gd::Expression("ObjectWithMyBehavior.MyBehavior::" +
gd::EventsBasedBehavior::GetSharedPropertyExpressionName(
"MyProperty") +
"()"));
event.GetActions().Insert(instruction);
eventList.InsertEvent(event);
}
if (eventList.GetEventsCount() != BehaviorExpression) {
throw std::logic_error("Invalid events setup: " + std::to_string(eventList.GetEventsCount()));
}
@@ -860,11 +811,6 @@ SetupProjectWithEventsFunctionExtension(gd::Project &project) {
eventsBasedBehavior.GetPropertyDescriptors()
.InsertNew("MyProperty", 0)
.SetType("Number");
// The same name is used for the shared property to ensure there is no name
// collision.
eventsBasedBehavior.GetSharedPropertyDescriptors()
.InsertNew("MyProperty", 0)
.SetType("Number");
}
// Add a events based object
@@ -1486,10 +1432,6 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
eventsList->GetEvent(BehaviorPropertyAction)) ==
"MyRenamedExtension::MyEventsBasedBehavior::"
"SetPropertyMyProperty");
REQUIRE(GetEventFirstActionType(
eventsList->GetEvent(BehaviorSharedPropertyAction)) ==
"MyRenamedExtension::MyEventsBasedBehavior::"
"SetSharedPropertyMyProperty");
// Check events-based behavior methods have *not* been renamed in
// expressions
@@ -1804,10 +1746,6 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
eventsList->GetEvent(BehaviorPropertyAction)) ==
"MyEventsExtension::MyRenamedEventsBasedBehavior::"
"SetPropertyMyProperty");
REQUIRE(GetEventFirstActionType(
eventsList->GetEvent(BehaviorSharedPropertyAction)) ==
"MyEventsExtension::MyRenamedEventsBasedBehavior::"
"SetSharedPropertyMyProperty");
// Check events-based behavior methods have *not* been renamed in
// expressions
@@ -2389,7 +2327,8 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
}
}
SECTION("(Events based behavior) property renamed (not a required behavior)") {
SECTION(
"(Events based behavior) property renamed (not a required behavior)") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
@@ -2417,67 +2356,6 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
REQUIRE(GetEventFirstActionFirstParameterString(
eventsList->GetEvent(BehaviorPropertyExpression)) ==
"ObjectWithMyBehavior.MyBehavior::PropertyMyRenamedProperty()");
// Ensure that the shared property was NOT renamed.
REQUIRE(GetEventFirstActionType(
eventsList->GetEvent(BehaviorSharedPropertyAction)) ==
"MyEventsExtension::MyEventsBasedBehavior::"
"SetSharedPropertyMyProperty");
REQUIRE(GetEventFirstConditionType(
eventsList->GetEvent(BehaviorSharedPropertyCondition)) ==
"MyEventsExtension::MyEventsBasedBehavior::"
"SharedPropertyMyProperty");
REQUIRE(GetEventFirstActionFirstParameterString(
eventsList->GetEvent(BehaviorSharedPropertyExpression)) ==
"ObjectWithMyBehavior.MyBehavior::SharedPropertyMyProperty()");
}
}
SECTION("(Events based behavior) shared property renamed") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
auto &eventsBasedBehavior =
eventsExtension.GetEventsBasedBehaviors().Get("MyEventsBasedBehavior");
gd::WholeProjectRefactorer::RenameEventsBasedBehaviorSharedProperty(
project, eventsExtension, eventsBasedBehavior, "MyProperty",
"MyRenamedProperty");
for (auto *eventsList : GetEventsLists(project)) {
// Check if events-based behaviors shared property has been renamed in
// instructions
REQUIRE(GetEventFirstActionType(
eventsList->GetEvent(BehaviorSharedPropertyAction)) ==
"MyEventsExtension::MyEventsBasedBehavior::"
"SetSharedPropertyMyRenamedProperty");
REQUIRE(GetEventFirstConditionType(
eventsList->GetEvent(BehaviorSharedPropertyCondition)) ==
"MyEventsExtension::MyEventsBasedBehavior::"
"SharedPropertyMyRenamedProperty");
REQUIRE(GetEventFirstActionFirstParameterString(
eventsList->GetEvent(BehaviorSharedPropertyExpression)) ==
"ObjectWithMyBehavior.MyBehavior::SharedPropertyMyRenamedProperty()");
// Ensure that the property was NOT renamed.
REQUIRE(GetEventFirstActionType(
eventsList->GetEvent(BehaviorPropertyAction)) ==
"MyEventsExtension::MyEventsBasedBehavior::"
"SetPropertyMyProperty");
REQUIRE(GetEventFirstConditionType(
eventsList->GetEvent(BehaviorPropertyCondition)) ==
"MyEventsExtension::MyEventsBasedBehavior::"
"PropertyMyProperty");
REQUIRE(GetEventFirstActionFirstParameterString(
eventsList->GetEvent(BehaviorPropertyExpression)) ==
"ObjectWithMyBehavior.MyBehavior::PropertyMyProperty()");
}
}

View File

@@ -62,6 +62,16 @@ describe('gdjs.AnchorRuntimeBehavior', function () {
return object;
}
function getAnchorBehavior(object) {
const behavior = object.getBehavior(anchorBehaviorName);
if (!(behavior instanceof gdjs.AnchorRuntimeBehavior)) {
throw new Error(
'Expected behavior to be an instance of gdjs.AnchorBehavior'
);
}
return behavior;
}
describe('(anchor horizontal edge)', function () {
['rightEdgeAnchor', 'leftEdgeAnchor'].forEach((objectEdge) => {
it(`anchors the ${objectEdge} edge of object to window left (fixed)`, function () {

View File

@@ -25,7 +25,6 @@ DestroyOutsideBehavior::GetProperties(
.SetValue(gd::String::From(
behaviorContent.GetDoubleAttribute("extraBorder", 0)))
.SetType("Number")
.SetMeasurementUnit(gd::MeasurementUnit::GetPixel())
.SetLabel(_("Margin before deleting the object, in pixels"));
return properties;

View File

@@ -15,9 +15,9 @@ namespace gdjs {
this._textToSet = behaviorData.property1;
// You can also access to the shared data:
const sharedData = instanceContainer.getInitialSharedDataForBehavior(
behaviorData.name
);
const sharedData = instanceContainer
.getScene()
.getInitialSharedDataForBehavior(behaviorData.name);
this._textToSet = (sharedData as any).sharedProperty1;
// You can also run arbitrary code at the creation of the behavior:

View File

@@ -239,14 +239,14 @@ describe('gdjs.LinksManager', function () {
manager.removeAllLinksOf(object1A);
manager.removeAllLinksOf(object1A);
{
const { pickedSomething } = pickObjectsLinkedTo(
const { pickedSomething, objectsLists } = pickObjectsLinkedTo(
object1A,
Hashtable.newFrom({ obj2: [object2A, object2B, object2C] })
);
expect(pickedSomething).to.be(false);
}
{
const { pickedSomething } = pickObjectsLinkedTo(
const { pickedSomething, objectsLists } = pickObjectsLinkedTo(
object2A,
Hashtable.newFrom({ obj1: [object1A, object1B, object1C] })
);

View File

@@ -33,24 +33,27 @@ namespace gdjs {
const StretchedSprite = !tiled ? PIXI.Sprite : PIXI.TilingSprite;
this._spritesContainer = new PIXI.Container();
this._wrapperContainer = new PIXI.Container();
this._centerSprite = new StretchedSprite(
new PIXI.Texture(texture.baseTexture)
);
// @ts-ignore
this._centerSprite = new StretchedSprite(new PIXI.Texture(texture));
this._borderSprites = [
// Right
new StretchedSprite(new PIXI.Texture(texture.baseTexture)),
// Top-Right
// @ts-ignore
new StretchedSprite(new PIXI.Texture(texture)),
//Right
new PIXI.Sprite(texture),
// Top
new StretchedSprite(new PIXI.Texture(texture.baseTexture)),
// Top-Left
//Top-Right
// @ts-ignore
new StretchedSprite(new PIXI.Texture(texture)),
//Top
new PIXI.Sprite(texture),
// Left
new StretchedSprite(new PIXI.Texture(texture.baseTexture)),
// Bottom-Left
//Top-Left
// @ts-ignore
new StretchedSprite(new PIXI.Texture(texture)),
//Left
new PIXI.Sprite(texture),
// Bottom
new StretchedSprite(new PIXI.Texture(texture.baseTexture)),
//Bottom-Left
// @ts-ignore
new StretchedSprite(new PIXI.Texture(texture)),
//Bottom
new PIXI.Sprite(texture),
];
@@ -74,19 +77,11 @@ namespace gdjs {
ensureUpToDate() {
if (this._spritesContainer.visible && this._wasRendered) {
// PIXI uses PIXI.SCALE_MODES.LINEAR for the cached image:
// this._spritesContainer._cacheData.sprite._texture.baseTexture.scaleMode
// There seems to be no way to configure this so the optimization is disabled.
if (
this._centerSprite.texture.baseTexture.scaleMode !==
PIXI.SCALE_MODES.NEAREST
) {
// Cache the rendered sprites as a bitmap to speed up rendering when
// lots of panel sprites are on the scene.
// Sadly, because of this, we need a wrapper container to workaround
// a PixiJS issue with alpha (see updateOpacity).
this._spritesContainer.cacheAsBitmap = true;
}
// Cache the rendered sprites as a bitmap to speed up rendering when
// lots of panel sprites are on the scene.
// Sadly, because of this, we need a wrapper container to workaround
// a PixiJS issue with alpha (see updateOpacity).
this._spritesContainer.cacheAsBitmap = true;
}
this._wasRendered = true;
}
@@ -198,10 +193,14 @@ namespace gdjs {
instanceContainer: gdjs.RuntimeInstanceContainer
): void {
const obj = this._object;
// @ts-ignore
const texture = instanceContainer
.getGame()
.getImageManager()
.getPIXITexture(textureName).baseTexture;
.getPIXITexture(textureName) as PIXI.BaseTexture<
PIXI.Resource,
PIXI.IAutoDetectOptions
>;
this._textureWidth = texture.width;
this._textureHeight = texture.height;

View File

@@ -27,10 +27,7 @@ class PathfindingBehaviorJsExtension : public gd::PlatformExtension {
"Extensions/PathfindingBehavior/pathfindingruntimebehavior.js")
.AddIncludeFile(
"Extensions/PathfindingBehavior/"
"pathfindingobstacleruntimebehavior.js")
.AddIncludeFile(
"Extensions/PathfindingBehavior/"
"PathTools.js");
"pathfindingobstacleruntimebehavior.js");
{
std::map<gd::String, gd::InstructionMetadata>& autActions =

View File

@@ -1,163 +0,0 @@
namespace gdjs {
export namespace pathfinding {
/**
* Simplify a path according to an allowed gap.
*
* The simplified path vertices are the same instances as the one in
* the source. They must be cloned to make them truly independent from each
* other.
*
* @param sourceVertices The path to simplify.
* @param maxGap The maximum distance the edge of the contour may deviate
* from the source geometry.
* @param simplifiedVertices The simplified path.
* @param workingVertices It avoids allocations.
*/
export const simplifyPath = (
sourceVertices: FloatPoint[],
maxGap: float,
simplifiedVertices: FloatPoint[] = [],
workingVertices: FloatPoint[] = []
): FloatPoint[] => {
if (sourceVertices.length <= 2) {
simplifiedVertices.length = 0;
simplifiedVertices.push.apply(simplifiedVertices, sourceVertices);
return simplifiedVertices;
}
const maxGapSq = maxGap * maxGap;
// We start with only one rope part.
// Stretch a rope between the start and the end of the path.
let previousStepVertices: FloatPoint[] = workingVertices;
previousStepVertices.length = 0;
previousStepVertices.push(sourceVertices[0]);
previousStepVertices.push(sourceVertices[sourceVertices.length - 1]);
do {
simplifiedVertices.length = 0;
simplifiedVertices.push(previousStepVertices[0]);
// For each part of the rope...
let sourceIndex = 0;
for (
let previousStepVerticesIndex = 0;
previousStepVerticesIndex + 1 < previousStepVertices.length;
previousStepVerticesIndex++
) {
const startVertex = previousStepVertices[previousStepVerticesIndex];
const endVertex = previousStepVertices[previousStepVerticesIndex + 1];
const startX = startVertex[0];
const startY = startVertex[1];
const endX = endVertex[0];
const endY = endVertex[1];
// Search the furthest vertex from the rope part.
let maxDeviationSq = maxGapSq;
let maxDeviationVertex: FloatPoint | null = null;
// The first and last vertices of the rope part are not checked.
for (
sourceIndex++;
sourceVertices[sourceIndex] !== endVertex;
sourceIndex++
) {
const sourceVertex = sourceVertices[sourceIndex];
const deviationSq = gdjs.pathfinding.getPointSegmentDistanceSq(
sourceVertex[0],
sourceVertex[1],
startX,
startY,
endX,
endY
);
if (deviationSq > maxDeviationSq) {
maxDeviationSq = deviationSq;
maxDeviationVertex = sourceVertex;
}
}
// Add the furthest vertex to the rope.
// The current rope part is split in 2 for the next step.
if (maxDeviationVertex) {
simplifiedVertices.push(maxDeviationVertex);
}
simplifiedVertices.push(endVertex);
}
const swapVertices = previousStepVertices;
previousStepVertices = simplifiedVertices;
simplifiedVertices = swapVertices;
} while (
// Stop when no new vertex were added.
// It means that the maxGap constraint is fulfilled.
// Otherwise, iterate over the full path once more.
simplifiedVertices.length !== previousStepVertices.length
);
return simplifiedVertices;
};
/**
* Returns the distance squared from the point to the line segment.
*
* Behavior is undefined if the the closest distance is outside the
* line segment.
*
* @param px The X position of point (px, py).
* @param py The Y position of point (px, py)
* @param ax The X position of the line segment's vertex A.
* @param ay The Y position of the line segment's vertex A.
* @param bx The X position of the line segment's vertex B.
* @param by The Y position of the line segment's vertex B.
* @return The distance squared from the point (px, py) to line segment AB.
*/
export const getPointSegmentDistanceSq = (
px: float,
py: float,
ax: float,
ay: float,
bx: float,
by: float
): float => {
// This implementation is strongly inspired from CritterAI class "Geometry".
//
// Reference: http://local.wasp.uwa.edu.au/~pbourke/geometry/pointline/
//
// The goal of the algorithm is to find the point on line segment AB
// that is closest to P and then calculate the distance between P
// and that point.
const deltaABx = bx - ax;
const deltaABy = by - ay;
const deltaAPx = px - ax;
const deltaAPy = py - ay;
const segmentABLengthSq = deltaABx * deltaABx + deltaABy * deltaABy;
if (segmentABLengthSq === 0) {
// AB is not a line segment. So just return
// distanceSq from P to A
return deltaAPx * deltaAPx + deltaAPy * deltaAPy;
}
const u = (deltaAPx * deltaABx + deltaAPy * deltaABy) / segmentABLengthSq;
if (u < 0) {
// Closest point on line AB is outside outside segment AB and
// closer to A. So return distanceSq from P to A.
return deltaAPx * deltaAPx + deltaAPy * deltaAPy;
} else if (u > 1) {
// Closest point on line AB is outside segment AB and closer to B.
// So return distanceSq from P to B.
return (px - bx) * (px - bx) + (py - by) * (py - by);
}
// Closest point on lineAB is inside segment AB. So find the exact
// point on AB and calculate the distanceSq from it to P.
// The calculation in parenthesis is the location of the point on
// the line segment.
const deltaX = ax + u * deltaABx - px;
const deltaY = ay + u * deltaABy - py;
return deltaX * deltaX + deltaY * deltaY;
};
}
}

View File

@@ -24,92 +24,39 @@ void PathfindingBehavior::InitializeContent(
behaviorContent.SetAttribute("gridOffsetX", 0);
behaviorContent.SetAttribute("gridOffsetY", 0);
behaviorContent.SetAttribute("extraBorder", 0);
behaviorContent.SetAttribute("smoothingMaxCellGap", 1);
}
#if defined(GD_IDE_ONLY)
std::map<gd::String, gd::PropertyDescriptor> PathfindingBehavior::GetProperties(
const gd::SerializerElement &behaviorContent) const {
const gd::SerializerElement& behaviorContent) const {
std::map<gd::String, gd::PropertyDescriptor> properties;
properties["AllowDiagonals"]
.SetLabel(_("Allows diagonals"))
properties[_("Allows diagonals")]
.SetValue(behaviorContent.GetBoolAttribute("allowDiagonals") ? "true"
: "false")
.SetGroup(_("Path smoothing"))
.SetType("Boolean");
properties["Acceleration"]
.SetLabel(_("Acceleration"))
.SetType("Number")
.SetMeasurementUnit(gd::MeasurementUnit::GetPixelAcceleration()).SetValue(
properties[_("Acceleration")].SetValue(
gd::String::From(behaviorContent.GetDoubleAttribute("acceleration")));
properties["MaxSpeed"]
.SetLabel(_("Max. speed"))
.SetType("Number")
.SetMeasurementUnit(gd::MeasurementUnit::GetPixelSpeed()).SetValue(
properties[_("Max. speed")].SetValue(
gd::String::From(behaviorContent.GetDoubleAttribute("maxSpeed")));
properties["AngularMaxSpeed"]
.SetLabel(_("Rotation speed"))
.SetGroup(_("Rotation"))
.SetType("Number")
.SetMeasurementUnit(gd::MeasurementUnit::GetAngularSpeed())
.SetValue(gd::String::From(
behaviorContent.GetDoubleAttribute("angularMaxSpeed")));
properties["RotateObject"]
.SetLabel(_("Rotate object"))
.SetGroup(_("Rotation"))
properties[_("Rotate speed")].SetGroup(_("Rotation")).SetValue(
gd::String::From(behaviorContent.GetDoubleAttribute("angularMaxSpeed")));
properties[_("Rotate object")].SetGroup(_("Rotation"))
.SetValue(behaviorContent.GetBoolAttribute("rotateObject") ? "true"
: "false")
.SetType("Boolean");
properties["AngleOffset"]
.SetLabel(_("Angle offset"))
.SetGroup(_("Rotation"))
.SetType("Number")
.SetMeasurementUnit(gd::MeasurementUnit::GetDegreeAngle())
.SetValue(
gd::String::From(behaviorContent.GetDoubleAttribute("angleOffset")));
properties["CellWidth"]
.SetLabel(_("Virtual cell width"))
.SetGroup(_("Virtual Grid"))
.SetType("Number")
.SetMeasurementUnit(gd::MeasurementUnit::GetPixel())
.SetValue(
gd::String::From(behaviorContent.GetDoubleAttribute("cellWidth", 0)));
properties["CellHeight"]
.SetLabel(_("Virtual cell height"))
.SetGroup(_("Virtual Grid"))
.SetType("Number")
.SetMeasurementUnit(gd::MeasurementUnit::GetPixel())
.SetValue(gd::String::From(
behaviorContent.GetDoubleAttribute("cellHeight", 0)));
properties["GridOffsetX"]
.SetLabel(_("Virtual grid X offset"))
.SetGroup(_("Virtual Grid"))
.SetType("Number")
.SetMeasurementUnit(gd::MeasurementUnit::GetPixel())
.SetValue(gd::String::From(
behaviorContent.GetDoubleAttribute("gridOffsetX", 0)));
properties["GridOffsetY"]
.SetLabel(_("Virtual grid Y offset"))
.SetGroup(_("Virtual Grid"))
.SetType("Number")
.SetMeasurementUnit(gd::MeasurementUnit::GetPixel())
.SetValue(gd::String::From(
behaviorContent.GetDoubleAttribute("gridOffsetY", 0)));
properties["ExtraBorder"]
.SetDescription(_("Extra border size"))
.SetGroup(_("Collision"))
.SetType("Number")
.SetMeasurementUnit(gd::MeasurementUnit::GetPixel())
.SetValue(
gd::String::From(behaviorContent.GetDoubleAttribute("extraBorder")));
properties["SmoothingMaxCellGap"]
.SetLabel(_("Smoothing max cell gap"))
.SetValue(gd::String::From(
behaviorContent.GetDoubleAttribute("smoothingMaxCellGap")))
.SetGroup(_("Path smoothing"))
.SetDescription(_("It's recommended to leave a max gap of 1 cell. "
"Setting it to 0 disable the smoothing."));
properties[_("Angle offset")].SetGroup(_("Rotation")).SetValue(
gd::String::From(behaviorContent.GetDoubleAttribute("angleOffset")));
properties[_("Virtual cell width")].SetGroup(_("Virtual Grid")).SetValue(
gd::String::From(behaviorContent.GetDoubleAttribute("cellWidth", 0)));
properties[_("Virtual cell height")].SetGroup(_("Virtual Grid")).SetValue(
gd::String::From(behaviorContent.GetDoubleAttribute("cellHeight", 0)));
properties[_("Virtual grid X offset")].SetGroup(_("Virtual Grid")).SetValue(
gd::String::From(behaviorContent.GetDoubleAttribute("gridOffsetX", 0)));
properties[_("Virtual grid Y offset")].SetGroup(_("Virtual Grid")).SetValue(
gd::String::From(behaviorContent.GetDoubleAttribute("gridOffsetY", 0)));
properties[_("Extra border size")].SetGroup(_("Collision")).SetValue(
gd::String::From(behaviorContent.GetDoubleAttribute("extraBorder")));
return properties;
}
@@ -117,39 +64,37 @@ std::map<gd::String, gd::PropertyDescriptor> PathfindingBehavior::GetProperties(
bool PathfindingBehavior::UpdateProperty(gd::SerializerElement& behaviorContent,
const gd::String& name,
const gd::String& value) {
if (name == "AllowDiagonals") {
if (name == _("Allows diagonals")) {
behaviorContent.SetAttribute("allowDiagonals", (value != "0"));
return true;
}
if (name == "RotateObject") {
if (name == _("Rotate object")) {
behaviorContent.SetAttribute("rotateObject", (value != "0"));
return true;
}
if (name == "ExtraBorder") {
if (name == _("Extra border size")) {
behaviorContent.SetAttribute("extraBorder", value.To<float>());
return true;
}
if (value.To<float>() < 0) return false;
if (name == "Acceleration")
if (name == _("Acceleration"))
behaviorContent.SetAttribute("acceleration", value.To<float>());
else if (name == "MaxSpeed")
else if (name == _("Max. speed"))
behaviorContent.SetAttribute("maxSpeed", value.To<float>());
else if (name == "AngularMaxSpeed")
else if (name == _("Rotate speed"))
behaviorContent.SetAttribute("angularMaxSpeed", value.To<float>());
else if (name == "AngleOffset")
else if (name == _("Angle offset"))
behaviorContent.SetAttribute("angleOffset", value.To<float>());
else if (name == "CellWidth")
else if (name == _("Virtual cell width"))
behaviorContent.SetAttribute("cellWidth", value.To<float>());
else if (name == "CellHeight")
else if (name == _("Virtual cell height"))
behaviorContent.SetAttribute("cellHeight", value.To<float>());
else if (name == "GridOffsetX")
else if (name == _("Virtual grid X offset"))
behaviorContent.SetAttribute("gridOffsetX", value.To<float>());
else if (name == "GridOffsetY")
else if (name == _("Virtual grid Y offset"))
behaviorContent.SetAttribute("gridOffsetY", value.To<float>());
else if (name == "SmoothingMaxCellGap")
behaviorContent.SetAttribute("smoothingMaxCellGap", value.To<float>());
else
return false;

View File

@@ -20,14 +20,12 @@ std::map<gd::String, gd::PropertyDescriptor>
PathfindingObstacleBehavior::GetProperties(
const gd::SerializerElement& behaviorContent) const {
std::map<gd::String, gd::PropertyDescriptor> properties;
properties["Impassable"]
.SetLabel(_("Impassable obstacle"))
properties[_("Impassable obstacle")]
.SetValue(behaviorContent.GetBoolAttribute("impassable") ? "true"
: "false")
.SetType("Boolean");
properties["Cost"]
.SetLabel(_("Cost (if not impassable)"))
.SetValue(gd::String::From(behaviorContent.GetDoubleAttribute("cost")));
properties[_("Cost (if not impassable)")].SetValue(
gd::String::From(behaviorContent.GetDoubleAttribute("cost")));
return properties;
}
@@ -36,14 +34,14 @@ bool PathfindingObstacleBehavior::UpdateProperty(
gd::SerializerElement& behaviorContent,
const gd::String& name,
const gd::String& value) {
if (name == "Impassable") {
if (name == _("Impassable obstacle")) {
behaviorContent.SetAttribute("impassable", (value != "0"));
return true;
}
if (value.To<float>() < 0) return false;
if (name == "Cost")
if (name == _("Cost (if not impassable)"))
behaviorContent.SetAttribute("cost", value.To<float>());
else
return false;

View File

@@ -10,10 +10,6 @@ namespace gdjs {
*/
export class PathfindingRuntimeBehavior extends gdjs.RuntimeBehavior {
_path: Array<FloatPoint> = [];
/** Used by the path simplification algorithm */
static _smoothingResultVertices: Array<FloatPoint> = [];
/** Used by the path simplification algorithm */
static _smoothingWorkingVertices: Array<FloatPoint> = [];
//Behavior configuration:
_allowDiagonals: boolean;
@@ -27,7 +23,6 @@ namespace gdjs {
_gridOffsetX: float;
_gridOffsetY: float;
_extraBorder: float;
_smoothingMaxCellGap: float;
//Attributes used for traveling on the path:
_pathFound: boolean = false;
@@ -65,7 +60,6 @@ namespace gdjs {
this._gridOffsetX = behaviorData.gridOffsetX || 0;
this._gridOffsetY = behaviorData.gridOffsetY || 0;
this._extraBorder = behaviorData.extraBorder;
this._smoothingMaxCellGap = behaviorData.smoothingMaxCellGap || 0;
this._manager = gdjs.PathfindingObstaclesManager.getManager(
instanceContainer
);
@@ -108,12 +102,6 @@ namespace gdjs {
if (oldBehaviorData.extraBorder !== newBehaviorData.extraBorder) {
this.setExtraBorder(newBehaviorData.extraBorder);
}
if (
oldBehaviorData.smoothingMaxCellGap !==
newBehaviorData.smoothingMaxCellGap
) {
this._smoothingMaxCellGap = newBehaviorData.smoothingMaxCellGap;
}
return true;
}
@@ -389,20 +377,6 @@ namespace gdjs {
this._path.reverse();
this._path[0][0] = owner.getX();
this._path[0][1] = owner.getY();
if (this._allowDiagonals && this._smoothingMaxCellGap > 0) {
gdjs.pathfinding.simplifyPath(
this._path,
this._smoothingMaxCellGap *
Math.min(this._cellWidth, this._cellHeight),
gdjs.PathfindingRuntimeBehavior._smoothingResultVertices,
gdjs.PathfindingRuntimeBehavior._smoothingWorkingVertices
);
let swapArray = this._path;
this._path = gdjs.PathfindingRuntimeBehavior._smoothingResultVertices;
gdjs.PathfindingRuntimeBehavior._smoothingResultVertices = swapArray;
}
this._enterSegment(0);
this._pathFound = true;
return;

View File

@@ -1,239 +0,0 @@
// @ts-check
describe('gdjs.PathfindingRuntimeBehavior', function () {
const epsilon = 1 / (2 << 16);
// tests cases where every collisionMethod has the same behavior.
let doCommonPathFindingTests = (collisionMethod) => {
const pathFindingName = 'auto1';
const createScene = (framePerSecond = 60) => {
const runtimeGame = new gdjs.RuntimeGame({
variables: [],
// @ts-ignore - missing properties.
properties: { windowWidth: 800, windowHeight: 600 },
resources: { resources: [] },
});
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
runtimeScene.loadFromScene({
layers: [
{
name: '',
visibility: true,
effects: [],
cameras: [],
ambientLightColorR: 0,
ambientLightColorG: 0,
ambientLightColorB: 0,
isLightingLayer: false,
followBaseLayerCamera: true,
},
],
variables: [],
r: 0,
v: 0,
b: 0,
mangledName: 'Scene1',
name: 'Scene1',
stopSoundsOnStartup: false,
title: '',
behaviorsSharedData: [],
objects: [],
instances: [],
});
setFramePerSecond(runtimeScene, framePerSecond);
return runtimeScene;
};
const setFramePerSecond = (runtimeScene, framePerSecond) => {
runtimeScene._timeManager.getElapsedTime = function () {
return 1000 / framePerSecond;
};
};
const addPlayer = (runtimeScene, allowDiagonals) => {
const player = new gdjs.RuntimeObject(runtimeScene, {
name: 'player',
type: '',
behaviors: [
{
type: 'PathfindingBehavior::PathfindingBehavior',
name: 'auto1',
// @ts-ignore - properties are not typed
allowDiagonals: allowDiagonals,
acceleration: 400,
maxSpeed: 200,
angularMaxSpeed: 180,
rotateObject: false,
angleOffset: 0,
cellWidth: 20,
cellHeight: 20,
extraBorder: 0,
collisionMethod: true,
},
],
effects: [],
});
player.getWidth = function () {
return 90;
};
player.getHeight = function () {
return 90;
};
runtimeScene.addObject(player);
return player;
};
const addObstacle = (runtimeScene) => {
const obstacle = new gdjs.RuntimeObject(runtimeScene, {
name: 'obstacle',
type: '',
behaviors: [
{
type: 'PathfindingBehavior::PathfindingObstacleBehavior',
// @ts-ignore - properties are not typed
impassable: true,
cost: 2,
},
],
effects: [],
});
obstacle.getWidth = function () {
return 100;
};
obstacle.getHeight = function () {
return 100;
};
runtimeScene.addObject(obstacle);
return obstacle;
};
const getPathLength = (player) => {
/** @type gdjs.PathfindingRuntimeBehavior */
const behavior = player.getBehavior(pathFindingName);
if (behavior.getNodeCount() < 2) {
return 0;
}
let pathLength = 0;
let previousNodeX = behavior.getNodeX(0);
let previousNodeY = behavior.getNodeY(0);
for (let index = 1; index < behavior.getNodeCount(); index++) {
const nodeX = behavior.getNodeX(index);
const nodeY = behavior.getNodeY(index);
pathLength += Math.hypot(nodeX - previousNodeX, nodeY - previousNodeY);
previousNodeX = nodeX;
previousNodeY = nodeY;
}
return pathLength;
};
describe(`(allowDiagonals: true)`, function () {
let runtimeScene;
let player;
beforeEach(function () {
runtimeScene = createScene();
const allowDiagonals = true;
player = addPlayer(runtimeScene, allowDiagonals);
});
[20, 30, 60, 120].forEach((framePerSecond) => {
describe(`(${framePerSecond} fps)`, function () {
it('can move on the path at the right speed', function () {
setFramePerSecond(runtimeScene, framePerSecond);
const obstacle = addObstacle(runtimeScene);
obstacle.setPosition(600, 300);
// To ensure obstacles are registered.
runtimeScene.renderAndStep(1000 / framePerSecond);
player.setPosition(480, 300);
player.getBehavior(pathFindingName).moveTo(runtimeScene, 720, 300);
expect(player.getBehavior(pathFindingName).pathFound()).to.be(true);
expect(getPathLength(player)).to.be.above(720 - 480 + 50);
// Move on the path and stop before the last 1/10 of second.
for (let i = 0; i < (framePerSecond * 17) / 10; i++) {
runtimeScene.renderAndStep(1000 / framePerSecond);
expect(
player.getBehavior(pathFindingName).destinationReached()
).to.be(false);
}
// The position is the same no matter the frame rate.
expect(player.getX()).to.be(720);
expect(player.getY()).to.be.within(
288.5786437626905 - epsilon,
288.5786437626905 + epsilon
);
// Let 1/10 of second pass,
// because the calculus interval is not the same for each case.
for (let i = 0; i < framePerSecond / 10; i++) {
runtimeScene.renderAndStep(1000 / framePerSecond);
}
// The destination is reached for every frame rate within 1/10 of second.
expect(player.getX()).to.be(720);
expect(player.getY()).to.be(300);
expect(
player.getBehavior(pathFindingName).destinationReached()
).to.be(true);
});
});
});
});
describe(`(allowDiagonals: false)`, function () {
let runtimeScene;
let player;
beforeEach(function () {
runtimeScene = createScene();
const allowDiagonals = false;
player = addPlayer(runtimeScene, allowDiagonals);
});
[20, 30, 60, 120].forEach((framePerSecond) => {
describe(`(${framePerSecond} fps)`, function () {
it('can move on the path at the right speed', function () {
setFramePerSecond(runtimeScene, framePerSecond);
const obstacle = addObstacle(runtimeScene);
obstacle.setPosition(600, 300);
// To ensure obstacles are registered.
runtimeScene.renderAndStep(1000 / framePerSecond);
player.setPosition(480, 300);
player.getBehavior(pathFindingName).moveTo(runtimeScene, 720, 300);
expect(player.getBehavior(pathFindingName).pathFound()).to.be(true);
expect(getPathLength(player)).to.be.above(720 - 480 + 100);
// Move on the path and stop before the last 1/10 of second.
for (let i = 0; i < (framePerSecond * 20) / 10; i++) {
runtimeScene.renderAndStep(1000 / framePerSecond);
expect(
player.getBehavior(pathFindingName).destinationReached()
).to.be(false);
}
expect(player.getX()).to.be(710);
expect(player.getY()).to.be.within(300 - epsilon, 300 + epsilon);
// Let 1/10 of second pass,
// because the calculus interval is not the same for each case.
for (let i = 0; i < (framePerSecond * 1) / 10; i++) {
runtimeScene.renderAndStep(1000 / framePerSecond);
}
// The destination is reached for every frame rate within 1/10 of second.
expect(player.getX()).to.be(720);
expect(player.getY()).to.be(300);
expect(
player.getBehavior(pathFindingName).destinationReached()
).to.be(true);
});
});
});
});
};
['Legacy'].forEach((collisionMethod) => {
describe(`(collisionMethod: ${collisionMethod}, `, function () {
doCommonPathFindingTests(collisionMethod);
});
});
});

View File

@@ -1,115 +0,0 @@
// @ts-check
describe('gdjs.pathfinding', function () {
it('can give back an empty path', function () {
expect(gdjs.pathfinding.simplifyPath([], 1)).to.eql([]);
});
it('can give back a path with only 1 vertex', function () {
expect(gdjs.pathfinding.simplifyPath([[2, 4]], 1)).to.eql([[2, 4]]);
});
it('can give back a path with only 2 vertex', function () {
expect(
gdjs.pathfinding.simplifyPath(
[
[2, 4],
[8, 1],
],
1
)
).to.eql([
[2, 4],
[8, 1],
]);
});
it('can give back a path with 3 vertex', function () {
expect(
gdjs.pathfinding.simplifyPath(
[
[2, 4],
[8, 1],
[16, 32],
],
1
)
).to.eql([
[2, 4],
[8, 1],
[16, 32],
]);
});
it('can simplify a line of vertices', function () {
expect(
gdjs.pathfinding.simplifyPath(
[
[2, 4],
[2, 5],
[2, 9],
[2, 9.5],
],
1
)
).to.eql([
[2, 4],
[2, 9.5],
]);
});
it('can simplify a line of vertices with some tangential noise', function () {
expect(
gdjs.pathfinding.simplifyPath(
[
[2, 4],
[2.5, 5],
[1.1, 9],
[2, 9.5],
],
1
)
).to.eql([
[2, 4],
[2, 9.5],
]);
});
it('can simplify an aliased oblique line', function () {
expect(
gdjs.pathfinding.simplifyPath(
[
[2, 4],
[3, 4],
[4, 3],
[5, 3],
[6, 3],
[7, 2],
[8, 2],
],
1
)
).to.eql([
[2, 4],
[8, 2],
]);
});
it('can preserve a bend', function () {
expect(
gdjs.pathfinding.simplifyPath(
[
[2, 4],
[2.5, 5],
[2, 9],
[3, 9.9],
[5, 9],
],
1
)
).to.eql([
[2, 4],
[2, 9],
[5, 9],
]);
});
});

View File

@@ -1,11 +1,8 @@
// @ts-check
describe('gdjs.PathfindingRuntimeBehavior', function () {
const epsilon = 1 / (2 << 16);
// tests cases where every collisionMethod has the same behavior.
let doCommonPathFindingTests = (
collisionMethod,
allowDiagonals,
smoothingMaxCellGap
) => {
let doCommonPathFindingTests = (collisionMethod, allowDiagonals) => {
const pathFindingName = 'auto1';
const createScene = (framePerSecond = 60) => {
@@ -70,7 +67,6 @@ describe('gdjs.PathfindingRuntimeBehavior', function () {
cellWidth: 20,
cellHeight: 20,
extraBorder: 0,
smoothingMaxCellGap: smoothingMaxCellGap,
collisionMethod: collisionMethod,
},
],
@@ -110,25 +106,6 @@ describe('gdjs.PathfindingRuntimeBehavior', function () {
return obstacle;
};
const getPathLength = (player) => {
/** @type gdjs.PathfindingRuntimeBehavior */
const behavior = player.getBehavior(pathFindingName);
if (behavior.getNodeCount() < 2) {
return 0;
}
let pathLength = 0;
let previousNodeX = behavior.getNodeX(0);
let previousNodeY = behavior.getNodeY(0);
for (let index = 1; index < behavior.getNodeCount(); index++) {
const nodeX = behavior.getNodeX(index);
const nodeY = behavior.getNodeY(index);
pathLength += Math.hypot(nodeX - previousNodeX, nodeY - previousNodeY);
previousNodeX = nodeX;
previousNodeY = nodeY;
}
return pathLength;
};
let runtimeScene;
let player;
beforeEach(function () {
@@ -140,7 +117,7 @@ describe('gdjs.PathfindingRuntimeBehavior', function () {
player.setPosition(480, 300);
player.getBehavior(pathFindingName).moveTo(runtimeScene, 720, 300);
expect(player.getBehavior(pathFindingName).pathFound()).to.be(true);
expect(getPathLength(player)).to.be(720 - 480);
expect(player.getBehavior(pathFindingName).getNodeCount()).to.be(13);
});
it('can find a path without any obstacle in the way', function () {
@@ -153,7 +130,7 @@ describe('gdjs.PathfindingRuntimeBehavior', function () {
player.setPosition(480, 300);
player.getBehavior(pathFindingName).moveTo(runtimeScene, 720, 300);
expect(player.getBehavior(pathFindingName).pathFound()).to.be(true);
expect(getPathLength(player)).to.be(720 - 480);
expect(player.getBehavior(pathFindingName).getNodeCount()).to.be(13);
});
it("mustn't find a path to the obstacle inside", function () {
@@ -178,9 +155,101 @@ describe('gdjs.PathfindingRuntimeBehavior', function () {
player.setPosition(480, 300);
player.getBehavior(pathFindingName).moveTo(runtimeScene, 720, 300);
expect(player.getBehavior(pathFindingName).pathFound()).to.be(true);
expect(getPathLength(player)).to.be.above(720 - 480 + 50);
expect(player.getBehavior(pathFindingName).getNodeCount()).to.be.above(
13
);
});
if (allowDiagonals) {
[20, 30, 60, 120].forEach((framePerSecond) => {
describe(`(${framePerSecond} fps)`, function () {
it('can move on the path at the right speed', function () {
setFramePerSecond(runtimeScene, framePerSecond);
const obstacle = addObstacle(runtimeScene);
obstacle.setPosition(600, 300);
// To ensure obstacles are registered.
runtimeScene.renderAndStep(1000 / framePerSecond);
player.setPosition(480, 300);
player.getBehavior(pathFindingName).moveTo(runtimeScene, 720, 300);
expect(player.getBehavior(pathFindingName).pathFound()).to.be(true);
expect(
player.getBehavior(pathFindingName).getNodeCount()
).to.be.above(13);
// Move on the path and stop before the last 1/10 of second.
for (let i = 0; i < (framePerSecond * 17) / 10; i++) {
runtimeScene.renderAndStep(1000 / framePerSecond);
expect(
player.getBehavior(pathFindingName).destinationReached()
).to.be(false);
}
// The position is the same no matter the frame rate.
expect(player.getX()).to.be(720);
expect(player.getY()).to.be.within(
288.5786437626905 - epsilon,
288.5786437626905 + epsilon
);
// Let 1/10 of second pass,
// because the calculus interval is not the same for each case.
for (let i = 0; i < framePerSecond / 10; i++) {
runtimeScene.renderAndStep(1000 / framePerSecond);
}
// The destination is reached for every frame rate within 1/10 of second.
expect(player.getX()).to.be(720);
expect(player.getY()).to.be(300);
expect(
player.getBehavior(pathFindingName).destinationReached()
).to.be(true);
});
});
});
} else {
[20, 30, 60, 120].forEach((framePerSecond) => {
describe(`(${framePerSecond} fps)`, function () {
it('can move on the path at the right speed', function () {
setFramePerSecond(runtimeScene, framePerSecond);
const obstacle = addObstacle(runtimeScene);
obstacle.setPosition(600, 300);
// To ensure obstacles are registered.
runtimeScene.renderAndStep(1000 / framePerSecond);
player.setPosition(480, 300);
player.getBehavior(pathFindingName).moveTo(runtimeScene, 720, 300);
expect(player.getBehavior(pathFindingName).pathFound()).to.be(true);
expect(
player.getBehavior(pathFindingName).getNodeCount()
).to.be.above(13);
// Move on the path and stop before the last 1/10 of second.
for (let i = 0; i < (framePerSecond * 20) / 10; i++) {
runtimeScene.renderAndStep(1000 / framePerSecond);
expect(
player.getBehavior(pathFindingName).destinationReached()
).to.be(false);
}
expect(player.getX()).to.be(710);
expect(player.getY()).to.be.within(300 - epsilon, 300 + epsilon);
// Let 1/10 of second pass,
// because the calculus interval is not the same for each case.
for (let i = 0; i < (framePerSecond * 1) / 10; i++) {
runtimeScene.renderAndStep(1000 / framePerSecond);
}
// The destination is reached for every frame rate within 1/10 of second.
expect(player.getX()).to.be(720);
expect(player.getY()).to.be(300);
expect(
player.getBehavior(pathFindingName).destinationReached()
).to.be(true);
});
});
});
}
it('can find a path between 2 obstacles', function () {
const obstacleTop = addObstacle(runtimeScene);
const obstacleBottom = addObstacle(runtimeScene);
@@ -193,7 +262,7 @@ describe('gdjs.PathfindingRuntimeBehavior', function () {
player.setPosition(480, 300);
player.getBehavior(pathFindingName).moveTo(runtimeScene, 720, 300);
expect(player.getBehavior(pathFindingName).pathFound()).to.be(true);
expect(getPathLength(player)).to.be(720 - 480);
expect(player.getBehavior(pathFindingName).getNodeCount()).to.be(13);
});
it("mustn't find a path to a closed room", function () {
@@ -219,15 +288,7 @@ describe('gdjs.PathfindingRuntimeBehavior', function () {
describe(`(collisionMethod: ${collisionMethod}, `, function () {
[false, true].forEach((allowDiagonals) => {
describe(`(allowDiagonals: ${allowDiagonals})`, function () {
[0, 1].forEach((smoothingMaxCellGap) => {
describe(`(smoothingMaxCellGap: ${smoothingMaxCellGap})`, function () {
doCommonPathFindingTests(
collisionMethod,
allowDiagonals,
smoothingMaxCellGap
);
});
});
doCommonPathFindingTests(collisionMethod, allowDiagonals);
});
});
});

View File

@@ -1,8 +1,6 @@
// @ts-check
describe('gdjs.PathfindingRuntimeBehavior', function () {
// limit tests cases on the legacy collision methods.
// Note that the legacy collision mode is still the only mode that exists
// because the new one were never merged.
let doLegacyPathFindingTests = (
cellSize,
objectCenteredOnCells,

View File

@@ -200,7 +200,6 @@ module.exports = {
.toString(10)
)
.setType('Number')
.setMeasurementUnit(gd.MeasurementUnit.getPixel())
.setLabel('Shape Dimension A');
behaviorProperties
.getOrCreate('shapeDimensionB')
@@ -211,7 +210,6 @@ module.exports = {
.toString(10)
)
.setType('Number')
.setMeasurementUnit(gd.MeasurementUnit.getPixel())
.setLabel('Shape Dimension B');
behaviorProperties
.getOrCreate('shapeOffsetX')
@@ -219,7 +217,6 @@ module.exports = {
behaviorContent.getChild('shapeOffsetX').getDoubleValue().toString(10)
)
.setType('Number')
.setMeasurementUnit(gd.MeasurementUnit.getPixel())
.setLabel('Shape Offset X');
behaviorProperties
.getOrCreate('shapeOffsetY')
@@ -227,7 +224,6 @@ module.exports = {
behaviorContent.getChild('shapeOffsetY').getDoubleValue().toString(10)
)
.setType('Number')
.setMeasurementUnit(gd.MeasurementUnit.getPixel())
.setLabel('Shape Offset Y');
behaviorProperties
.getOrCreate('polygonOrigin')
@@ -377,15 +373,13 @@ module.exports = {
.setValue(
sharedContent.getChild('gravityX').getDoubleValue().toString(10)
)
.setType('Number')
.setMeasurementUnit(gd.MeasurementUnit.getNewton());
.setType('Number');
sharedProperties
.getOrCreate('gravityY')
.setValue(
sharedContent.getChild('gravityY').getDoubleValue().toString(10)
)
.setType('Number')
.setMeasurementUnit(gd.MeasurementUnit.getNewton());
.setType('Number');
sharedProperties
.getOrCreate('scaleX')
.setValue(

View File

@@ -825,12 +825,8 @@ namespace gdjs {
}
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 (
!this._sharedData.stepped &&
!instanceContainer.getScene().getTimeManager().isFirstFrame()
) {
// Step the world if not done this frame yet
if (!this._sharedData.stepped) {
// Reset started and ended contacts array for all physics instances.
this._sharedData.resetStartedAndEndedCollisions();
this._sharedData.updateBodiesFromObjects();

View File

@@ -64,7 +64,7 @@ describe('Physics2RuntimeBehavior', () => {
resources: { resources: [] },
properties: { windowWidth: 1000, windowHeight: 1000 },
});
const runtimeScene = new gdjs.TestRuntimeScene(runtimeGame);
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
runtimeScene.loadFromScene({
layers: [
{
@@ -236,39 +236,6 @@ describe('Physics2RuntimeBehavior', () => {
expect(behavior.getRestitution()).to.be(0.5);
});
it('should not resolve collision before the 1st frame events', () => {
const fps = 60;
runtimeScene._timeManager.getElapsedTime = function () {
return (1 / fps) * 1000;
};
// Create objects in contact
const {
object: object1,
behavior: object1Behavior,
} = createObjectWithPhysicsBehavior(runtimeScene, {
bodyType: 'Dynamic',
});
object1.setPosition(10, 0);
const {
object: object2,
behavior: object2Behavior,
} = createObjectWithPhysicsBehavior(runtimeScene, {
bodyType: 'Static',
restitution: 0,
});
object2.setPosition(20, 0);
// First frame
runtimeScene.renderAndStep(1000 / fps);
// The object has not moved.
expect(object1.getX()).to.be(10);
expect(object1.getY()).to.be(0);
expect(object2.getX()).to.be(20);
expect(object2.getY()).to.be(0);
});
it('should clear contacts when deactivating the physics2 behavior', () => {
const fps = 60;
runtimeGame.setGameResolutionSize(1000, 1000);
@@ -276,9 +243,6 @@ describe('Physics2RuntimeBehavior', () => {
return (1 / fps) * 1000;
};
// The behavior doesn't call Box2D step at the 1st frame.
runtimeScene.renderAndStep(1000 / fps);
// Create objects not in contact
const {
object: object1,
@@ -307,10 +271,11 @@ describe('Physics2RuntimeBehavior', () => {
).to.be(true);
// Put objects in contact and assert collision started during the frame
runtimeScene.renderAndStepWithEventsFunction(1000 / fps, () => {
runtimeScene.setEventsFunction(() => {
object1.setPosition(10, 0);
object2.setPosition(20, 0);
});
runtimeScene.renderAndStep(1000 / fps);
// After post event, collision should be present
assertCollision(object1, object2, {
@@ -319,6 +284,9 @@ describe('Physics2RuntimeBehavior', () => {
stopped: false,
});
// Reset scene events
runtimeScene.setEventsFunction(() => {});
// Deactivate physics behavior and test that collisions are cleared.
object1.activateBehavior('Physics2', false);
assertCollision(object1, object2, {
@@ -456,28 +424,33 @@ describe('Physics2RuntimeBehavior', () => {
movingObjectBehavior.setLinearVelocityY(40000);
let hasBounced = false;
for (let stepIndex = 0; stepIndex < 10 && !hasBounced; stepIndex++) {
runtimeScene.renderAndStepWithEventsFunction(1000 / fps, () => {
if (movingObjectBehavior.getLinearVelocityY() > 0) {
// If the moving object has a positive velocity, it hasn't bounced
// on the static object
assertCollision(movingObject, staticObject, {
started: false,
collision: false,
stopped: false,
});
} else {
hasBounced = true;
expect(movingObject.getY() < staticObject.getY()).to.be(true);
assertCollision(movingObject, staticObject, {
started: true,
collision: true,
stopped: true,
});
}
});
let stepIndex = 0;
runtimeScene.setEventsFunction(() => {
if (movingObjectBehavior.getLinearVelocityY() > 0) {
// If the moving object has a positive velocity, it hasn't bounced
// on the static object
assertCollision(movingObject, staticObject, {
started: false,
collision: false,
stopped: false,
});
} else {
hasBounced = true;
expect(movingObject.getY() < staticObject.getY()).to.be(true);
assertCollision(movingObject, staticObject, {
started: true,
collision: true,
stopped: true,
});
}
});
while (stepIndex < 10 && !hasBounced) {
runtimeScene.renderAndStep(1000 / fps);
stepIndex++;
}
runtimeScene.setEventsFunction(() => {});
runtimeScene.renderAndStep(1000 / fps);
assertCollision(movingObject, staticObject, {
started: false,
@@ -513,31 +486,32 @@ describe('Physics2RuntimeBehavior', () => {
movingObjectBehavior.setLinearVelocityY(40000);
let hasBegunBouncing = false;
for (
let stepIndex = 0;
stepIndex < 10 && !hasBegunBouncing;
stepIndex++
) {
runtimeScene.renderAndStepWithEventsFunction(1000 / fps, () => {
if (movingObjectBehavior.getLinearVelocityY() > 0) {
// If the moving object has a positive velocity, it hasn't bounced
// on the static object
assertCollision(movingObject, staticObject, {
started: false,
collision: false,
stopped: false,
});
} else {
hasBegunBouncing = true;
// At first frame, collision should have only started
expect(movingObject.getY() < staticObject.getY()).to.be(true);
assertCollision(movingObject, staticObject, {
started: true,
collision: true,
stopped: false,
});
}
});
let stepIndex = 0;
runtimeScene.setEventsFunction(() => {
if (movingObjectBehavior.getLinearVelocityY() > 0) {
// If the moving object has a positive velocity, it hasn't bounced
// on the static object
assertCollision(movingObject, staticObject, {
started: false,
collision: false,
stopped: false,
});
} else {
hasBegunBouncing = true;
// At first frame, collision should have only started
expect(movingObject.getY() < staticObject.getY()).to.be(true);
assertCollision(movingObject, staticObject, {
started: true,
collision: true,
stopped: false,
});
}
});
while (stepIndex < 10 && !hasBegunBouncing) {
runtimeScene.renderAndStep(1000 / fps);
stepIndex++;
}
if (!hasBegunBouncing) {
@@ -548,7 +522,8 @@ describe('Physics2RuntimeBehavior', () => {
// At next frame, end of collision should be detected
let hasFinishedBouncing = false;
runtimeScene.renderAndStepWithEventsFunction(1000 / fps, () => {
runtimeScene.setEventsFunction(() => {
hasFinishedBouncing = true;
assertCollision(movingObject, staticObject, {
started: false,
@@ -557,6 +532,8 @@ describe('Physics2RuntimeBehavior', () => {
});
});
runtimeScene.renderAndStep(1000 / fps);
if (!hasFinishedBouncing) {
throw new Error('End of contact was not detected, nothing was tested.');
}
@@ -569,9 +546,6 @@ describe('Physics2RuntimeBehavior', () => {
return (1 / fps) * 1000;
};
// The behavior doesn't call Box2D step at the 1st frame.
runtimeScene.renderAndStep(1000 / fps);
const {
behavior: movingObjectBehavior,
object: movingObject,
@@ -593,7 +567,7 @@ describe('Physics2RuntimeBehavior', () => {
stopped: false,
});
runtimeScene.renderAndStepWithEventsFunction(1000 / fps, () => {
runtimeScene.setEventsFunction(() => {
// Manually call onContactEnd and onContactBegin methods to simulate
// a loss of contact followed by a contact beginning during the preEvent.
movingObject
@@ -610,6 +584,7 @@ describe('Physics2RuntimeBehavior', () => {
stopped: false,
});
});
runtimeScene.renderAndStep(1000 / fps);
});
it('should not detect a new contact if the contact ended and jittered.', () => {
@@ -619,9 +594,6 @@ describe('Physics2RuntimeBehavior', () => {
return (1 / fps) * 1000;
};
// The behavior doesn't call Box2D step at the 1st frame.
runtimeScene.renderAndStep(1000 / fps);
const {
behavior: movingObjectBehavior,
object: movingObject,
@@ -643,7 +615,7 @@ describe('Physics2RuntimeBehavior', () => {
stopped: false,
});
runtimeScene.renderAndStepWithEventsFunction(1000 / fps, () => {
runtimeScene.setEventsFunction(() => {
// Manually call onContactEnd and onContactBegin methods to simulate
// a loss of contact followed by a contact beginning and another loss
// of contact during the event.
@@ -665,6 +637,7 @@ describe('Physics2RuntimeBehavior', () => {
stopped: true,
});
});
runtimeScene.renderAndStep(1000 / fps);
});
it('it should end collision on resize (body updated in pre-event).', () => {
@@ -674,9 +647,6 @@ describe('Physics2RuntimeBehavior', () => {
return (1 / fps) * 1000;
};
// The behavior doesn't call Box2D step at the 1st frame.
runtimeScene.renderAndStep(1000 / fps);
const {
behavior: movingObjectBehavior,
object: movingObject,
@@ -698,15 +668,18 @@ describe('Physics2RuntimeBehavior', () => {
});
// Resize.
runtimeScene.renderAndStepWithEventsFunction(1000 / fps, () => {
runtimeScene.setEventsFunction(() => {
movingObject.setCustomWidthAndHeight(5, 5);
});
runtimeScene.renderAndStep(1000 / fps);
assertCollision(movingObject, staticObject, {
started: false,
collision: true,
stopped: false,
});
runtimeScene.setEventsFunction(() => {});
runtimeScene.renderAndStep(1000 / fps);
assertCollision(movingObject, staticObject, {
started: false,
@@ -722,9 +695,6 @@ describe('Physics2RuntimeBehavior', () => {
return (1 / fps) * 1000;
};
// The behavior doesn't call Box2D step at the 1st frame.
runtimeScene.renderAndStep(1000 / fps);
const {
behavior: movingObjectBehavior,
object: movingObject,
@@ -746,11 +716,13 @@ describe('Physics2RuntimeBehavior', () => {
stopped: false,
});
// Destroy (handled by postEvent).
runtimeScene.renderAndStepWithEventsFunction(1000 / fps, () => {
// Destroy (postEvent operation).
runtimeScene.setEventsFunction(() => {
movingObject.deleteFromScene(runtimeScene);
});
runtimeScene.renderAndStep(1000 / fps);
// Collision should be reset on destroyed object and
// added to contactsEndedThisFrame array of the other object.
assertCollision(movingObject, staticObject, {

View File

@@ -36,24 +36,18 @@ std::map<gd::String, gd::PropertyDescriptor> PlatformBehavior::GetProperties(
else if (platformType == "Jumpthru")
platformTypeStr = _("Jumpthru platform");
properties["PlatformType"]
.SetLabel(_("Type"))
properties[_("Type")]
.SetValue(platformTypeStr)
.SetType("Choice")
.AddExtraInfo(_("Platform"))
.AddExtraInfo(_("Jumpthru platform"))
.AddExtraInfo(_("Ladder"));
properties["CanBeGrabbed"]
.SetLabel(_("Ledges can be grabbed"))
.SetGroup(_("Ledge"))
properties[_("Ledges can be grabbed")].SetGroup(_("Ledge"))
.SetValue(behaviorContent.GetBoolAttribute("canBeGrabbed", true)
? "true"
: "false")
.SetType("Boolean");
properties["YGrabOffset"]
.SetLabel(_("Grab offset on Y axis"))
.SetGroup(_("Ledge"))
.SetValue(
properties[_("Grab offset on Y axis")].SetGroup(_("Ledge")).SetValue(
gd::String::From(behaviorContent.GetDoubleAttribute("yGrabOffset")));
return properties;
@@ -62,16 +56,16 @@ std::map<gd::String, gd::PropertyDescriptor> PlatformBehavior::GetProperties(
bool PlatformBehavior::UpdateProperty(gd::SerializerElement& behaviorContent,
const gd::String& name,
const gd::String& value) {
if (name == "CanBeGrabbed")
if (name == _("Ledges can be grabbed"))
behaviorContent.SetAttribute("canBeGrabbed", (value == "1"));
else if (name == "PlatformType") {
else if (name == _("Type")) {
if (value == _("Jumpthru platform"))
behaviorContent.SetAttribute("platformType", "Jumpthru");
else if (value == _("Ladder"))
behaviorContent.SetAttribute("platformType", "Ladder");
else
behaviorContent.SetAttribute("platformType", "NormalPlatform");
} else if (name == "YGrabOffset")
} else if (name == _("Grab offset on Y axis"))
behaviorContent.SetAttribute("yGrabOffset", value.To<double>());
else
return false;

View File

@@ -13,7 +13,6 @@ This project is released under the MIT License.
#include "GDCore/CommonTools.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/MeasurementUnit.h"
#include "GDCore/Project/PropertyDescriptor.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/Tools/Localization.h"
@@ -45,109 +44,60 @@ PlatformerObjectBehavior::GetProperties(
const gd::SerializerElement& behaviorContent) const {
std::map<gd::String, gd::PropertyDescriptor> properties;
properties["Gravity"]
.SetLabel(_("Gravity"))
properties[_("Gravity")].SetGroup(_("Jump")).SetValue(
gd::String::From(behaviorContent.GetDoubleAttribute("gravity")));
properties[_("Jump speed")].SetGroup(_("Jump")).SetValue(
gd::String::From(behaviorContent.GetDoubleAttribute("jumpSpeed")));
properties["jumpSustainTime"]
.SetGroup(_("Jump"))
.SetType("Number")
.SetMeasurementUnit(gd::MeasurementUnit::GetPixelAcceleration())
.SetValue(
gd::String::From(behaviorContent.GetDoubleAttribute("gravity")));
properties["JumpSpeed"]
.SetLabel(_("Jump speed"))
.SetGroup(_("Jump"))
.SetType("Number")
.SetMeasurementUnit(gd::MeasurementUnit::GetPixelSpeed())
.SetValue(
gd::String::From(behaviorContent.GetDoubleAttribute("jumpSpeed")));
properties["JumpSustainTime"]
.SetLabel(_("Jump sustain time"))
.SetGroup(_("Jump"))
.SetType("Number")
.SetMeasurementUnit(gd::MeasurementUnit::GetSecond())
.SetValue(gd::String::From(
behaviorContent.GetDoubleAttribute("jumpSustainTime", 0)))
.SetLabel(_("Jump sustain time"))
.SetDescription(
_("Maximum time (in seconds) during which the jump strength is "
"sustained if the jump key is held - allowing variable height "
"jumps."));
properties["MaxFallingSpeed"]
.SetLabel(_("Max. falling speed"))
.SetGroup(_("Jump"))
.SetType("Number")
.SetMeasurementUnit(gd::MeasurementUnit::GetPixelSpeed())
.SetValue(gd::String::From(
behaviorContent.GetDoubleAttribute("maxFallingSpeed")));
properties["LadderClimbingSpeed"]
.SetLabel(_("Ladder climbing speed"))
properties[_("Max. falling speed")].SetGroup(_("Jump")).SetValue(
gd::String::From(behaviorContent.GetDoubleAttribute("maxFallingSpeed")));
properties[_("Ladder climbing speed")]
.SetGroup(_("Ladder"))
.SetType("Number")
.SetMeasurementUnit(gd::MeasurementUnit::GetPixelSpeed())
.SetValue(gd::String::From(
behaviorContent.GetDoubleAttribute("ladderClimbingSpeed", 150)));
properties["Acceleration"]
.SetLabel(_("Acceleration"))
.SetGroup(_("Walk"))
.SetType("Number")
.SetMeasurementUnit(gd::MeasurementUnit::GetPixelAcceleration())
.SetValue(
gd::String::From(behaviorContent.GetDoubleAttribute("acceleration")));
properties["Deceleration"]
.SetLabel(_("Deceleration"))
.SetGroup(_("Walk"))
.SetType("Number")
.SetMeasurementUnit(gd::MeasurementUnit::GetPixelAcceleration())
.SetValue(
gd::String::From(behaviorContent.GetDoubleAttribute("deceleration")));
properties["MaxSpeed"]
.SetLabel(_("Max. speed"))
.SetGroup(_("Walk"))
.SetType("Number")
.SetMeasurementUnit(gd::MeasurementUnit::GetPixelSpeed())
.SetValue(
gd::String::From(behaviorContent.GetDoubleAttribute("maxSpeed")));
properties["IgnoreDefaultControls"]
.SetLabel(_("Default controls"))
properties[_("Acceleration")].SetGroup(_("Walk")).SetValue(
gd::String::From(behaviorContent.GetDoubleAttribute("acceleration")));
properties[_("Deceleration")].SetGroup(_("Walk")).SetValue(
gd::String::From(behaviorContent.GetDoubleAttribute("deceleration")));
properties[_("Max. speed")].SetGroup(_("Walk")).SetValue(
gd::String::From(behaviorContent.GetDoubleAttribute("maxSpeed")));
properties[_("Default controls")]
.SetValue(behaviorContent.GetBoolAttribute("ignoreDefaultControls")
? "false"
: "true")
.SetType("Boolean");
properties["SlopeMaxAngle"]
.SetLabel(_("Slope max. angle"))
.SetGroup(_("Walk"))
.SetType("Number")
.SetMeasurementUnit(gd::MeasurementUnit::GetDegreeAngle())
.SetValue(gd::String::From(
behaviorContent.GetDoubleAttribute("slopeMaxAngle")));
properties["CanGrabPlatforms"]
.SetLabel(_("Can grab platform ledges"))
properties[_("Slope max. angle")].SetGroup(_("Walk")).SetValue(
gd::String::From(behaviorContent.GetDoubleAttribute("slopeMaxAngle")));
properties[_("Can grab platform ledges")]
.SetGroup(_("Ledge"))
.SetValue(behaviorContent.GetBoolAttribute("canGrabPlatforms", false)
? "true"
: "false")
.SetType("Boolean");
properties["CanGrabWithoutMoving"]
.SetLabel(_("Automatically grab platform ledges without having to move "
"horizontally"))
properties[_("Automatically grab platform ledges without having to move "
"horizontally")]
.SetGroup(_("Ledge"))
.SetValue(behaviorContent.GetBoolAttribute("canGrabWithoutMoving", false)
? "true"
: "false")
.SetType("Boolean");
properties["YGrabOffset"]
.SetLabel(_("Grab offset on Y axis"))
properties[_("Grab offset on Y axis")]
.SetGroup(_("Ledge"))
.SetType("Number")
.SetMeasurementUnit(gd::MeasurementUnit::GetPixel())
.SetValue(
gd::String::From(behaviorContent.GetDoubleAttribute("yGrabOffset")));
properties["XGrabTolerance"]
.SetLabel(_("Grab tolerance on X axis"))
properties[_("Grab tolerance on X axis")]
.SetGroup(_("Ledge"))
.SetType("Number")
.SetMeasurementUnit(gd::MeasurementUnit::GetPixel())
.SetValue(gd::String::From(
behaviorContent.GetDoubleAttribute("xGrabTolerance", 10)));
properties["UseLegacyTrajectory"]
properties["useLegacyTrajectory"]
.SetLabel(_("Use frame rate dependent trajectories (deprecated, it's "
"recommended to leave this unchecked)"))
.SetGroup(_("Deprecated options (advanced)"))
@@ -155,8 +105,7 @@ PlatformerObjectBehavior::GetProperties(
? "true"
: "false")
.SetType("Boolean");
properties["CanGoDownFromJumpthru"]
.SetLabel(_("Can go down from jumpthru platforms"))
properties[_("Can go down from jumpthru platforms")]
.SetGroup(_("Walk"))
.SetValue(behaviorContent.GetBoolAttribute("canGoDownFromJumpthru", false)
? "true"
@@ -169,43 +118,44 @@ bool PlatformerObjectBehavior::UpdateProperty(
gd::SerializerElement& behaviorContent,
const gd::String& name,
const gd::String& value) {
if (name == "IgnoreDefaultControls")
if (name == _("Default controls"))
behaviorContent.SetAttribute("ignoreDefaultControls", (value == "0"));
else if (name == "CanGrabPlatforms")
else if (name == _("Can grab platform ledges"))
behaviorContent.SetAttribute("canGrabPlatforms", (value == "1"));
else if (name == "CanGrabWithoutMoving")
else if (name == _("Automatically grab platform ledges without having to "
"move horizontally"))
behaviorContent.SetAttribute("canGrabWithoutMoving", (value == "1"));
else if (name == "UseLegacyTrajectory")
else if (name == "useLegacyTrajectory")
behaviorContent.SetAttribute("useLegacyTrajectory", (value == "1"));
else if (name == "CanGoDownFromJumpthru")
else if (name == _("Can go down from jumpthru platforms"))
behaviorContent.SetAttribute("canGoDownFromJumpthru", (value == "1"));
else if (name == "YGrabOffset")
else if (name == _("Grab offset on Y axis"))
behaviorContent.SetAttribute("yGrabOffset", value.To<double>());
else {
if (value.To<double>() < 0) return false;
if (name == "Gravity")
if (name == _("Gravity"))
behaviorContent.SetAttribute("gravity", value.To<double>());
else if (name == "MaxFallingSpeed")
else if (name == _("Max. falling speed"))
behaviorContent.SetAttribute("maxFallingSpeed", value.To<double>());
else if (name == "LadderClimbingSpeed")
else if (name == _("Ladder climbing speed"))
behaviorContent.SetAttribute("ladderClimbingSpeed", value.To<double>());
else if (name == "Acceleration")
else if (name == _("Acceleration"))
behaviorContent.SetAttribute("acceleration", value.To<double>());
else if (name == "Deceleration")
else if (name == _("Deceleration"))
behaviorContent.SetAttribute("deceleration", value.To<double>());
else if (name == "MaxSpeed")
else if (name == _("Max. speed"))
behaviorContent.SetAttribute("maxSpeed", value.To<double>());
else if (name == "JumpSpeed")
else if (name == _("Jump speed"))
behaviorContent.SetAttribute("jumpSpeed", value.To<double>());
else if (name == "JumpSustainTime")
else if (name == "jumpSustainTime")
behaviorContent.SetAttribute("jumpSustainTime", value.To<double>());
else if (name == "SlopeMaxAngle") {
else if (name == _("Slope max. angle")) {
double newMaxAngle = value.To<double>();
if (newMaxAngle < 0 || newMaxAngle >= 90) return false;
behaviorContent.SetAttribute("slopeMaxAngle", newMaxAngle);
} else if (name == "XGrabTolerance")
} else if (name == _("Grab tolerance on X axis"))
behaviorContent.SetAttribute("xGrabTolerance", value.To<double>());
else
return false;

View File

@@ -1143,14 +1143,6 @@ namespace gdjs {
return this._gravity;
}
/**
* Get maximum angle of a slope for the Platformer Object to run on it as a floor.
* @returns the slope maximum angle, in degrees.
*/
getSlopeMaxAngle(): float {
return this._slopeMaxAngle;
}
/**
* Get the maximum falling speed of the Platformer Object.
* @returns The maximum falling speed.

View File

@@ -404,9 +404,7 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () {
expect(object.getY()).to.be(-30); // -30 = -10 (platform y) + -20 (object height)
// Make the platform under the character feet smaller.
runtimeScene.renderAndStepWithEventsFunction(1000 / 60, () => {
object.setCustomWidthAndHeight(object.getWidth(), 9);
});
object.setCustomWidthAndHeight(object.getWidth(), 9);
runtimeScene.renderAndStep(1000 / 60);
expect(object.getBehavior('auto1').isFalling()).to.be(false);
expect(object.getBehavior('auto1').isFallingWithoutJumping()).to.be(
@@ -681,11 +679,18 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () {
expect(object.getY()).to.be.within(140.6297999, 140.6298001);
// Move the platform by 6 pixels to the right.
for (let index = 0; index < 6; index++) {
runtimeScene.renderAndStepWithEventsFunction(1000 / 60, () => {
platform.setX(platform.getX() + 1);
});
}
platform.setX(platform.getX() + 1);
runtimeScene.renderAndStep(1000 / 60);
platform.setX(platform.getX() + 1);
runtimeScene.renderAndStep(1000 / 60);
platform.setX(platform.getX() + 1);
runtimeScene.renderAndStep(1000 / 60);
platform.setX(platform.getX() + 1);
runtimeScene.renderAndStep(1000 / 60);
platform.setX(platform.getX() + 1);
runtimeScene.renderAndStep(1000 / 60);
platform.setX(platform.getX() + 1);
runtimeScene.renderAndStep(1000 / 60);
// Ensure the object followed the platform on the X axis.
// If the floating point errors caused oscillations between two Y positions,
@@ -694,9 +699,6 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () {
expect(object.getBehavior('auto1').isFalling()).to.be(false);
expect(object.getBehavior('auto1').isOnFloor()).to.be(true);
expect(object.getY()).to.be.within(140.6297999, 140.6298001);
// TODO Remove the 1-frame delay
expect(object.getX()).to.be(5);
runtimeScene.renderAndStep(1000 / 60);
expect(object.getX()).to.be(6);
});
});

View File

@@ -59,18 +59,13 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () {
// Check that the object follow the platform, even if the
// movement is less than one pixel.
runtimeScene.renderAndStepWithEventsFunction(1000 / 60, () => {
platform.setX(platform.getX() + 0.12);
});
runtimeScene.renderAndStepWithEventsFunction(1000 / 60, () => {
platform.setX(platform.getX() + 0.12);
});
runtimeScene.renderAndStepWithEventsFunction(1000 / 60, () => {
platform.setX(platform.getX() + 0.12);
});
// TODO Remove the 1-frame delay
expect(object.getX()).to.be(0.24);
platform.setX(platform.getX() + 0.12);
runtimeScene.renderAndStep(1000 / 60);
platform.setX(platform.getX() + 0.12);
runtimeScene.renderAndStep(1000 / 60);
platform.setX(platform.getX() + 0.12);
runtimeScene.renderAndStep(1000 / 60);
expect(object.getX()).to.be(0.36);
});
@@ -245,28 +240,22 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () {
// Check that the object follow the platform, even if the
// movement is less than one pixel.
for (let i = 0; i < 5; ++i) {
const previousPlatformY = platform.getY();
runtimeScene.renderAndStepWithEventsFunction(1000 / 60, () => {
platform.setPosition(
platform.getX() + deltaX,
platform.getY() + deltaY
);
});
platform.setPosition(
platform.getX() + deltaX,
platform.getY() + deltaY
);
runtimeScene.renderAndStep(1000 / 60);
expect(object.getBehavior('auto1').isOnFloor()).to.be(true);
expect(object.getBehavior('auto1').isFalling()).to.be(false);
expect(object.getBehavior('auto1').isMoving()).to.be(false);
// The object follow the platform
// The rounding error is probably due to a separate call.
// TODO Try to make it exact or find why
// TODO Remove the 1-frame delay
expect(object.getY()).to.be.within(
previousPlatformY - object.getHeight() - epsilon,
previousPlatformY - object.getHeight() + epsilon
platform.getY() - object.getHeight() - epsilon,
platform.getY() - object.getHeight() + epsilon
);
}
// TODO Remove the 1-frame delay
expect(object.getX()).to.be(0 + 4 * deltaX);
runtimeScene.renderAndStep(1000 / 60);
expect(object.getX()).to.be(0 + 5 * deltaX);
});
});
@@ -389,26 +378,20 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () {
// Check that the object follow the platform, even if the
// movement is less than one pixel.
for (let i = 0; i < 5; ++i) {
const previousPlatformY = platform.getY();
runtimeScene.renderAndStepWithEventsFunction(1000 / 60, () => {
platform.setPosition(
platform.getX() + deltaX,
platform.getY() + deltaY
);
});
platform.setPosition(
platform.getX() + deltaX,
platform.getY() + deltaY
);
runtimeScene.renderAndStep(1000 / 60);
expect(object.getBehavior('auto1').isOnFloor()).to.be(true);
expect(object.getBehavior('auto1').isFalling()).to.be(false);
expect(object.getBehavior('auto1').isMoving()).to.be(false);
// The object must not be inside the platform or it gets stuck
// TODO Remove the 1-frame delay
expect(object.getY()).to.be.within(
previousPlatformY - object.getHeight() - epsilon,
previousPlatformY - object.getHeight() + epsilon
platform.getY() - object.getHeight() - epsilon,
platform.getY() - object.getHeight() + epsilon
);
}
// TODO Remove the 1-frame delay
expect(object.getX()).to.be(0 + 4 * deltaX);
runtimeScene.renderAndStep(1000 / 60);
expect(object.getX()).to.be(0 + 5 * deltaX);
});
});

View File

@@ -7,7 +7,7 @@
},
properties: { windowWidth: 800, windowHeight: 600 },
});
const runtimeScene = new gdjs.TestRuntimeScene(runtimeGame);
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
runtimeScene.loadFromScene({
layers: [{ name: '', visibility: true, effects: [] }],
variables: [],

View File

@@ -111,6 +111,7 @@ describe('gdjs.TextInputRuntimeObject (using a PixiJS RuntimeGame with DOM eleme
const {
runtimeScene,
gameDomElementContainer,
object,
} = await setupObjectAndGetDomElementContainer();
expect(gameDomElementContainer.querySelector('input')).not.to.be(null);

View File

@@ -170,21 +170,19 @@ namespace gdjs {
// Position the input on the container on top of the canvas.
workingPoint[0] = canvasLeft;
workingPoint[1] = canvasTop;
runtimeGameRenderer.convertCanvasToDomElementContainerCoords(
workingPoint,
const topLeftPageCoordinates = runtimeGameRenderer.convertCanvasToDomElementContainerCoords(
workingPoint
);
const pageLeft = workingPoint[0];
const pageTop = workingPoint[1];
const pageLeft = topLeftPageCoordinates[0];
const pageTop = topLeftPageCoordinates[1];
workingPoint[0] = canvasRight;
workingPoint[1] = canvasBottom;
runtimeGameRenderer.convertCanvasToDomElementContainerCoords(
workingPoint,
const bottomRightPageCoordinates = runtimeGameRenderer.convertCanvasToDomElementContainerCoords(
workingPoint
);
const pageRight = workingPoint[0];
const pageBottom = workingPoint[1];
const pageRight = bottomRightPageCoordinates[0];
const pageBottom = bottomRightPageCoordinates[1];
const widthInContainer = pageRight - pageLeft;
const heightInContainer = pageBottom - pageTop;

View File

@@ -1,4 +1,6 @@
namespace gdjs {
const logger = new gdjs.Logger('Text input object');
const supportedInputTypes = [
'text',
'email',

View File

@@ -29,8 +29,7 @@ TextObject::TextObject()
underlined(false),
colorR(0),
colorG(0),
colorB(0),
textAlignment("left")
colorB(0)
{
}
@@ -40,7 +39,6 @@ void TextObject::DoUnserializeFrom(gd::Project& project,
const gd::SerializerElement& element) {
SetString(element.GetChild("string", 0, "String").GetValue().GetString());
SetFontName(element.GetChild("font", 0, "Font").GetValue().GetString());
SetTextAlignment(element.GetChild("textAlignment").GetValue().GetString());
SetCharacterSize(element.GetChild("characterSize", 0, "CharacterSize")
.GetValue()
.GetInt());
@@ -58,7 +56,6 @@ void TextObject::DoUnserializeFrom(gd::Project& project,
void TextObject::DoSerializeTo(gd::SerializerElement& element) const {
element.AddChild("string").SetValue(GetString());
element.AddChild("font").SetValue(GetFontName());
element.AddChild("textAlignment").SetValue(GetTextAlignment());
element.AddChild("characterSize").SetValue(GetCharacterSize());
element.AddChild("color")
.SetAttribute("r", (int)GetColorR())

View File

@@ -53,9 +53,6 @@ class GD_EXTENSION_API TextObject : public gd::ObjectConfiguration {
*/
void SetFontName(const gd::String& resourceName) { fontName = resourceName; };
inline const gd::String& GetTextAlignment() const { return textAlignment; };
void SetTextAlignment(const gd::String& textAlignment_) { textAlignment = textAlignment_; };
bool IsBold() const { return bold; };
void SetBold(bool enable) { bold = enable; };
bool IsItalic() const { return italic; };
@@ -90,7 +87,6 @@ class GD_EXTENSION_API TextObject : public gd::ObjectConfiguration {
unsigned int colorR;
unsigned int colorG;
unsigned int colorB;
gd::String textAlignment;
};
#endif // TEXTOBJECT_H

View File

@@ -94,25 +94,7 @@ namespace gdjs {
}
updatePosition(): void {
if (this._object.isWrapping()) {
const alignmentX =
this._object._textAlign === 'right'
? 1
: this._object._textAlign === 'center'
? 0.5
: 0;
const width = this._object.getWrappingWidth();
// A vector from the custom size center to the renderer center.
const centerToCenterX = (width - this._text.width) * (alignmentX - 0.5);
this._text.position.x = this._object.x + width / 2;
this._text.anchor.x = 0.5 - centerToCenterX / this._text.width;
} else {
this._text.position.x = this._object.x + this._text.width / 2;
this._text.anchor.x = 0.5;
}
this._text.position.x = this._object.x + this._text.width / 2;
this._text.position.y = this._object.y + this._text.height / 2;
}

View File

@@ -26,7 +26,6 @@ namespace gdjs {
};
/** The text of the object */
string: string;
textAlignment: string;
};
export type TextObjectData = ObjectData & TextObjectDataType;
@@ -83,7 +82,6 @@ namespace gdjs {
textObjectData.color.b,
];
this._str = textObjectData.string;
this._textAlign = textObjectData.textAlignment;
this._renderer = new gdjs.TextRuntimeObjectRenderer(
this,
instanceContainer
@@ -129,9 +127,6 @@ namespace gdjs {
if (oldObjectData.underlined !== newObjectData.underlined) {
return false;
}
if (oldObjectData.textAlignment !== newObjectData.textAlignment) {
this.setTextAlignment(newObjectData.textAlignment);
}
return true;
}
@@ -293,7 +288,7 @@ namespace gdjs {
* Get width of the text.
*/
getWidth(): float {
return this._wrapping ? this._wrappingWidth : this._renderer.getWidth();
return this._renderer.getWidth();
}
/**

View File

@@ -124,7 +124,7 @@ describe('gdjs.TileMapCollisionMaskRuntimeObject', function () {
index < 200 && tileMap._collisionTileMap.getDimensionX() === 0;
index++
) {
await delay(100);
await delay(25);
}
if (tileMap._collisionTileMap.getDimensionX() === 0) {
throw new Error('Timeout reading the tile map JSON file.');

View File

@@ -1,5 +1,7 @@
/// <reference path="helper/TileMapHelper.d.ts" />
namespace gdjs {
const logger = new gdjs.Logger('Tilemap object');
/**
* An object that handle hitboxes for a tile map.
* @extends gdjs.RuntimeObject
@@ -180,7 +182,7 @@ namespace gdjs {
updateHitBoxes(): void {
this.updateTransformation();
// Update the RuntimeObject hitboxes attribute.
for (const _ of this._collisionTileMap.getAllHitboxes(
for (const hitboxes of this._collisionTileMap.getAllHitboxes(
this._collisionMaskTag
)) {
// RuntimeObject.hitBoxes contains the same polygons instances as the

View File

@@ -1,6 +1,8 @@
/// <reference path="helper/TileMapHelper.d.ts" />
/// <reference path="pixi-tilemap/dist/pixi-tilemap.d.ts" />
namespace gdjs {
const logger = new gdjs.Logger('Tilemap object');
/**
* The PIXI.js renderer for the Tile map runtime object.
*

View File

@@ -2,6 +2,7 @@
namespace gdjs {
import PIXI = GlobalPIXIModule.PIXI;
const logger = new gdjs.Logger('Tilemap object');
/**
* Displays a Tilemap object (mapeditor.org supported).
*/

View File

@@ -66,13 +66,13 @@ namespace gdjs {
return this._tiledSprite.height;
}
setWidth(width: float): void {
setWidth(width): void {
this._tiledSprite.width = width;
this._tiledSprite.pivot.x = width / 2;
this.updatePosition();
}
setHeight(height: float): void {
setHeight(height): void {
this._tiledSprite.height = height;
this._tiledSprite.pivot.y = height / 2;
this.updatePosition();
@@ -94,7 +94,7 @@ namespace gdjs {
-this._object._yOffset % this._tiledSprite.texture.height;
}
setColor(rgbColor: string): void {
setColor(rgbColor): void {
const colors = rgbColor.split(';');
if (colors.length < 3) {
return;

View File

@@ -14,7 +14,6 @@ This project is released under the MIT License.
#include <set>
#include "GDCore/CommonTools.h"
#include "GDCore/Project/MeasurementUnit.h"
#include "GDCore/Tools/Localization.h"
#include "GDCore/CommonTools.h"
#include "GDCore/Project/Layout.h"
@@ -46,55 +45,26 @@ TopDownMovementBehavior::GetProperties(
const gd::SerializerElement& behaviorContent) const {
std::map<gd::String, gd::PropertyDescriptor> properties;
properties["AllowDiagonals"]
.SetLabel(_("Allows diagonals"))
.SetGroup(_("Movement"))
properties[_("Allows diagonals")].SetGroup(_("Movement"))
.SetValue(behaviorContent.GetBoolAttribute("allowDiagonals") ? "true"
: "false")
.SetType("Boolean");
properties["Acceleration"]
.SetLabel(_("Acceleration"))
.SetGroup(_("Movement"))
.SetType("Number")
.SetMeasurementUnit(gd::MeasurementUnit::GetPixelAcceleration())
.SetValue(
gd::String::From(behaviorContent.GetDoubleAttribute("acceleration")));
properties["Deceleration"]
.SetLabel(_("Deceleration"))
.SetGroup(_("Movement"))
.SetType("Number")
.SetMeasurementUnit(gd::MeasurementUnit::GetPixelAcceleration())
.SetValue(
gd::String::From(behaviorContent.GetDoubleAttribute("deceleration")));
properties["MaxSpeed"]
.SetLabel(_("Max. speed"))
.SetGroup(_("Movement"))
.SetType("Number")
.SetMeasurementUnit(gd::MeasurementUnit::GetPixelSpeed())
.SetValue(
gd::String::From(behaviorContent.GetDoubleAttribute("maxSpeed")));
properties["AngularMaxSpeed"]
.SetLabel(_("Rotation speed"))
.SetGroup(_("Rotation"))
.SetType("Number")
.SetMeasurementUnit(gd::MeasurementUnit::GetAngularSpeed())
.SetValue(gd::String::From(
behaviorContent.GetDoubleAttribute("angularMaxSpeed")));
properties["RotateObject"]
.SetLabel(_("Rotate object"))
properties[_("Acceleration")].SetGroup(_("Movement")).SetValue(
gd::String::From(behaviorContent.GetDoubleAttribute("acceleration")));
properties[_("Deceleration")].SetGroup(_("Movement")).SetValue(
gd::String::From(behaviorContent.GetDoubleAttribute("deceleration")));
properties[_("Max. speed")].SetGroup(_("Movement")).SetValue(
gd::String::From(behaviorContent.GetDoubleAttribute("maxSpeed")));
properties[_("Rotate speed")].SetGroup(_("Rotation")).SetValue(
gd::String::From(behaviorContent.GetDoubleAttribute("angularMaxSpeed")));
properties[_("Rotate object")]
.SetGroup(_("Rotation"))
.SetValue(behaviorContent.GetBoolAttribute("rotateObject") ? "true"
: "false")
.SetType("Boolean");
properties["AngleOffset"]
.SetLabel(_("Angle offset"))
.SetGroup(_("Rotation"))
.SetType("Number")
.SetMeasurementUnit(gd::MeasurementUnit::GetDegreeAngle())
.SetValue(
gd::String::From(behaviorContent.GetDoubleAttribute("angleOffset")));
properties["IgnoreDefaultControls"]
.SetLabel(_("Default controls"))
properties[_("Angle offset")].SetGroup(_("Rotation")).SetValue(
gd::String::From(behaviorContent.GetDoubleAttribute("angleOffset")));
properties[_("Default controls")]
.SetValue(behaviorContent.GetBoolAttribute("ignoreDefaultControls")
? "false"
: "true")
@@ -110,8 +80,7 @@ TopDownMovementBehavior::GetProperties(
viewpointStr = _("True Isometry (30°)");
else if (viewpoint == "CustomIsometry")
viewpointStr = _("Custom Isometry");
properties["Viewpoint"]
.SetLabel(_("Viewpoint"))
properties[_("Viewpoint")]
.SetGroup(_("Viewpoint"))
.SetValue(viewpointStr)
.SetType("Choice")
@@ -119,20 +88,14 @@ TopDownMovementBehavior::GetProperties(
.AddExtraInfo(_("Isometry 2:1 (26.565°)"))
.AddExtraInfo(_("True Isometry (30°)"))
.AddExtraInfo(_("Custom Isometry"));
properties["CustomIsometryAngle"]
.SetLabel(_("Custom isometry angle"))
properties[_("Custom isometry angle")]
.SetGroup(_("Viewpoint"))
.SetType("Number")
.SetMeasurementUnit(gd::MeasurementUnit::GetDegreeAngle())
.SetValue(gd::String::From(
behaviorContent.GetDoubleAttribute("customIsometryAngle")))
.SetDescription(_("If you choose \"Custom Isometry\", this allows to "
"specify the angle of your isometry projection."));
properties["MovementAngleOffset"]
.SetLabel(_("Movement angle offset"))
properties[_("Movement angle offset")]
.SetGroup(_("Viewpoint"))
.SetType("Number")
.SetMeasurementUnit(gd::MeasurementUnit::GetDegreeAngle())
.SetValue(gd::String::From(
behaviorContent.GetDoubleAttribute("movementAngleOffset")))
.SetDescription(_(
@@ -146,19 +109,19 @@ bool TopDownMovementBehavior::UpdateProperty(
gd::SerializerElement& behaviorContent,
const gd::String& name,
const gd::String& value) {
if (name == "IgnoreDefaultControls") {
if (name == _("Default controls")) {
behaviorContent.SetAttribute("ignoreDefaultControls", (value == "0"));
return true;
}
if (name == "AllowDiagonals") {
if (name == _("Allows diagonals")) {
behaviorContent.SetAttribute("allowDiagonals", (value != "0"));
return true;
}
if (name == "RotateObject") {
if (name == _("Rotate object")) {
behaviorContent.SetAttribute("rotateObject", (value != "0"));
return true;
}
if (name == "Viewpoint") {
if (name == _("Viewpoint")) {
// Fix the offset angle when switching between top-down and isometry
const gd::String& oldValue =
behaviorContent.GetStringAttribute("viewpoint", "TopDown", "");
@@ -184,23 +147,23 @@ bool TopDownMovementBehavior::UpdateProperty(
behaviorContent.SetAttribute("viewpoint", "TopDown");
return true;
}
if (name == "MovementAngleOffset") {
if (name == _("Movement angle offset")) {
behaviorContent.SetAttribute("movementAngleOffset", value.To<float>());
}
if (value.To<float>() < 0) return false;
if (name == "Acceleration")
if (name == _("Acceleration"))
behaviorContent.SetAttribute("acceleration", value.To<float>());
else if (name == "Deceleration")
else if (name == _("Deceleration"))
behaviorContent.SetAttribute("deceleration", value.To<float>());
else if (name == "MaxSpeed")
else if (name == _("Max. speed"))
behaviorContent.SetAttribute("maxSpeed", value.To<float>());
else if (name == "RotationSpeed")
else if (name == _("Rotate speed"))
behaviorContent.SetAttribute("angularMaxSpeed", value.To<float>());
else if (name == "AngleOffset")
else if (name == _("Angle offset"))
behaviorContent.SetAttribute("angleOffset", value.To<float>());
else if (name == "CustomIsometryAngle") {
else if (name == _("Custom isometry angle")) {
if (value.To<float>() < 1 || value.To<float>() > 44) return false;
behaviorContent.SetAttribute("customIsometryAngle", value.To<float>());
} else

View File

@@ -24,6 +24,8 @@ namespace gdjs {
private _angle: float = 0;
//Attributes used when moving
private _x: float = 0;
private _y: float = 0;
private _xVelocity: float = 0;
private _yVelocity: float = 0;
private _angularSpeed: float = 0;

View File

@@ -42,7 +42,7 @@ JsCodeEvent::GetAllExpressionsWithMetadata() const {
}
void JsCodeEvent::SerializeTo(gd::SerializerElement& element) const {
element.AddChild("inlineCode").SetMultilineStringValue(inlineCode);
element.AddChild("inlineCode").SetValue(inlineCode);
element.AddChild("parameterObjects")
.SetValue(parameterObjects.GetPlainString());
element.AddChild("useStrict").SetValue(useStrict);
@@ -51,7 +51,7 @@ void JsCodeEvent::SerializeTo(gd::SerializerElement& element) const {
void JsCodeEvent::UnserializeFrom(gd::Project& project,
const gd::SerializerElement& element) {
inlineCode = element.GetChild("inlineCode").GetMultilineStringValue();
inlineCode = element.GetChild("inlineCode").GetValue().GetString();
parameterObjects = gd::Expression(
element.GetChild("parameterObjects").GetValue().GetString());

View File

@@ -22,130 +22,95 @@ gd::String BehaviorCodeGenerator::GenerateRuntimeBehaviorCompleteCode(
auto& eventsFunctionsVector =
eventsBasedBehavior.GetEventsFunctions().GetInternalVector();
auto generateInitializePropertiesCode = [&]() {
gd::String runtimeBehaviorDataInitializationCode;
for (auto& property :
eventsBasedBehavior.GetPropertyDescriptors().GetInternalVector()) {
runtimeBehaviorDataInitializationCode +=
property->IsHidden()
? GenerateInitializePropertyFromDefaultValueCode(*property)
: GenerateInitializePropertyFromDataCode(*property);
}
return runtimeBehaviorDataInitializationCode;
};
auto generatePropertiesCode = [&]() {
gd::String runtimeBehaviorPropertyMethodsCode;
for (auto& property :
eventsBasedBehavior.GetPropertyDescriptors().GetInternalVector()) {
runtimeBehaviorPropertyMethodsCode +=
GenerateRuntimeBehaviorPropertyTemplateCode(
eventsBasedBehavior, *property);
}
return runtimeBehaviorPropertyMethodsCode;
};
auto generateInitializeSharedPropertiesCode = [&]() {
gd::String runtimeBehaviorSharedDataInitializationCode;
for (auto& property :
eventsBasedBehavior.GetSharedPropertyDescriptors().GetInternalVector()) {
runtimeBehaviorSharedDataInitializationCode +=
property->IsHidden()
? GenerateInitializeSharedPropertyFromDefaultValueCode(*property)
: GenerateInitializeSharedPropertyFromDataCode(*property);
}
return runtimeBehaviorSharedDataInitializationCode;
};
auto generateSharedPropertiesCode = [&]() {
gd::String runtimeBehaviorSharedPropertyMethodsCode;
for (auto& property :
eventsBasedBehavior.GetSharedPropertyDescriptors().GetInternalVector()) {
runtimeBehaviorSharedPropertyMethodsCode +=
GenerateRuntimeBehaviorSharedPropertyTemplateCode(
eventsBasedBehavior, *property);
}
return runtimeBehaviorSharedPropertyMethodsCode;
};
// TODO: Update code generation to be able to generate methods (which would allow
// for a cleaner output, not having to add methods to the prototype).
auto generateMethodsCode = [&]() {
gd::String runtimeBehaviorMethodsCode;
for (auto& eventsFunction : eventsFunctionsVector) {
const gd::String& functionName =
behaviorMethodMangledNames.find(eventsFunction->GetName()) !=
behaviorMethodMangledNames.end()
? behaviorMethodMangledNames.find(eventsFunction->GetName())
->second
: "UNKNOWN_FUNCTION_fix_behaviorMethodMangledNames_please";
gd::String methodCodeNamespace =
codeNamespace + "." + eventsBasedBehavior.GetName() +
".prototype." + functionName + "Context";
gd::String methodFullyQualifiedName = codeNamespace + "." +
eventsBasedBehavior.GetName() +
".prototype." + functionName;
runtimeBehaviorMethodsCode +=
EventsCodeGenerator::GenerateBehaviorEventsFunctionCode(
project,
eventsBasedBehavior,
*eventsFunction,
methodCodeNamespace,
methodFullyQualifiedName,
"that._onceTriggers",
functionName == doStepPreEventsFunctionName
? GenerateDoStepPreEventsPreludeCode()
: "",
includeFiles,
compilationForRuntime);
// Compatibility with GD <= 5.0 beta 75
if (functionName == "onOwnerRemovedFromScene") {
runtimeBehaviorMethodsCode +=
GenerateBehaviorOnDestroyToDeprecatedOnOwnerRemovedFromScene(
eventsBasedBehavior, codeNamespace);
}
// end of compatibility code
}
bool hasDoStepPreEventsFunction =
eventsBasedBehavior.GetEventsFunctions().HasEventsFunctionNamed(
doStepPreEventsFunctionName);
if (!hasDoStepPreEventsFunction) {
runtimeBehaviorMethodsCode +=
GenerateDefaultDoStepPreEventsFunctionCode(eventsBasedBehavior,
codeNamespace);
}
return runtimeBehaviorMethodsCode;
};
auto generateUpdateFromBehaviorDataCode = [&]() {
gd::String updateFromBehaviorCode;
for (auto& property :
eventsBasedBehavior.GetPropertyDescriptors().GetInternalVector()) {
updateFromBehaviorCode +=
GenerateUpdatePropertyFromBehaviorDataCode(
eventsBasedBehavior, *property);
}
return updateFromBehaviorCode;
};
return GenerateRuntimeBehaviorTemplateCode(
extensionName,
eventsBasedBehavior,
codeNamespace,
generateInitializePropertiesCode,
generatePropertiesCode,
generateInitializeSharedPropertiesCode,
generateSharedPropertiesCode,
generateMethodsCode,
generateUpdateFromBehaviorDataCode);
[&]() {
gd::String runtimeBehaviorDataInitializationCode;
for (auto& property :
eventsBasedBehavior.GetPropertyDescriptors().GetInternalVector()) {
runtimeBehaviorDataInitializationCode +=
property->IsHidden()
? GenerateInitializePropertyFromDefaultValueCode(*property)
: GenerateInitializePropertyFromDataCode(*property);
}
return runtimeBehaviorDataInitializationCode;
},
[&]() {
gd::String runtimeBehaviorPropertyMethodsCode;
for (auto& property :
eventsBasedBehavior.GetPropertyDescriptors().GetInternalVector()) {
runtimeBehaviorPropertyMethodsCode +=
GenerateRuntimeBehaviorPropertyTemplateCode(
eventsBasedBehavior, *property);
}
return runtimeBehaviorPropertyMethodsCode;
},
// TODO: Update code generation to be able to generate methods (which would allow
// for a cleaner output, not having to add methods to the prototype).
[&]() {
gd::String runtimeBehaviorMethodsCode;
for (auto& eventsFunction : eventsFunctionsVector) {
const gd::String& functionName =
behaviorMethodMangledNames.find(eventsFunction->GetName()) !=
behaviorMethodMangledNames.end()
? behaviorMethodMangledNames.find(eventsFunction->GetName())
->second
: "UNKNOWN_FUNCTION_fix_behaviorMethodMangledNames_please";
gd::String methodCodeNamespace =
codeNamespace + "." + eventsBasedBehavior.GetName() +
".prototype." + functionName + "Context";
gd::String methodFullyQualifiedName = codeNamespace + "." +
eventsBasedBehavior.GetName() +
".prototype." + functionName;
runtimeBehaviorMethodsCode +=
EventsCodeGenerator::GenerateBehaviorEventsFunctionCode(
project,
eventsBasedBehavior,
*eventsFunction,
methodCodeNamespace,
methodFullyQualifiedName,
"that._onceTriggers",
functionName == doStepPreEventsFunctionName
? GenerateDoStepPreEventsPreludeCode()
: "",
includeFiles,
compilationForRuntime);
// Compatibility with GD <= 5.0 beta 75
if (functionName == "onOwnerRemovedFromScene") {
runtimeBehaviorMethodsCode +=
GenerateBehaviorOnDestroyToDeprecatedOnOwnerRemovedFromScene(
eventsBasedBehavior, codeNamespace);
}
// end of compatibility code
}
bool hasDoStepPreEventsFunction =
eventsBasedBehavior.GetEventsFunctions().HasEventsFunctionNamed(
doStepPreEventsFunctionName);
if (!hasDoStepPreEventsFunction) {
runtimeBehaviorMethodsCode +=
GenerateDefaultDoStepPreEventsFunctionCode(eventsBasedBehavior,
codeNamespace);
}
return runtimeBehaviorMethodsCode;
},
[&]() {
gd::String updateFromBehaviorCode;
for (auto& property :
eventsBasedBehavior.GetPropertyDescriptors().GetInternalVector()) {
updateFromBehaviorCode +=
GenerateUpdatePropertyFromBehaviorDataCode(
eventsBasedBehavior, *property);
}
return updateFromBehaviorCode;
});
}
gd::String BehaviorCodeGenerator::GenerateRuntimeBehaviorTemplateCode(
@@ -154,8 +119,6 @@ gd::String BehaviorCodeGenerator::GenerateRuntimeBehaviorTemplateCode(
const gd::String& codeNamespace,
std::function<gd::String()> generateInitializePropertiesCode,
std::function<gd::String()> generatePropertiesCode,
std::function<gd::String()> generateInitializeSharedPropertiesCode,
std::function<gd::String()> generateSharedPropertiesCode,
std::function<gd::String()> generateMethodsCode,
std::function<gd::String()> generateUpdateFromBehaviorDataCode) {
return gd::String(R"jscode_template(
@@ -165,16 +128,12 @@ CODE_NAMESPACE = CODE_NAMESPACE || {};
* Behavior generated from BEHAVIOR_FULL_NAME
*/
CODE_NAMESPACE.RUNTIME_BEHAVIOR_CLASSNAME = class RUNTIME_BEHAVIOR_CLASSNAME extends gdjs.RuntimeBehavior {
constructor(instanceContainer, behaviorData, owner) {
super(instanceContainer, behaviorData, owner);
this._runtimeScene = instanceContainer;
constructor(runtimeScene, behaviorData, owner) {
super(runtimeScene, behaviorData, owner);
this._runtimeScene = runtimeScene;
this._onceTriggers = new gdjs.OnceTriggers();
this._behaviorData = {};
this._sharedData = CODE_NAMESPACE.RUNTIME_BEHAVIOR_CLASSNAME.getSharedData(
instanceContainer,
behaviorData.name
);
INITIALIZE_PROPERTIES_CODE
}
@@ -189,30 +148,6 @@ CODE_NAMESPACE.RUNTIME_BEHAVIOR_CLASSNAME = class RUNTIME_BEHAVIOR_CLASSNAME ext
PROPERTIES_CODE
}
/**
* Shared data generated from BEHAVIOR_FULL_NAME
*/
CODE_NAMESPACE.RUNTIME_BEHAVIOR_CLASSNAME.SharedData = class RUNTIME_BEHAVIOR_CLASSNAMESharedData {
constructor(sharedData) {
INITIALIZE_SHARED_PROPERTIES_CODE
}
// Shared properties:
SHARED_PROPERTIES_CODE
}
CODE_NAMESPACE.RUNTIME_BEHAVIOR_CLASSNAME.getSharedData = function(instanceContainer, behaviorName) {
if (!instanceContainer._EXTENSION_NAME_RUNTIME_BEHAVIOR_CLASSNAMESharedData) {
const initialData = instanceContainer.getInitialSharedDataForBehavior(
behaviorName
);
instanceContainer._EXTENSION_NAME_RUNTIME_BEHAVIOR_CLASSNAMESharedData = new CODE_NAMESPACE.RUNTIME_BEHAVIOR_CLASSNAME.SharedData(
initialData
);
}
return instanceContainer._EXTENSION_NAME_RUNTIME_BEHAVIOR_CLASSNAMESharedData;
}
// Methods:
METHODS_CODE
@@ -224,13 +159,9 @@ gdjs.registerBehavior("EXTENSION_NAME::BEHAVIOR_NAME", CODE_NAMESPACE.RUNTIME_BE
.FindAndReplace("RUNTIME_BEHAVIOR_CLASSNAME",
eventsBasedBehavior.GetName())
.FindAndReplace("CODE_NAMESPACE", codeNamespace)
.FindAndReplace("INITIALIZE_SHARED_PROPERTIES_CODE",
generateInitializeSharedPropertiesCode())
.FindAndReplace("INITIALIZE_PROPERTIES_CODE",
generateInitializePropertiesCode())
.FindAndReplace("UPDATE_FROM_BEHAVIOR_DATA_CODE", generateUpdateFromBehaviorDataCode())
// It must be done before PROPERTIES_CODE.
.FindAndReplace("SHARED_PROPERTIES_CODE", generateSharedPropertiesCode())
.FindAndReplace("PROPERTIES_CODE", generatePropertiesCode())
.FindAndReplace("METHODS_CODE", generateMethodsCode());
;
@@ -243,15 +174,6 @@ gd::String BehaviorCodeGenerator::GenerateInitializePropertyFromDataCode(
.FindAndReplace("PROPERTY_NAME", property.GetName())
.FindAndReplace("DEFAULT_VALUE", GeneratePropertyValueCode(property));
}
gd::String BehaviorCodeGenerator::GenerateInitializeSharedPropertyFromDataCode(
const gd::NamedPropertyDescriptor& property) {
return gd::String(R"jscode_template(
this.PROPERTY_NAME = sharedData.PROPERTY_NAME !== undefined ? sharedData.PROPERTY_NAME : DEFAULT_VALUE;)jscode_template")
.FindAndReplace("PROPERTY_NAME", property.GetName())
.FindAndReplace("DEFAULT_VALUE", GeneratePropertyValueCode(property));
}
gd::String
BehaviorCodeGenerator::GenerateInitializePropertyFromDefaultValueCode(
const gd::NamedPropertyDescriptor& property) {
@@ -261,15 +183,6 @@ BehaviorCodeGenerator::GenerateInitializePropertyFromDefaultValueCode(
.FindAndReplace("DEFAULT_VALUE", GeneratePropertyValueCode(property));
}
gd::String
BehaviorCodeGenerator::GenerateInitializeSharedPropertyFromDefaultValueCode(
const gd::NamedPropertyDescriptor& property) {
return gd::String(R"jscode_template(
this.PROPERTY_NAME = DEFAULT_VALUE;)jscode_template")
.FindAndReplace("PROPERTY_NAME", property.GetName())
.FindAndReplace("DEFAULT_VALUE", GeneratePropertyValueCode(property));
}
gd::String BehaviorCodeGenerator::GenerateRuntimeBehaviorPropertyTemplateCode(
const gd::EventsBasedBehavior& eventsBasedBehavior,
const gd::NamedPropertyDescriptor& property) {
@@ -290,26 +203,6 @@ gd::String BehaviorCodeGenerator::GenerateRuntimeBehaviorPropertyTemplateCode(
eventsBasedBehavior.GetName());
}
gd::String BehaviorCodeGenerator::GenerateRuntimeBehaviorSharedPropertyTemplateCode(
const gd::EventsBasedBehavior& eventsBasedBehavior,
const gd::NamedPropertyDescriptor& property) {
return gd::String(R"jscode_template(
GETTER_NAME() {
return this.PROPERTY_NAME !== undefined ? this.PROPERTY_NAME : DEFAULT_VALUE;
}
SETTER_NAME(newValue) {
this.PROPERTY_NAME = newValue;
})jscode_template")
.FindAndReplace("PROPERTY_NAME", property.GetName())
.FindAndReplace("GETTER_NAME",
GetBehaviorSharedPropertyGetterInternalName(property.GetName()))
.FindAndReplace("SETTER_NAME",
GetBehaviorSharedPropertySetterInternalName(property.GetName()))
.FindAndReplace("DEFAULT_VALUE", GeneratePropertyValueCode(property))
.FindAndReplace("RUNTIME_BEHAVIOR_CLASSNAME",
eventsBasedBehavior.GetName());
}
gd::String BehaviorCodeGenerator::GenerateUpdatePropertyFromBehaviorDataCode(
const gd::EventsBasedBehavior& eventsBasedBehavior,
const gd::NamedPropertyDescriptor& property) {
@@ -370,23 +263,4 @@ gd::String BehaviorCodeGenerator::GenerateDoStepPreEventsPreludeCode() {
return "this._onceTriggers.startNewFrame();";
}
gd::String BehaviorCodeGenerator::GetBehaviorSharedPropertyGetterName(
const gd::String& propertyName) {
return "_sharedData." + GetBehaviorSharedPropertyGetterInternalName(propertyName);
}
gd::String BehaviorCodeGenerator::GetBehaviorSharedPropertySetterName(
const gd::String& propertyName) {
return "_sharedData." + GetBehaviorSharedPropertySetterInternalName(propertyName);
}
gd::String BehaviorCodeGenerator::GetBehaviorSharedPropertyGetterInternalName(
const gd::String& propertyName) {
return "_get" + propertyName;
}
gd::String BehaviorCodeGenerator::GetBehaviorSharedPropertySetterInternalName(
const gd::String& propertyName) {
return "_set" + propertyName;
}
} // namespace gdjs

View File

@@ -57,79 +57,32 @@ class BehaviorCodeGenerator {
return "_set" + propertyName;
}
/**
* \brief Generate the name of the method to get the value of the shared property
* of a behavior.
*/
static gd::String GetBehaviorSharedPropertyGetterName(
const gd::String& propertyName);
/**
* \brief Generate the name of the method to set the value of the shared property
* of a behavior.
*/
static gd::String GetBehaviorSharedPropertySetterName(
const gd::String& propertyName);
private:
/**
* \brief Generate the name of the method to get the value of the shared property
* of a behavior form within the shared data class.
*/
static gd::String GetBehaviorSharedPropertyGetterInternalName(
const gd::String& propertyName);
/**
* \brief Generate the name of the method to set the value of the shared property
* of a behavior form within the shared data class.
*/
static gd::String GetBehaviorSharedPropertySetterInternalName(
const gd::String& propertyName);
gd::String GenerateRuntimeBehaviorTemplateCode(
const gd::String& extensionName,
const gd::EventsBasedBehavior& eventsBasedBehavior,
const gd::String& codeNamespace,
std::function<gd::String()> generateInitializePropertiesCode,
std::function<gd::String()> generatePropertiesCode,
std::function<gd::String()> generateInitializeSharedPropertiesCode,
std::function<gd::String()> generateSharedPropertiesCode,
std::function<gd::String()> generateMethodsCode,
std::function<gd::String()> generatePropertiesCode,
std::function<gd::String()> generateUpdateFromBehaviorDataCode);
gd::String GenerateRuntimeBehaviorPropertyTemplateCode(
const gd::EventsBasedBehavior& eventsBasedBehavior,
const gd::NamedPropertyDescriptor& property);
gd::String GenerateInitializePropertyFromDataCode(
const gd::NamedPropertyDescriptor& property);
gd::String GenerateInitializePropertyFromDefaultValueCode(
const gd::NamedPropertyDescriptor& property);
gd::String GenerateRuntimeBehaviorSharedPropertyTemplateCode(
const gd::EventsBasedBehavior& eventsBasedBehavior,
const gd::NamedPropertyDescriptor& property);
gd::String GenerateInitializeSharedPropertyFromDataCode(
const gd::NamedPropertyDescriptor& property);
gd::String GenerateInitializeSharedPropertyFromDefaultValueCode(
const gd::NamedPropertyDescriptor& property);
gd::String GeneratePropertyValueCode(const gd::PropertyDescriptor& property);
gd::String GenerateUpdatePropertyFromBehaviorDataCode(
const gd::EventsBasedBehavior& eventsBasedBehavior,
const gd::NamedPropertyDescriptor& property);
gd::String GenerateBehaviorOnDestroyToDeprecatedOnOwnerRemovedFromScene(
const gd::EventsBasedBehavior& eventsBasedBehavior,
const gd::String& codeNamespace);
gd::String GenerateDefaultDoStepPreEventsFunctionCode(
const gd::EventsBasedBehavior& eventsBasedBehavior,
const gd::String& codeNamespace);
gd::String GenerateDoStepPreEventsPreludeCode();
gd::Project& project;

View File

@@ -121,62 +121,6 @@ AdvancedExtension::AdvancedExtension() {
"eventsFunctionContext.getArgument(" +
parameterNameCode + ") : \"\")";
});
GetAllConditions()["CompareArgumentAsNumber"]
.GetCodeExtraInformation()
.SetCustomCodeGenerator([](gd::Instruction &instruction,
gd::EventsCodeGenerator &codeGenerator,
gd::EventsCodeGenerationContext &context) {
gd::String parameterNameCode =
gd::ExpressionCodeGenerator::GenerateExpressionCode(
codeGenerator, context, "string",
instruction.GetParameter(0).GetPlainString());
gd::String operatorCode = codeGenerator.GenerateRelationalOperatorCodes(
instruction.GetParameter(1).GetPlainString());
gd::String operandCode =
gd::ExpressionCodeGenerator::GenerateExpressionCode(
codeGenerator, context, "number",
instruction.GetParameter(2).GetPlainString());
gd::String resultingBoolean =
codeGenerator.GenerateBooleanFullName("conditionTrue", context) +
".val";
return resultingBoolean + " = ((typeof eventsFunctionContext !== 'undefined' ? "
"Number(eventsFunctionContext.getArgument(" +
parameterNameCode + ")) || 0 : 0) " + operatorCode + " " +
operandCode + ");\n";
});
GetAllConditions()["CompareArgumentAsString"]
.GetCodeExtraInformation()
.SetCustomCodeGenerator([](gd::Instruction &instruction,
gd::EventsCodeGenerator &codeGenerator,
gd::EventsCodeGenerationContext &context) {
gd::String parameterNameCode =
gd::ExpressionCodeGenerator::GenerateExpressionCode(
codeGenerator, context, "string",
instruction.GetParameter(0).GetPlainString());
gd::String operatorCode = codeGenerator.GenerateRelationalOperatorCodes(
instruction.GetParameter(1).GetPlainString());
gd::String operandCode =
gd::ExpressionCodeGenerator::GenerateExpressionCode(
codeGenerator, context, "string",
instruction.GetParameter(2).GetPlainString());
gd::String resultingBoolean =
codeGenerator.GenerateBooleanFullName("conditionTrue", context) +
".val";
return resultingBoolean + " = ((typeof eventsFunctionContext !== 'undefined' ? "
"\"\" + eventsFunctionContext.getArgument(" +
parameterNameCode + ") : \"\") " + operatorCode + " " +
operandCode + ");\n";
});
}
} // namespace gdjs
} // namespace gdjs

View File

@@ -55,24 +55,40 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
codeGenerator,
context,
"number",
instruction.GetParameter(0).GetPlainString());
gd::String operatorCode = codeGenerator.GenerateRelationalOperatorCodes(
instruction.GetParameter(1).GetPlainString());
instruction.GetParameters()[0].GetPlainString());
gd::String value2Code =
gd::ExpressionCodeGenerator::GenerateExpressionCode(
codeGenerator,
context,
"number",
instruction.GetParameter(2).GetPlainString());
instruction.GetParameters()[2].GetPlainString());
gd::String resultingBoolean =
codeGenerator.GenerateBooleanFullName("conditionTrue", context) +
".val";
return resultingBoolean + " = (" + value1Code + " " + operatorCode +
" " + value2Code + ");\n";
if (instruction.GetParameters()[1].GetPlainString() == "=" ||
instruction.GetParameters()[1].GetPlainString().empty())
return resultingBoolean + " = (" + value1Code + " == " + value2Code +
");\n";
else if (instruction.GetParameters()[1].GetPlainString() == ">")
return resultingBoolean + " = (" + value1Code + " > " + value2Code +
");\n";
else if (instruction.GetParameters()[1].GetPlainString() == "<")
return resultingBoolean + " = (" + value1Code + " < " + value2Code +
");\n";
else if (instruction.GetParameters()[1].GetPlainString() == "<=")
return resultingBoolean + " = (" + value1Code + " <= " + value2Code +
");\n";
else if (instruction.GetParameters()[1].GetPlainString() == ">=")
return resultingBoolean + " = (" + value1Code + " >= " + value2Code +
");\n";
else if (instruction.GetParameters()[1].GetPlainString() == "!=")
return resultingBoolean + " = (" + value1Code + " != " + value2Code +
");\n";
return gd::String("");
});
GetAllConditions()["BuiltinCommonInstructions::CompareNumbers"]
.codeExtraInformation = GetAllConditions()["Egal"].codeExtraInformation;
@@ -86,24 +102,27 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
codeGenerator,
context,
"string",
instruction.GetParameter(0).GetPlainString());
gd::String operatorCode = codeGenerator.GenerateRelationalOperatorCodes(
instruction.GetParameter(1).GetPlainString());
instruction.GetParameters()[0].GetPlainString());
gd::String value2Code =
gd::ExpressionCodeGenerator::GenerateExpressionCode(
codeGenerator,
context,
"string",
instruction.GetParameter(2).GetPlainString());
instruction.GetParameters()[2].GetPlainString());
gd::String resultingBoolean =
codeGenerator.GenerateBooleanFullName("conditionTrue", context) +
".val";
return resultingBoolean + " = (" + value1Code + " " + operatorCode +
" " + value2Code + ");\n";
if (instruction.GetParameters()[1].GetPlainString() == "=")
return resultingBoolean + " = (" + value1Code + " == " + value2Code +
");\n";
else if (instruction.GetParameters()[1].GetPlainString() == "!=")
return resultingBoolean + " = (" + value1Code + " != " + value2Code +
");\n";
return gd::String("");
});
GetAllConditions()["BuiltinCommonInstructions::CompareStrings"]
.codeExtraInformation =

View File

@@ -71,7 +71,6 @@ MathematicalToolsExtension::MathematicalToolsExtension() {
GetAllExpressions()["XFromAngleAndDistance"].SetFunctionName("gdjs.evtTools.common.getXFromAngleAndDistance");
GetAllExpressions()["YFromAngleAndDistance"].SetFunctionName("gdjs.evtTools.common.getYFromAngleAndDistance");
GetAllExpressions()["Pi"].SetFunctionName("gdjs.evtTools.common.pi");
GetAllExpressions()["lerpAngle"].SetFunctionName("gdjs.evtTools.common.lerpAngle");
StripUnimplementedInstructionsAndExpressions();
}

View File

@@ -46,7 +46,6 @@ SpriteExtension::SpriteExtension() {
spriteConditions["Direction"].SetFunctionName("getDirectionOrAngle");
spriteConditions["Sprite"].SetFunctionName("getAnimationFrame");
spriteConditions["AnimationEnded"].SetFunctionName("hasAnimationEnded");
spriteConditions["AnimationEnded2"].SetFunctionName("hasAnimationEnded2");
spriteActions["PauseAnimation"].SetFunctionName("pauseAnimation");
spriteActions["PlayAnimation"].SetFunctionName("playAnimation");
spriteConditions["AnimStopped"].SetFunctionName("animationPaused");

View File

@@ -265,6 +265,7 @@ namespace gdjs {
cy *= absScaleY;
// Rotation
const oldX = x;
const angleInRadians = (this.angle / 180) * Math.PI;
const cosValue = Math.cos(angleInRadians);
const sinValue = Math.sin(angleInRadians);

View File

@@ -5,6 +5,9 @@
*/
namespace gdjs {
const logger = new gdjs.Logger('CustomRuntimeObject');
const setupWarningLogger = new gdjs.Logger(
'CustomRuntimeObject (setup warnings)'
);
/**
* The instance container of a custom object, containing instances of objects rendered on screen.
@@ -197,8 +200,12 @@ namespace gdjs {
*/
_updateObjectsPreRender() {
const allInstancesList = this.getAdhocListOfAllInstances();
for (let i = 0, len = allInstancesList.length; i < len; ++i) {
const object = allInstancesList[i];
for (
let i = 0, len = this.getAdhocListOfAllInstances().length;
i < len;
++i
) {
const object = this.getAdhocListOfAllInstances()[i];
const rendererObject = object.getRendererObject();
if (rendererObject) {
rendererObject.visible = !object.isHidden();
@@ -215,7 +222,7 @@ namespace gdjs {
// to see what is rendered).
if (this._debugDrawEnabled) {
this._debuggerRenderer.renderDebugDraw(
allInstancesList,
this.getAdhocListOfAllInstances(),
this._debugDrawShowHiddenInstances,
this._debugDrawShowPointsNames,
this._debugDrawShowCustomPoints

View File

@@ -5,13 +5,14 @@
*/
namespace gdjs {
const logger = new gdjs.Logger('RuntimeInstanceContainer');
const setupWarningLogger = new gdjs.Logger(
'RuntimeInstanceContainer (setup warnings)'
);
/**
* A container of object instances rendered on screen.
*/
export abstract class RuntimeInstanceContainer {
_initialBehaviorSharedData: Hashtable<BehaviorSharedData | null>;
/** Contains the instances living on the container */
_instances: Hashtable<RuntimeObject[]>;
@@ -42,7 +43,6 @@ namespace gdjs {
_debugDrawShowCustomPoints: boolean = false;
constructor() {
this._initialBehaviorSharedData = new Hashtable();
this._instances = new Hashtable();
this._instancesCache = new Hashtable();
this._objects = new Hashtable();
@@ -266,32 +266,6 @@ namespace gdjs {
}
}
/**
* Get the data representing the initial shared data of the scene for the specified behavior.
* @param name The name of the behavior
* @returns The shared data for the behavior, if any.
*/
getInitialSharedDataForBehavior(name: string): BehaviorSharedData | null {
const behaviorSharedData = this._initialBehaviorSharedData.get(name);
if (behaviorSharedData) {
return behaviorSharedData;
}
logger.error("Can't find shared data for behavior with name: " + name);
return null;
}
/**
* Set the data representing the initial shared data of the scene for the specified behavior.
* @param name The name of the behavior
* @param sharedData The shared data for the behavior, or null to remove it.
*/
setInitialSharedDataForBehavior(
name: string,
sharedData: BehaviorSharedData | null
): void {
this._initialBehaviorSharedData.put(name, sharedData);
}
/**
* Set the default Z order for each layer, which is the highest Z order found on each layer.
* Useful as it ensures that instances created from events are, by default, shown in front

View File

@@ -408,30 +408,6 @@ namespace gdjs {
);
}
/**
* Callback called when the game is paused.
*/
sendGamePaused(): void {
this._sendMessage(
circularSafeStringify({
command: 'game.paused',
payload: null,
})
);
}
/**
* Callback called when the game is resumed.
*/
sendGameResumed(): void {
this._sendMessage(
circularSafeStringify({
command: 'game.resumed',
payload: null,
})
);
}
/**
* Send profiling results.
* @param framesAverageMeasures The measures made for each frames.

Some files were not shown because too many files have changed in this diff Show More