mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
Compare commits
107 Commits
experiment
...
v5.4.213
Author | SHA1 | Date | |
---|---|---|---|
![]() |
6cda5d08be | ||
![]() |
1a3a27b73b | ||
![]() |
96d912a6f2 | ||
![]() |
ed3acd5f0d | ||
![]() |
3f269206d1 | ||
![]() |
76b5aefdbc | ||
![]() |
9ef7af803c | ||
![]() |
c77f9b9e0c | ||
![]() |
035ddb8a7a | ||
![]() |
e7dac1bafc | ||
![]() |
54f00e7c57 | ||
![]() |
0bf9dae2b0 | ||
![]() |
428aac8ab0 | ||
![]() |
9391fc2841 | ||
![]() |
cea34337c6 | ||
![]() |
dc45f3dae5 | ||
![]() |
0ca26a865e | ||
![]() |
1bce13f326 | ||
![]() |
34f8f5750a | ||
![]() |
3b9a612094 | ||
![]() |
3a84ed7c89 | ||
![]() |
ef604fd442 | ||
![]() |
d88dc4772f | ||
![]() |
02d40a1d52 | ||
![]() |
35082825d4 | ||
![]() |
6a7c3daa8e | ||
![]() |
95af02bada | ||
![]() |
30516a903e | ||
![]() |
762f7ca19c | ||
![]() |
852bf78c81 | ||
![]() |
b0da0cee34 | ||
![]() |
73771f938b | ||
![]() |
b9dbe6dbb5 | ||
![]() |
0c2341c6e5 | ||
![]() |
e9b4de2ca9 | ||
![]() |
75a4114ce8 | ||
![]() |
20abb9b45a | ||
![]() |
56436fd44a | ||
![]() |
0dd5fc55c9 | ||
![]() |
93db4cb508 | ||
![]() |
f7888abf45 | ||
![]() |
c8144da704 | ||
![]() |
832e8cd593 | ||
![]() |
ef66a9f1a4 | ||
![]() |
5efbaa8c58 | ||
![]() |
ecbf38ccda | ||
![]() |
b3fcfc3f55 | ||
![]() |
a515836add | ||
![]() |
a7c81b47b2 | ||
![]() |
0f22e462ad | ||
![]() |
e6e4d9048f | ||
![]() |
12f5f95d0c | ||
![]() |
c52168a967 | ||
![]() |
1e33a13cc5 | ||
![]() |
505debd60c | ||
![]() |
e3b7109154 | ||
![]() |
9e25899d3e | ||
![]() |
87cb8f0d47 | ||
![]() |
481c6da992 | ||
![]() |
7cbebbb82f | ||
![]() |
fcf668788b | ||
![]() |
0cc844a77f | ||
![]() |
a234d9bd35 | ||
![]() |
465a6ce2ab | ||
![]() |
7e2e19eb33 | ||
![]() |
95101763f7 | ||
![]() |
d4bd5fc671 | ||
![]() |
c7fcf48ba5 | ||
![]() |
8926d4406f | ||
![]() |
9ed2173038 | ||
![]() |
2fc3bc337f | ||
![]() |
0b7cac79ef | ||
![]() |
8721c0099e | ||
![]() |
4453eee3b9 | ||
![]() |
0215ab7dbb | ||
![]() |
87f6d5b99f | ||
![]() |
a440b16f84 | ||
![]() |
f3822ba0df | ||
![]() |
6c5813affd | ||
![]() |
be4fe62bb6 | ||
![]() |
0a29999894 | ||
![]() |
e8ac41f37e | ||
![]() |
70657ae334 | ||
![]() |
39ae2d4852 | ||
![]() |
39815bfe6c | ||
![]() |
9ff8db25dd | ||
![]() |
1da887f656 | ||
![]() |
e88ae0a7a9 | ||
![]() |
7ae74990b2 | ||
![]() |
22ea1ce42c | ||
![]() |
a54367e360 | ||
![]() |
011abaf808 | ||
![]() |
a606a6567a | ||
![]() |
c9a6b88422 | ||
![]() |
e7f7fb1583 | ||
![]() |
3cbb6644d4 | ||
![]() |
d34db53e09 | ||
![]() |
9c62a5e0f1 | ||
![]() |
4b04101638 | ||
![]() |
ad31a7843a | ||
![]() |
57371c3759 | ||
![]() |
6e7fc75e75 | ||
![]() |
65653c92d6 | ||
![]() |
a4106b7f79 | ||
![]() |
3e9f2f3f3a | ||
![]() |
45f25df292 | ||
![]() |
55eddb4972 |
2
.vscode/GDevelopExtensions.code-snippets
vendored
2
.vscode/GDevelopExtensions.code-snippets
vendored
@@ -107,7 +107,7 @@
|
||||
"description": "Define a parameter in a GDevelop extension definition.",
|
||||
"prefix": "gdparam",
|
||||
"body": [
|
||||
".addParameter('${1|string,expression,object,behavior,yesorno,stringWithSelector,scenevar,globalvar,objectvar,objectList,objectListWithoutPicking,color,key,sceneName,file,layer,relationalOperator,operator,trueorfalse,musicfile,soundfile,police,mouse,passwordjoyaxis,camera,objectPtr,forceMultiplier|}', '${2:Parameter description}', '${3:Optional parameter data}', /*parameterIsOptional=*/${4|false,true|})"
|
||||
".addParameter('${1|string,expression,object,behavior,yesorno,stringWithSelector,scenevar,globalvar,objectvar,objectList,objectListWithoutPicking,color,key,sceneName,file,layer,relationalOperator,operator,trueorfalse,musicfile,soundfile,mouse,passwordjoyaxis,camera,objectPtr,forceMultiplier|}', '${2:Parameter description}', '${3:Optional parameter data}', /*parameterIsOptional=*/${4|false,true|})"
|
||||
]
|
||||
},
|
||||
"Add code only parameter": {
|
||||
|
@@ -60,7 +60,7 @@ if("${CMAKE_BUILD_TYPE}" STREQUAL "Release" AND NOT WIN32 AND CMAKE_COMPILER_IS_
|
||||
endif()
|
||||
|
||||
#Activate C++11
|
||||
set(CMAKE_CXX_STANDARD 11) # Upgrading to C++17 would need to remove usage of bind2nd (should be easy).
|
||||
set(CMAKE_CXX_STANDARD 11) # Upgrading to C++17 should be tried.
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
# Mark some warnings as errors
|
||||
|
@@ -11,6 +11,11 @@ set(CMAKE_CXX_USE_RESPONSE_FILE_FOR_INCLUDES 1)
|
||||
set(GDCORE_include_dir ${GD_base_dir}/Core PARENT_SCOPE)
|
||||
set(GDCORE_lib_dir ${GD_base_dir}/Binaries/Output/${CMAKE_BUILD_TYPE}_${CMAKE_SYSTEM_NAME} PARENT_SCOPE)
|
||||
|
||||
# Create VersionPriv.h - only useful for testing.
|
||||
if (NOT EMSCRIPTEN)
|
||||
file(WRITE "${GD_base_dir}/Core/GDCore/Tools/VersionPriv.h" "#define GD_VERSION_STRING \"0.0.0-0\"")
|
||||
endif()
|
||||
|
||||
# Dependencies on external libraries:
|
||||
#
|
||||
|
||||
|
@@ -42,14 +42,15 @@ gd::String EventsCodeGenerator::GenerateRelationalOperatorCall(
|
||||
const vector<gd::String>& arguments,
|
||||
const gd::String& callStartString,
|
||||
std::size_t startFromArgument) {
|
||||
std::size_t relationalOperatorIndex = instrInfos.parameters.size();
|
||||
for (std::size_t i = startFromArgument; i < instrInfos.parameters.size();
|
||||
std::size_t relationalOperatorIndex = instrInfos.parameters.GetParametersCount();
|
||||
for (std::size_t i = startFromArgument; i < instrInfos.parameters.GetParametersCount();
|
||||
++i) {
|
||||
if (instrInfos.parameters[i].GetType() == "relationalOperator")
|
||||
if (instrInfos.parameters.GetParameter(i).GetType() == "relationalOperator") {
|
||||
relationalOperatorIndex = i;
|
||||
}
|
||||
}
|
||||
// Ensure that there is at least one parameter after the relational operator
|
||||
if (relationalOperatorIndex + 1 >= instrInfos.parameters.size()) {
|
||||
if (relationalOperatorIndex + 1 >= instrInfos.parameters.GetParametersCount()) {
|
||||
ReportError();
|
||||
return "";
|
||||
}
|
||||
@@ -76,11 +77,11 @@ gd::String EventsCodeGenerator::GenerateRelationalOperatorCall(
|
||||
|
||||
/**
|
||||
* @brief Generate a relational operation
|
||||
*
|
||||
*
|
||||
* @param relationalOperator the operator
|
||||
* @param lhs the left hand operand
|
||||
* @param rhs the right hand operand
|
||||
* @return gd::String
|
||||
* @return gd::String
|
||||
*/
|
||||
gd::String EventsCodeGenerator::GenerateRelationalOperation(
|
||||
const gd::String& relationalOperator,
|
||||
@@ -122,14 +123,16 @@ gd::String EventsCodeGenerator::GenerateOperatorCall(
|
||||
const gd::String& callStartString,
|
||||
const gd::String& getterStartString,
|
||||
std::size_t startFromArgument) {
|
||||
std::size_t operatorIndex = instrInfos.parameters.size();
|
||||
for (std::size_t i = startFromArgument; i < instrInfos.parameters.size();
|
||||
std::size_t operatorIndex = instrInfos.parameters.GetParametersCount();
|
||||
for (std::size_t i = startFromArgument; i < instrInfos.parameters.GetParametersCount();
|
||||
++i) {
|
||||
if (instrInfos.parameters[i].GetType() == "operator") operatorIndex = i;
|
||||
if (instrInfos.parameters.GetParameter(i).GetType() == "operator") {
|
||||
operatorIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that there is at least one parameter after the operator
|
||||
if (operatorIndex + 1 >= instrInfos.parameters.size()) {
|
||||
if (operatorIndex + 1 >= instrInfos.parameters.GetParametersCount()) {
|
||||
ReportError();
|
||||
return "";
|
||||
}
|
||||
@@ -191,14 +194,16 @@ gd::String EventsCodeGenerator::GenerateCompoundOperatorCall(
|
||||
const vector<gd::String>& arguments,
|
||||
const gd::String& callStartString,
|
||||
std::size_t startFromArgument) {
|
||||
std::size_t operatorIndex = instrInfos.parameters.size();
|
||||
for (std::size_t i = startFromArgument; i < instrInfos.parameters.size();
|
||||
std::size_t operatorIndex = instrInfos.parameters.GetParametersCount();
|
||||
for (std::size_t i = startFromArgument; i < instrInfos.parameters.GetParametersCount();
|
||||
++i) {
|
||||
if (instrInfos.parameters[i].GetType() == "operator") operatorIndex = i;
|
||||
if (instrInfos.parameters.GetParameter(i).GetType() == "operator") {
|
||||
operatorIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that there is at least one parameter after the operator
|
||||
if (operatorIndex + 1 >= instrInfos.parameters.size()) {
|
||||
if (operatorIndex + 1 >= instrInfos.parameters.GetParametersCount()) {
|
||||
ReportError();
|
||||
return "";
|
||||
}
|
||||
@@ -242,14 +247,16 @@ gd::String EventsCodeGenerator::GenerateMutatorCall(
|
||||
const vector<gd::String>& arguments,
|
||||
const gd::String& callStartString,
|
||||
std::size_t startFromArgument) {
|
||||
std::size_t operatorIndex = instrInfos.parameters.size();
|
||||
for (std::size_t i = startFromArgument; i < instrInfos.parameters.size();
|
||||
std::size_t operatorIndex = instrInfos.parameters.GetParametersCount();
|
||||
for (std::size_t i = startFromArgument; i < instrInfos.parameters.GetParametersCount();
|
||||
++i) {
|
||||
if (instrInfos.parameters[i].GetType() == "operator") operatorIndex = i;
|
||||
if (instrInfos.parameters.GetParameter(i).GetType() == "operator") {
|
||||
operatorIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that there is at least one parameter after the operator
|
||||
if (operatorIndex + 1 >= instrInfos.parameters.size()) {
|
||||
if (operatorIndex + 1 >= instrInfos.parameters.GetParametersCount()) {
|
||||
ReportError();
|
||||
return "";
|
||||
}
|
||||
@@ -316,7 +323,7 @@ gd::String EventsCodeGenerator::GenerateConditionCode(
|
||||
}
|
||||
|
||||
// Insert code only parameters and be sure there is no lack of parameter.
|
||||
while (condition.GetParameters().size() < instrInfos.parameters.size()) {
|
||||
while (condition.GetParameters().size() < instrInfos.parameters.GetParametersCount()) {
|
||||
vector<gd::Expression> parameters = condition.GetParameters();
|
||||
parameters.push_back(gd::Expression(""));
|
||||
condition.SetParameters(parameters);
|
||||
@@ -324,13 +331,13 @@ gd::String EventsCodeGenerator::GenerateConditionCode(
|
||||
|
||||
gd::EventsCodeGenerator::CheckBehaviorParameters(condition, instrInfos);
|
||||
// Verify that there are no mismatches between object type in parameters.
|
||||
for (std::size_t pNb = 0; pNb < instrInfos.parameters.size(); ++pNb) {
|
||||
if (ParameterMetadata::IsObject(instrInfos.parameters[pNb].GetType())) {
|
||||
for (std::size_t pNb = 0; pNb < instrInfos.parameters.GetParametersCount(); ++pNb) {
|
||||
if (ParameterMetadata::IsObject(instrInfos.parameters.GetParameter(pNb).GetType())) {
|
||||
gd::String objectInParameter =
|
||||
condition.GetParameter(pNb).GetPlainString();
|
||||
|
||||
const auto &expectedObjectType =
|
||||
instrInfos.parameters[pNb].GetExtraInfo();
|
||||
instrInfos.parameters.GetParameter(pNb).GetExtraInfo();
|
||||
const auto &actualObjectType =
|
||||
GetObjectsContainersList().GetTypeOfObject(objectInParameter);
|
||||
if (!GetObjectsContainersList().HasObjectOrGroupNamed(
|
||||
@@ -353,7 +360,7 @@ gd::String EventsCodeGenerator::GenerateConditionCode(
|
||||
|
||||
if (instrInfos.IsObjectInstruction()) {
|
||||
gd::String objectName = condition.GetParameter(0).GetPlainString();
|
||||
if (!objectName.empty() && !instrInfos.parameters.empty()) {
|
||||
if (!objectName.empty() && instrInfos.parameters.GetParametersCount() > 0) {
|
||||
std::vector<gd::String> realObjects =
|
||||
GetObjectsContainersList().ExpandObjectName(objectName, context.GetCurrentObject());
|
||||
for (std::size_t i = 0; i < realObjects.size(); ++i) {
|
||||
@@ -381,7 +388,7 @@ gd::String EventsCodeGenerator::GenerateConditionCode(
|
||||
}
|
||||
}
|
||||
} else if (instrInfos.IsBehaviorInstruction()) {
|
||||
if (instrInfos.parameters.size() >= 2) {
|
||||
if (instrInfos.parameters.GetParametersCount() >= 2) {
|
||||
const gd::String &objectName = condition.GetParameter(0).GetPlainString();
|
||||
const gd::String &behaviorName =
|
||||
condition.GetParameter(1).GetPlainString();
|
||||
@@ -539,7 +546,7 @@ gd::String EventsCodeGenerator::GenerateActionCode(
|
||||
: instrInfos.codeExtraInformation.functionCallName;
|
||||
|
||||
// Be sure there is no lack of parameter.
|
||||
while (action.GetParameters().size() < instrInfos.parameters.size()) {
|
||||
while (action.GetParameters().size() < instrInfos.parameters.GetParametersCount()) {
|
||||
vector<gd::Expression> parameters = action.GetParameters();
|
||||
parameters.push_back(gd::Expression(""));
|
||||
action.SetParameters(parameters);
|
||||
@@ -547,12 +554,12 @@ gd::String EventsCodeGenerator::GenerateActionCode(
|
||||
|
||||
gd::EventsCodeGenerator::CheckBehaviorParameters(action, instrInfos);
|
||||
// Verify that there are no mismatches between object type in parameters.
|
||||
for (std::size_t pNb = 0; pNb < instrInfos.parameters.size(); ++pNb) {
|
||||
if (ParameterMetadata::IsObject(instrInfos.parameters[pNb].GetType())) {
|
||||
for (std::size_t pNb = 0; pNb < instrInfos.parameters.GetParametersCount(); ++pNb) {
|
||||
if (ParameterMetadata::IsObject(instrInfos.parameters.GetParameter(pNb).GetType())) {
|
||||
gd::String objectInParameter = action.GetParameter(pNb).GetPlainString();
|
||||
|
||||
const auto &expectedObjectType =
|
||||
instrInfos.parameters[pNb].GetExtraInfo();
|
||||
instrInfos.parameters.GetParameter(pNb).GetExtraInfo();
|
||||
const auto &actualObjectType =
|
||||
GetObjectsContainersList().GetTypeOfObject(objectInParameter);
|
||||
if (!GetObjectsContainersList().HasObjectOrGroupNamed(
|
||||
@@ -577,7 +584,7 @@ gd::String EventsCodeGenerator::GenerateActionCode(
|
||||
if (instrInfos.IsObjectInstruction()) {
|
||||
gd::String objectName = action.GetParameter(0).GetPlainString();
|
||||
|
||||
if (!instrInfos.parameters.empty()) {
|
||||
if (instrInfos.parameters.GetParametersCount() > 0) {
|
||||
std::vector<gd::String> realObjects =
|
||||
GetObjectsContainersList().ExpandObjectName(objectName, context.GetCurrentObject());
|
||||
for (std::size_t i = 0; i < realObjects.size(); ++i) {
|
||||
@@ -605,7 +612,7 @@ gd::String EventsCodeGenerator::GenerateActionCode(
|
||||
}
|
||||
}
|
||||
} else if (instrInfos.IsBehaviorInstruction()) {
|
||||
if (instrInfos.parameters.size() >= 2) {
|
||||
if (instrInfos.parameters.GetParametersCount() >= 2) {
|
||||
const gd::String &objectName = action.GetParameter(0).GetPlainString();
|
||||
const gd::String &behaviorName = action.GetParameter(1).GetPlainString();
|
||||
const gd::String &actualBehaviorType =
|
||||
@@ -821,7 +828,7 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
|
||||
metadata.GetType() == "spineResource" ||
|
||||
// Deprecated, old parameter names:
|
||||
metadata.GetType() == "password" || metadata.GetType() == "musicfile" ||
|
||||
metadata.GetType() == "soundfile" || metadata.GetType() == "police") {
|
||||
metadata.GetType() == "soundfile") {
|
||||
argOutput = "\"" + ConvertToString(parameter.GetPlainString()) + "\"";
|
||||
} else if (metadata.GetType() == "mouse") {
|
||||
argOutput = "\"" + ConvertToString(parameter.GetPlainString()) + "\"";
|
||||
@@ -863,7 +870,7 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
|
||||
|
||||
vector<gd::String> EventsCodeGenerator::GenerateParametersCodes(
|
||||
const vector<gd::Expression>& parameters,
|
||||
const vector<gd::ParameterMetadata>& parametersInfo,
|
||||
const ParameterMetadataContainer& parametersInfo,
|
||||
EventsCodeGenerationContext& context,
|
||||
std::vector<std::pair<gd::String, gd::String> >*
|
||||
supplementaryParametersTypes) {
|
||||
@@ -1000,7 +1007,7 @@ gd::String EventsCodeGenerator::GenerateEventsListCode(
|
||||
|
||||
output += "\n" + scopeBegin + "\n" + declarationsCode + "\n" +
|
||||
eventCoreCode + "\n" + scopeEnd + "\n";
|
||||
|
||||
|
||||
if (event.HasVariables()) {
|
||||
GetProjectScopedContainers().GetVariablesContainersList().Pop();
|
||||
}
|
||||
@@ -1100,10 +1107,10 @@ gd::String EventsCodeGenerator::GenerateFreeCondition(
|
||||
|
||||
// Add logical not if needed
|
||||
bool conditionAlreadyTakeCareOfInversion = false;
|
||||
for (std::size_t i = 0; i < instrInfos.parameters.size();
|
||||
for (std::size_t i = 0; i < instrInfos.parameters.GetParametersCount();
|
||||
++i) // Some conditions already have a "conditionInverted" parameter
|
||||
{
|
||||
if (instrInfos.parameters[i].GetType() == "conditionInverted")
|
||||
if (instrInfos.parameters.GetParameter(i).GetType() == "conditionInverted")
|
||||
conditionAlreadyTakeCareOfInversion = true;
|
||||
}
|
||||
if (!conditionAlreadyTakeCareOfInversion && conditionInverted)
|
||||
@@ -1124,7 +1131,7 @@ gd::String EventsCodeGenerator::GenerateObjectCondition(
|
||||
// Prepare call
|
||||
// Add a static_cast if necessary
|
||||
gd::String objectFunctionCallNamePart =
|
||||
(!instrInfos.parameters[0].GetExtraInfo().empty())
|
||||
(!instrInfos.parameters.GetParameter(0).GetExtraInfo().empty())
|
||||
? "static_cast<" + objInfo.className + "*>(" +
|
||||
GetObjectListName(objectName, context) + "[i])->" +
|
||||
instrInfos.codeExtraInformation.functionCallName
|
||||
|
@@ -128,7 +128,7 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
*/
|
||||
std::vector<gd::String> GenerateParametersCodes(
|
||||
const std::vector<gd::Expression>& parameters,
|
||||
const std::vector<gd::ParameterMetadata>& parametersInfo,
|
||||
const ParameterMetadataContainer& parametersInfo,
|
||||
EventsCodeGenerationContext& context,
|
||||
std::vector<std::pair<gd::String, gd::String> >*
|
||||
supplementaryParametersTypes = 0);
|
||||
@@ -528,7 +528,7 @@ protected:
|
||||
parameter -> string
|
||||
* - operator : Used to update a value using a setter and a getter -> string
|
||||
* - key, mouse, objectvar, scenevar, globalvar, password, musicfile,
|
||||
soundfile, police -> string
|
||||
soundfile -> string
|
||||
* - trueorfalse, yesorno -> boolean ( See GenerateTrue/GenerateFalse ).
|
||||
*
|
||||
* <br><br>
|
||||
@@ -849,7 +849,7 @@ protected:
|
||||
instructionUniqueIds; ///< The unique ids generated for instructions.
|
||||
size_t eventsListNextUniqueId; ///< The next identifier to use for an events
|
||||
///< list function name.
|
||||
|
||||
|
||||
gd::DiagnosticReport* diagnosticReport;
|
||||
};
|
||||
|
||||
|
@@ -430,11 +430,11 @@ gd::String ExpressionCodeGenerator::GenerateParametersCodes(
|
||||
size_t nonCodeOnlyParameterIndex = 0;
|
||||
gd::String parametersCode;
|
||||
for (std::size_t i = initialParameterIndex;
|
||||
i < expressionMetadata.parameters.size();
|
||||
i < expressionMetadata.GetParameters().GetParametersCount();
|
||||
++i) {
|
||||
if (i != initialParameterIndex) parametersCode += ", ";
|
||||
|
||||
auto& parameterMetadata = expressionMetadata.parameters[i];
|
||||
auto& parameterMetadata = expressionMetadata.GetParameters().GetParameter(i);
|
||||
if (!parameterMetadata.IsCodeOnly()) {
|
||||
if (nonCodeOnlyParameterIndex < parameters.size()) {
|
||||
auto objectName = gd::ExpressionVariableOwnerFinder::GetObjectName(codeGenerator.GetPlatform(),
|
||||
|
@@ -182,10 +182,10 @@ void EventsListSerialization::UpdateInstructionsFromGD2x(
|
||||
// Common updates for some parameters
|
||||
const std::vector<gd::Expression>& parameters = instr.GetParameters();
|
||||
for (std::size_t j = 0;
|
||||
j < parameters.size() && j < metadata.parameters.size();
|
||||
j < parameters.size() && j < metadata.parameters.GetParametersCount();
|
||||
++j) {
|
||||
if (metadata.parameters[j].GetType() == "relationalOperator" ||
|
||||
metadata.parameters[j].GetType() == "operator") {
|
||||
if (metadata.parameters.GetParameter(j).GetType() == "relationalOperator" ||
|
||||
metadata.parameters.GetParameter(j).GetType() == "operator") {
|
||||
if (j == parameters.size() - 1) {
|
||||
std::cout << "ERROR: No more parameters after a [relational]operator "
|
||||
"when trying to update an instruction from GD2.x";
|
||||
|
@@ -24,18 +24,23 @@
|
||||
namespace gd {
|
||||
|
||||
SpriteObject::SpriteObject()
|
||||
: updateIfNotVisible(false) {}
|
||||
: updateIfNotVisible(false),
|
||||
preScale(1) {}
|
||||
|
||||
SpriteObject::~SpriteObject(){};
|
||||
|
||||
void SpriteObject::DoUnserializeFrom(gd::Project& project,
|
||||
const gd::SerializerElement& element) {
|
||||
updateIfNotVisible = element.GetBoolAttribute("updateIfNotVisible", true);
|
||||
preScale = element.GetDoubleAttribute("preScale", 1);
|
||||
animations.UnserializeFrom(element);
|
||||
}
|
||||
|
||||
void SpriteObject::DoSerializeTo(gd::SerializerElement& element) const {
|
||||
element.SetAttribute("updateIfNotVisible", updateIfNotVisible);
|
||||
if (preScale != 1) {
|
||||
element.SetAttribute("preScale", preScale);
|
||||
}
|
||||
animations.SerializeTo(element);
|
||||
}
|
||||
|
||||
|
@@ -82,6 +82,23 @@ class GD_CORE_API SpriteObject : public gd::ObjectConfiguration {
|
||||
*/
|
||||
bool GetUpdateIfNotVisible() const { return updateIfNotVisible; }
|
||||
|
||||
/**
|
||||
* \brief Return the scale applied to object to evaluate the default dimensions.
|
||||
*/
|
||||
double GetPreScale() { return preScale; }
|
||||
|
||||
/**
|
||||
* \brief Set the scale applied to object to evaluate the default dimensions.
|
||||
*
|
||||
* Its value must be strictly positive.
|
||||
*/
|
||||
void SetPreScale(double preScale_) {
|
||||
if (preScale_ <= 0) {
|
||||
return;
|
||||
}
|
||||
preScale = preScale_;
|
||||
}
|
||||
|
||||
private:
|
||||
void DoUnserializeFrom(gd::Project& project,
|
||||
const gd::SerializerElement& element) override;
|
||||
@@ -92,6 +109,7 @@ class GD_CORE_API SpriteObject : public gd::ObjectConfiguration {
|
||||
bool updateIfNotVisible; ///< If set to true, ask the game engine to play
|
||||
///< object animation even if hidden or far from
|
||||
///< the screen.
|
||||
double preScale;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -37,7 +37,8 @@ BehaviorMetadata::BehaviorMetadata(
|
||||
className(className_),
|
||||
iconFilename(icon24x24),
|
||||
instance(instance_),
|
||||
sharedDatasInstance(sharedDatasInstance_) {
|
||||
sharedDatasInstance(sharedDatasInstance_),
|
||||
quickCustomizationVisibility(QuickCustomization::Visibility::Default) {
|
||||
SetFullName(gd::String(fullname_));
|
||||
SetDescription(gd::String(description_));
|
||||
SetDefaultName(gd::String(defaultName_));
|
||||
@@ -424,7 +425,7 @@ std::map<gd::String, gd::PropertyDescriptor> BehaviorMetadata::GetProperties() c
|
||||
return instance->GetProperties();
|
||||
}
|
||||
|
||||
gd::BehaviorsSharedData* BehaviorMetadata::GetSharedDataInstance() const {
|
||||
gd::BehaviorsSharedData* BehaviorMetadata::GetSharedDataInstance() const {
|
||||
return sharedDatasInstance.get();
|
||||
}
|
||||
|
||||
@@ -440,12 +441,18 @@ std::map<gd::String, gd::PropertyDescriptor> BehaviorMetadata::GetSharedProperti
|
||||
|
||||
const std::vector<gd::String>& BehaviorMetadata::GetRequiredBehaviorTypes() const {
|
||||
requiredBehaviors.clear();
|
||||
for (auto& property : Get().GetProperties()) {
|
||||
if (!instance) {
|
||||
return requiredBehaviors;
|
||||
}
|
||||
for (auto& property : instance->GetProperties()) {
|
||||
const String& propertyName = property.first;
|
||||
const gd::PropertyDescriptor& propertyDescriptor = property.second;
|
||||
|
||||
if (propertyDescriptor.GetType() == "Behavior") {
|
||||
requiredBehaviors.push_back(propertyDescriptor.GetExtraInfo()[0]);
|
||||
const auto& extraInfos = propertyDescriptor.GetExtraInfo();
|
||||
if (extraInfos.size() > 0) {
|
||||
requiredBehaviors.push_back(extraInfos[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return requiredBehaviors;
|
||||
|
@@ -12,6 +12,7 @@
|
||||
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
|
||||
#include "GDCore/String.h"
|
||||
#include "GDCore/Project/QuickCustomization.h"
|
||||
namespace gd {
|
||||
class Behavior;
|
||||
class BehaviorsSharedData;
|
||||
@@ -41,10 +42,10 @@ class GD_CORE_API BehaviorMetadata : public InstructionOrExpressionContainerMeta
|
||||
const gd::String& className_,
|
||||
std::shared_ptr<gd::Behavior> instance,
|
||||
std::shared_ptr<gd::BehaviorsSharedData> sharedDatasInstance);
|
||||
|
||||
|
||||
/**
|
||||
* \brief Construct a behavior metadata, without "blueprint" behavior.
|
||||
*
|
||||
*
|
||||
* \note This is used by events based behaviors.
|
||||
*/
|
||||
BehaviorMetadata(
|
||||
@@ -297,9 +298,18 @@ class GD_CORE_API BehaviorMetadata : public InstructionOrExpressionContainerMeta
|
||||
return *this;
|
||||
}
|
||||
|
||||
QuickCustomization::Visibility GetQuickCustomizationVisibility() const {
|
||||
return quickCustomizationVisibility;
|
||||
}
|
||||
|
||||
BehaviorMetadata &SetQuickCustomizationVisibility(QuickCustomization::Visibility visibility) {
|
||||
quickCustomizationVisibility = visibility;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return the associated gd::Behavior, handling behavior contents.
|
||||
*
|
||||
*
|
||||
* \note Returns a dumb Behavior for events based behaviors as CustomBehavior
|
||||
* are using EventBasedBehavior.
|
||||
*/
|
||||
@@ -317,7 +327,7 @@ class GD_CORE_API BehaviorMetadata : public InstructionOrExpressionContainerMeta
|
||||
/**
|
||||
* \brief Return the associated gd::BehaviorsSharedData, handling behavior
|
||||
* shared data, if any (nullptr if none).
|
||||
*
|
||||
*
|
||||
* \note Returns nullptr for events based behaviors as they don't declare
|
||||
* shared data yet.
|
||||
*/
|
||||
@@ -374,6 +384,7 @@ class GD_CORE_API BehaviorMetadata : public InstructionOrExpressionContainerMeta
|
||||
mutable std::vector<gd::String> requiredBehaviors;
|
||||
bool isPrivate = false;
|
||||
bool isHidden = false;
|
||||
QuickCustomization::Visibility quickCustomizationVisibility;
|
||||
|
||||
// TODO: Nitpicking: convert these to std::unique_ptr to clarify ownership.
|
||||
std::shared_ptr<gd::Behavior> instance;
|
||||
|
@@ -38,12 +38,12 @@ gd::ExpressionMetadata& ExpressionMetadata::AddParameter(
|
||||
const gd::String& description,
|
||||
const gd::String& supplementaryInformation,
|
||||
bool parameterIsOptional) {
|
||||
gd::ParameterMetadata info;
|
||||
info.SetType(type);
|
||||
info.description = description;
|
||||
info.codeOnly = false;
|
||||
info.SetOptional(parameterIsOptional);
|
||||
info.SetExtraInfo(
|
||||
parameters.AddNewParameter("")
|
||||
.SetType(type)
|
||||
.SetDescription(description)
|
||||
.SetCodeOnly(false)
|
||||
.SetOptional(parameterIsOptional)
|
||||
.SetExtraInfo(
|
||||
// For objects/behavior, the supplementary information
|
||||
// parameter is an object/behavior type...
|
||||
((gd::ParameterMetadata::IsObject(type) ||
|
||||
@@ -59,22 +59,16 @@ gd::ExpressionMetadata& ExpressionMetadata::AddParameter(
|
||||
// TODO: Assert against supplementaryInformation === "emsc" (when running with
|
||||
// Emscripten), and warn about a missing argument when calling addParameter.
|
||||
|
||||
parameters.push_back(info);
|
||||
return *this;
|
||||
}
|
||||
|
||||
gd::ExpressionMetadata& ExpressionMetadata::AddCodeOnlyParameter(
|
||||
const gd::String& type, const gd::String& supplementaryInformation) {
|
||||
gd::ParameterMetadata info;
|
||||
info.SetType(type);
|
||||
info.codeOnly = true;
|
||||
info.SetExtraInfo(supplementaryInformation);
|
||||
|
||||
parameters.push_back(info);
|
||||
gd::ExpressionMetadata &ExpressionMetadata::AddCodeOnlyParameter(
|
||||
const gd::String &type, const gd::String &supplementaryInformation) {
|
||||
parameters.AddNewParameter("").SetType(type).SetCodeOnly().SetExtraInfo(
|
||||
supplementaryInformation);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
gd::ExpressionMetadata& ExpressionMetadata::SetRequiresBaseObjectCapability(
|
||||
const gd::String& capability) {
|
||||
requiredBaseObjectCapability = capability;
|
||||
|
@@ -193,8 +193,9 @@ class GD_CORE_API ExpressionMetadata : public gd::AbstractFunctionMetadata {
|
||||
* \see AddParameter
|
||||
*/
|
||||
ExpressionMetadata &SetDefaultValue(const gd::String &defaultValue) override {
|
||||
if (!parameters.empty())
|
||||
parameters.back().SetDefaultValue(defaultValue);
|
||||
if (parameters.GetParametersCount() > 0) {
|
||||
parameters.GetInternalVector().back()->SetDefaultValue(defaultValue);
|
||||
}
|
||||
return *this;
|
||||
};
|
||||
|
||||
@@ -206,8 +207,9 @@ class GD_CORE_API ExpressionMetadata : public gd::AbstractFunctionMetadata {
|
||||
*/
|
||||
ExpressionMetadata &
|
||||
SetParameterLongDescription(const gd::String &longDescription) override {
|
||||
if (!parameters.empty())
|
||||
parameters.back().SetLongDescription(longDescription);
|
||||
if (parameters.GetParametersCount() > 0) {
|
||||
parameters.GetInternalVector().back()->SetLongDescription(longDescription);
|
||||
}
|
||||
return *this;
|
||||
};
|
||||
|
||||
@@ -220,7 +222,9 @@ class GD_CORE_API ExpressionMetadata : public gd::AbstractFunctionMetadata {
|
||||
*/
|
||||
ExpressionMetadata &SetParameterExtraInfo(
|
||||
const gd::String &extraInfo) override {
|
||||
if (!parameters.empty()) parameters.back().SetExtraInfo(extraInfo);
|
||||
if (parameters.GetParametersCount() > 0) {
|
||||
parameters.GetInternalVector().back()->SetExtraInfo(extraInfo);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -248,19 +252,16 @@ class GD_CORE_API ExpressionMetadata : public gd::AbstractFunctionMetadata {
|
||||
const gd::String& GetGroup() const { return group; }
|
||||
const gd::String& GetSmallIconFilename() const { return smallIconFilename; }
|
||||
const gd::ParameterMetadata& GetParameter(std::size_t id) const {
|
||||
return parameters[id];
|
||||
return parameters.GetParameter(id);
|
||||
};
|
||||
gd::ParameterMetadata& GetParameter(std::size_t id) {
|
||||
return parameters[id];
|
||||
return parameters.GetParameter(id);
|
||||
};
|
||||
std::size_t GetParametersCount() const { return parameters.size(); };
|
||||
const std::vector<gd::ParameterMetadata>& GetParameters() const {
|
||||
std::size_t GetParametersCount() const { return parameters.GetParametersCount(); };
|
||||
const gd::ParameterMetadataContainer& GetParameters() const {
|
||||
return parameters;
|
||||
};
|
||||
|
||||
std::vector<gd::ParameterMetadata> parameters;
|
||||
|
||||
|
||||
/**
|
||||
* \brief Set the function name which will be used when generating the code.
|
||||
* \param functionName the name of the function to call
|
||||
@@ -368,6 +369,8 @@ class GD_CORE_API ExpressionMetadata : public gd::AbstractFunctionMetadata {
|
||||
bool isPrivate;
|
||||
gd::String requiredBaseObjectCapability;
|
||||
gd::String relevantContext;
|
||||
|
||||
gd::ParameterMetadataContainer parameters;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "GDCore/CommonTools.h"
|
||||
#include "GDCore/Extensions/PlatformExtension.h"
|
||||
#include "GDCore/Project/ParameterMetadataContainer.h"
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
#include "GDCore/Tools/Localization.h"
|
||||
#include "GDCore/Tools/Log.h"
|
||||
@@ -77,7 +78,7 @@ InstructionMetadata& InstructionMetadata::AddParameter(
|
||||
// TODO: Assert against supplementaryInformation === "emsc" (when running with
|
||||
// Emscripten), and warn about a missing argument when calling addParameter.
|
||||
|
||||
parameters.push_back(info);
|
||||
parameters.AddParameter(info);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -88,7 +89,7 @@ InstructionMetadata& InstructionMetadata::AddCodeOnlyParameter(
|
||||
info.codeOnly = true;
|
||||
info.SetExtraInfo(supplementaryInformation);
|
||||
|
||||
parameters.push_back(info);
|
||||
parameters.AddParameter(info);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -102,7 +103,7 @@ InstructionMetadata& InstructionMetadata::UseStandardOperatorParameters(
|
||||
AddParameter(
|
||||
"yesorno",
|
||||
options.description.empty() ? _("New value") : options.description);
|
||||
size_t valueParamIndex = parameters.size() - 1;
|
||||
size_t valueParamIndex = parameters.GetParametersCount() - 1;
|
||||
|
||||
if (isObjectInstruction || isBehaviorInstruction) {
|
||||
gd::String templateSentence = _("Set _PARAM0_ as <subject>: <value>");
|
||||
@@ -127,8 +128,8 @@ InstructionMetadata& InstructionMetadata::UseStandardOperatorParameters(
|
||||
options.description.empty() ? _("Value") : options.description,
|
||||
options.typeExtraInfo);
|
||||
|
||||
size_t operatorParamIndex = parameters.size() - 2;
|
||||
size_t valueParamIndex = parameters.size() - 1;
|
||||
size_t operatorParamIndex = parameters.GetParametersCount() - 2;
|
||||
size_t valueParamIndex = parameters.GetParametersCount() - 1;
|
||||
|
||||
if (isObjectInstruction || isBehaviorInstruction) {
|
||||
gd::String templateSentence = _("Change <subject> of _PARAM0_: <operator> <value>");
|
||||
@@ -181,8 +182,8 @@ InstructionMetadata::UseStandardRelationalOperatorParameters(
|
||||
AddParameter(type,
|
||||
options.description.empty() ? _("Value to compare") : options.description,
|
||||
options.typeExtraInfo);
|
||||
size_t operatorParamIndex = parameters.size() - 2;
|
||||
size_t valueParamIndex = parameters.size() - 1;
|
||||
size_t operatorParamIndex = parameters.GetParametersCount() - 2;
|
||||
size_t valueParamIndex = parameters.GetParametersCount() - 1;
|
||||
|
||||
if (isObjectInstruction || isBehaviorInstruction) {
|
||||
gd::String templateSentence = _("<subject> of _PARAM0_ <operator> <value>");
|
||||
|
@@ -14,6 +14,7 @@
|
||||
#include <memory>
|
||||
|
||||
#include "GDCore/Events/Instruction.h"
|
||||
#include "GDCore/Project/ParameterMetadataContainer.h"
|
||||
#include "GDCore/String.h"
|
||||
#include "ParameterMetadata.h"
|
||||
#include "ParameterOptions.h"
|
||||
@@ -61,12 +62,12 @@ class GD_CORE_API InstructionMetadata : public gd::AbstractFunctionMetadata {
|
||||
const gd::String &GetDescription() const { return description; }
|
||||
const gd::String &GetSentence() const { return sentence; }
|
||||
const gd::String &GetGroup() const { return group; }
|
||||
ParameterMetadata &GetParameter(size_t i) { return parameters[i]; }
|
||||
ParameterMetadata &GetParameter(size_t i) { return parameters.GetParameter(i); }
|
||||
const ParameterMetadata &GetParameter(size_t i) const {
|
||||
return parameters[i];
|
||||
return parameters.GetParameter(i);
|
||||
}
|
||||
size_t GetParametersCount() const { return parameters.size(); }
|
||||
const std::vector<ParameterMetadata> &GetParameters() const {
|
||||
size_t GetParametersCount() const { return parameters.GetParametersCount(); }
|
||||
const ParameterMetadataContainer &GetParameters() const {
|
||||
return parameters;
|
||||
}
|
||||
const gd::String &GetIconFilename() const { return iconFilename; }
|
||||
@@ -256,7 +257,9 @@ class GD_CORE_API InstructionMetadata : public gd::AbstractFunctionMetadata {
|
||||
* \see AddParameter
|
||||
*/
|
||||
InstructionMetadata &SetDefaultValue(const gd::String &defaultValue_) override {
|
||||
if (!parameters.empty()) parameters.back().SetDefaultValue(defaultValue_);
|
||||
if (parameters.GetParametersCount() > 0) {
|
||||
parameters.GetInternalVector().back()->SetDefaultValue(defaultValue_);
|
||||
}
|
||||
return *this;
|
||||
};
|
||||
|
||||
@@ -268,8 +271,9 @@ class GD_CORE_API InstructionMetadata : public gd::AbstractFunctionMetadata {
|
||||
*/
|
||||
InstructionMetadata &SetParameterLongDescription(
|
||||
const gd::String &longDescription) override {
|
||||
if (!parameters.empty())
|
||||
parameters.back().SetLongDescription(longDescription);
|
||||
if (parameters.GetParametersCount() > 0) {
|
||||
parameters.GetInternalVector().back()->SetLongDescription(longDescription);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -281,7 +285,9 @@ class GD_CORE_API InstructionMetadata : public gd::AbstractFunctionMetadata {
|
||||
* \see AddParameter
|
||||
*/
|
||||
InstructionMetadata &SetParameterExtraInfo(const gd::String &extraInfo) override {
|
||||
if (!parameters.empty()) parameters.back().SetExtraInfo(extraInfo);
|
||||
if (parameters.GetParametersCount() > 0) {
|
||||
parameters.GetInternalVector().back()->SetExtraInfo(extraInfo);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -560,7 +566,7 @@ class GD_CORE_API InstructionMetadata : public gd::AbstractFunctionMetadata {
|
||||
*/
|
||||
InstructionMetadata &GetCodeExtraInformation() { return *this; }
|
||||
|
||||
std::vector<ParameterMetadata> parameters;
|
||||
ParameterMetadataContainer parameters;
|
||||
|
||||
private:
|
||||
gd::String fullname;
|
||||
|
@@ -454,11 +454,12 @@ const gd::ParameterMetadata* MetadataProvider::GetFunctionCallParameterMetadata(
|
||||
// TODO use a badMetadata instead of a nullptr?
|
||||
const gd::ParameterMetadata* parameterMetadata = nullptr;
|
||||
while (metadataParameterIndex <
|
||||
metadata.parameters.size()) {
|
||||
if (!metadata.parameters[metadataParameterIndex]
|
||||
metadata.GetParameters().GetParametersCount()) {
|
||||
if (!metadata.GetParameters().GetParameter(metadataParameterIndex)
|
||||
.IsCodeOnly()) {
|
||||
if (visibleParameterIndex == parameterIndex) {
|
||||
parameterMetadata = &metadata.parameters[metadataParameterIndex];
|
||||
parameterMetadata =
|
||||
&metadata.GetParameters().GetParameter(metadataParameterIndex);
|
||||
}
|
||||
visibleParameterIndex++;
|
||||
}
|
||||
|
@@ -4,8 +4,8 @@
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
#ifndef PARAMETER_METADATA_H
|
||||
#define PARAMETER_METADATA_H
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
@@ -29,6 +29,12 @@ class GD_CORE_API ParameterMetadata {
|
||||
ParameterMetadata();
|
||||
virtual ~ParameterMetadata(){};
|
||||
|
||||
/**
|
||||
* \brief Return a pointer to a new ParameterMetadata constructed from
|
||||
* this one.
|
||||
*/
|
||||
ParameterMetadata* Clone() const { return new ParameterMetadata(*this); };
|
||||
|
||||
/**
|
||||
* \brief Return the metadata of the parameter type.
|
||||
*/
|
||||
@@ -248,5 +254,3 @@ class GD_CORE_API ParameterMetadata {
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
||||
#endif // PARAMETER_METADATA_H
|
||||
|
@@ -9,6 +9,7 @@
|
||||
#include "GDCore/Project/Object.h"
|
||||
#include "GDCore/Project/ObjectsContainer.h"
|
||||
#include "GDCore/Project/ObjectsContainersList.h"
|
||||
#include "GDCore/Project/ParameterMetadataContainer.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/String.h"
|
||||
#include "InstructionMetadata.h"
|
||||
@@ -20,13 +21,13 @@ const ParameterMetadata ParameterMetadataTools::badParameterMetadata;
|
||||
|
||||
void ParameterMetadataTools::ParametersToObjectsContainer(
|
||||
const gd::Project& project,
|
||||
const std::vector<gd::ParameterMetadata>& parameters,
|
||||
const ParameterMetadataContainer& parameters,
|
||||
gd::ObjectsContainer& outputObjectsContainer) {
|
||||
outputObjectsContainer.GetObjects().clear();
|
||||
|
||||
gd::String lastObjectName;
|
||||
for (std::size_t i = 0; i < parameters.size(); ++i) {
|
||||
const auto& parameter = parameters[i];
|
||||
for (std::size_t i = 0; i < parameters.GetParametersCount(); ++i) {
|
||||
const auto& parameter = parameters.GetParameter(i);
|
||||
if (parameter.GetName().empty()) continue;
|
||||
|
||||
if (gd::ParameterMetadata::IsObject(parameter.GetType())) {
|
||||
@@ -61,31 +62,34 @@ void ParameterMetadataTools::ParametersToObjectsContainer(
|
||||
}
|
||||
|
||||
void ParameterMetadataTools::ForEachParameterMatchingSearch(
|
||||
const std::vector<const std::vector<gd::ParameterMetadata>*>&
|
||||
const std::vector<const ParameterMetadataContainer*>&
|
||||
parametersVectorsList,
|
||||
const gd::String& search,
|
||||
std::function<void(const gd::ParameterMetadata&)> cb) {
|
||||
for (auto it = parametersVectorsList.rbegin();
|
||||
it != parametersVectorsList.rend();
|
||||
++it) {
|
||||
const std::vector<gd::ParameterMetadata>* parametersVector = *it;
|
||||
const ParameterMetadataContainer* parametersVector = *it;
|
||||
|
||||
for (const auto& parameterMetadata: *parametersVector) {
|
||||
if (parameterMetadata.GetName().FindCaseInsensitive(search) != gd::String::npos) cb(parameterMetadata);
|
||||
for (const auto ¶meterMetadata :
|
||||
parametersVector->GetInternalVector()) {
|
||||
if (parameterMetadata->GetName().FindCaseInsensitive(search) !=
|
||||
gd::String::npos)
|
||||
cb(*parameterMetadata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ParameterMetadataTools::Has(
|
||||
const std::vector<const std::vector<gd::ParameterMetadata>*>& parametersVectorsList,
|
||||
const std::vector<const ParameterMetadataContainer*>& parametersVectorsList,
|
||||
const gd::String& parameterName) {
|
||||
for (auto it = parametersVectorsList.rbegin();
|
||||
it != parametersVectorsList.rend();
|
||||
++it) {
|
||||
const std::vector<gd::ParameterMetadata>* parametersVector = *it;
|
||||
const ParameterMetadataContainer* parametersVector = *it;
|
||||
|
||||
for (const auto& parameterMetadata: *parametersVector) {
|
||||
if (parameterMetadata.GetName() == parameterName) return true;
|
||||
for (const auto& parameterMetadata: parametersVector->GetInternalVector()) {
|
||||
if (parameterMetadata->GetName() == parameterName) return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,16 +97,18 @@ bool ParameterMetadataTools::Has(
|
||||
}
|
||||
|
||||
const gd::ParameterMetadata& ParameterMetadataTools::Get(
|
||||
const std::vector<const std::vector<gd::ParameterMetadata>*>&
|
||||
const std::vector<const ParameterMetadataContainer*>&
|
||||
parametersVectorsList,
|
||||
const gd::String& parameterName) {
|
||||
for (auto it = parametersVectorsList.rbegin();
|
||||
it != parametersVectorsList.rend();
|
||||
++it) {
|
||||
const std::vector<gd::ParameterMetadata>* parametersVector = *it;
|
||||
const ParameterMetadataContainer* parametersVector = *it;
|
||||
|
||||
for (const auto& parameterMetadata: *parametersVector) {
|
||||
if (parameterMetadata.GetName() == parameterName) return parameterMetadata;
|
||||
for (const auto ¶meterMetadata :
|
||||
parametersVector->GetInternalVector()) {
|
||||
if (parameterMetadata->GetName() == parameterName)
|
||||
return *parameterMetadata;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +117,7 @@ const gd::ParameterMetadata& ParameterMetadataTools::Get(
|
||||
|
||||
void ParameterMetadataTools::IterateOverParameters(
|
||||
const std::vector<gd::Expression>& parameters,
|
||||
const std::vector<gd::ParameterMetadata>& parametersMetadata,
|
||||
const ParameterMetadataContainer& parametersMetadata,
|
||||
std::function<void(const gd::ParameterMetadata& parameterMetadata,
|
||||
const gd::Expression& parameterValue,
|
||||
const gd::String& lastObjectName)> fn) {
|
||||
@@ -128,15 +134,17 @@ void ParameterMetadataTools::IterateOverParameters(
|
||||
|
||||
void ParameterMetadataTools::IterateOverParametersWithIndex(
|
||||
const std::vector<gd::Expression>& parameters,
|
||||
const std::vector<gd::ParameterMetadata>& parametersMetadata,
|
||||
const ParameterMetadataContainer& parametersMetadata,
|
||||
std::function<void(const gd::ParameterMetadata& parameterMetadata,
|
||||
const gd::Expression& parameterValue,
|
||||
size_t parameterIndex,
|
||||
const gd::String& lastObjectName)> fn) {
|
||||
gd::String lastObjectName = "";
|
||||
for (std::size_t pNb = 0; pNb < parametersMetadata.size(); ++pNb) {
|
||||
const gd::ParameterMetadata& parameterMetadata = parametersMetadata[pNb];
|
||||
const gd::Expression& parameterValue =
|
||||
for (std::size_t pNb = 0; pNb < parametersMetadata.GetParametersCount();
|
||||
++pNb) {
|
||||
const gd::ParameterMetadata ¶meterMetadata =
|
||||
parametersMetadata.GetParameter(pNb);
|
||||
const gd::Expression ¶meterValue =
|
||||
pNb < parameters.size() ? parameters[pNb].GetPlainString() : "";
|
||||
const gd::Expression& parameterValueOrDefault =
|
||||
parameterValue.GetPlainString().empty() && parameterMetadata.IsOptional()
|
||||
@@ -179,10 +187,10 @@ void ParameterMetadataTools::IterateOverParametersWithIndex(
|
||||
|
||||
size_t parameterIndex = 0;
|
||||
for (size_t metadataIndex = (isObjectFunction ? 1 : 0);
|
||||
metadataIndex < metadata.parameters.size() &&
|
||||
metadataIndex < metadata.GetParameters().GetParametersCount() &&
|
||||
parameterIndex < node.parameters.size();
|
||||
++metadataIndex) {
|
||||
auto ¶meterMetadata = metadata.parameters[metadataIndex];
|
||||
auto ¶meterMetadata = metadata.GetParameters().GetParameter(metadataIndex);
|
||||
if (parameterMetadata.IsCodeOnly()) {
|
||||
continue;
|
||||
}
|
||||
@@ -204,16 +212,17 @@ void ParameterMetadataTools::IterateOverParametersWithIndex(
|
||||
}
|
||||
|
||||
size_t ParameterMetadataTools::GetObjectParameterIndexFor(
|
||||
const std::vector<gd::ParameterMetadata>& parametersMetadata,
|
||||
const ParameterMetadataContainer& parametersMetadata,
|
||||
size_t parameterIndex) {
|
||||
// By convention, parameters that require
|
||||
// an object (mainly, "objectvar" and "behavior") should be placed after
|
||||
// the object in the list of parameters (if possible, just after).
|
||||
// Search "lastObjectName" in the codebase for other place where this
|
||||
// convention is enforced.
|
||||
for (std::size_t pNb = parameterIndex; pNb < parametersMetadata.size();
|
||||
pNb--) {
|
||||
if (gd::ParameterMetadata::IsObject(parametersMetadata[pNb].GetType())) {
|
||||
for (std::size_t pNb = parameterIndex;
|
||||
pNb < parametersMetadata.GetParametersCount(); pNb--) {
|
||||
if (gd::ParameterMetadata::IsObject(
|
||||
parametersMetadata.GetParameter(pNb).GetType())) {
|
||||
return pNb;
|
||||
}
|
||||
}
|
||||
|
@@ -15,6 +15,7 @@ class ObjectsContainer;
|
||||
class ObjectsContainersList;
|
||||
class ParameterMetadata;
|
||||
class Expression;
|
||||
class ParameterMetadataContainer;
|
||||
struct FunctionCallNode;
|
||||
struct ExpressionNode;
|
||||
} // namespace gd
|
||||
@@ -24,20 +25,20 @@ class GD_CORE_API ParameterMetadataTools {
|
||||
public:
|
||||
static void ParametersToObjectsContainer(
|
||||
const gd::Project& project,
|
||||
const std::vector<gd::ParameterMetadata>& parameters,
|
||||
const ParameterMetadataContainer& parameters,
|
||||
gd::ObjectsContainer& outputObjectsContainer);
|
||||
|
||||
static void ForEachParameterMatchingSearch(
|
||||
const std::vector<const std::vector<gd::ParameterMetadata>*>& parametersVectorsList,
|
||||
const std::vector<const ParameterMetadataContainer*>& parametersVectorsList,
|
||||
const gd::String& search,
|
||||
std::function<void(const gd::ParameterMetadata&)> cb);
|
||||
|
||||
static bool Has(
|
||||
const std::vector<const std::vector<gd::ParameterMetadata>*>& parametersVectorsList,
|
||||
const std::vector<const ParameterMetadataContainer*>& parametersVectorsList,
|
||||
const gd::String& parameterName);
|
||||
|
||||
static const gd::ParameterMetadata& Get(
|
||||
const std::vector<const std::vector<gd::ParameterMetadata>*>& parametersVectorsList,
|
||||
const std::vector<const ParameterMetadataContainer*>& parametersVectorsList,
|
||||
const gd::String& parameterName);
|
||||
|
||||
/**
|
||||
@@ -47,7 +48,7 @@ class GD_CORE_API ParameterMetadataTools {
|
||||
*/
|
||||
static void IterateOverParameters(
|
||||
const std::vector<gd::Expression>& parameters,
|
||||
const std::vector<gd::ParameterMetadata>& parametersMetadata,
|
||||
const ParameterMetadataContainer& parametersMetadata,
|
||||
std::function<void(const gd::ParameterMetadata& parameterMetadata,
|
||||
const gd::Expression& parameterValue,
|
||||
const gd::String& lastObjectName)> fn);
|
||||
@@ -59,7 +60,7 @@ class GD_CORE_API ParameterMetadataTools {
|
||||
*/
|
||||
static void IterateOverParametersWithIndex(
|
||||
const std::vector<gd::Expression>& parameters,
|
||||
const std::vector<gd::ParameterMetadata>& parametersMetadata,
|
||||
const ParameterMetadataContainer& parametersMetadata,
|
||||
std::function<void(const gd::ParameterMetadata& parameterMetadata,
|
||||
const gd::Expression& parameterValue,
|
||||
size_t parameterIndex,
|
||||
@@ -84,7 +85,7 @@ class GD_CORE_API ParameterMetadataTools {
|
||||
* it's linked to.
|
||||
*/
|
||||
static size_t GetObjectParameterIndexFor(
|
||||
const std::vector<gd::ParameterMetadata>& parametersMetadata,
|
||||
const ParameterMetadataContainer& parametersMetadata,
|
||||
size_t parameterIndex);
|
||||
|
||||
private:
|
||||
|
@@ -210,8 +210,8 @@ class GD_CORE_API ValueTypeMetadata {
|
||||
parameterType == "scenevar";
|
||||
} else if (type == "resource") {
|
||||
return parameterType == "fontResource" ||
|
||||
parameterType == "soundfile" ||
|
||||
parameterType == "musicfile" ||
|
||||
parameterType == "audioResource" ||
|
||||
parameterType == "videoResource" ||
|
||||
parameterType == "bitmapFontResource" ||
|
||||
parameterType == "imageResource" ||
|
||||
parameterType == "jsonResource" ||
|
||||
@@ -219,7 +219,10 @@ class GD_CORE_API ValueTypeMetadata {
|
||||
parameterType == "tilesetResource" ||
|
||||
parameterType == "model3DResource" ||
|
||||
parameterType == "atlasResource" ||
|
||||
parameterType == "spineResource";
|
||||
parameterType == "spineResource" ||
|
||||
// Deprecated, old parameter types:
|
||||
parameterType == "soundfile" ||
|
||||
parameterType == "musicfile";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@@ -72,12 +72,12 @@ public:
|
||||
gd::String lastObjectParameter = "";
|
||||
const gd::InstructionMetadata &instrInfos =
|
||||
MetadataProvider::GetActionMetadata(platform, instruction.GetType());
|
||||
for (std::size_t pNb = 0; pNb < instrInfos.parameters.size(); ++pNb) {
|
||||
for (std::size_t pNb = 0; pNb < instrInfos.parameters.GetParametersCount(); ++pNb) {
|
||||
|
||||
if (ParameterMetadata::IsExpression(
|
||||
"number", instrInfos.parameters[pNb].GetType()) ||
|
||||
"number", instrInfos.parameters.GetParameter(pNb).GetType()) ||
|
||||
ParameterMetadata::IsExpression(
|
||||
"string", instrInfos.parameters[pNb].GetType())) {
|
||||
"string", instrInfos.parameters.GetParameter(pNb).GetType())) {
|
||||
auto node = instruction.GetParameter(pNb).GetRootNode();
|
||||
node->Visit(*this);
|
||||
}
|
||||
|
@@ -86,9 +86,11 @@ class GD_CORE_API IdentifierFinderExpressionNodeWorker
|
||||
}
|
||||
|
||||
size_t parameterIndex = 0;
|
||||
for (size_t metadataIndex = (isObjectFunction ? 1 : 0); metadataIndex < metadata.parameters.size()
|
||||
&& parameterIndex < node.parameters.size(); ++metadataIndex) {
|
||||
auto& parameterMetadata = metadata.parameters[metadataIndex];
|
||||
for (size_t metadataIndex = (isObjectFunction ? 1 : 0);
|
||||
metadataIndex < metadata.GetParameters().GetParametersCount() &&
|
||||
parameterIndex < node.parameters.size();
|
||||
++metadataIndex) {
|
||||
auto& parameterMetadata = metadata.GetParameters().GetParameter(metadataIndex);
|
||||
if (parameterMetadata.IsCodeOnly()) {
|
||||
continue;
|
||||
}
|
||||
@@ -144,10 +146,10 @@ class GD_CORE_API IdentifierFinderEventWorker
|
||||
platform, instruction.GetType())
|
||||
: MetadataProvider::GetActionMetadata(
|
||||
platform, instruction.GetType());
|
||||
for (std::size_t pNb = 0; pNb < instrInfos.parameters.size(); ++pNb) {
|
||||
for (std::size_t pNb = 0; pNb < instrInfos.parameters.GetParametersCount(); ++pNb) {
|
||||
// The parameter has the searched type...
|
||||
if (instrInfos.parameters[pNb].GetType() == "identifier"
|
||||
&& instrInfos.parameters[pNb].GetExtraInfo() == identifierType) {
|
||||
if (instrInfos.parameters.GetParameter(pNb).GetType() == "identifier"
|
||||
&& instrInfos.parameters.GetParameter(pNb).GetExtraInfo() == identifierType) {
|
||||
//...remember the value of the parameter.
|
||||
if (objectName.empty() || lastObjectParameter == objectName) {
|
||||
results.insert(instruction.GetParameter(pNb).GetPlainString());
|
||||
@@ -155,9 +157,9 @@ class GD_CORE_API IdentifierFinderEventWorker
|
||||
}
|
||||
// Search in expressions
|
||||
else if (ParameterMetadata::IsExpression(
|
||||
"number", instrInfos.parameters[pNb].GetType()) ||
|
||||
"number", instrInfos.parameters.GetParameter(pNb).GetType()) ||
|
||||
ParameterMetadata::IsExpression(
|
||||
"string", instrInfos.parameters[pNb].GetType())) {
|
||||
"string", instrInfos.parameters.GetParameter(pNb).GetType())) {
|
||||
auto node = instruction.GetParameter(pNb).GetRootNode();
|
||||
|
||||
IdentifierFinderExpressionNodeWorker searcher(
|
||||
@@ -170,7 +172,7 @@ class GD_CORE_API IdentifierFinderEventWorker
|
||||
}
|
||||
// Remember the value of the last "object" parameter.
|
||||
else if (gd::ParameterMetadata::IsObject(
|
||||
instrInfos.parameters[pNb].GetType())) {
|
||||
instrInfos.parameters.GetParameter(pNb).GetType())) {
|
||||
lastObjectParameter =
|
||||
instruction.GetParameter(pNb).GetPlainString();
|
||||
}
|
||||
|
@@ -305,14 +305,14 @@ bool EventsRefactorer::RenameObjectInActions(const gd::Platform& platform,
|
||||
for (std::size_t aId = 0; aId < actions.size(); ++aId) {
|
||||
const gd::InstructionMetadata& instrInfos =
|
||||
MetadataProvider::GetActionMetadata(platform, actions[aId].GetType());
|
||||
for (std::size_t pNb = 0; pNb < instrInfos.parameters.size(); ++pNb) {
|
||||
for (std::size_t pNb = 0; pNb < instrInfos.parameters.GetParametersCount(); ++pNb) {
|
||||
// Replace object's name in parameters
|
||||
if (gd::ParameterMetadata::IsObject(instrInfos.parameters[pNb].GetType()) &&
|
||||
if (gd::ParameterMetadata::IsObject(instrInfos.parameters.GetParameter(pNb).GetType()) &&
|
||||
actions[aId].GetParameter(pNb).GetPlainString() == oldName)
|
||||
actions[aId].SetParameter(pNb, gd::Expression(newName));
|
||||
// Replace object's name in expressions
|
||||
else if (ParameterMetadata::IsExpression(
|
||||
"number", instrInfos.parameters[pNb].GetType())) {
|
||||
"number", instrInfos.parameters.GetParameter(pNb).GetType())) {
|
||||
auto node = actions[aId].GetParameter(pNb).GetRootNode();
|
||||
|
||||
if (ExpressionObjectRenamer::Rename(platform, projectScopedContainers, "number", *node, oldName, newName)) {
|
||||
@@ -322,7 +322,7 @@ bool EventsRefactorer::RenameObjectInActions(const gd::Platform& platform,
|
||||
}
|
||||
// Replace object's name in text expressions
|
||||
else if (ParameterMetadata::IsExpression(
|
||||
"string", instrInfos.parameters[pNb].GetType())) {
|
||||
"string", instrInfos.parameters.GetParameter(pNb).GetType())) {
|
||||
auto node = actions[aId].GetParameter(pNb).GetRootNode();
|
||||
|
||||
if (ExpressionObjectRenamer::Rename(platform, projectScopedContainers, "string", *node, oldName, newName)) {
|
||||
@@ -357,14 +357,14 @@ bool EventsRefactorer::RenameObjectInConditions(
|
||||
const gd::InstructionMetadata& instrInfos =
|
||||
MetadataProvider::GetConditionMetadata(platform,
|
||||
conditions[cId].GetType());
|
||||
for (std::size_t pNb = 0; pNb < instrInfos.parameters.size(); ++pNb) {
|
||||
for (std::size_t pNb = 0; pNb < instrInfos.parameters.GetParametersCount(); ++pNb) {
|
||||
// Replace object's name in parameters
|
||||
if (gd::ParameterMetadata::IsObject(instrInfos.parameters[pNb].GetType()) &&
|
||||
if (gd::ParameterMetadata::IsObject(instrInfos.parameters.GetParameter(pNb).GetType()) &&
|
||||
conditions[cId].GetParameter(pNb).GetPlainString() == oldName)
|
||||
conditions[cId].SetParameter(pNb, gd::Expression(newName));
|
||||
// Replace object's name in expressions
|
||||
else if (ParameterMetadata::IsExpression(
|
||||
"number", instrInfos.parameters[pNb].GetType())) {
|
||||
"number", instrInfos.parameters.GetParameter(pNb).GetType())) {
|
||||
auto node = conditions[cId].GetParameter(pNb).GetRootNode();
|
||||
|
||||
if (ExpressionObjectRenamer::Rename(platform, projectScopedContainers, "number", *node, oldName, newName)) {
|
||||
@@ -374,7 +374,7 @@ bool EventsRefactorer::RenameObjectInConditions(
|
||||
}
|
||||
// Replace object's name in text expressions
|
||||
else if (ParameterMetadata::IsExpression(
|
||||
"string", instrInfos.parameters[pNb].GetType())) {
|
||||
"string", instrInfos.parameters.GetParameter(pNb).GetType())) {
|
||||
auto node = conditions[cId].GetParameter(pNb).GetRootNode();
|
||||
|
||||
if (ExpressionObjectRenamer::Rename(platform, projectScopedContainers, "string", *node, oldName, newName)) {
|
||||
@@ -485,16 +485,16 @@ bool EventsRefactorer::RemoveObjectInActions(const gd::Platform& platform,
|
||||
|
||||
const gd::InstructionMetadata& instrInfos =
|
||||
MetadataProvider::GetActionMetadata(platform, actions[aId].GetType());
|
||||
for (std::size_t pNb = 0; pNb < instrInfos.parameters.size(); ++pNb) {
|
||||
for (std::size_t pNb = 0; pNb < instrInfos.parameters.GetParametersCount(); ++pNb) {
|
||||
// Find object's name in parameters
|
||||
if (gd::ParameterMetadata::IsObject(instrInfos.parameters[pNb].GetType()) &&
|
||||
if (gd::ParameterMetadata::IsObject(instrInfos.parameters.GetParameter(pNb).GetType()) &&
|
||||
actions[aId].GetParameter(pNb).GetPlainString() == name) {
|
||||
deleteMe = true;
|
||||
break;
|
||||
}
|
||||
// Find object's name in expressions
|
||||
else if (ParameterMetadata::IsExpression(
|
||||
"number", instrInfos.parameters[pNb].GetType())) {
|
||||
"number", instrInfos.parameters.GetParameter(pNb).GetType())) {
|
||||
auto node = actions[aId].GetParameter(pNb).GetRootNode();
|
||||
|
||||
if (ExpressionObjectFinder::CheckIfHasObject(platform, projectScopedContainers, "number", *node, name)) {
|
||||
@@ -504,7 +504,7 @@ bool EventsRefactorer::RemoveObjectInActions(const gd::Platform& platform,
|
||||
}
|
||||
// Find object's name in text expressions
|
||||
else if (ParameterMetadata::IsExpression(
|
||||
"string", instrInfos.parameters[pNb].GetType())) {
|
||||
"string", instrInfos.parameters.GetParameter(pNb).GetType())) {
|
||||
auto node = actions[aId].GetParameter(pNb).GetRootNode();
|
||||
|
||||
if (ExpressionObjectFinder::CheckIfHasObject(platform, projectScopedContainers, "string", *node, name)) {
|
||||
@@ -543,16 +543,16 @@ bool EventsRefactorer::RemoveObjectInConditions(
|
||||
const gd::InstructionMetadata& instrInfos =
|
||||
MetadataProvider::GetConditionMetadata(platform,
|
||||
conditions[cId].GetType());
|
||||
for (std::size_t pNb = 0; pNb < instrInfos.parameters.size(); ++pNb) {
|
||||
for (std::size_t pNb = 0; pNb < instrInfos.parameters.GetParametersCount(); ++pNb) {
|
||||
// Find object's name in parameters
|
||||
if (gd::ParameterMetadata::IsObject(instrInfos.parameters[pNb].GetType()) &&
|
||||
if (gd::ParameterMetadata::IsObject(instrInfos.parameters.GetParameter(pNb).GetType()) &&
|
||||
conditions[cId].GetParameter(pNb).GetPlainString() == name) {
|
||||
deleteMe = true;
|
||||
break;
|
||||
}
|
||||
// Find object's name in expressions
|
||||
else if (ParameterMetadata::IsExpression(
|
||||
"number", instrInfos.parameters[pNb].GetType())) {
|
||||
"number", instrInfos.parameters.GetParameter(pNb).GetType())) {
|
||||
auto node = conditions[cId].GetParameter(pNb).GetRootNode();
|
||||
|
||||
if (ExpressionObjectFinder::CheckIfHasObject(platform, projectScopedContainers, "number", *node, name)) {
|
||||
@@ -562,7 +562,7 @@ bool EventsRefactorer::RemoveObjectInConditions(
|
||||
}
|
||||
// Find object's name in text expressions
|
||||
else if (ParameterMetadata::IsExpression(
|
||||
"string", instrInfos.parameters[pNb].GetType())) {
|
||||
"string", instrInfos.parameters.GetParameter(pNb).GetType())) {
|
||||
auto node = conditions[cId].GetParameter(pNb).GetRootNode();
|
||||
|
||||
if (ExpressionObjectFinder::CheckIfHasObject(platform, projectScopedContainers, "string", *node, name)) {
|
||||
|
@@ -93,9 +93,11 @@ class GD_CORE_API VariableFinderExpressionNodeWorker
|
||||
}
|
||||
|
||||
size_t parameterIndex = 0;
|
||||
for (size_t metadataIndex = (isObjectFunction ? 1 : 0); metadataIndex < metadata.parameters.size()
|
||||
&& parameterIndex < node.parameters.size(); ++metadataIndex) {
|
||||
auto& parameterMetadata = metadata.parameters[metadataIndex];
|
||||
for (size_t metadataIndex = (isObjectFunction ? 1 : 0);
|
||||
metadataIndex < metadata.GetParameters().GetParametersCount() &&
|
||||
parameterIndex < node.parameters.size();
|
||||
++metadataIndex) {
|
||||
auto& parameterMetadata = metadata.GetParameters().GetParameter(metadataIndex);
|
||||
if (parameterMetadata.IsCodeOnly()) {
|
||||
continue;
|
||||
}
|
||||
@@ -150,18 +152,18 @@ class GD_CORE_API VariableFinderEventWorker
|
||||
platform, instruction.GetType())
|
||||
: MetadataProvider::GetActionMetadata(
|
||||
platform, instruction.GetType());
|
||||
for (std::size_t pNb = 0; pNb < instrInfos.parameters.size(); ++pNb) {
|
||||
for (std::size_t pNb = 0; pNb < instrInfos.parameters.GetParametersCount(); ++pNb) {
|
||||
// The parameter has the searched type...
|
||||
if (instrInfos.parameters[pNb].GetType() == parameterType) {
|
||||
if (instrInfos.parameters.GetParameter(pNb).GetType() == parameterType) {
|
||||
//...remember the value of the parameter.
|
||||
if (objectName.empty() || lastObjectParameter == objectName)
|
||||
results.insert(instruction.GetParameter(pNb).GetPlainString());
|
||||
}
|
||||
// Search in expressions
|
||||
else if (ParameterMetadata::IsExpression(
|
||||
"number", instrInfos.parameters[pNb].GetType()) ||
|
||||
"number", instrInfos.parameters.GetParameter(pNb).GetType()) ||
|
||||
ParameterMetadata::IsExpression(
|
||||
"string", instrInfos.parameters[pNb].GetType())) {
|
||||
"string", instrInfos.parameters.GetParameter(pNb).GetType())) {
|
||||
auto node = instruction.GetParameter(pNb).GetRootNode();
|
||||
|
||||
VariableFinderExpressionNodeWorker searcher(
|
||||
@@ -174,7 +176,7 @@ class GD_CORE_API VariableFinderEventWorker
|
||||
}
|
||||
// Remember the value of the last "object" parameter.
|
||||
else if (gd::ParameterMetadata::IsObject(
|
||||
instrInfos.parameters[pNb].GetType())) {
|
||||
instrInfos.parameters.GetParameter(pNb).GetType())) {
|
||||
lastObjectParameter =
|
||||
instruction.GetParameter(pNb).GetPlainString();
|
||||
}
|
||||
|
@@ -463,11 +463,15 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
MetadataProvider::GetFunctionCallMetadata(
|
||||
platform, objectsContainersList, *functionCall);
|
||||
|
||||
const gd::ParameterMetadata* parameterMetadata = nullptr;
|
||||
while (metadataParameterIndex < metadata.parameters.size()) {
|
||||
if (!metadata.parameters[metadataParameterIndex].IsCodeOnly()) {
|
||||
const gd::ParameterMetadata *parameterMetadata = nullptr;
|
||||
while (metadataParameterIndex <
|
||||
metadata.GetParameters().GetParametersCount()) {
|
||||
if (!metadata.GetParameters()
|
||||
.GetParameter(metadataParameterIndex)
|
||||
.IsCodeOnly()) {
|
||||
if (visibleParameterIndex == parameterIndex) {
|
||||
parameterMetadata = &metadata.parameters[metadataParameterIndex];
|
||||
parameterMetadata =
|
||||
&metadata.GetParameters().GetParameter(metadataParameterIndex);
|
||||
}
|
||||
visibleParameterIndex++;
|
||||
}
|
||||
|
@@ -36,13 +36,15 @@ namespace {
|
||||
* (by convention, 1 for object functions and 2 for behavior functions).
|
||||
*/
|
||||
size_t GetMinimumParametersNumber(
|
||||
const std::vector<gd::ParameterMetadata>& parameters,
|
||||
const gd::ParameterMetadataContainer& parameters,
|
||||
size_t initialParameterIndex) {
|
||||
size_t nb = 0;
|
||||
for (std::size_t i = initialParameterIndex; i < parameters.size(); ++i) {
|
||||
if (!parameters[i].IsOptional() && !parameters[i].codeOnly) nb++;
|
||||
for (std::size_t i = initialParameterIndex;
|
||||
i < parameters.GetParametersCount(); ++i) {
|
||||
if (!parameters.GetParameter(i).IsOptional() &&
|
||||
!parameters.GetParameter(i).IsCodeOnly())
|
||||
nb++;
|
||||
}
|
||||
|
||||
return nb;
|
||||
}
|
||||
|
||||
@@ -51,13 +53,14 @@ size_t GetMinimumParametersNumber(
|
||||
* (by convention, 1 for object functions and 2 for behavior functions).
|
||||
*/
|
||||
size_t GetMaximumParametersNumber(
|
||||
const std::vector<gd::ParameterMetadata>& parameters,
|
||||
const gd::ParameterMetadataContainer& parameters,
|
||||
size_t initialParameterIndex) {
|
||||
size_t nb = 0;
|
||||
for (std::size_t i = initialParameterIndex; i < parameters.size(); ++i) {
|
||||
if (!parameters[i].codeOnly) nb++;
|
||||
for (std::size_t i = initialParameterIndex;
|
||||
i < parameters.GetParametersCount(); ++i) {
|
||||
if (!parameters.GetParameter(i).IsCodeOnly())
|
||||
nb++;
|
||||
}
|
||||
|
||||
return nb;
|
||||
}
|
||||
|
||||
@@ -322,11 +325,11 @@ ExpressionValidator::Type ExpressionValidator::ValidateFunction(
|
||||
|
||||
// Validate parameters count
|
||||
size_t minParametersCount = GetMinimumParametersNumber(
|
||||
metadata.parameters,
|
||||
metadata.GetParameters(),
|
||||
ExpressionParser2::WrittenParametersFirstIndex(function.objectName,
|
||||
function.behaviorName));
|
||||
size_t maxParametersCount = GetMaximumParametersNumber(
|
||||
metadata.parameters,
|
||||
metadata.GetParameters(),
|
||||
ExpressionParser2::WrittenParametersFirstIndex(function.objectName,
|
||||
function.behaviorName));
|
||||
if (function.parameters.size() < minParametersCount ||
|
||||
@@ -366,11 +369,11 @@ ExpressionValidator::Type ExpressionValidator::ValidateFunction(
|
||||
for (int parameterIndex = 0; parameterIndex < function.parameters.size();
|
||||
parameterIndex++) {
|
||||
auto& parameter = function.parameters[parameterIndex];
|
||||
while (metadata.GetParameters()[metadataIndex].IsCodeOnly()) {
|
||||
while (metadata.GetParameters().GetParameter(metadataIndex).IsCodeOnly()) {
|
||||
// The sizes are already checked above.
|
||||
metadataIndex++;
|
||||
}
|
||||
auto& parameterMetadata = metadata.GetParameters()[metadataIndex];
|
||||
auto& parameterMetadata = metadata.GetParameters().GetParameter(metadataIndex);
|
||||
|
||||
if (!parameterMetadata.IsOptional() ||
|
||||
dynamic_cast<EmptyNode*>(parameter.get()) == nullptr) {
|
||||
|
@@ -144,10 +144,10 @@ bool ExpressionsParameterMover::DoVisitInstruction(gd::Instruction& instruction,
|
||||
: gd::MetadataProvider::GetActionMetadata(
|
||||
platform, instruction.GetType());
|
||||
|
||||
for (std::size_t pNb = 0; pNb < metadata.parameters.size() &&
|
||||
for (std::size_t pNb = 0; pNb < metadata.parameters.GetParametersCount() &&
|
||||
pNb < instruction.GetParametersCount();
|
||||
++pNb) {
|
||||
const gd::String& type = metadata.parameters[pNb].GetType();
|
||||
const gd::String& type = metadata.parameters.GetParameter(pNb).GetType();
|
||||
const gd::Expression& expression = instruction.GetParameter(pNb);
|
||||
|
||||
auto node = expression.GetRootNode();
|
||||
|
@@ -151,7 +151,7 @@ bool ExpressionsRenamer::DoVisitInstruction(gd::Instruction& instruction,
|
||||
: gd::MetadataProvider::GetActionMetadata(
|
||||
platform, instruction.GetType());
|
||||
|
||||
for (std::size_t pNb = 0; pNb < metadata.parameters.size() &&
|
||||
for (std::size_t pNb = 0; pNb < metadata.parameters.GetParametersCount() &&
|
||||
pNb < instruction.GetParametersCount();
|
||||
++pNb) {
|
||||
const gd::Expression& expression = instruction.GetParameter(pNb);
|
||||
|
@@ -43,7 +43,7 @@ InstructionSentenceFormatter::GetAsFormattedText(
|
||||
parse = false;
|
||||
size_t firstParamPosition = gd::String::npos;
|
||||
size_t firstParamIndex = gd::String::npos;
|
||||
for (std::size_t i = 0; i < metadata.parameters.size(); ++i) {
|
||||
for (std::size_t i = 0; i < metadata.parameters.GetParametersCount(); ++i) {
|
||||
size_t paramPosition =
|
||||
sentence.find("_PARAM" + gd::String::From(i) + "_");
|
||||
if (paramPosition < firstParamPosition) {
|
||||
|
@@ -225,9 +225,7 @@ bool ResourceWorkerInEventsWorker::DoVisitInstruction(gd::Instruction& instructi
|
||||
size_t parameterIndex,
|
||||
const gd::String& lastObjectName) {
|
||||
const String& parameterValue = parameterExpression.GetPlainString();
|
||||
if (parameterMetadata.GetType() ==
|
||||
"police" || // Should be renamed fontResource
|
||||
parameterMetadata.GetType() == "fontResource") {
|
||||
if (parameterMetadata.GetType() == "fontResource") {
|
||||
gd::String updatedParameterValue = parameterValue;
|
||||
worker.ExposeFont(updatedParameterValue);
|
||||
instruction.SetParameter(parameterIndex, updatedParameterValue);
|
||||
|
@@ -15,10 +15,10 @@ namespace gd {
|
||||
|
||||
void FunctionParameterBehaviorTypeRenamer::DoVisitEventsFunction(
|
||||
gd::EventsFunction &eventsFunction) {
|
||||
for (auto &¶meter : eventsFunction.GetParameters()) {
|
||||
if (gd::ParameterMetadata::IsBehavior(parameter.GetType()) &&
|
||||
parameter.GetExtraInfo() == oldBehaviorType) {
|
||||
parameter.SetExtraInfo(newBehaviorType);
|
||||
for (auto &¶meter : eventsFunction.GetParameters().GetInternalVector()) {
|
||||
if (gd::ParameterMetadata::IsBehavior(parameter->GetType()) &&
|
||||
parameter->GetExtraInfo() == oldBehaviorType) {
|
||||
parameter->SetExtraInfo(newBehaviorType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -15,10 +15,10 @@ namespace gd {
|
||||
|
||||
void FunctionParameterObjectTypeRenamer::DoVisitEventsFunction(
|
||||
gd::EventsFunction &eventsFunction) {
|
||||
for (auto &¶meter : eventsFunction.GetParameters()) {
|
||||
if (gd::ParameterMetadata::IsObject(parameter.GetType()) &&
|
||||
parameter.GetExtraInfo() == oldObjectType) {
|
||||
parameter.SetExtraInfo(newObjectType);
|
||||
for (auto &¶meter : eventsFunction.GetParameters().GetInternalVector()) {
|
||||
if (gd::ParameterMetadata::IsObject(parameter->GetType()) &&
|
||||
parameter->GetExtraInfo() == oldObjectType) {
|
||||
parameter->SetExtraInfo(newObjectType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -153,7 +153,7 @@ void PropertyFunctionGenerator::GenerateGetterAndSetter(
|
||||
extension.GetName(), eventsBasedEntity.GetName());
|
||||
objectParameter.SetExtraInfo(objectFullType);
|
||||
}
|
||||
setter.GetParameters().push_back(objectParameter);
|
||||
setter.GetParameters().AddParameter(objectParameter);
|
||||
if (isBehavior) {
|
||||
gd::ParameterMetadata behaviorParameter;
|
||||
gd::String behaviorFullType =
|
||||
@@ -163,7 +163,7 @@ void PropertyFunctionGenerator::GenerateGetterAndSetter(
|
||||
.SetName("Behavior")
|
||||
.SetDescription("Behavior")
|
||||
.SetExtraInfo(behaviorFullType);
|
||||
setter.GetParameters().push_back(behaviorParameter);
|
||||
setter.GetParameters().AddParameter(behaviorParameter);
|
||||
}
|
||||
gd::ParameterMetadata valueParameter;
|
||||
valueParameter.SetType("yesorno")
|
||||
@@ -171,7 +171,7 @@ void PropertyFunctionGenerator::GenerateGetterAndSetter(
|
||||
.SetDescription(capitalizedName)
|
||||
.SetOptional(true)
|
||||
.SetDefaultValue("yes");
|
||||
setter.GetParameters().push_back(valueParameter);
|
||||
setter.GetParameters().AddParameter(valueParameter);
|
||||
} else {
|
||||
setter.SetFunctionType(gd::EventsFunction::ActionWithOperator);
|
||||
setter.SetGetterName(getterName);
|
||||
|
@@ -102,17 +102,17 @@ void WholeProjectRefactorer::EnsureBehaviorEventsFunctionsProperParameters(
|
||||
for (auto &eventsFunction :
|
||||
eventsBasedBehavior.GetEventsFunctions().GetInternalVector()) {
|
||||
auto ¶meters = eventsFunction->GetParameters();
|
||||
while (parameters.size() < 2) {
|
||||
while (parameters.GetParametersCount() < 2) {
|
||||
gd::ParameterMetadata newParameter;
|
||||
parameters.push_back(newParameter);
|
||||
parameters.AddParameter(newParameter);
|
||||
}
|
||||
|
||||
parameters[0]
|
||||
parameters.GetParameter(0)
|
||||
.SetType("object")
|
||||
.SetName(behaviorObjectParameterName)
|
||||
.SetDescription("Object")
|
||||
.SetExtraInfo(eventsBasedBehavior.GetObjectType());
|
||||
parameters[1]
|
||||
parameters.GetParameter(1)
|
||||
.SetType("behavior")
|
||||
.SetName("Behavior")
|
||||
.SetDescription("Behavior")
|
||||
@@ -127,12 +127,12 @@ void WholeProjectRefactorer::EnsureObjectEventsFunctionsProperParameters(
|
||||
for (auto &eventsFunction :
|
||||
eventsBasedObject.GetEventsFunctions().GetInternalVector()) {
|
||||
auto ¶meters = eventsFunction->GetParameters();
|
||||
while (parameters.size() < 1) {
|
||||
while (parameters.GetParametersCount() < 1) {
|
||||
gd::ParameterMetadata newParameter;
|
||||
parameters.push_back(newParameter);
|
||||
parameters.AddParameter(newParameter);
|
||||
}
|
||||
|
||||
parameters[0]
|
||||
parameters.GetParameter(0)
|
||||
.SetType("object")
|
||||
.SetName(parentObjectParameterName)
|
||||
.SetDescription("Object")
|
||||
|
@@ -9,6 +9,7 @@
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include "GDCore/Serialization/Serializer.h"
|
||||
#include "GDCore/Project/QuickCustomization.h"
|
||||
#include "GDCore/String.h"
|
||||
|
||||
namespace gd {
|
||||
@@ -31,10 +32,10 @@ namespace gd {
|
||||
*/
|
||||
class GD_CORE_API BehaviorConfigurationContainer {
|
||||
public:
|
||||
BehaviorConfigurationContainer() : folded(false){};
|
||||
BehaviorConfigurationContainer() : folded(false), quickCustomizationVisibility(QuickCustomization::Visibility::Default){};
|
||||
BehaviorConfigurationContainer(const gd::String& name_,
|
||||
const gd::String& type_)
|
||||
: name(name_), type(type_), folded(false){};
|
||||
: name(name_), type(type_), folded(false), quickCustomizationVisibility(QuickCustomization::Visibility::Default){};
|
||||
virtual ~BehaviorConfigurationContainer();
|
||||
virtual BehaviorConfigurationContainer* Clone() const { return new BehaviorConfigurationContainer(*this); }
|
||||
|
||||
@@ -114,6 +115,13 @@ class GD_CORE_API BehaviorConfigurationContainer {
|
||||
*/
|
||||
bool IsFolded() const { return folded; }
|
||||
|
||||
void SetQuickCustomizationVisibility(QuickCustomization::Visibility visibility) {
|
||||
quickCustomizationVisibility = visibility;
|
||||
}
|
||||
|
||||
QuickCustomization::Visibility GetQuickCustomizationVisibility() const {
|
||||
return quickCustomizationVisibility;
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
@@ -160,6 +168,7 @@ protected:
|
||||
|
||||
gd::SerializerElement content; // Storage for the behavior properties
|
||||
bool folded;
|
||||
QuickCustomization::Visibility quickCustomizationVisibility;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -26,7 +26,7 @@ void CustomConfigurationHelper::InitializeContent(
|
||||
|
||||
if (propertyType == "String" || propertyType == "Choice" ||
|
||||
propertyType == "Color" || propertyType == "Behavior" ||
|
||||
propertyType == "resource") {
|
||||
propertyType == "Resource") {
|
||||
element.SetStringValue(property->GetValue());
|
||||
} else if (propertyType == "Number") {
|
||||
element.SetDoubleValue(property->GetValue().To<double>());
|
||||
@@ -39,21 +39,21 @@ void CustomConfigurationHelper::InitializeContent(
|
||||
std::map<gd::String, gd::PropertyDescriptor> CustomConfigurationHelper::GetProperties(
|
||||
const gd::PropertiesContainer &properties,
|
||||
const gd::SerializerElement &configurationContent) {
|
||||
auto behaviorProperties = std::map<gd::String, gd::PropertyDescriptor>();
|
||||
auto objectProperties = std::map<gd::String, gd::PropertyDescriptor>();
|
||||
|
||||
for (auto &property : properties.GetInternalVector()) {
|
||||
const auto &propertyName = property->GetName();
|
||||
const auto &propertyType = property->GetType();
|
||||
|
||||
// Copy the property
|
||||
behaviorProperties[propertyName] = *property;
|
||||
objectProperties[propertyName] = *property;
|
||||
|
||||
auto &newProperty = behaviorProperties[propertyName];
|
||||
auto &newProperty = objectProperties[propertyName];
|
||||
|
||||
if (configurationContent.HasChild(propertyName)) {
|
||||
if (propertyType == "String" || propertyType == "Choice" ||
|
||||
propertyType == "Color" || propertyType == "Behavior" ||
|
||||
propertyType == "resource") {
|
||||
propertyType == "Resource") {
|
||||
newProperty.SetValue(
|
||||
configurationContent.GetChild(propertyName).GetStringValue());
|
||||
} else if (propertyType == "Number") {
|
||||
@@ -71,7 +71,7 @@ std::map<gd::String, gd::PropertyDescriptor> CustomConfigurationHelper::GetPrope
|
||||
}
|
||||
}
|
||||
|
||||
return behaviorProperties;
|
||||
return objectProperties;
|
||||
}
|
||||
|
||||
bool CustomConfigurationHelper::UpdateProperty(
|
||||
@@ -89,7 +89,7 @@ bool CustomConfigurationHelper::UpdateProperty(
|
||||
|
||||
if (propertyType == "String" || propertyType == "Choice" ||
|
||||
propertyType == "Color" || propertyType == "Behavior" ||
|
||||
propertyType == "resource") {
|
||||
propertyType == "Resource") {
|
||||
element.SetStringValue(newValue);
|
||||
} else if (propertyType == "Number") {
|
||||
element.SetDoubleValue(newValue.To<double>());
|
||||
|
@@ -21,6 +21,9 @@ void CustomObjectConfiguration::Init(const gd::CustomObjectConfiguration& object
|
||||
project = objectConfiguration.project;
|
||||
objectContent = objectConfiguration.objectContent;
|
||||
animations = objectConfiguration.animations;
|
||||
isMarkedAsOverridingEventsBasedObjectChildrenConfiguration =
|
||||
objectConfiguration
|
||||
.isMarkedAsOverridingEventsBasedObjectChildrenConfiguration;
|
||||
|
||||
// There is no default copy for a map of unique_ptr like childObjectConfigurations.
|
||||
childObjectConfigurations.clear();
|
||||
@@ -42,6 +45,26 @@ const gd::EventsBasedObject* CustomObjectConfiguration::GetEventsBasedObject() c
|
||||
return &project->GetEventsBasedObject(GetType());
|
||||
}
|
||||
|
||||
bool CustomObjectConfiguration::
|
||||
IsForcedToOverrideEventsBasedObjectChildrenConfiguration() const {
|
||||
const auto *eventsBasedObject = GetEventsBasedObject();
|
||||
if (!eventsBasedObject) {
|
||||
// True is safer because nothing will be lost when serializing.
|
||||
return true;
|
||||
}
|
||||
return eventsBasedObject->GetInitialInstances().GetInstancesCount() == 0;
|
||||
}
|
||||
|
||||
bool CustomObjectConfiguration::
|
||||
IsOverridingEventsBasedObjectChildrenConfiguration() const {
|
||||
return isMarkedAsOverridingEventsBasedObjectChildrenConfiguration ||
|
||||
IsForcedToOverrideEventsBasedObjectChildrenConfiguration();
|
||||
}
|
||||
|
||||
void CustomObjectConfiguration::ClearChildrenConfiguration() {
|
||||
childObjectConfigurations.clear();
|
||||
}
|
||||
|
||||
gd::ObjectConfiguration &CustomObjectConfiguration::GetChildObjectConfiguration(const gd::String &objectName) {
|
||||
const auto *eventsBasedObject = GetEventsBasedObject();
|
||||
if (!eventsBasedObject) {
|
||||
@@ -55,6 +78,18 @@ gd::ObjectConfiguration &CustomObjectConfiguration::GetChildObjectConfiguration(
|
||||
}
|
||||
|
||||
auto &childObject = eventsBasedObject->GetObjects().GetObject(objectName);
|
||||
|
||||
if (!IsOverridingEventsBasedObjectChildrenConfiguration()) {
|
||||
// It should be fine because the editor doesn't allow to edit values when
|
||||
// the default values from the events-based object is used.
|
||||
//
|
||||
// Resource refactor operations may modify it but they will do the same
|
||||
// thing on the custom object as on the event-based object children so it
|
||||
// shouldn't have any side effect.
|
||||
return const_cast<gd::ObjectConfiguration &>(
|
||||
childObject.GetConfiguration());
|
||||
}
|
||||
|
||||
auto configurationPosition = childObjectConfigurations.find(objectName);
|
||||
if (configurationPosition == childObjectConfigurations.end()) {
|
||||
childObjectConfigurations.insert(std::make_pair(
|
||||
@@ -67,7 +102,7 @@ gd::ObjectConfiguration &CustomObjectConfiguration::GetChildObjectConfiguration(
|
||||
auto &configuration = pair.second;
|
||||
return *configuration;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::map<gd::String, gd::PropertyDescriptor> CustomObjectConfiguration::GetProperties() const {
|
||||
auto objectProperties = std::map<gd::String, gd::PropertyDescriptor>();
|
||||
@@ -128,26 +163,14 @@ void CustomObjectConfiguration::DoSerializeTo(SerializerElement& element) const
|
||||
animations.SerializeTo(animatableElement);
|
||||
}
|
||||
|
||||
auto &childrenContentElement = element.AddChild("childrenContent");
|
||||
for (auto &pair : childObjectConfigurations) {
|
||||
auto &childName = pair.first;
|
||||
auto &childConfiguration = pair.second;
|
||||
auto &childElement = childrenContentElement.AddChild(childName);
|
||||
childConfiguration->SerializeTo(childElement);
|
||||
}
|
||||
|
||||
const auto *eventsBasedObject = GetEventsBasedObject();
|
||||
if (eventsBasedObject) {
|
||||
eventsBasedObject->GetInitialInstances().SerializeTo(
|
||||
element.AddChild("instances"));
|
||||
eventsBasedObject->GetLayers().SerializeLayersTo(
|
||||
element.AddChild("layers"));
|
||||
element.SetIntAttribute("areaMinX", eventsBasedObject->GetAreaMinX());
|
||||
element.SetIntAttribute("areaMinY", eventsBasedObject->GetAreaMinY());
|
||||
element.SetIntAttribute("areaMinZ", eventsBasedObject->GetAreaMinZ());
|
||||
element.SetIntAttribute("areaMaxX", eventsBasedObject->GetAreaMaxX());
|
||||
element.SetIntAttribute("areaMaxY", eventsBasedObject->GetAreaMaxY());
|
||||
element.SetIntAttribute("areaMaxZ", eventsBasedObject->GetAreaMaxZ());
|
||||
if (IsOverridingEventsBasedObjectChildrenConfiguration()) {
|
||||
auto &childrenContentElement = element.AddChild("childrenContent");
|
||||
for (auto &pair : childObjectConfigurations) {
|
||||
auto &childName = pair.first;
|
||||
auto &childConfiguration = pair.second;
|
||||
auto &childElement = childrenContentElement.AddChild(childName);
|
||||
childConfiguration->SerializeTo(childElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
void CustomObjectConfiguration::DoUnserializeFrom(Project& project,
|
||||
@@ -159,12 +182,16 @@ void CustomObjectConfiguration::DoUnserializeFrom(Project& project,
|
||||
animations.UnserializeFrom(animatableElement);
|
||||
}
|
||||
|
||||
auto &childrenContentElement = element.GetChild("childrenContent");
|
||||
for (auto &pair : childrenContentElement.GetAllChildren()) {
|
||||
auto &childName = pair.first;
|
||||
auto &childElement = pair.second;
|
||||
auto &childConfiguration = GetChildObjectConfiguration(childName);
|
||||
childConfiguration.UnserializeFrom(project, *childElement);
|
||||
isMarkedAsOverridingEventsBasedObjectChildrenConfiguration =
|
||||
element.HasChild("childrenContent");
|
||||
if (isMarkedAsOverridingEventsBasedObjectChildrenConfiguration) {
|
||||
auto &childrenContentElement = element.GetChild("childrenContent");
|
||||
for (auto &pair : childrenContentElement.GetAllChildren()) {
|
||||
auto &childName = pair.first;
|
||||
auto &childElement = pair.second;
|
||||
auto &childConfiguration = GetChildObjectConfiguration(childName);
|
||||
childConfiguration.UnserializeFrom(project, *childElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,3 +272,16 @@ const SpriteAnimationList& CustomObjectConfiguration::GetAnimations() const {
|
||||
SpriteAnimationList& CustomObjectConfiguration::GetAnimations() {
|
||||
return animations;
|
||||
}
|
||||
|
||||
const gd::CustomObjectConfiguration::EdgeAnchor
|
||||
CustomObjectConfiguration::GetEdgeAnchorFromString(const gd::String &value) {
|
||||
return (value == _("Window left") || value == _("Window top"))
|
||||
? gd::CustomObjectConfiguration::EdgeAnchor::MinEdge
|
||||
: (value == _("Window right") || value == _("Window bottom"))
|
||||
? gd::CustomObjectConfiguration::EdgeAnchor::MaxEdge
|
||||
: value == _("Proportional")
|
||||
? gd::CustomObjectConfiguration::EdgeAnchor::Proportional
|
||||
: value == _("Window center")
|
||||
? gd::CustomObjectConfiguration::EdgeAnchor::Center
|
||||
: gd::CustomObjectConfiguration::EdgeAnchor::NoAnchor;
|
||||
}
|
||||
|
@@ -30,7 +30,7 @@ namespace gd {
|
||||
class CustomObjectConfiguration : public gd::ObjectConfiguration {
|
||||
public:
|
||||
CustomObjectConfiguration(const Project& project_, const String& type_)
|
||||
: project(&project_) {
|
||||
: project(&project_), isMarkedAsOverridingEventsBasedObjectChildrenConfiguration(false) {
|
||||
SetType(type_);
|
||||
}
|
||||
std::unique_ptr<gd::ObjectConfiguration> Clone() const override;
|
||||
@@ -65,7 +65,22 @@ class CustomObjectConfiguration : public gd::ObjectConfiguration {
|
||||
|
||||
void ExposeResources(gd::ArbitraryResourceWorker& worker) override;
|
||||
|
||||
gd::ObjectConfiguration &GetChildObjectConfiguration(const gd::String& objectName);
|
||||
bool IsForcedToOverrideEventsBasedObjectChildrenConfiguration() const;
|
||||
|
||||
bool IsMarkedAsOverridingEventsBasedObjectChildrenConfiguration() const {
|
||||
return isMarkedAsOverridingEventsBasedObjectChildrenConfiguration;
|
||||
}
|
||||
|
||||
void SetMarkedAsOverridingEventsBasedObjectChildrenConfiguration(
|
||||
bool isOverridingEventsBasedObjectChildrenConfiguration_) {
|
||||
isMarkedAsOverridingEventsBasedObjectChildrenConfiguration =
|
||||
isOverridingEventsBasedObjectChildrenConfiguration_;
|
||||
}
|
||||
|
||||
void ClearChildrenConfiguration();
|
||||
|
||||
gd::ObjectConfiguration &
|
||||
GetChildObjectConfiguration(const gd::String &objectName);
|
||||
|
||||
std::size_t GetAnimationsCount() const override;
|
||||
|
||||
@@ -83,6 +98,17 @@ class CustomObjectConfiguration : public gd::ObjectConfiguration {
|
||||
*/
|
||||
SpriteAnimationList& GetAnimations();
|
||||
|
||||
enum EdgeAnchor {
|
||||
NoAnchor = 0,
|
||||
MinEdge = 1,
|
||||
MaxEdge = 2,
|
||||
Proportional = 3,
|
||||
Center = 4,
|
||||
};
|
||||
|
||||
static const gd::CustomObjectConfiguration::EdgeAnchor
|
||||
GetEdgeAnchorFromString(const gd::String &value);
|
||||
|
||||
protected:
|
||||
void DoSerializeTo(SerializerElement& element) const override;
|
||||
void DoUnserializeFrom(Project& project, const SerializerElement& element) override;
|
||||
@@ -90,10 +116,14 @@ protected:
|
||||
private:
|
||||
const gd::EventsBasedObject* GetEventsBasedObject() const;
|
||||
|
||||
bool IsOverridingEventsBasedObjectChildrenConfiguration() const;
|
||||
|
||||
const Project* project; ///< The project is used to get the
|
||||
///< EventBasedObject from the fullType.
|
||||
gd::SerializerElement objectContent;
|
||||
std::map<gd::String, std::unique_ptr<gd::ObjectConfiguration>> childObjectConfigurations;
|
||||
|
||||
bool isMarkedAsOverridingEventsBasedObjectChildrenConfiguration;
|
||||
mutable std::map<gd::String, std::unique_ptr<gd::ObjectConfiguration>> childObjectConfigurations;
|
||||
|
||||
static gd::ObjectConfiguration badObjectConfiguration;
|
||||
|
||||
|
@@ -21,8 +21,8 @@ namespace gd {
|
||||
*/
|
||||
class GD_CORE_API Effect {
|
||||
public:
|
||||
Effect(){};
|
||||
virtual ~Effect(){};
|
||||
Effect() : folded(false) {};
|
||||
virtual ~Effect() {};
|
||||
|
||||
void SetName(const gd::String& name_) { name = name_; }
|
||||
const gd::String& GetName() const { return name; }
|
||||
@@ -32,6 +32,9 @@ class GD_CORE_API Effect {
|
||||
}
|
||||
const gd::String& GetEffectType() const { return effectType; }
|
||||
|
||||
void SetFolded(bool fold = true) { folded = fold; }
|
||||
bool IsFolded() const { return folded; }
|
||||
|
||||
void SetDoubleParameter(const gd::String& name, double value) {
|
||||
doubleParameters[name] = value;
|
||||
}
|
||||
@@ -85,6 +88,7 @@ class GD_CORE_API Effect {
|
||||
void UnserializeFrom(const SerializerElement& element);
|
||||
|
||||
private:
|
||||
bool folded;
|
||||
gd::String name; ///< The name of the layer.
|
||||
gd::String effectType; ///< The name of the effect to apply.
|
||||
std::map<gd::String, double> doubleParameters; ///< Values of parameters being doubles, keyed by names.
|
||||
|
@@ -4,6 +4,7 @@
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#include "EventsBasedBehavior.h"
|
||||
|
||||
#include "EventsFunctionsContainer.h"
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
#include "GDCore/Tools/MakeUnique.h"
|
||||
@@ -12,9 +13,10 @@ namespace gd {
|
||||
|
||||
EventsBasedBehavior::EventsBasedBehavior()
|
||||
: AbstractEventsBasedEntity(
|
||||
"MyBehavior",
|
||||
gd::EventsFunctionsContainer::FunctionOwner::Behavior),
|
||||
sharedPropertyDescriptors(gd::EventsFunctionsContainer::FunctionOwner::Behavior) {}
|
||||
"MyBehavior", gd::EventsFunctionsContainer::FunctionOwner::Behavior),
|
||||
sharedPropertyDescriptors(
|
||||
gd::EventsFunctionsContainer::FunctionOwner::Behavior),
|
||||
quickCustomizationVisibility(QuickCustomization::Visibility::Default) {}
|
||||
|
||||
void EventsBasedBehavior::SerializeTo(SerializerElement& element) const {
|
||||
AbstractEventsBasedEntity::SerializeTo(element);
|
||||
@@ -24,6 +26,13 @@ void EventsBasedBehavior::SerializeTo(SerializerElement& element) const {
|
||||
}
|
||||
sharedPropertyDescriptors.SerializeElementsTo(
|
||||
"propertyDescriptor", element.AddChild("sharedPropertyDescriptors"));
|
||||
if (quickCustomizationVisibility != QuickCustomization::Visibility::Default) {
|
||||
element.SetStringAttribute(
|
||||
"quickCustomizationVisibility",
|
||||
quickCustomizationVisibility == QuickCustomization::Visibility::Visible
|
||||
? "visible"
|
||||
: "hidden");
|
||||
}
|
||||
}
|
||||
|
||||
void EventsBasedBehavior::UnserializeFrom(gd::Project& project,
|
||||
@@ -33,6 +42,14 @@ void EventsBasedBehavior::UnserializeFrom(gd::Project& project,
|
||||
isPrivate = element.GetBoolAttribute("private");
|
||||
sharedPropertyDescriptors.UnserializeElementsFrom(
|
||||
"propertyDescriptor", element.GetChild("sharedPropertyDescriptors"));
|
||||
if (element.HasChild("quickCustomizationVisibility")) {
|
||||
quickCustomizationVisibility =
|
||||
element.GetStringAttribute("quickCustomizationVisibility") == "visible"
|
||||
? QuickCustomization::Visibility::Visible
|
||||
: QuickCustomization::Visibility::Hidden;
|
||||
} else {
|
||||
quickCustomizationVisibility = QuickCustomization::Visibility::Default;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -11,6 +11,7 @@
|
||||
#include "GDCore/Project/NamedPropertyDescriptor.h"
|
||||
#include "GDCore/Project/PropertiesContainer.h"
|
||||
#include "GDCore/Project/EventsFunctionsContainer.h"
|
||||
#include "GDCore/Project/QuickCustomization.h"
|
||||
#include "GDCore/String.h"
|
||||
namespace gd {
|
||||
class SerializerElement;
|
||||
@@ -88,6 +89,15 @@ class GD_CORE_API EventsBasedBehavior: public AbstractEventsBasedEntity {
|
||||
return *this;
|
||||
}
|
||||
|
||||
QuickCustomization::Visibility GetQuickCustomizationVisibility() const {
|
||||
return quickCustomizationVisibility;
|
||||
}
|
||||
|
||||
EventsBasedBehavior& SetQuickCustomizationVisibility(QuickCustomization::Visibility visibility) {
|
||||
quickCustomizationVisibility = visibility;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return a reference to the list of shared properties.
|
||||
*/
|
||||
@@ -141,6 +151,7 @@ class GD_CORE_API EventsBasedBehavior: public AbstractEventsBasedEntity {
|
||||
gd::String objectType;
|
||||
bool isPrivate = false;
|
||||
gd::PropertiesContainer sharedPropertyDescriptors;
|
||||
QuickCustomization::Visibility quickCustomizationVisibility;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -16,6 +16,8 @@ EventsBasedObject::EventsBasedObject()
|
||||
isRenderedIn3D(false),
|
||||
isAnimatable(false),
|
||||
isTextContainer(false),
|
||||
isInnerAreaFollowingParentSize(false),
|
||||
isUsingLegacyInstancesRenderer(false),
|
||||
areaMinX(0),
|
||||
areaMinY(0),
|
||||
areaMinZ(0),
|
||||
@@ -37,6 +39,10 @@ void EventsBasedObject::SerializeTo(SerializerElement& element) const {
|
||||
if (isTextContainer) {
|
||||
element.SetBoolAttribute("isTextContainer", true);
|
||||
}
|
||||
if (isInnerAreaFollowingParentSize) {
|
||||
element.SetBoolAttribute("isInnerAreaFollowingParentSize", true);
|
||||
}
|
||||
element.SetBoolAttribute("isUsingLegacyInstancesRenderer", isUsingLegacyInstancesRenderer);
|
||||
element.SetIntAttribute("areaMinX", areaMinX);
|
||||
element.SetIntAttribute("areaMinY", areaMinY);
|
||||
element.SetIntAttribute("areaMinZ", areaMinZ);
|
||||
@@ -59,6 +65,8 @@ void EventsBasedObject::UnserializeFrom(gd::Project& project,
|
||||
isRenderedIn3D = element.GetBoolAttribute("is3D", false);
|
||||
isAnimatable = element.GetBoolAttribute("isAnimatable", false);
|
||||
isTextContainer = element.GetBoolAttribute("isTextContainer", false);
|
||||
isInnerAreaFollowingParentSize =
|
||||
element.GetBoolAttribute("isInnerAreaFollowingParentSize", false);
|
||||
areaMinX = element.GetIntAttribute("areaMinX", 0);
|
||||
areaMinY = element.GetIntAttribute("areaMinY", 0);
|
||||
areaMinZ = element.GetIntAttribute("areaMinZ", 0);
|
||||
@@ -82,6 +90,15 @@ void EventsBasedObject::UnserializeFrom(gd::Project& project,
|
||||
}
|
||||
|
||||
initialInstances.UnserializeFrom(element.GetChild("instances"));
|
||||
if (element.HasAttribute("isUsingLegacyInstancesRenderer")) {
|
||||
isUsingLegacyInstancesRenderer =
|
||||
element.GetBoolAttribute("isUsingLegacyInstancesRenderer", false);
|
||||
}
|
||||
else {
|
||||
// Compatibility with GD <= 5.4.212
|
||||
isUsingLegacyInstancesRenderer = initialInstances.GetInstancesCount() == 0;
|
||||
// end of compatibility code
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -101,11 +101,53 @@ class GD_CORE_API EventsBasedObject: public AbstractEventsBasedEntity {
|
||||
/**
|
||||
* \brief Declare a TextContainer capability.
|
||||
*/
|
||||
EventsBasedObject& MarkAsTextContainer(bool isTextContainer_) {
|
||||
EventsBasedObject &MarkAsTextContainer(bool isTextContainer_) {
|
||||
isTextContainer = isTextContainer_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Declare that the parent scale will always be 1 and children will
|
||||
* adapt there size. This is removing the ScalableCapability.
|
||||
*/
|
||||
EventsBasedObject &
|
||||
MarkAsInnerAreaFollowingParentSize(bool isInnerAreaExpandingWithParent_) {
|
||||
isInnerAreaFollowingParentSize = isInnerAreaExpandingWithParent_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return true if objects handle size changes on their own and
|
||||
* don't have the ScalableCapability.
|
||||
*
|
||||
* When the parent dimensions change:
|
||||
* - if `false`, the object is stretch proportionally while children local
|
||||
* positions stay the same.
|
||||
* - if `true`, the children local positions need to be adapted by events
|
||||
* to follow their parent size.
|
||||
*/
|
||||
bool IsInnerAreaFollowingParentSize() const {
|
||||
return isInnerAreaFollowingParentSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Declare that custom object are rendered using their child-objects
|
||||
* instead of their child-instances.
|
||||
*/
|
||||
EventsBasedObject &
|
||||
MakAsUsingLegacyInstancesRenderer(bool isUsingLegacyInstancesRenderer_) {
|
||||
isUsingLegacyInstancesRenderer = isUsingLegacyInstancesRenderer_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return true if custom object are rendered using their child-objects
|
||||
* instead of their child-instances.
|
||||
*/
|
||||
bool IsUsingLegacyInstancesRenderer() const {
|
||||
return isUsingLegacyInstancesRenderer;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return true if the object needs a TextContainer capability.
|
||||
*/
|
||||
@@ -279,6 +321,8 @@ class GD_CORE_API EventsBasedObject: public AbstractEventsBasedEntity {
|
||||
bool isRenderedIn3D;
|
||||
bool isAnimatable;
|
||||
bool isTextContainer;
|
||||
bool isInnerAreaFollowingParentSize;
|
||||
bool isUsingLegacyInstancesRenderer;
|
||||
gd::InitialInstancesContainer initialInstances;
|
||||
gd::LayersContainer layers;
|
||||
gd::ObjectsContainer objectsContainer;
|
||||
|
@@ -16,41 +16,41 @@ EventsFunction::EventsFunction() : functionType(Action) {
|
||||
expressionType.SetName("expression");
|
||||
}
|
||||
|
||||
const std::vector<gd::ParameterMetadata>& EventsFunction::GetParametersForEvents(
|
||||
const gd::EventsFunctionsContainer& functionsContainer) const {
|
||||
const gd::ParameterMetadataContainer &EventsFunction::GetParametersForEvents(
|
||||
const gd::EventsFunctionsContainer &functionsContainer) const {
|
||||
if (functionType != FunctionType::ActionWithOperator) {
|
||||
// For most function types, the parameters are specified in the function.
|
||||
return parameters;
|
||||
}
|
||||
// For ActionWithOperator, the parameters are auto generated.
|
||||
actionWithOperationParameters.clear();
|
||||
actionWithOperationParameters.ClearParameters();
|
||||
if (!functionsContainer.HasEventsFunctionNamed(getterName)) {
|
||||
return actionWithOperationParameters;
|
||||
}
|
||||
const auto& expression = functionsContainer.GetEventsFunction(getterName);
|
||||
const auto& expressionParameters = expression.parameters;
|
||||
const auto &expression = functionsContainer.GetEventsFunction(getterName);
|
||||
const auto &expressionParameters = expression.parameters;
|
||||
const auto functionsSource = functionsContainer.GetOwner();
|
||||
const int expressionValueParameterIndex =
|
||||
functionsSource == gd::EventsFunctionsContainer::FunctionOwner::Behavior ?
|
||||
2 :
|
||||
functionsSource == gd::EventsFunctionsContainer::FunctionOwner::Object ?
|
||||
1 :
|
||||
0;
|
||||
|
||||
for (size_t i = 0;
|
||||
i < expressionValueParameterIndex && i < expressionParameters.size();
|
||||
i++)
|
||||
{
|
||||
actionWithOperationParameters.push_back(expressionParameters[i]);
|
||||
functionsSource == gd::EventsFunctionsContainer::FunctionOwner::Behavior
|
||||
? 2
|
||||
: functionsSource == gd::EventsFunctionsContainer::FunctionOwner::Object
|
||||
? 1
|
||||
: 0;
|
||||
|
||||
for (size_t i = 0; i < expressionValueParameterIndex &&
|
||||
i < expressionParameters.GetParametersCount();
|
||||
i++) {
|
||||
actionWithOperationParameters.AddParameter(
|
||||
expressionParameters.GetParameter(i));
|
||||
}
|
||||
gd::ParameterMetadata parameterMetadata;
|
||||
parameterMetadata.SetName("Value").SetValueTypeMetadata(expression.expressionType);
|
||||
actionWithOperationParameters.push_back(parameterMetadata);
|
||||
parameterMetadata.SetName("Value").SetValueTypeMetadata(
|
||||
expression.expressionType);
|
||||
actionWithOperationParameters.AddParameter(parameterMetadata);
|
||||
for (size_t i = expressionValueParameterIndex;
|
||||
i < expressionParameters.size();
|
||||
i++)
|
||||
{
|
||||
actionWithOperationParameters.push_back(expressionParameters[i]);
|
||||
i < expressionParameters.GetParametersCount(); i++) {
|
||||
actionWithOperationParameters.AddParameter(
|
||||
expressionParameters.GetParameter(i));
|
||||
}
|
||||
|
||||
return actionWithOperationParameters;
|
||||
@@ -101,10 +101,7 @@ void EventsFunction::SerializeTo(SerializerElement& element) const {
|
||||
expressionType.SerializeTo(element.AddChild("expressionType"));
|
||||
}
|
||||
gd::SerializerElement& parametersElement = element.AddChild("parameters");
|
||||
parametersElement.ConsiderAsArrayOf("parameter");
|
||||
for (const auto& parameter : parameters) {
|
||||
parameter.SerializeTo(parametersElement.AddChild("parameter"));
|
||||
}
|
||||
parameters.SerializeParametersTo(parametersElement);
|
||||
|
||||
objectGroups.SerializeTo(element.AddChild("objectGroups"));
|
||||
}
|
||||
@@ -146,15 +143,9 @@ void EventsFunction::UnserializeFrom(gd::Project& project,
|
||||
else
|
||||
functionType = Action;
|
||||
|
||||
const gd::SerializerElement& parametersElement =
|
||||
const gd::SerializerElement ¶metersElement =
|
||||
element.GetChild("parameters");
|
||||
parameters.clear();
|
||||
parametersElement.ConsiderAsArrayOf("parameter");
|
||||
for (std::size_t i = 0; i < parametersElement.GetChildrenCount(); ++i) {
|
||||
ParameterMetadata parameter;
|
||||
parameter.UnserializeFrom(parametersElement.GetChild(i));
|
||||
parameters.push_back(parameter);
|
||||
}
|
||||
parameters.UnserializeParametersFrom(parametersElement);
|
||||
|
||||
objectGroups.UnserializeFrom(element.GetChild("objectGroups"));
|
||||
}
|
||||
|
@@ -3,14 +3,13 @@
|
||||
* 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 GDCORE_EVENTSFUNCTION_H
|
||||
#define GDCORE_EVENTSFUNCTION_H
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "GDCore/Events/EventsList.h"
|
||||
#include "GDCore/Project/ObjectGroupsContainer.h"
|
||||
#include "GDCore/Project/ParameterMetadataContainer.h"
|
||||
#include "GDCore/String.h"
|
||||
#include "GDCore/Extensions/Metadata/ValueTypeMetadata.h"
|
||||
// TODO: In theory (for separation of concerns between Project and
|
||||
@@ -241,7 +240,7 @@ class GD_CORE_API EventsFunction {
|
||||
* to the generated function, like "runtimeScene" and "eventsFunctionContext".
|
||||
* This should be transparent to the user.
|
||||
*/
|
||||
const std::vector<gd::ParameterMetadata>& GetParametersForEvents(
|
||||
const gd::ParameterMetadataContainer& GetParametersForEvents(
|
||||
const gd::EventsFunctionsContainer& functionsContainer) const;
|
||||
|
||||
/**
|
||||
@@ -254,14 +253,14 @@ class GD_CORE_API EventsFunction {
|
||||
* to the generated function, like "runtimeScene" and "eventsFunctionContext".
|
||||
* This should be transparent to the user.
|
||||
*/
|
||||
const std::vector<gd::ParameterMetadata>& GetParameters() const {
|
||||
const ParameterMetadataContainer& GetParameters() const {
|
||||
return parameters;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Return the parameters.
|
||||
*/
|
||||
std::vector<gd::ParameterMetadata>& GetParameters() { return parameters; };
|
||||
ParameterMetadataContainer& GetParameters() { return parameters; };
|
||||
|
||||
/**
|
||||
* \brief Return a reference to the object groups that can be used in the
|
||||
@@ -300,14 +299,11 @@ class GD_CORE_API EventsFunction {
|
||||
gd::ValueTypeMetadata expressionType;
|
||||
gd::EventsList events;
|
||||
FunctionType functionType;
|
||||
std::vector<gd::ParameterMetadata> parameters;
|
||||
mutable std::vector<gd::ParameterMetadata> actionWithOperationParameters;
|
||||
ParameterMetadataContainer parameters;
|
||||
mutable gd::ParameterMetadataContainer actionWithOperationParameters;
|
||||
gd::ObjectGroupsContainer objectGroups;
|
||||
bool isPrivate = false;
|
||||
bool isAsync = false;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
||||
#endif // GDCORE_EVENTSFUNCTION_H
|
||||
#endif
|
||||
|
@@ -96,19 +96,6 @@ class GD_CORE_API ExternalLayout {
|
||||
gd::String associatedLayout;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Functor testing ExternalLayout' name
|
||||
*/
|
||||
struct ExternalLayoutHasName
|
||||
: public std::binary_function<std::unique_ptr<gd::ExternalLayout>,
|
||||
gd::String,
|
||||
bool> {
|
||||
bool operator()(const std::unique_ptr<gd::ExternalLayout>& externalLayout,
|
||||
gd::String name) const {
|
||||
return externalLayout->GetName() == name;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
||||
#endif // GDCORE_EXTERNALLAYOUT_H
|
||||
|
@@ -27,7 +27,11 @@ InitialInstance::InitialInstance()
|
||||
rotationX(0),
|
||||
rotationY(0),
|
||||
zOrder(0),
|
||||
opacity(255),
|
||||
layer(""),
|
||||
flippedX(false),
|
||||
flippedY(false),
|
||||
flippedZ(false),
|
||||
customSize(false),
|
||||
customDepth(false),
|
||||
width(0),
|
||||
@@ -57,7 +61,11 @@ void InitialInstance::UnserializeFrom(const SerializerElement& element) {
|
||||
SetHasCustomDepth(false);
|
||||
}
|
||||
SetZOrder(element.GetIntAttribute("zOrder", 0, "plan"));
|
||||
SetOpacity(element.GetIntAttribute("opacity", 255));
|
||||
SetLayer(element.GetStringAttribute("layer"));
|
||||
SetFlippedX(element.GetBoolAttribute("flippedX", false));
|
||||
SetFlippedY(element.GetBoolAttribute("flippedY", false));
|
||||
SetFlippedZ(element.GetBoolAttribute("flippedZ", false));
|
||||
SetLocked(element.GetBoolAttribute("locked", false));
|
||||
SetSealed(element.GetBoolAttribute("sealed", false));
|
||||
SetShouldKeepRatio(element.GetBoolAttribute("keepRatio", false));
|
||||
@@ -113,6 +121,10 @@ void InitialInstance::SerializeTo(SerializerElement& element) const {
|
||||
element.SetAttribute("y", GetY());
|
||||
if (GetZ() != 0) element.SetAttribute("z", GetZ());
|
||||
element.SetAttribute("zOrder", GetZOrder());
|
||||
if (GetOpacity() != 255) element.SetAttribute("opacity", GetOpacity());
|
||||
if (IsFlippedX()) element.SetAttribute("flippedX", IsFlippedX());
|
||||
if (IsFlippedY()) element.SetAttribute("flippedY", IsFlippedY());
|
||||
if (IsFlippedZ()) element.SetAttribute("flippedZ", IsFlippedZ());
|
||||
element.SetAttribute("layer", GetLayer());
|
||||
element.SetAttribute("angle", GetAngle());
|
||||
if (GetRotationX() != 0) element.SetAttribute("rotationX", GetRotationX());
|
||||
@@ -155,8 +167,8 @@ InitialInstance& InitialInstance::ResetPersistentUuid() {
|
||||
|
||||
std::map<gd::String, gd::PropertyDescriptor>
|
||||
InitialInstance::GetCustomProperties(
|
||||
gd::ObjectsContainer &globalObjectsContainer,
|
||||
gd::ObjectsContainer &objectsContainer) {
|
||||
gd::ObjectsContainer& globalObjectsContainer,
|
||||
gd::ObjectsContainer& objectsContainer) {
|
||||
// Find an object
|
||||
if (objectsContainer.HasObjectNamed(GetObjectName()))
|
||||
return objectsContainer.GetObject(GetObjectName())
|
||||
@@ -172,9 +184,10 @@ InitialInstance::GetCustomProperties(
|
||||
}
|
||||
|
||||
bool InitialInstance::UpdateCustomProperty(
|
||||
const gd::String &name, const gd::String &value,
|
||||
gd::ObjectsContainer &globalObjectsContainer,
|
||||
gd::ObjectsContainer &objectsContainer) {
|
||||
const gd::String& name,
|
||||
const gd::String& value,
|
||||
gd::ObjectsContainer& globalObjectsContainer,
|
||||
gd::ObjectsContainer& objectsContainer) {
|
||||
if (objectsContainer.HasObjectNamed(GetObjectName()))
|
||||
return objectsContainer.GetObject(GetObjectName())
|
||||
.GetConfiguration()
|
||||
|
@@ -29,7 +29,7 @@ class GD_CORE_API InitialInstance {
|
||||
* \brief Create an initial instance pointing to no object, at position (0,0).
|
||||
*/
|
||||
InitialInstance();
|
||||
virtual ~InitialInstance(){};
|
||||
virtual ~InitialInstance() {};
|
||||
|
||||
/**
|
||||
* Must return a pointer to a copy of the object. A such method is needed to
|
||||
@@ -123,6 +123,46 @@ class GD_CORE_API InitialInstance {
|
||||
*/
|
||||
void SetZOrder(int zOrder_) { zOrder = zOrder_; }
|
||||
|
||||
/**
|
||||
* \brief Get Opacity.
|
||||
*/
|
||||
int GetOpacity() const { return opacity; }
|
||||
|
||||
/**
|
||||
* \brief Set the opacity of the instance.
|
||||
*/
|
||||
void SetOpacity(int opacity_) { opacity = opacity_; }
|
||||
|
||||
/**
|
||||
* \brief Return true if the instance is flipped on X axis.
|
||||
*/
|
||||
bool IsFlippedX() const { return flippedX; }
|
||||
|
||||
/**
|
||||
* \brief Set whether the instance is flipped on X axis.
|
||||
*/
|
||||
void SetFlippedX(bool flippedX_) { flippedX = flippedX_; }
|
||||
|
||||
/**
|
||||
* \brief Return true if the instance is flipped on Y axis.
|
||||
*/
|
||||
bool IsFlippedY() const { return flippedY; }
|
||||
|
||||
/**
|
||||
* \brief Set whether the instance is flipped on Y axis.
|
||||
*/
|
||||
void SetFlippedY(bool flippedY_) { flippedY = flippedY_; }
|
||||
|
||||
/**
|
||||
* \brief Return true if the instance is flipped on Z axis.
|
||||
*/
|
||||
bool IsFlippedZ() const { return flippedZ; }
|
||||
|
||||
/**
|
||||
* \brief Set whether the instance is flipped on Z axis.
|
||||
*/
|
||||
void SetFlippedZ(bool flippedZ_) { flippedZ = flippedZ_; }
|
||||
|
||||
/**
|
||||
* \brief Get the layer the instance belongs to.
|
||||
*/
|
||||
@@ -134,8 +174,9 @@ class GD_CORE_API InitialInstance {
|
||||
void SetLayer(const gd::String& layer_) { layer = layer_; }
|
||||
|
||||
/**
|
||||
* \brief Return true if the instance has a width/height which is different from its
|
||||
* object default width/height. This is independent from `HasCustomDepth`.
|
||||
* \brief Return true if the instance has a width/height which is different
|
||||
* from its object default width/height. This is independent from
|
||||
* `HasCustomDepth`.
|
||||
*
|
||||
* \see gd::Object
|
||||
*/
|
||||
@@ -150,15 +191,13 @@ class GD_CORE_API InitialInstance {
|
||||
bool HasCustomDepth() const { return customDepth; }
|
||||
|
||||
/**
|
||||
* \brief Set whether the instance has a width/height which is different from its
|
||||
* object default width/height or not.
|
||||
* This is independent from `SetHasCustomDepth`.
|
||||
* \brief Set whether the instance has a width/height which is different from
|
||||
* its object default width/height or not. This is independent from
|
||||
* `SetHasCustomDepth`.
|
||||
*
|
||||
* \see gd::Object
|
||||
*/
|
||||
void SetHasCustomSize(bool hasCustomSize_) {
|
||||
customSize = hasCustomSize_;
|
||||
}
|
||||
void SetHasCustomSize(bool hasCustomSize_) { customSize = hasCustomSize_; }
|
||||
|
||||
/**
|
||||
* \brief Set whether the instance has a depth which is different from its
|
||||
@@ -264,18 +303,19 @@ class GD_CORE_API InitialInstance {
|
||||
* \note Common properties ( name, position... ) do not need to be
|
||||
* inserted in this map
|
||||
*/
|
||||
std::map<gd::String, gd::PropertyDescriptor>
|
||||
GetCustomProperties(gd::ObjectsContainer &globalObjectsContainer,
|
||||
gd::ObjectsContainer &objectsContainer);
|
||||
std::map<gd::String, gd::PropertyDescriptor> GetCustomProperties(
|
||||
gd::ObjectsContainer& globalObjectsContainer,
|
||||
gd::ObjectsContainer& objectsContainer);
|
||||
|
||||
/**
|
||||
* \brief Update the property called \a name with the new \a value.
|
||||
*
|
||||
* \return false if the property could not be updated.
|
||||
*/
|
||||
bool UpdateCustomProperty(const gd::String &name, const gd::String &value,
|
||||
gd::ObjectsContainer &globalObjectsContainer,
|
||||
gd::ObjectsContainer &objectsContainer);
|
||||
bool UpdateCustomProperty(const gd::String& name,
|
||||
const gd::String& value,
|
||||
gd::ObjectsContainer& globalObjectsContainer,
|
||||
gd::ObjectsContainer& objectsContainer);
|
||||
|
||||
/**
|
||||
* \brief Get the value of a double property stored in the instance.
|
||||
@@ -343,6 +383,10 @@ class GD_CORE_API InitialInstance {
|
||||
double rotationX; ///< Instance angle on X axis (for a 3D object)
|
||||
double rotationY; ///< Instance angle on Y axis (for a 3D object)
|
||||
int zOrder; ///< Instance Z order (for a 2D object)
|
||||
int opacity; ///< Instance opacity
|
||||
bool flippedX; ///< True if the instance is flipped on X axis
|
||||
bool flippedY; ///< True if the instance is flipped on Y axis
|
||||
bool flippedZ; ///< True if the instance is flipped on Z axis
|
||||
gd::String layer; ///< Instance layer
|
||||
bool customSize; ///< True if object has a custom width and height
|
||||
bool customDepth; ///< True if object has a custom depth
|
||||
@@ -352,13 +396,13 @@ class GD_CORE_API InitialInstance {
|
||||
gd::VariablesContainer initialVariables; ///< Instance specific variables
|
||||
bool locked; ///< True if the instance is locked
|
||||
bool sealed; ///< True if the instance is sealed
|
||||
bool keepRatio; ///< True if the instance's dimensions
|
||||
/// should keep the same ratio.
|
||||
bool keepRatio; ///< True if the instance's dimensions
|
||||
/// should keep the same ratio.
|
||||
mutable gd::String persistentUuid; ///< A persistent random version 4 UUID,
|
||||
/// useful for hot reloading.
|
||||
|
||||
static gd::String*
|
||||
badStringPropertyValue; ///< Empty string returned by GetRawStringProperty
|
||||
static gd::String* badStringPropertyValue; ///< Empty string returned by
|
||||
///< GetRawStringProperty
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -405,18 +405,6 @@ class GD_CORE_API Layout {
|
||||
const gd::String& behaviorsType);
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Functor testing layout name.
|
||||
* \see gd::Layout
|
||||
*/
|
||||
struct LayoutHasName
|
||||
: public std::binary_function<std::unique_ptr<Layout>, gd::String, bool> {
|
||||
bool operator()(const std::unique_ptr<Layout>& layout,
|
||||
gd::String name) const {
|
||||
return layout->GetName() == name;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Get the names of all layers from the given layout
|
||||
* that are invisible.
|
||||
|
@@ -19,13 +19,20 @@ namespace gd {
|
||||
ObjectFolderOrObject ObjectFolderOrObject::badObjectFolderOrObject;
|
||||
|
||||
ObjectFolderOrObject::ObjectFolderOrObject()
|
||||
: folderName("__NULL"), object(nullptr) {}
|
||||
: folderName("__NULL"),
|
||||
object(nullptr),
|
||||
quickCustomizationVisibility(QuickCustomization::Visibility::Default) {}
|
||||
ObjectFolderOrObject::ObjectFolderOrObject(gd::String folderName_,
|
||||
ObjectFolderOrObject* parent_)
|
||||
: folderName(folderName_), parent(parent_), object(nullptr) {}
|
||||
: folderName(folderName_),
|
||||
parent(parent_),
|
||||
object(nullptr),
|
||||
quickCustomizationVisibility(QuickCustomization::Visibility::Default) {}
|
||||
ObjectFolderOrObject::ObjectFolderOrObject(gd::Object* object_,
|
||||
ObjectFolderOrObject* parent_)
|
||||
: object(object_), parent(parent_) {}
|
||||
: object(object_),
|
||||
parent(parent_),
|
||||
quickCustomizationVisibility(QuickCustomization::Visibility::Default) {}
|
||||
ObjectFolderOrObject::~ObjectFolderOrObject() {}
|
||||
|
||||
bool ObjectFolderOrObject::HasObjectNamed(const gd::String& name) {
|
||||
@@ -66,7 +73,8 @@ ObjectFolderOrObject& ObjectFolderOrObject::GetChildAt(std::size_t index) {
|
||||
if (index >= children.size()) return badObjectFolderOrObject;
|
||||
return *children[index];
|
||||
}
|
||||
const ObjectFolderOrObject& ObjectFolderOrObject::GetChildAt(std::size_t index) const {
|
||||
const ObjectFolderOrObject& ObjectFolderOrObject::GetChildAt(
|
||||
std::size_t index) const {
|
||||
if (index >= children.size()) return badObjectFolderOrObject;
|
||||
return *children[index];
|
||||
}
|
||||
@@ -206,6 +214,14 @@ void ObjectFolderOrObject::SerializeTo(SerializerElement& element) const {
|
||||
} else {
|
||||
element.SetAttribute("objectName", GetObject().GetName());
|
||||
}
|
||||
|
||||
if (quickCustomizationVisibility != QuickCustomization::Visibility::Default) {
|
||||
element.SetStringAttribute(
|
||||
"quickCustomizationVisibility",
|
||||
quickCustomizationVisibility == QuickCustomization::Visibility::Visible
|
||||
? "visible"
|
||||
: "hidden");
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectFolderOrObject::UnserializeFrom(
|
||||
@@ -243,6 +259,15 @@ void ObjectFolderOrObject::UnserializeFrom(
|
||||
object = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (element.HasChild("quickCustomizationVisibility")) {
|
||||
quickCustomizationVisibility =
|
||||
element.GetStringAttribute("quickCustomizationVisibility") == "visible"
|
||||
? QuickCustomization::Visibility::Visible
|
||||
: QuickCustomization::Visibility::Hidden;
|
||||
} else {
|
||||
quickCustomizationVisibility = QuickCustomization::Visibility::Default;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace gd
|
@@ -10,6 +10,7 @@
|
||||
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
#include "GDCore/String.h"
|
||||
#include "GDCore/Project/QuickCustomization.h"
|
||||
|
||||
namespace gd {
|
||||
class Project;
|
||||
@@ -166,6 +167,11 @@ class GD_CORE_API ObjectFolderOrObject {
|
||||
gd::ObjectFolderOrObject& newParentFolder,
|
||||
std::size_t newPosition);
|
||||
|
||||
QuickCustomization::Visibility GetQuickCustomizationVisibility() const { return quickCustomizationVisibility; }
|
||||
void SetQuickCustomizationVisibility(QuickCustomization::Visibility visibility) {
|
||||
quickCustomizationVisibility = visibility;
|
||||
}
|
||||
|
||||
/** \name Saving and loading
|
||||
* Members functions related to saving and loading the objects of the class.
|
||||
*/
|
||||
@@ -188,6 +194,7 @@ class GD_CORE_API ObjectFolderOrObject {
|
||||
|
||||
gd::ObjectFolderOrObject*
|
||||
parent; // nullptr if root folder, points to the parent folder otherwise.
|
||||
QuickCustomization::Visibility quickCustomizationVisibility;
|
||||
|
||||
// Representing an object:
|
||||
gd::Object* object; // nullptr if folderName is set.
|
||||
|
@@ -589,4 +589,13 @@ std::vector<gd::String> ObjectsContainersList::GetAnimationNamesOfObject(
|
||||
return animationNames;
|
||||
}
|
||||
|
||||
const gd::ObjectsContainer &
|
||||
ObjectsContainersList::GetObjectsContainer(std::size_t index) const {
|
||||
return *objectsContainers[index];
|
||||
}
|
||||
|
||||
std::size_t ObjectsContainersList::GetObjectsContainersCount() const {
|
||||
return objectsContainers.size();
|
||||
}
|
||||
|
||||
} // namespace gd
|
@@ -173,6 +173,16 @@ class GD_CORE_API ObjectsContainersList {
|
||||
std::function<void(const gd::String& variableName,
|
||||
const gd::Variable& variable)> fn) const;
|
||||
|
||||
/**
|
||||
* \brief Return a the objects container at position \a index.
|
||||
*/
|
||||
const gd::ObjectsContainer &GetObjectsContainer(std::size_t index) const;
|
||||
|
||||
/**
|
||||
* \brief Return the number of objects containers.
|
||||
*/
|
||||
std::size_t GetObjectsContainersCount() const;
|
||||
|
||||
/** Do not use - should be private but accessible to let Emscripten create a
|
||||
* temporary. */
|
||||
ObjectsContainersList(){};
|
||||
|
148
Core/GDCore/Project/ParameterMetadataContainer.h
Normal file
148
Core/GDCore/Project/ParameterMetadataContainer.h
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "GDCore/Extensions/Metadata/ParameterMetadata.h"
|
||||
#include "GDCore/String.h"
|
||||
#include "GDCore/Tools/SerializableWithNameList.h"
|
||||
#include <vector>
|
||||
namespace gd {
|
||||
class SerializerElement;
|
||||
}
|
||||
|
||||
namespace gd {
|
||||
|
||||
/**
|
||||
* \brief Used as a base class for classes that will own events-backed
|
||||
* functions.
|
||||
*
|
||||
* \see gd::ParameterMetadata
|
||||
* \ingroup PlatformDefinition
|
||||
*/
|
||||
class GD_CORE_API ParameterMetadataContainer
|
||||
: private SerializableWithNameList<gd::ParameterMetadata> {
|
||||
public:
|
||||
ParameterMetadataContainer() {}
|
||||
|
||||
/** \name Events Functions management
|
||||
*/
|
||||
///@{
|
||||
/**
|
||||
* \brief Check if the function with the specified name exists.
|
||||
*/
|
||||
bool HasParameterNamed(const gd::String &name) const { return Has(name); }
|
||||
|
||||
/**
|
||||
* \brief Get the function with the specified name.
|
||||
*
|
||||
* \warning Trying to access to a not existing function will result in
|
||||
* undefined behavior.
|
||||
*/
|
||||
gd::ParameterMetadata &GetParameter(const gd::String &name) {
|
||||
return Get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the function with the specified name.
|
||||
*
|
||||
* \warning Trying to access to a not existing function will result in
|
||||
* undefined behavior.
|
||||
*/
|
||||
const gd::ParameterMetadata &GetParameter(const gd::String &name) const {
|
||||
return Get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the function at the specified index in the list.
|
||||
*
|
||||
* \warning Trying to access to a not existing function will result in
|
||||
* undefined behavior.
|
||||
*/
|
||||
gd::ParameterMetadata &GetParameter(std::size_t index) { return Get(index); }
|
||||
|
||||
/**
|
||||
* \brief Get the function at the specified index in the list.
|
||||
*
|
||||
* \warning Trying to access to a not existing function will result in
|
||||
* undefined behavior.
|
||||
*/
|
||||
const gd::ParameterMetadata &GetParameter(std::size_t index) const {
|
||||
return Get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return the number of functions.
|
||||
*/
|
||||
std::size_t GetParametersCount() const { return GetCount(); }
|
||||
|
||||
gd::ParameterMetadata &InsertNewParameter(const gd::String &name,
|
||||
std::size_t position) {
|
||||
return InsertNew(name, position);
|
||||
}
|
||||
gd::ParameterMetadata &InsertParameter(const gd::ParameterMetadata &object,
|
||||
std::size_t position) {
|
||||
return Insert(object, position);
|
||||
}
|
||||
gd::ParameterMetadata &AddNewParameter(const gd::String &name) {
|
||||
return InsertNew(name, GetCount());
|
||||
}
|
||||
gd::ParameterMetadata &AddParameter(const gd::ParameterMetadata &object) {
|
||||
return Insert(object, GetCount());
|
||||
}
|
||||
void RemoveParameter(const gd::String &name) { return Remove(name); }
|
||||
void ClearParameters() { return Clear(); }
|
||||
void MoveParameter(std::size_t oldIndex, std::size_t newIndex) {
|
||||
return Move(oldIndex, newIndex);
|
||||
};
|
||||
std::size_t
|
||||
GetParameterPosition(const gd::ParameterMetadata ¶meterMetadata) {
|
||||
return GetPosition(parameterMetadata);
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Provide a raw access to the vector containing the functions.
|
||||
*/
|
||||
const std::vector<std::unique_ptr<gd::ParameterMetadata>> &
|
||||
GetInternalVector() const {
|
||||
return elements;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Provide a raw access to the vector containing the functions.
|
||||
*/
|
||||
std::vector<std::unique_ptr<gd::ParameterMetadata>> &GetInternalVector() {
|
||||
return elements;
|
||||
};
|
||||
///@}
|
||||
|
||||
/** \name Serialization
|
||||
*/
|
||||
///@{
|
||||
/**
|
||||
* \brief Serialize events functions.
|
||||
*/
|
||||
void SerializeParametersTo(SerializerElement &element) const {
|
||||
return SerializeElementsTo("parameters", element);
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Unserialize the events functions.
|
||||
*/
|
||||
void UnserializeParametersFrom(const SerializerElement &element) {
|
||||
return UnserializeElementsFrom("parameters", element);
|
||||
};
|
||||
///@}
|
||||
protected:
|
||||
/**
|
||||
* Initialize object using another object. Used by copy-ctor and assign-op.
|
||||
* Don't forget to update me if members were changed!
|
||||
*/
|
||||
void Init(const gd::ParameterMetadataContainer &other) {
|
||||
return SerializableWithNameList<gd::ParameterMetadata>::Init(other);
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace gd
|
@@ -264,15 +264,21 @@ bool Project::RemovePlatform(const gd::String& platformName) {
|
||||
bool Project::HasLayoutNamed(const gd::String& name) const {
|
||||
return (find_if(scenes.begin(),
|
||||
scenes.end(),
|
||||
bind2nd(gd::LayoutHasName(), name)) != scenes.end());
|
||||
[&name](const std::unique_ptr<gd::Layout>& layout) {
|
||||
return layout->GetName() == name;
|
||||
}) != scenes.end());
|
||||
}
|
||||
gd::Layout& Project::GetLayout(const gd::String& name) {
|
||||
return *(*find_if(
|
||||
scenes.begin(), scenes.end(), bind2nd(gd::LayoutHasName(), name)));
|
||||
scenes.begin(), scenes.end(), [&name](const std::unique_ptr<gd::Layout>& layout) {
|
||||
return layout->GetName() == name;
|
||||
}));
|
||||
}
|
||||
const gd::Layout& Project::GetLayout(const gd::String& name) const {
|
||||
return *(*find_if(
|
||||
scenes.begin(), scenes.end(), bind2nd(gd::LayoutHasName(), name)));
|
||||
scenes.begin(), scenes.end(), [&name](const std::unique_ptr<gd::Layout>& layout) {
|
||||
return layout->GetName() == name;
|
||||
}));
|
||||
}
|
||||
gd::Layout& Project::GetLayout(std::size_t index) { return *scenes[index]; }
|
||||
const gd::Layout& Project::GetLayout(std::size_t index) const {
|
||||
@@ -317,7 +323,9 @@ gd::Layout& Project::InsertLayout(const gd::Layout& layout,
|
||||
|
||||
void Project::RemoveLayout(const gd::String& name) {
|
||||
std::vector<std::unique_ptr<gd::Layout> >::iterator scene =
|
||||
find_if(scenes.begin(), scenes.end(), bind2nd(gd::LayoutHasName(), name));
|
||||
find_if(scenes.begin(), scenes.end(), [&name](const std::unique_ptr<gd::Layout>& layout) {
|
||||
return layout->GetName() == name;
|
||||
});
|
||||
if (scene == scenes.end()) return;
|
||||
|
||||
scenes.erase(scene);
|
||||
@@ -326,19 +334,24 @@ void Project::RemoveLayout(const gd::String& name) {
|
||||
bool Project::HasExternalEventsNamed(const gd::String& name) const {
|
||||
return (find_if(externalEvents.begin(),
|
||||
externalEvents.end(),
|
||||
bind2nd(gd::ExternalEventsHasName(), name)) !=
|
||||
externalEvents.end());
|
||||
[&name](const std::unique_ptr<gd::ExternalEvents>& externalEvents) {
|
||||
return externalEvents->GetName() == name;
|
||||
}) != externalEvents.end());
|
||||
}
|
||||
gd::ExternalEvents& Project::GetExternalEvents(const gd::String& name) {
|
||||
return *(*find_if(externalEvents.begin(),
|
||||
externalEvents.end(),
|
||||
bind2nd(gd::ExternalEventsHasName(), name)));
|
||||
[&name](const std::unique_ptr<gd::ExternalEvents>& externalEvents) {
|
||||
return externalEvents->GetName() == name;
|
||||
}));
|
||||
}
|
||||
const gd::ExternalEvents& Project::GetExternalEvents(
|
||||
const gd::String& name) const {
|
||||
return *(*find_if(externalEvents.begin(),
|
||||
externalEvents.end(),
|
||||
bind2nd(gd::ExternalEventsHasName(), name)));
|
||||
[&name](const std::unique_ptr<gd::ExternalEvents>& externalEvents) {
|
||||
return externalEvents->GetName() == name;
|
||||
}));
|
||||
}
|
||||
gd::ExternalEvents& Project::GetExternalEvents(std::size_t index) {
|
||||
return *externalEvents[index];
|
||||
@@ -382,7 +395,9 @@ void Project::RemoveExternalEvents(const gd::String& name) {
|
||||
std::vector<std::unique_ptr<gd::ExternalEvents> >::iterator events =
|
||||
find_if(externalEvents.begin(),
|
||||
externalEvents.end(),
|
||||
bind2nd(gd::ExternalEventsHasName(), name));
|
||||
[&name](const std::unique_ptr<gd::ExternalEvents>& externalEvents) {
|
||||
return externalEvents->GetName() == name;
|
||||
});
|
||||
if (events == externalEvents.end()) return;
|
||||
|
||||
externalEvents.erase(events);
|
||||
@@ -448,19 +463,24 @@ void Project::SwapExternalLayouts(std::size_t first, std::size_t second) {
|
||||
bool Project::HasExternalLayoutNamed(const gd::String& name) const {
|
||||
return (find_if(externalLayouts.begin(),
|
||||
externalLayouts.end(),
|
||||
bind2nd(gd::ExternalLayoutHasName(), name)) !=
|
||||
externalLayouts.end());
|
||||
[&name](const std::unique_ptr<gd::ExternalLayout>& externalLayout) {
|
||||
return externalLayout->GetName() == name;
|
||||
}) != externalLayouts.end());
|
||||
}
|
||||
gd::ExternalLayout& Project::GetExternalLayout(const gd::String& name) {
|
||||
return *(*find_if(externalLayouts.begin(),
|
||||
externalLayouts.end(),
|
||||
bind2nd(gd::ExternalLayoutHasName(), name)));
|
||||
[&name](const std::unique_ptr<gd::ExternalLayout>& externalLayout) {
|
||||
return externalLayout->GetName() == name;
|
||||
}));
|
||||
}
|
||||
const gd::ExternalLayout& Project::GetExternalLayout(
|
||||
const gd::String& name) const {
|
||||
return *(*find_if(externalLayouts.begin(),
|
||||
externalLayouts.end(),
|
||||
bind2nd(gd::ExternalLayoutHasName(), name)));
|
||||
[&name](const std::unique_ptr<gd::ExternalLayout>& externalLayout) {
|
||||
return externalLayout->GetName() == name;
|
||||
}));
|
||||
}
|
||||
gd::ExternalLayout& Project::GetExternalLayout(std::size_t index) {
|
||||
return *externalLayouts[index];
|
||||
@@ -504,7 +524,9 @@ void Project::RemoveExternalLayout(const gd::String& name) {
|
||||
std::vector<std::unique_ptr<gd::ExternalLayout> >::iterator externalLayout =
|
||||
find_if(externalLayouts.begin(),
|
||||
externalLayouts.end(),
|
||||
bind2nd(gd::ExternalLayoutHasName(), name));
|
||||
[&name](const std::unique_ptr<gd::ExternalLayout>& externalLayout) {
|
||||
return externalLayout->GetName() == name;
|
||||
});
|
||||
if (externalLayout == externalLayouts.end()) return;
|
||||
|
||||
externalLayouts.erase(externalLayout);
|
||||
@@ -1076,7 +1098,9 @@ bool Project::HasSourceFile(gd::String name, gd::String language) const {
|
||||
vector<std::unique_ptr<SourceFile> >::const_iterator sourceFile =
|
||||
find_if(externalSourceFiles.begin(),
|
||||
externalSourceFiles.end(),
|
||||
bind2nd(gd::ExternalSourceFileHasName(), name));
|
||||
[&name](const std::unique_ptr<SourceFile>& sourceFile) {
|
||||
return sourceFile->GetFileName() == name;
|
||||
});
|
||||
|
||||
if (sourceFile == externalSourceFiles.end()) return false;
|
||||
|
||||
@@ -1086,20 +1110,26 @@ bool Project::HasSourceFile(gd::String name, gd::String language) const {
|
||||
gd::SourceFile& Project::GetSourceFile(const gd::String& name) {
|
||||
return *(*find_if(externalSourceFiles.begin(),
|
||||
externalSourceFiles.end(),
|
||||
bind2nd(gd::ExternalSourceFileHasName(), name)));
|
||||
[&name](const std::unique_ptr<SourceFile>& sourceFile) {
|
||||
return sourceFile->GetFileName() == name;
|
||||
}));
|
||||
}
|
||||
|
||||
const gd::SourceFile& Project::GetSourceFile(const gd::String& name) const {
|
||||
return *(*find_if(externalSourceFiles.begin(),
|
||||
externalSourceFiles.end(),
|
||||
bind2nd(gd::ExternalSourceFileHasName(), name)));
|
||||
[&name](const std::unique_ptr<SourceFile>& sourceFile) {
|
||||
return sourceFile->GetFileName() == name;
|
||||
}));
|
||||
}
|
||||
|
||||
void Project::RemoveSourceFile(const gd::String& name) {
|
||||
std::vector<std::unique_ptr<gd::SourceFile> >::iterator sourceFile =
|
||||
find_if(externalSourceFiles.begin(),
|
||||
externalSourceFiles.end(),
|
||||
bind2nd(gd::ExternalSourceFileHasName(), name));
|
||||
[&name](const std::unique_ptr<SourceFile>& sourceFile) {
|
||||
return sourceFile->GetFileName() == name;
|
||||
});
|
||||
if (sourceFile == externalSourceFiles.end()) return;
|
||||
|
||||
externalSourceFiles.erase(sourceFile);
|
||||
|
@@ -13,6 +13,7 @@ class ObjectsContainersList;
|
||||
class VariablesContainersList;
|
||||
class PropertiesContainersList;
|
||||
class NamedPropertyDescriptor;
|
||||
class ParameterMetadataContainer;
|
||||
class BaseEvent;
|
||||
class EventsFunctionsExtension;
|
||||
class EventsFunction;
|
||||
@@ -131,7 +132,7 @@ class ProjectScopedContainers {
|
||||
}
|
||||
|
||||
ProjectScopedContainers &AddParameters(
|
||||
const std::vector<gd::ParameterMetadata> ¶meters) {
|
||||
const ParameterMetadataContainer ¶meters) {
|
||||
parametersVectorsList.push_back(¶meters);
|
||||
|
||||
return *this;
|
||||
@@ -224,7 +225,7 @@ class ProjectScopedContainers {
|
||||
return propertiesContainersList;
|
||||
};
|
||||
|
||||
const std::vector<const std::vector<gd::ParameterMetadata> *> &GetParametersVectorsList() const {
|
||||
const std::vector<const ParameterMetadataContainer *> &GetParametersVectorsList() const {
|
||||
return parametersVectorsList;
|
||||
};
|
||||
|
||||
@@ -236,7 +237,7 @@ class ProjectScopedContainers {
|
||||
gd::ObjectsContainersList objectsContainersList;
|
||||
gd::VariablesContainersList variablesContainersList;
|
||||
gd::PropertiesContainersList propertiesContainersList;
|
||||
std::vector<const std::vector<gd::ParameterMetadata> *> parametersVectorsList;
|
||||
std::vector<const ParameterMetadataContainer *> parametersVectorsList;
|
||||
};
|
||||
|
||||
} // namespace gd
|
@@ -38,6 +38,13 @@ void PropertyDescriptor::SerializeTo(SerializerElement& element) const {
|
||||
if (advanced) {
|
||||
element.AddChild("advanced").SetBoolValue(advanced);
|
||||
}
|
||||
if (quickCustomizationVisibility != QuickCustomization::Visibility::Default) {
|
||||
element.AddChild("quickCustomizationVisibility")
|
||||
.SetStringValue(quickCustomizationVisibility ==
|
||||
QuickCustomization::Visibility::Visible
|
||||
? "visible"
|
||||
: "hidden");
|
||||
}
|
||||
}
|
||||
|
||||
void PropertyDescriptor::UnserializeFrom(const SerializerElement& element) {
|
||||
@@ -67,11 +74,21 @@ void PropertyDescriptor::UnserializeFrom(const SerializerElement& element) {
|
||||
? element.GetChild("hidden").GetBoolValue()
|
||||
: false;
|
||||
deprecated = element.HasChild("deprecated")
|
||||
? element.GetChild("deprecated").GetBoolValue()
|
||||
: false;
|
||||
? element.GetChild("deprecated").GetBoolValue()
|
||||
: false;
|
||||
advanced = element.HasChild("advanced")
|
||||
? element.GetChild("advanced").GetBoolValue()
|
||||
: false;
|
||||
? element.GetChild("advanced").GetBoolValue()
|
||||
: false;
|
||||
|
||||
if (element.HasChild("quickCustomizationVisibility")) {
|
||||
quickCustomizationVisibility =
|
||||
element.GetChild("quickCustomizationVisibility").GetStringValue() ==
|
||||
"visible"
|
||||
? QuickCustomization::Visibility::Visible
|
||||
: QuickCustomization::Visibility::Hidden;
|
||||
} else {
|
||||
quickCustomizationVisibility = QuickCustomization::Visibility::Default;
|
||||
}
|
||||
}
|
||||
|
||||
void PropertyDescriptor::SerializeValuesTo(SerializerElement& element) const {
|
||||
|
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "GDCore/String.h"
|
||||
#include "GDCore/Project/MeasurementUnit.h"
|
||||
#include "GDCore/Project/QuickCustomization.h"
|
||||
|
||||
namespace gd {
|
||||
class SerializerElement;
|
||||
@@ -32,14 +33,18 @@ class GD_CORE_API PropertyDescriptor {
|
||||
PropertyDescriptor(gd::String propertyValue)
|
||||
: currentValue(propertyValue), type("string"), label(""), hidden(false),
|
||||
deprecated(false), advanced(false),
|
||||
measurementUnit(gd::MeasurementUnit::GetUndefined()) {}
|
||||
hasImpactOnOtherProperties(false),
|
||||
measurementUnit(gd::MeasurementUnit::GetUndefined()),
|
||||
quickCustomizationVisibility(QuickCustomization::Visibility::Default) {}
|
||||
|
||||
/**
|
||||
* \brief Empty constructor creating an empty property to be displayed.
|
||||
*/
|
||||
PropertyDescriptor()
|
||||
: hidden(false), deprecated(false), advanced(false),
|
||||
measurementUnit(gd::MeasurementUnit::GetUndefined()){};
|
||||
hasImpactOnOtherProperties(false),
|
||||
measurementUnit(gd::MeasurementUnit::GetUndefined()),
|
||||
quickCustomizationVisibility(QuickCustomization::Visibility::Default){};
|
||||
|
||||
/**
|
||||
* \brief Destructor
|
||||
@@ -109,7 +114,7 @@ class GD_CORE_API PropertyDescriptor {
|
||||
extraInformation.push_back(info);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Change the unit of measurement of the property value.
|
||||
*/
|
||||
@@ -128,7 +133,7 @@ class GD_CORE_API PropertyDescriptor {
|
||||
const std::vector<gd::String>& GetExtraInfo() const {
|
||||
return extraInformation;
|
||||
}
|
||||
|
||||
|
||||
std::vector<gd::String>& GetExtraInfo() {
|
||||
return extraInformation;
|
||||
}
|
||||
@@ -172,6 +177,28 @@ class GD_CORE_API PropertyDescriptor {
|
||||
*/
|
||||
bool IsAdvanced() const { return advanced; }
|
||||
|
||||
/**
|
||||
* \brief Check if the property has impact on other properties - which means a change
|
||||
* must re-render other properties.
|
||||
*/
|
||||
bool HasImpactOnOtherProperties() const { return hasImpactOnOtherProperties; }
|
||||
|
||||
/**
|
||||
* \brief Set if the property has impact on other properties - which means a change
|
||||
* must re-render other properties.
|
||||
*/
|
||||
PropertyDescriptor& SetHasImpactOnOtherProperties(bool enable) {
|
||||
hasImpactOnOtherProperties = enable;
|
||||
return *this;
|
||||
}
|
||||
|
||||
QuickCustomization::Visibility GetQuickCustomizationVisibility() const { return quickCustomizationVisibility; }
|
||||
|
||||
PropertyDescriptor& SetQuickCustomizationVisibility(QuickCustomization::Visibility visibility) {
|
||||
quickCustomizationVisibility = visibility;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** \name Serialization
|
||||
*/
|
||||
///@{
|
||||
@@ -211,7 +238,9 @@ class GD_CORE_API PropertyDescriptor {
|
||||
bool hidden;
|
||||
bool deprecated;
|
||||
bool advanced;
|
||||
bool hasImpactOnOtherProperties;
|
||||
gd::MeasurementUnit measurementUnit; //< The unit of measurement of the property vale.
|
||||
QuickCustomization::Visibility quickCustomizationVisibility;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
16
Core/GDCore/Project/QuickCustomization.h
Normal file
16
Core/GDCore/Project/QuickCustomization.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
namespace gd {
|
||||
class QuickCustomization {
|
||||
public:
|
||||
enum Visibility {
|
||||
/** Visibility based on the parent or editor heuristics (probably visible). */
|
||||
Default,
|
||||
/** Visible in the quick customization editor. */
|
||||
Visible,
|
||||
/** Not visible in the quick customization editor. */
|
||||
Hidden
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace gd
|
@@ -87,20 +87,6 @@ class GD_CORE_API SourceFile {
|
||||
///< SetAssociatedEvent.
|
||||
};
|
||||
|
||||
//"Tool" Functions
|
||||
|
||||
/**
|
||||
* Functor testing Source Files name
|
||||
*/
|
||||
struct ExternalSourceFileHasName
|
||||
: public std::
|
||||
binary_function<std::unique_ptr<SourceFile>, gd::String, bool> {
|
||||
bool operator()(const std::unique_ptr<SourceFile>& externalEvents,
|
||||
gd::String name) const {
|
||||
return externalEvents->GetFileName() == name;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
||||
#endif // SOURCEFILE_H
|
||||
|
@@ -323,6 +323,7 @@ class GD_CORE_API SerializerElement {
|
||||
gd::String deprecatedName = "") const;
|
||||
|
||||
/**
|
||||
* \deprecated Use HasChild instead. This should be removed from the codebase.
|
||||
* \brief Return true if the specified attribute exists.
|
||||
* \param name The name of the attribute to find.
|
||||
*/
|
||||
|
1
Core/GDCore/Tools/.gitignore
vendored
Normal file
1
Core/GDCore/Tools/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
VersionPriv.h
|
@@ -1,8 +0,0 @@
|
||||
// Deprecated version number that was used for GDevelop 4, but still
|
||||
// used to version the project files.
|
||||
// Might be a good idea to refactor this at some point to make it
|
||||
// clearer this is used for the versioning of the project files.
|
||||
#define GD_VERSION_STRING "4.0.99-0-release"
|
||||
#define GD_VERSION_RC 4,0,99,0-0-release
|
||||
#define GD_VERSION_RC_STRING "4, 0, 99, 0-0-release\0"
|
||||
#define GD_DATE_STRING __DATE__
|
@@ -24,15 +24,6 @@ int VersionWrapper::Revision() {
|
||||
: 0;
|
||||
}
|
||||
gd::String VersionWrapper::FullString() { return GD_VERSION_STRING; }
|
||||
gd::String VersionWrapper::Date() {
|
||||
return gd::String(GD_DATE_STRING).substr(4, 2);
|
||||
}
|
||||
gd::String VersionWrapper::Month() {
|
||||
return gd::String(GD_DATE_STRING).substr(0, 3);
|
||||
}
|
||||
gd::String VersionWrapper::Year() {
|
||||
return gd::String(GD_DATE_STRING).substr(7, 4);
|
||||
}
|
||||
gd::String VersionWrapper::Status() {
|
||||
return Revision() == 0 ? "Release" : "Dev";
|
||||
}
|
||||
|
@@ -46,21 +46,6 @@ class GD_CORE_API VersionWrapper {
|
||||
*/
|
||||
static gd::String Status();
|
||||
|
||||
/**
|
||||
* \brief Get Year of the release
|
||||
*/
|
||||
static gd::String Year();
|
||||
|
||||
/**
|
||||
* \brief Get Month of the release
|
||||
*/
|
||||
static gd::String Month();
|
||||
|
||||
/**
|
||||
* \brief Get Day of the release
|
||||
*/
|
||||
static gd::String Date();
|
||||
|
||||
/**
|
||||
* \brief Return true if the first version is older
|
||||
* than the second version.
|
||||
|
@@ -527,15 +527,9 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
}
|
||||
}
|
||||
SECTION("Parameters (1 level)") {
|
||||
std::vector<gd::ParameterMetadata> parameters;
|
||||
gd::ParameterMetadata param1;
|
||||
param1.SetName("MyParameter1");
|
||||
param1.SetType("number");
|
||||
gd::ParameterMetadata param2;
|
||||
param2.SetName("MyParameter2");
|
||||
param2.SetType("string");
|
||||
parameters.push_back(param1);
|
||||
parameters.push_back(param2);
|
||||
gd::ParameterMetadataContainer parameters;
|
||||
parameters.InsertNewParameter("MyParameter1", 0).SetType("number");
|
||||
parameters.InsertNewParameter("MyParameter2", 1).SetType("string");
|
||||
|
||||
auto projectScopedContainersWithParameters = gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
|
||||
projectScopedContainersWithParameters.AddParameters(parameters);
|
||||
@@ -568,19 +562,10 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
}
|
||||
}
|
||||
SECTION("Parameters (1 level, number|string)") {
|
||||
std::vector<gd::ParameterMetadata> parameters;
|
||||
gd::ParameterMetadata param1;
|
||||
param1.SetName("MyNumberParameter");
|
||||
param1.SetType("number");
|
||||
gd::ParameterMetadata param2;
|
||||
param2.SetName("MyStringParameter");
|
||||
param2.SetType("string");
|
||||
gd::ParameterMetadata param3;
|
||||
param3.SetName("MyBooleanParameter");
|
||||
param3.SetType("yesorno");
|
||||
parameters.push_back(param1);
|
||||
parameters.push_back(param2);
|
||||
parameters.push_back(param3);
|
||||
gd::ParameterMetadataContainer parameters;
|
||||
parameters.InsertNewParameter("MyNumberParameter", 0).SetType("number");
|
||||
parameters.InsertNewParameter("MyStringParameter", 1).SetType("string");
|
||||
parameters.InsertNewParameter("MyBooleanParameter", 2).SetType("yesorno");
|
||||
|
||||
auto projectScopedContainersWithParameters = gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
|
||||
projectScopedContainersWithParameters.AddParameters(parameters);
|
||||
|
@@ -1031,25 +1031,10 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
}
|
||||
}
|
||||
SECTION("Numbers and texts mismatches ('number|string' type, with a parameter first)") {
|
||||
std::vector<gd::ParameterMetadata> parameters;
|
||||
{
|
||||
gd::ParameterMetadata param;
|
||||
param.SetName("MyNumberParameter");
|
||||
param.SetType("number");
|
||||
parameters.push_back(param);
|
||||
}
|
||||
{
|
||||
gd::ParameterMetadata param;
|
||||
param.SetName("MyStringParameter");
|
||||
param.SetType("string");
|
||||
parameters.push_back(param);
|
||||
}
|
||||
{
|
||||
gd::ParameterMetadata param;
|
||||
param.SetName("MyBooleanParameter");
|
||||
param.SetType("yesorno");
|
||||
parameters.push_back(param);
|
||||
}
|
||||
gd::ParameterMetadataContainer parameters;
|
||||
parameters.InsertNewParameter("MyNumberParameter", 0).SetType("number");
|
||||
parameters.InsertNewParameter("MyStringParameter", 1).SetType("string");
|
||||
parameters.InsertNewParameter("MyBooleanParameter", 2).SetType("yesorno");
|
||||
|
||||
auto projectScopedContainersWithParameters = gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
|
||||
projectScopedContainersWithParameters.AddParameters(parameters);
|
||||
@@ -2017,19 +2002,10 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
}
|
||||
|
||||
SECTION("Valid parameter") {
|
||||
std::vector<gd::ParameterMetadata> parameters;
|
||||
gd::ParameterMetadata param1;
|
||||
param1.SetName("MyParameter1");
|
||||
param1.SetType("number");
|
||||
gd::ParameterMetadata param2;
|
||||
param2.SetName("MyParameter2");
|
||||
param2.SetType("string");
|
||||
gd::ParameterMetadata param3;
|
||||
param3.SetName("MyParameter3");
|
||||
param3.SetType("yesorno");
|
||||
parameters.push_back(param1);
|
||||
parameters.push_back(param2);
|
||||
parameters.push_back(param3);
|
||||
gd::ParameterMetadataContainer parameters;
|
||||
parameters.InsertNewParameter("MyParameter1", 0).SetType("number");
|
||||
parameters.InsertNewParameter("MyParameter2", 1).SetType("string");
|
||||
parameters.InsertNewParameter("MyParameter3", 2).SetType("yesorno");
|
||||
|
||||
auto projectScopedContainersWithParameters = gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
|
||||
projectScopedContainersWithParameters.AddParameters(parameters);
|
||||
@@ -2098,15 +2074,9 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
|
||||
SECTION("Invalid parameter (wrong type)") {
|
||||
{
|
||||
std::vector<gd::ParameterMetadata> parameters;
|
||||
gd::ParameterMetadata param1;
|
||||
param1.SetName("MyParameter1");
|
||||
param1.SetType("number");
|
||||
gd::ParameterMetadata param2;
|
||||
param2.SetName("MyParameter2");
|
||||
param2.SetType("audioResource");
|
||||
parameters.push_back(param1);
|
||||
parameters.push_back(param2);
|
||||
gd::ParameterMetadataContainer parameters;
|
||||
parameters.InsertNewParameter("MyParameter1", 0).SetType("number");
|
||||
parameters.InsertNewParameter("MyParameter2", 1).SetType("audioResource");
|
||||
|
||||
auto projectScopedContainersWithParameters = gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
|
||||
projectScopedContainersWithParameters.AddParameters(parameters);
|
||||
@@ -2123,15 +2093,9 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
|
||||
SECTION("Invalid parameter (non existing name)") {
|
||||
{
|
||||
std::vector<gd::ParameterMetadata> parameters;
|
||||
gd::ParameterMetadata param1;
|
||||
param1.SetName("MyParameter1");
|
||||
param1.SetType("number");
|
||||
gd::ParameterMetadata param2;
|
||||
param2.SetName("MyParameter2");
|
||||
param2.SetType("string");
|
||||
parameters.push_back(param1);
|
||||
parameters.push_back(param2);
|
||||
gd::ParameterMetadataContainer parameters;
|
||||
parameters.InsertNewParameter("MyParameter1", 0).SetType("number");
|
||||
parameters.InsertNewParameter("MyParameter2", 1).SetType("string");
|
||||
|
||||
auto projectScopedContainersWithParameters = gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
|
||||
projectScopedContainersWithParameters.AddParameters(parameters);
|
||||
@@ -2148,15 +2112,9 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
|
||||
SECTION("Invalid parameter (unsupported child syntax, 1 level)") {
|
||||
{
|
||||
std::vector<gd::ParameterMetadata> parameters;
|
||||
gd::ParameterMetadata param1;
|
||||
param1.SetName("MyParameter1");
|
||||
param1.SetType("number");
|
||||
gd::ParameterMetadata param2;
|
||||
param2.SetName("MyParameter2");
|
||||
param2.SetType("string");
|
||||
parameters.push_back(param1);
|
||||
parameters.push_back(param2);
|
||||
gd::ParameterMetadataContainer parameters;
|
||||
parameters.InsertNewParameter("MyParameter1", 0).SetType("number");
|
||||
parameters.InsertNewParameter("MyParameter2", 1).SetType("string");
|
||||
|
||||
auto projectScopedContainersWithParameters = gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
|
||||
projectScopedContainersWithParameters.AddParameters(parameters);
|
||||
@@ -2172,15 +2130,9 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
}
|
||||
SECTION("Invalid parameter (unsupported child syntax, 2 levels)") {
|
||||
{
|
||||
std::vector<gd::ParameterMetadata> parameters;
|
||||
gd::ParameterMetadata param1;
|
||||
param1.SetName("MyParameter1");
|
||||
param1.SetType("number");
|
||||
gd::ParameterMetadata param2;
|
||||
param2.SetName("MyParameter2");
|
||||
param2.SetType("string");
|
||||
parameters.push_back(param1);
|
||||
parameters.push_back(param2);
|
||||
gd::ParameterMetadataContainer parameters;
|
||||
parameters.InsertNewParameter("MyParameter1", 0).SetType("number");
|
||||
parameters.InsertNewParameter("MyParameter2", 1).SetType("string");
|
||||
|
||||
auto projectScopedContainersWithParameters = gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
|
||||
projectScopedContainersWithParameters.AddParameters(parameters);
|
||||
@@ -3104,25 +3056,10 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
}
|
||||
}
|
||||
SECTION("Valid type inferred from expressions with type 'number|string', with a parameter first") {
|
||||
std::vector<gd::ParameterMetadata> parameters;
|
||||
{
|
||||
gd::ParameterMetadata param;
|
||||
param.SetName("MyNumberParameter");
|
||||
param.SetType("number");
|
||||
parameters.push_back(param);
|
||||
}
|
||||
{
|
||||
gd::ParameterMetadata param;
|
||||
param.SetName("MyStringParameter");
|
||||
param.SetType("string");
|
||||
parameters.push_back(param);
|
||||
}
|
||||
{
|
||||
gd::ParameterMetadata param;
|
||||
param.SetName("MyBooleanParameter");
|
||||
param.SetType("yesorno");
|
||||
parameters.push_back(param);
|
||||
}
|
||||
gd::ParameterMetadataContainer parameters;
|
||||
parameters.InsertNewParameter("MyNumberParameter", 0).SetType("number");
|
||||
parameters.InsertNewParameter("MyStringParameter", 1).SetType("string");
|
||||
parameters.InsertNewParameter("MyBooleanParameter", 2).SetType("yesorno");
|
||||
|
||||
auto projectScopedContainersWithParameters = gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
|
||||
projectScopedContainersWithParameters.AddParameters(parameters);
|
||||
|
@@ -79,7 +79,7 @@ TEST_CASE("PropertyFunctionGenerator", "[common]") {
|
||||
"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.GetParameters().GetParametersCount() == 0);
|
||||
|
||||
REQUIRE(getter.GetEvents().GetEventsCount() == 1);
|
||||
REQUIRE(getter.GetEvents().GetEvent(0).GetType() ==
|
||||
@@ -106,7 +106,7 @@ TEST_CASE("PropertyFunctionGenerator", "[common]") {
|
||||
REQUIRE(setter.GetDescription() == "");
|
||||
REQUIRE(setter.GetSentence() == "");
|
||||
// Object and behavior parameters are added automatically.
|
||||
REQUIRE(setter.GetParameters().size() == 0);
|
||||
REQUIRE(setter.GetParameters().GetParametersCount() == 0);
|
||||
|
||||
REQUIRE(setter.GetEvents().GetEventsCount() == 1);
|
||||
REQUIRE(setter.GetEvents().GetEvent(0).GetType() ==
|
||||
@@ -195,7 +195,7 @@ TEST_CASE("PropertyFunctionGenerator", "[common]") {
|
||||
"this behavior only.");
|
||||
REQUIRE(getter.GetSentence() == "_PARAM0_ rotate object");
|
||||
// Object and behavior parameters are added automatically.
|
||||
REQUIRE(getter.GetParameters().size() == 0);
|
||||
REQUIRE(getter.GetParameters().GetParametersCount() == 0);
|
||||
|
||||
REQUIRE(getter.GetEvents().GetEventsCount() == 1);
|
||||
REQUIRE(getter.GetEvents().GetEvent(0).GetType() ==
|
||||
@@ -232,16 +232,16 @@ TEST_CASE("PropertyFunctionGenerator", "[common]") {
|
||||
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(setter.GetParameters().GetParametersCount() == 3);
|
||||
auto &objectParameter = setter.GetParameters().GetParameter(0);
|
||||
REQUIRE(objectParameter.GetName() == "Object");
|
||||
REQUIRE(objectParameter.GetType() == "object");
|
||||
auto &behaviorParameter = setter.GetParameters().at(1);
|
||||
auto &behaviorParameter = setter.GetParameters().GetParameter(1);
|
||||
REQUIRE(behaviorParameter.GetName() == "Behavior");
|
||||
REQUIRE(behaviorParameter.GetType() == "behavior");
|
||||
REQUIRE(behaviorParameter.GetExtraInfo() ==
|
||||
"MyEventsExtension::MyEventsBasedBehavior");
|
||||
auto &valueParameter = setter.GetParameters().at(2);
|
||||
auto &valueParameter = setter.GetParameters().GetParameter(2);
|
||||
REQUIRE(valueParameter.GetName() == "Value");
|
||||
REQUIRE(valueParameter.GetType() == "yesorno");
|
||||
|
||||
@@ -329,7 +329,7 @@ TEST_CASE("PropertyFunctionGenerator", "[common]") {
|
||||
"angle of the trajectory direction.");
|
||||
REQUIRE(getter.GetSentence() == "the movement angle");
|
||||
// Object parameter is added automatically.
|
||||
REQUIRE(getter.GetParameters().size() == 0);
|
||||
REQUIRE(getter.GetParameters().GetParametersCount() == 0);
|
||||
|
||||
REQUIRE(getter.GetEvents().GetEventsCount() == 1);
|
||||
REQUIRE(getter.GetEvents().GetEvent(0).GetType() ==
|
||||
@@ -356,7 +356,7 @@ TEST_CASE("PropertyFunctionGenerator", "[common]") {
|
||||
REQUIRE(setter.GetDescription() == "");
|
||||
REQUIRE(setter.GetSentence() == "");
|
||||
// Object parameter is added automatically.
|
||||
REQUIRE(setter.GetParameters().size() == 0);
|
||||
REQUIRE(setter.GetParameters().GetParametersCount() == 0);
|
||||
|
||||
REQUIRE(setter.GetEvents().GetEventsCount() == 1);
|
||||
REQUIRE(setter.GetEvents().GetEvent(0).GetType() ==
|
||||
@@ -443,7 +443,7 @@ TEST_CASE("PropertyFunctionGenerator", "[common]") {
|
||||
"this object.");
|
||||
REQUIRE(getter.GetSentence() == "_PARAM0_ rotate object");
|
||||
// The Object parameter is added automatically.
|
||||
REQUIRE(getter.GetParameters().size() == 0);
|
||||
REQUIRE(getter.GetParameters().GetParametersCount() == 0);
|
||||
|
||||
REQUIRE(getter.GetEvents().GetEventsCount() == 1);
|
||||
REQUIRE(getter.GetEvents().GetEvent(0).GetType() ==
|
||||
@@ -478,13 +478,13 @@ TEST_CASE("PropertyFunctionGenerator", "[common]") {
|
||||
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(setter.GetParameters().GetParametersCount() == 2);
|
||||
auto &objectParameter = setter.GetParameters().GetParameter(0);
|
||||
REQUIRE(objectParameter.GetName() == "Object");
|
||||
REQUIRE(objectParameter.GetType() == "object");
|
||||
REQUIRE(objectParameter.GetExtraInfo() ==
|
||||
"MyEventsExtension::MyEventsBasedObject");
|
||||
auto &valueParameter = setter.GetParameters().at(1);
|
||||
auto &valueParameter = setter.GetParameters().GetParameter(1);
|
||||
REQUIRE(valueParameter.GetName() == "Value");
|
||||
REQUIRE(valueParameter.GetType() == "yesorno");
|
||||
|
||||
|
@@ -867,26 +867,22 @@ SetupProjectWithEventsFunctionExtension(gd::Project &project) {
|
||||
auto &behaviorEventsFunctions = eventsBasedBehavior.GetEventsFunctions();
|
||||
auto &behaviorAction = behaviorEventsFunctions.InsertNewEventsFunction(
|
||||
"MyBehaviorEventsFunction", 0);
|
||||
behaviorAction.GetParameters().push_back(
|
||||
gd::ParameterMetadata()
|
||||
.SetName("Object")
|
||||
.SetType("object")
|
||||
.SetExtraInfo("MyEventsExtension::MyEventsBasedObject"));
|
||||
behaviorAction.GetParameters().push_back(
|
||||
gd::ParameterMetadata()
|
||||
.SetName("Behavior")
|
||||
.SetType("behavior")
|
||||
.SetExtraInfo("MyEventsExtension::MyEventsBasedBehavior"));
|
||||
behaviorAction.GetParameters().push_back(
|
||||
gd::ParameterMetadata()
|
||||
.SetName("ObjectWithMyBehavior")
|
||||
.SetType("object")
|
||||
.SetExtraInfo("MyEventsExtension::MyEventsBasedObject"));
|
||||
behaviorAction.GetParameters().push_back(
|
||||
gd::ParameterMetadata()
|
||||
.SetName("OtherBehavior")
|
||||
.SetType("behavior")
|
||||
.SetExtraInfo("MyEventsExtension::MyEventsBasedBehavior"));
|
||||
behaviorAction.GetParameters()
|
||||
.InsertNewParameter("Object", 0)
|
||||
.SetType("object")
|
||||
.SetExtraInfo("MyEventsExtension::MyEventsBasedObject");
|
||||
behaviorAction.GetParameters()
|
||||
.InsertNewParameter("Behavior", 1)
|
||||
.SetType("behavior")
|
||||
.SetExtraInfo("MyEventsExtension::MyEventsBasedBehavior");
|
||||
behaviorAction.GetParameters()
|
||||
.InsertNewParameter("ObjectWithMyBehavior", 2)
|
||||
.SetType("object")
|
||||
.SetExtraInfo("MyEventsExtension::MyEventsBasedObject");
|
||||
behaviorAction.GetParameters()
|
||||
.InsertNewParameter("OtherBehavior", 3)
|
||||
.SetType("behavior")
|
||||
.SetExtraInfo("MyEventsExtension::MyEventsBasedBehavior");
|
||||
auto &group = behaviorAction.GetObjectGroups().InsertNew("GroupWithMyBehavior");
|
||||
group.AddObject("ObjectWithMyBehavior");
|
||||
|
||||
@@ -894,36 +890,32 @@ SetupProjectWithEventsFunctionExtension(gd::Project &project) {
|
||||
behaviorEventsFunctions
|
||||
.InsertNewEventsFunction("MyBehaviorEventsFunctionExpression", 1)
|
||||
.SetFunctionType(gd::EventsFunction::Expression);
|
||||
behaviorExpression.GetParameters().push_back(
|
||||
gd::ParameterMetadata()
|
||||
.SetName("Object")
|
||||
.SetType("object")
|
||||
.SetExtraInfo("MyEventsExtension::MyEventsBasedObject"));
|
||||
behaviorExpression.GetParameters().push_back(
|
||||
gd::ParameterMetadata()
|
||||
.SetName("Behavior")
|
||||
.SetType("behavior")
|
||||
.SetExtraInfo("MyEventsExtension::MyEventsBasedBehavior"));
|
||||
behaviorExpression.GetParameters()
|
||||
.InsertNewParameter("Object", 0)
|
||||
.SetType("object")
|
||||
.SetExtraInfo("MyEventsExtension::MyEventsBasedObject");
|
||||
behaviorExpression.GetParameters()
|
||||
.InsertNewParameter("Behavior", 1)
|
||||
.SetType("behavior")
|
||||
.SetExtraInfo("MyEventsExtension::MyEventsBasedBehavior");
|
||||
|
||||
auto &behaviorExpressionAndCondition =
|
||||
behaviorEventsFunctions
|
||||
.InsertNewEventsFunction("MyBehaviorEventsFunctionExpressionAndCondition", 2)
|
||||
.SetFunctionType(gd::EventsFunction::ExpressionAndCondition);
|
||||
behaviorExpressionAndCondition.GetParameters().push_back(
|
||||
gd::ParameterMetadata().SetName("Object").SetType("object"));
|
||||
behaviorExpressionAndCondition.GetParameters().push_back(
|
||||
gd::ParameterMetadata()
|
||||
.SetName("Behavior")
|
||||
.SetType("behavior")
|
||||
.SetExtraInfo("MyExtension::MyEventsBasedBehavior"));
|
||||
behaviorExpressionAndCondition.GetParameters().push_back(
|
||||
gd::ParameterMetadata()
|
||||
.SetName("Value1")
|
||||
.SetType("expression"));
|
||||
behaviorExpressionAndCondition.GetParameters().push_back(
|
||||
gd::ParameterMetadata()
|
||||
.SetName("Value2")
|
||||
.SetType("expression"));
|
||||
behaviorExpressionAndCondition.GetParameters()
|
||||
.InsertNewParameter("Object", 0)
|
||||
.SetType("object");
|
||||
behaviorExpressionAndCondition.GetParameters()
|
||||
.InsertNewParameter("Behavior", 1)
|
||||
.SetType("behavior")
|
||||
.SetExtraInfo("MyExtension::MyEventsBasedBehavior");
|
||||
behaviorExpressionAndCondition.GetParameters()
|
||||
.InsertNewParameter("Value1", 2)
|
||||
.SetType("expression");
|
||||
behaviorExpressionAndCondition.GetParameters()
|
||||
.InsertNewParameter("Value2", 3)
|
||||
.SetType("expression");
|
||||
|
||||
behaviorEventsFunctions
|
||||
.InsertNewEventsFunction("MyBehaviorEventsFunctionActionWithOperator", 2)
|
||||
@@ -956,46 +948,41 @@ SetupProjectWithEventsFunctionExtension(gd::Project &project) {
|
||||
auto &objectEventsFunctions = eventsBasedObject.GetEventsFunctions();
|
||||
auto &objectAction = objectEventsFunctions.InsertNewEventsFunction(
|
||||
"MyObjectEventsFunction", 0);
|
||||
objectAction.GetParameters().push_back(
|
||||
gd::ParameterMetadata()
|
||||
.SetName("Object")
|
||||
.SetType("object")
|
||||
.SetExtraInfo("MyEventsExtension::MyEventsBasedObject"));
|
||||
objectAction.GetParameters().push_back(
|
||||
gd::ParameterMetadata()
|
||||
.SetName("OtherObject")
|
||||
.SetType("object")
|
||||
.SetExtraInfo("MyEventsExtension::MyEventsBasedObject"));
|
||||
objectAction.GetParameters().push_back(
|
||||
gd::ParameterMetadata()
|
||||
.SetName("OtherBehavior")
|
||||
.SetType("behavior")
|
||||
.SetExtraInfo("MyEventsExtension::MyEventsBasedBehavior"));
|
||||
objectAction.GetParameters()
|
||||
.InsertNewParameter("Object", 0)
|
||||
.SetType("object")
|
||||
.SetExtraInfo("MyEventsExtension::MyEventsBasedObject");
|
||||
objectAction.GetParameters()
|
||||
.InsertNewParameter("OtherObject", 1)
|
||||
.SetType("object")
|
||||
.SetExtraInfo("MyEventsExtension::MyEventsBasedObject");
|
||||
objectAction.GetParameters()
|
||||
.InsertNewParameter("OtherBehavior", 2)
|
||||
.SetType("behavior")
|
||||
.SetExtraInfo("MyEventsExtension::MyEventsBasedBehavior");
|
||||
|
||||
auto &objectExpression =
|
||||
objectEventsFunctions
|
||||
.InsertNewEventsFunction("MyObjectEventsFunctionExpression", 1)
|
||||
.SetFunctionType(gd::EventsFunction::Expression);
|
||||
objectExpression.GetParameters().push_back(
|
||||
gd::ParameterMetadata()
|
||||
.SetName("Object")
|
||||
objectExpression.GetParameters().InsertNewParameter("Object", 0)
|
||||
.SetType("object")
|
||||
.SetExtraInfo("MyEventsExtension::MyEventsBasedObject"));
|
||||
.SetExtraInfo("MyEventsExtension::MyEventsBasedObject");
|
||||
|
||||
auto &objectExpressionAndCondition =
|
||||
objectEventsFunctions
|
||||
.InsertNewEventsFunction("MyObjectEventsFunctionExpressionAndCondition", 2)
|
||||
.InsertNewEventsFunction(
|
||||
"MyObjectEventsFunctionExpressionAndCondition", 2)
|
||||
.SetFunctionType(gd::EventsFunction::ExpressionAndCondition);
|
||||
objectExpressionAndCondition.GetParameters().push_back(
|
||||
gd::ParameterMetadata().SetName("Object").SetType("object"));
|
||||
objectExpressionAndCondition.GetParameters().push_back(
|
||||
gd::ParameterMetadata()
|
||||
.SetName("Value1")
|
||||
.SetType("expression"));
|
||||
objectExpressionAndCondition.GetParameters().push_back(
|
||||
gd::ParameterMetadata()
|
||||
.SetName("Value2")
|
||||
.SetType("expression"));
|
||||
objectExpressionAndCondition.GetParameters()
|
||||
.InsertNewParameter("Object", 0)
|
||||
.SetType("object");
|
||||
objectExpressionAndCondition.GetParameters()
|
||||
.InsertNewParameter("Value1", 1)
|
||||
.SetType("expression");
|
||||
objectExpressionAndCondition.GetParameters()
|
||||
.InsertNewParameter("Value2", 2)
|
||||
.SetType("expression");
|
||||
|
||||
objectEventsFunctions
|
||||
.InsertNewEventsFunction("MyObjectEventsFunctionActionWithOperator", 2)
|
||||
@@ -1022,32 +1009,27 @@ SetupProjectWithEventsFunctionExtension(gd::Project &project) {
|
||||
auto &behaviorEventsFunctions = eventsBasedBehavior.GetEventsFunctions();
|
||||
auto &behaviorAction = behaviorEventsFunctions.InsertNewEventsFunction(
|
||||
"MyBehaviorEventsFunction", 0);
|
||||
behaviorAction.GetParameters().push_back(
|
||||
gd::ParameterMetadata()
|
||||
.SetName("Object")
|
||||
.SetType("object")
|
||||
.SetExtraInfo("MyEventsExtension::MyEventsBasedObject"));
|
||||
behaviorAction.GetParameters().push_back(
|
||||
gd::ParameterMetadata()
|
||||
.SetName("Behavior")
|
||||
.SetType("behavior")
|
||||
.SetExtraInfo("MyEventsExtension::MyEventsBasedBehavior"));
|
||||
behaviorAction.GetParameters()
|
||||
.InsertNewParameter("Object", 0)
|
||||
.SetType("object")
|
||||
.SetExtraInfo("MyEventsExtension::MyEventsBasedObject");
|
||||
behaviorAction.GetParameters()
|
||||
.InsertNewParameter("Behavior", 1)
|
||||
.SetType("behavior")
|
||||
.SetExtraInfo("MyEventsExtension::MyEventsBasedBehavior");
|
||||
// Define the same objects as in the layout to be consistent with events.
|
||||
behaviorAction.GetParameters().push_back(
|
||||
gd::ParameterMetadata()
|
||||
.SetName("ObjectWithMyBehavior")
|
||||
.SetType("object")
|
||||
.SetExtraInfo("MyExtension::Sprite"));
|
||||
behaviorAction.GetParameters().push_back(
|
||||
gd::ParameterMetadata()
|
||||
.SetName("MyBehavior")
|
||||
.SetType("behavior")
|
||||
.SetExtraInfo("MyEventsExtension::MyEventsBasedBehavior"));
|
||||
behaviorAction.GetParameters().push_back(
|
||||
gd::ParameterMetadata()
|
||||
.SetName("MyCustomObject")
|
||||
.SetType("object")
|
||||
.SetExtraInfo("MyEventsExtension::MyEventsBasedObject"));
|
||||
behaviorAction.GetParameters()
|
||||
.InsertNewParameter("ObjectWithMyBehavior", 2)
|
||||
.SetType("object")
|
||||
.SetExtraInfo("MyExtension::Sprite");
|
||||
behaviorAction.GetParameters()
|
||||
.InsertNewParameter("MyBehavior", 3)
|
||||
.SetType("behavior")
|
||||
.SetExtraInfo("MyEventsExtension::MyEventsBasedBehavior");
|
||||
behaviorAction.GetParameters()
|
||||
.InsertNewParameter("MyCustomObject", 4)
|
||||
.SetType("object")
|
||||
.SetExtraInfo("MyEventsExtension::MyEventsBasedObject");
|
||||
}
|
||||
|
||||
// Add an other events based object that uses previously defined events based
|
||||
@@ -1062,11 +1044,10 @@ SetupProjectWithEventsFunctionExtension(gd::Project &project) {
|
||||
auto &objectEventsFunctions = eventsBasedObject.GetEventsFunctions();
|
||||
auto &objectAction = objectEventsFunctions.InsertNewEventsFunction(
|
||||
"MyObjectEventsFunction", 0);
|
||||
objectAction.GetParameters().push_back(
|
||||
gd::ParameterMetadata()
|
||||
.SetName("Object")
|
||||
.SetType("object")
|
||||
.SetExtraInfo("MyEventsExtension::MyOtherEventsBasedObject"));
|
||||
objectAction.GetParameters()
|
||||
.InsertNewParameter("Object", 0)
|
||||
.SetType("object")
|
||||
.SetExtraInfo("MyEventsExtension::MyOtherEventsBasedObject");
|
||||
|
||||
// Add a child-object with the same names the one from the scene
|
||||
// to be able to use the same events list.
|
||||
@@ -1091,39 +1072,33 @@ SetupProjectWithEventsFunctionExtension(gd::Project &project) {
|
||||
{
|
||||
auto &action =
|
||||
eventsExtension.InsertNewEventsFunction("MyEventsFunction", 0);
|
||||
action.GetParameters().push_back(gd::ParameterMetadata()
|
||||
.SetName("currentScene")
|
||||
.SetType("")
|
||||
.SetCodeOnly(true));
|
||||
action.GetParameters().push_back(
|
||||
gd::ParameterMetadata()
|
||||
.SetName("Object")
|
||||
.SetType("object")
|
||||
.SetExtraInfo("MyEventsExtension::MyEventsBasedObject"));
|
||||
action.GetParameters().push_back(
|
||||
gd::ParameterMetadata()
|
||||
.SetName("Behavior")
|
||||
.SetType("behavior")
|
||||
.SetExtraInfo("MyEventsExtension::MyEventsBasedBehavior"));
|
||||
action.GetParameters()
|
||||
.InsertNewParameter("currentScene", 0)
|
||||
.SetType("")
|
||||
.SetCodeOnly(true);
|
||||
action.GetParameters()
|
||||
.InsertNewParameter("Object", 1)
|
||||
.SetType("object")
|
||||
.SetExtraInfo("MyEventsExtension::MyEventsBasedObject");
|
||||
action.GetParameters()
|
||||
.InsertNewParameter("Behavior", 2)
|
||||
.SetType("behavior")
|
||||
.SetExtraInfo("MyEventsExtension::MyEventsBasedBehavior");
|
||||
|
||||
auto &expression =
|
||||
eventsExtension.InsertNewEventsFunction("MyEventsFunctionExpression", 1)
|
||||
.SetFunctionType(gd::EventsFunction::Expression);
|
||||
expression.GetParameters().push_back(gd::ParameterMetadata()
|
||||
.SetName("currentScene")
|
||||
.SetType("")
|
||||
.SetCodeOnly(true));
|
||||
expression.GetParameters()
|
||||
.InsertNewParameter("currentScene", 0)
|
||||
.SetType("")
|
||||
.SetCodeOnly(true);
|
||||
|
||||
auto &freeExpressionAndCondition = eventsExtension.InsertNewEventsFunction("MyEventsFunctionExpressionAndCondition", 2)
|
||||
.SetFunctionType(gd::EventsFunction::ExpressionAndCondition);
|
||||
freeExpressionAndCondition.GetParameters().push_back(
|
||||
gd::ParameterMetadata()
|
||||
.SetName("Value1")
|
||||
.SetType("expression"));
|
||||
freeExpressionAndCondition.GetParameters().push_back(
|
||||
gd::ParameterMetadata()
|
||||
.SetName("Value2")
|
||||
.SetType("expression"));
|
||||
freeExpressionAndCondition.GetParameters().InsertNewParameter("Value1", 0)
|
||||
.SetType("expression");
|
||||
freeExpressionAndCondition.GetParameters().InsertNewParameter("Value2", 1)
|
||||
.SetType("expression");
|
||||
|
||||
eventsExtension.InsertNewEventsFunction("MyEventsFunctionActionWithOperator", 2)
|
||||
.SetFunctionType(gd::EventsFunction::ActionWithOperator)
|
||||
@@ -1137,21 +1112,18 @@ SetupProjectWithEventsFunctionExtension(gd::Project &project) {
|
||||
auto &action =
|
||||
eventsExtension.InsertNewEventsFunction("MyOtherEventsFunction", 0);
|
||||
// Define the same objects as in the layout to be consistent with events.
|
||||
action.GetParameters().push_back(
|
||||
gd::ParameterMetadata()
|
||||
.SetName("ObjectWithMyBehavior")
|
||||
.SetType("object")
|
||||
.SetExtraInfo("MyExtension::Sprite"));
|
||||
action.GetParameters().push_back(
|
||||
gd::ParameterMetadata()
|
||||
.SetName("MyBehavior")
|
||||
.SetType("behavior")
|
||||
.SetExtraInfo("MyEventsExtension::MyEventsBasedBehavior"));
|
||||
action.GetParameters().push_back(
|
||||
gd::ParameterMetadata()
|
||||
.SetName("MyCustomObject")
|
||||
.SetType("object")
|
||||
.SetExtraInfo("MyEventsExtension::MyEventsBasedObject"));
|
||||
action.GetParameters()
|
||||
.InsertNewParameter("ObjectWithMyBehavior", 0)
|
||||
.SetType("object")
|
||||
.SetExtraInfo("MyExtension::Sprite");
|
||||
action.GetParameters()
|
||||
.InsertNewParameter("MyBehavior", 1)
|
||||
.SetType("behavior")
|
||||
.SetExtraInfo("MyEventsExtension::MyEventsBasedBehavior");
|
||||
action.GetParameters()
|
||||
.InsertNewParameter("MyCustomObject", 2)
|
||||
.SetType("object")
|
||||
.SetExtraInfo("MyEventsExtension::MyEventsBasedObject");
|
||||
auto &group = action.GetObjectGroups().InsertNew("GroupWithMyBehavior");
|
||||
group.AddObject("ObjectWithMyBehavior");
|
||||
}
|
||||
@@ -2071,9 +2043,9 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
auto &myEventsFunction =
|
||||
project.GetEventsFunctionsExtension("MyEventsExtension")
|
||||
.GetEventsFunction("MyEventsFunction");
|
||||
REQUIRE(myEventsFunction.GetParameters().at(1).GetExtraInfo() ==
|
||||
REQUIRE(myEventsFunction.GetParameters().GetParameter(1).GetExtraInfo() ==
|
||||
"MyRenamedExtension::MyEventsBasedObject");
|
||||
REQUIRE(myEventsFunction.GetParameters().at(2).GetExtraInfo() ==
|
||||
REQUIRE(myEventsFunction.GetParameters().GetParameter(2).GetExtraInfo() ==
|
||||
"MyRenamedExtension::MyEventsBasedBehavior");
|
||||
|
||||
// Behavior function
|
||||
@@ -2084,9 +2056,12 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
.Get("MyEventsBasedBehavior")
|
||||
.GetEventsFunctions()
|
||||
.GetEventsFunction("MyBehaviorEventsFunction");
|
||||
REQUIRE(myBehaviorEventsFunction.GetParameters().at(2).GetExtraInfo() ==
|
||||
"MyRenamedExtension::MyEventsBasedObject");
|
||||
REQUIRE(myBehaviorEventsFunction.GetParameters().at(3).GetExtraInfo() ==
|
||||
REQUIRE(myBehaviorEventsFunction.GetParameters()
|
||||
.GetParameter(2)
|
||||
.GetExtraInfo() == "MyRenamedExtension::MyEventsBasedObject");
|
||||
REQUIRE(myBehaviorEventsFunction.GetParameters()
|
||||
.GetParameter(3)
|
||||
.GetExtraInfo() ==
|
||||
"MyRenamedExtension::MyEventsBasedBehavior");
|
||||
}
|
||||
|
||||
@@ -2098,9 +2073,12 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
.Get("MyEventsBasedObject")
|
||||
.GetEventsFunctions()
|
||||
.GetEventsFunction("MyObjectEventsFunction");
|
||||
REQUIRE(myBehaviorEventsFunction.GetParameters().at(1).GetExtraInfo() ==
|
||||
"MyRenamedExtension::MyEventsBasedObject");
|
||||
REQUIRE(myBehaviorEventsFunction.GetParameters().at(2).GetExtraInfo() ==
|
||||
REQUIRE(myBehaviorEventsFunction.GetParameters()
|
||||
.GetParameter(1)
|
||||
.GetExtraInfo() == "MyRenamedExtension::MyEventsBasedObject");
|
||||
REQUIRE(myBehaviorEventsFunction.GetParameters()
|
||||
.GetParameter(2)
|
||||
.GetExtraInfo() ==
|
||||
"MyRenamedExtension::MyEventsBasedBehavior");
|
||||
}
|
||||
}
|
||||
@@ -2343,7 +2321,7 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
auto &myEventsFunction =
|
||||
project.GetEventsFunctionsExtension("MyEventsExtension")
|
||||
.GetEventsFunction("MyEventsFunction");
|
||||
REQUIRE(myEventsFunction.GetParameters().at(2).GetExtraInfo() ==
|
||||
REQUIRE(myEventsFunction.GetParameters().GetParameter(2).GetExtraInfo() ==
|
||||
"MyEventsExtension::MyRenamedEventsBasedBehavior");
|
||||
|
||||
// Behavior function
|
||||
@@ -2354,7 +2332,9 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
.Get("MyEventsBasedBehavior")
|
||||
.GetEventsFunctions()
|
||||
.GetEventsFunction("MyBehaviorEventsFunction");
|
||||
REQUIRE(myBehaviorEventsFunction.GetParameters().at(3).GetExtraInfo() ==
|
||||
REQUIRE(myBehaviorEventsFunction.GetParameters()
|
||||
.GetParameter(3)
|
||||
.GetExtraInfo() ==
|
||||
"MyEventsExtension::MyRenamedEventsBasedBehavior");
|
||||
}
|
||||
|
||||
@@ -2366,7 +2346,9 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
.Get("MyEventsBasedObject")
|
||||
.GetEventsFunctions()
|
||||
.GetEventsFunction("MyObjectEventsFunction");
|
||||
REQUIRE(myBehaviorEventsFunction.GetParameters().at(2).GetExtraInfo() ==
|
||||
REQUIRE(myBehaviorEventsFunction.GetParameters()
|
||||
.GetParameter(2)
|
||||
.GetExtraInfo() ==
|
||||
"MyEventsExtension::MyRenamedEventsBasedBehavior");
|
||||
}
|
||||
}
|
||||
@@ -2449,7 +2431,7 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
auto &myEventsFunction =
|
||||
project.GetEventsFunctionsExtension("MyEventsExtension")
|
||||
.GetEventsFunction("MyEventsFunction");
|
||||
REQUIRE(myEventsFunction.GetParameters().at(1).GetExtraInfo() ==
|
||||
REQUIRE(myEventsFunction.GetParameters().GetParameter(1).GetExtraInfo() ==
|
||||
"MyEventsExtension::MyRenamedEventsBasedObject");
|
||||
|
||||
// Behavior function
|
||||
@@ -2460,7 +2442,9 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
.Get("MyEventsBasedBehavior")
|
||||
.GetEventsFunctions()
|
||||
.GetEventsFunction("MyBehaviorEventsFunction");
|
||||
REQUIRE(myBehaviorEventsFunction.GetParameters().at(2).GetExtraInfo() ==
|
||||
REQUIRE(myBehaviorEventsFunction.GetParameters()
|
||||
.GetParameter(2)
|
||||
.GetExtraInfo() ==
|
||||
"MyEventsExtension::MyRenamedEventsBasedObject");
|
||||
}
|
||||
|
||||
@@ -2472,7 +2456,9 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
.Get("MyEventsBasedObject")
|
||||
.GetEventsFunctions()
|
||||
.GetEventsFunction("MyObjectEventsFunction");
|
||||
REQUIRE(myBehaviorEventsFunction.GetParameters().at(1).GetExtraInfo() ==
|
||||
REQUIRE(myBehaviorEventsFunction.GetParameters()
|
||||
.GetParameter(1)
|
||||
.GetExtraInfo() ==
|
||||
"MyEventsExtension::MyRenamedEventsBasedObject");
|
||||
}
|
||||
}
|
||||
|
@@ -164,8 +164,18 @@ namespace gdjs {
|
||||
this.setWidth(initialInstanceData.width);
|
||||
this.setHeight(initialInstanceData.height);
|
||||
}
|
||||
if (initialInstanceData.depth !== undefined)
|
||||
if (initialInstanceData.depth !== undefined) {
|
||||
this.setDepth(initialInstanceData.depth);
|
||||
}
|
||||
if (initialInstanceData.flippedX) {
|
||||
this.flipX(initialInstanceData.flippedX);
|
||||
}
|
||||
if (initialInstanceData.flippedY) {
|
||||
this.flipY(initialInstanceData.flippedY);
|
||||
}
|
||||
if (initialInstanceData.flippedZ) {
|
||||
this.flipZ(initialInstanceData.flippedZ);
|
||||
}
|
||||
}
|
||||
|
||||
setX(x: float): void {
|
||||
|
@@ -72,8 +72,18 @@ namespace gdjs {
|
||||
|
||||
extraInitializationFromInitialInstance(initialInstanceData: InstanceData) {
|
||||
super.extraInitializationFromInitialInstance(initialInstanceData);
|
||||
if (initialInstanceData.depth !== undefined)
|
||||
if (initialInstanceData.depth !== undefined) {
|
||||
this.setDepth(initialInstanceData.depth);
|
||||
}
|
||||
if (initialInstanceData.flippedX) {
|
||||
this.flipX(initialInstanceData.flippedX);
|
||||
}
|
||||
if (initialInstanceData.flippedY) {
|
||||
this.flipY(initialInstanceData.flippedY);
|
||||
}
|
||||
if (initialInstanceData.flippedZ) {
|
||||
this.flipZ(initialInstanceData.flippedZ);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -304,8 +314,15 @@ namespace gdjs {
|
||||
*/
|
||||
setDepth(depth: float): void {
|
||||
const unscaledDepth = this.getUnscaledDepth();
|
||||
if (unscaledDepth !== 0) {
|
||||
this.setScaleZ(depth / unscaledDepth);
|
||||
if (unscaledDepth === 0) {
|
||||
return;
|
||||
}
|
||||
const scaleZ = depth / unscaledDepth;
|
||||
if (this._innerArea && this._isInnerAreaFollowingParentSize) {
|
||||
this._innerArea.min[2] *= scaleZ;
|
||||
this._innerArea.max[2] *= scaleZ;
|
||||
} else {
|
||||
this.setScaleZ(scaleZ);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -325,6 +342,10 @@ namespace gdjs {
|
||||
* @param newScale The new scale (must be greater than 0).
|
||||
*/
|
||||
setScaleZ(newScale: number): void {
|
||||
if (this._innerArea && this._isInnerAreaFollowingParentSize) {
|
||||
// The scale is always 1;
|
||||
return;
|
||||
}
|
||||
if (newScale < 0) {
|
||||
newScale = 0;
|
||||
}
|
||||
|
@@ -2216,8 +2216,14 @@ module.exports = {
|
||||
this._centerY / objectTextureFrame.height;
|
||||
|
||||
this._pixiTexturedObject.angle = this._instance.getAngle();
|
||||
this._pixiTexturedObject.scale.x = width / objectTextureFrame.width;
|
||||
this._pixiTexturedObject.scale.y = height / objectTextureFrame.height;
|
||||
const scaleX =
|
||||
(width / objectTextureFrame.width) *
|
||||
(this._instance.isFlippedX() ? -1 : 1);
|
||||
const scaleY =
|
||||
(height / objectTextureFrame.height) *
|
||||
(this._instance.isFlippedY() ? -1 : 1);
|
||||
this._pixiTexturedObject.scale.x = scaleX;
|
||||
this._pixiTexturedObject.scale.y = scaleY;
|
||||
|
||||
this._pixiTexturedObject.position.x =
|
||||
this._instance.getX() +
|
||||
@@ -2244,6 +2250,9 @@ module.exports = {
|
||||
this._pixiFallbackObject.position.y =
|
||||
this._instance.getY() + height / 2;
|
||||
this._pixiFallbackObject.angle = this._instance.getAngle();
|
||||
|
||||
if (this._instance.isFlippedX()) this._pixiFallbackObject.scale.x = -1;
|
||||
if (this._instance.isFlippedY()) this._pixiFallbackObject.scale.y = -1;
|
||||
}
|
||||
|
||||
update() {
|
||||
@@ -2393,12 +2402,16 @@ module.exports = {
|
||||
RenderedInstance.toRad(this._instance.getAngle())
|
||||
);
|
||||
|
||||
const scaleX = width * (this._instance.isFlippedX() ? -1 : 1);
|
||||
const scaleY = height * (this._instance.isFlippedY() ? -1 : 1);
|
||||
const scaleZ = depth * (this._instance.isFlippedZ() ? -1 : 1);
|
||||
|
||||
if (
|
||||
width !== this._threeObject.scale.width ||
|
||||
height !== this._threeObject.scale.height ||
|
||||
depth !== this._threeObject.scale.depth
|
||||
scaleX !== this._threeObject.scale.width ||
|
||||
scaleY !== this._threeObject.scale.height ||
|
||||
scaleZ !== this._threeObject.scale.depth
|
||||
) {
|
||||
this._threeObject.scale.set(width, height, depth);
|
||||
this._threeObject.scale.set(scaleX, scaleY, scaleZ);
|
||||
this.updateTextureUvMapping();
|
||||
}
|
||||
}
|
||||
@@ -3186,12 +3199,16 @@ module.exports = {
|
||||
RenderedInstance.toRad(this._instance.getAngle())
|
||||
);
|
||||
|
||||
const scaleX = width * (this._instance.isFlippedX() ? -1 : 1);
|
||||
const scaleY = height * (this._instance.isFlippedY() ? -1 : 1);
|
||||
const scaleZ = depth * (this._instance.isFlippedZ() ? -1 : 1);
|
||||
|
||||
if (
|
||||
width !== this._threeObject.scale.width ||
|
||||
height !== this._threeObject.scale.height ||
|
||||
depth !== this._threeObject.scale.depth
|
||||
scaleX !== this._threeObject.scale.width ||
|
||||
scaleY !== this._threeObject.scale.height ||
|
||||
scaleZ !== this._threeObject.scale.depth
|
||||
) {
|
||||
this._threeObject.scale.set(width, height, depth);
|
||||
this._threeObject.scale.set(scaleX, scaleY, scaleZ);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -333,6 +333,7 @@ namespace gdjs {
|
||||
adUnitId,
|
||||
position: atTop ? 'top' : 'bottom',
|
||||
size: bannerRequestedAdSizeType,
|
||||
offset: 0,
|
||||
});
|
||||
|
||||
banner.on('load', () => {
|
||||
|
@@ -25,7 +25,6 @@ void AnchorBehavior::InitializeContent(gd::SerializerElement& content) {
|
||||
content.SetAttribute("useLegacyBottomAndRightAnchors", false);
|
||||
}
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
namespace {
|
||||
gd::String GetAnchorAsString(AnchorBehavior::HorizontalAnchor anchor) {
|
||||
if (anchor == AnchorBehavior::ANCHOR_HORIZONTAL_WINDOW_LEFT)
|
||||
@@ -34,6 +33,8 @@ gd::String GetAnchorAsString(AnchorBehavior::HorizontalAnchor anchor) {
|
||||
return _("Window right");
|
||||
else if (anchor == AnchorBehavior::ANCHOR_HORIZONTAL_PROPORTIONAL)
|
||||
return _("Proportional");
|
||||
else if (anchor == AnchorBehavior::ANCHOR_HORIZONTAL_WINDOW_CENTER)
|
||||
return _("Window center");
|
||||
else
|
||||
return _("No anchor");
|
||||
}
|
||||
@@ -45,6 +46,8 @@ gd::String GetAnchorAsString(AnchorBehavior::VerticalAnchor anchor) {
|
||||
return _("Window bottom");
|
||||
else if (anchor == AnchorBehavior::ANCHOR_VERTICAL_PROPORTIONAL)
|
||||
return _("Proportional");
|
||||
else if (anchor == AnchorBehavior::ANCHOR_VERTICAL_WINDOW_CENTER)
|
||||
return _("Window center");
|
||||
else
|
||||
return _("No anchor");
|
||||
}
|
||||
@@ -63,47 +66,55 @@ std::map<gd::String, gd::PropertyDescriptor> AnchorBehavior::GetProperties(
|
||||
.SetDescription(_("otherwise, objects are anchored according to the "
|
||||
"window size when the object is created."));
|
||||
|
||||
properties[_("Left edge anchor")]
|
||||
properties["leftEdgeAnchor"]
|
||||
.SetValue(GetAnchorAsString(static_cast<HorizontalAnchor>(
|
||||
behaviorContent.GetIntAttribute("leftEdgeAnchor"))))
|
||||
.SetType("Choice")
|
||||
.AddExtraInfo(_("No anchor"))
|
||||
.AddExtraInfo(_("Window left"))
|
||||
.AddExtraInfo(_("Window center"))
|
||||
.AddExtraInfo(_("Window right"))
|
||||
.AddExtraInfo(_("Proportional"))
|
||||
.SetLabel(_("Left edge anchor"))
|
||||
.SetDescription(_("Anchor the left edge of the object on X axis."));
|
||||
|
||||
properties[_("Right edge anchor")]
|
||||
properties["rightEdgeAnchor"]
|
||||
.SetValue(GetAnchorAsString(static_cast<HorizontalAnchor>(
|
||||
behaviorContent.GetIntAttribute("rightEdgeAnchor"))))
|
||||
.SetType("Choice")
|
||||
.AddExtraInfo(_("No anchor"))
|
||||
.AddExtraInfo(_("Window left"))
|
||||
.AddExtraInfo(_("Window center"))
|
||||
.AddExtraInfo(_("Window right"))
|
||||
.AddExtraInfo(_("Proportional"))
|
||||
.SetLabel(_("Right edge anchor"))
|
||||
.SetDescription(_("Anchor the right edge of the object on X axis."));
|
||||
|
||||
properties[_("Top edge anchor")]
|
||||
properties["topEdgeAnchor"]
|
||||
.SetValue(GetAnchorAsString(static_cast<VerticalAnchor>(
|
||||
behaviorContent.GetIntAttribute("topEdgeAnchor"))))
|
||||
.SetType("Choice")
|
||||
.AddExtraInfo(_("No anchor"))
|
||||
.AddExtraInfo(_("Window top"))
|
||||
.AddExtraInfo(_("Window center"))
|
||||
.AddExtraInfo(_("Window bottom"))
|
||||
.AddExtraInfo(_("Proportional"))
|
||||
.SetLabel(_("Top edge anchor"))
|
||||
.SetDescription(_("Anchor the top edge of the object on Y axis."));
|
||||
|
||||
properties[_("Bottom edge anchor")]
|
||||
properties["bottomEdgeAnchor"]
|
||||
.SetValue(GetAnchorAsString(static_cast<VerticalAnchor>(
|
||||
behaviorContent.GetIntAttribute("bottomEdgeAnchor"))))
|
||||
.SetType("Choice")
|
||||
.AddExtraInfo(_("No anchor"))
|
||||
.AddExtraInfo(_("Window top"))
|
||||
.AddExtraInfo(_("Window center"))
|
||||
.AddExtraInfo(_("Window bottom"))
|
||||
.AddExtraInfo(_("Proportional"))
|
||||
.SetLabel(_("Bottom edge anchor"))
|
||||
.SetDescription(_("Anchor the bottom edge of the object on Y axis."));
|
||||
|
||||
properties[("useLegacyBottomAndRightAnchors")]
|
||||
properties["useLegacyBottomAndRightAnchors"]
|
||||
.SetLabel(_(
|
||||
"Stretch object when anchoring right or bottom edge (deprecated, "
|
||||
"it's recommended to leave this unchecked and anchor both sides if "
|
||||
@@ -127,6 +138,8 @@ AnchorBehavior::HorizontalAnchor GetHorizontalAnchorFromString(
|
||||
return AnchorBehavior::ANCHOR_HORIZONTAL_WINDOW_RIGHT;
|
||||
else if (value == _("Proportional"))
|
||||
return AnchorBehavior::ANCHOR_HORIZONTAL_PROPORTIONAL;
|
||||
else if (value == _("Window center"))
|
||||
return AnchorBehavior::ANCHOR_HORIZONTAL_WINDOW_CENTER;
|
||||
else
|
||||
return AnchorBehavior::ANCHOR_HORIZONTAL_NONE;
|
||||
}
|
||||
@@ -139,6 +152,8 @@ AnchorBehavior::VerticalAnchor GetVerticalAnchorFromString(
|
||||
return AnchorBehavior::ANCHOR_VERTICAL_WINDOW_BOTTOM;
|
||||
else if (value == _("Proportional"))
|
||||
return AnchorBehavior::ANCHOR_VERTICAL_PROPORTIONAL;
|
||||
else if (value == _("Window center"))
|
||||
return AnchorBehavior::ANCHOR_VERTICAL_WINDOW_CENTER;
|
||||
else
|
||||
return AnchorBehavior::ANCHOR_VERTICAL_NONE;
|
||||
}
|
||||
@@ -147,20 +162,20 @@ AnchorBehavior::VerticalAnchor GetVerticalAnchorFromString(
|
||||
bool AnchorBehavior::UpdateProperty(gd::SerializerElement& behaviorContent,
|
||||
const gd::String& name,
|
||||
const gd::String& value) {
|
||||
if (name == _("relativeToOriginalWindowSize"))
|
||||
if (name == "relativeToOriginalWindowSize")
|
||||
behaviorContent.SetAttribute("relativeToOriginalWindowSize", value == "1");
|
||||
else if (name == _("Left edge anchor"))
|
||||
else if (name == "leftEdgeAnchor")
|
||||
behaviorContent.SetAttribute(
|
||||
"leftEdgeAnchor",
|
||||
static_cast<int>(GetHorizontalAnchorFromString(value)));
|
||||
else if (name == _("Right edge anchor"))
|
||||
else if (name == "rightEdgeAnchor")
|
||||
behaviorContent.SetAttribute(
|
||||
"rightEdgeAnchor",
|
||||
static_cast<int>(GetHorizontalAnchorFromString(value)));
|
||||
else if (name == _("Top edge anchor"))
|
||||
else if (name == "topEdgeAnchor")
|
||||
behaviorContent.SetAttribute(
|
||||
"topEdgeAnchor", static_cast<int>(GetVerticalAnchorFromString(value)));
|
||||
else if (name == _("Bottom edge anchor"))
|
||||
else if (name == "bottomEdgeAnchor")
|
||||
behaviorContent.SetAttribute(
|
||||
"bottomEdgeAnchor",
|
||||
static_cast<int>(GetVerticalAnchorFromString(value)));
|
||||
@@ -172,4 +187,3 @@ bool AnchorBehavior::UpdateProperty(gd::SerializerElement& behaviorContent,
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
@@ -3,8 +3,8 @@ GDevelop - Anchor Behavior Extension
|
||||
Copyright (c) 2016 Victor Levasseur (victorlevasseur52@gmail.com)
|
||||
This project is released under the MIT License.
|
||||
*/
|
||||
#ifndef ANCHORBEHAVIOR_H
|
||||
#define ANCHORBEHAVIOR_H
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "GDCore/Project/Behavior.h"
|
||||
#include "GDCore/Project/Object.h"
|
||||
@@ -22,14 +22,16 @@ class GD_EXTENSION_API AnchorBehavior : public gd::Behavior {
|
||||
ANCHOR_HORIZONTAL_NONE = 0,
|
||||
ANCHOR_HORIZONTAL_WINDOW_LEFT = 1,
|
||||
ANCHOR_HORIZONTAL_WINDOW_RIGHT = 2,
|
||||
ANCHOR_HORIZONTAL_PROPORTIONAL = 3
|
||||
ANCHOR_HORIZONTAL_PROPORTIONAL = 3,
|
||||
ANCHOR_HORIZONTAL_WINDOW_CENTER = 4
|
||||
};
|
||||
|
||||
enum VerticalAnchor {
|
||||
ANCHOR_VERTICAL_NONE = 0,
|
||||
ANCHOR_VERTICAL_WINDOW_TOP = 1,
|
||||
ANCHOR_VERTICAL_WINDOW_BOTTOM = 2,
|
||||
ANCHOR_VERTICAL_PROPORTIONAL = 3
|
||||
ANCHOR_VERTICAL_PROPORTIONAL = 3,
|
||||
ANCHOR_VERTICAL_WINDOW_CENTER = 4
|
||||
};
|
||||
|
||||
AnchorBehavior() {};
|
||||
@@ -47,5 +49,3 @@ class GD_EXTENSION_API AnchorBehavior : public gd::Behavior {
|
||||
virtual void InitializeContent(
|
||||
gd::SerializerElement& behaviorContent) override;
|
||||
};
|
||||
|
||||
#endif // ANCHORBEHAVIOR_H
|
||||
|
@@ -30,5 +30,6 @@ void DeclareAnchorBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
"CppPlatform/Extensions/AnchorIcon.png",
|
||||
"AnchorBehavior",
|
||||
std::make_shared<AnchorBehavior>(),
|
||||
std::make_shared<gd::BehaviorsSharedData>());
|
||||
std::make_shared<gd::BehaviorsSharedData>())
|
||||
.SetQuickCustomizationVisibility(gd::QuickCustomization::Hidden);
|
||||
}
|
||||
|
@@ -4,10 +4,25 @@ Copyright (c) 2013-2016 Florian Rival (Florian.Rival@gmail.com)
|
||||
*/
|
||||
|
||||
namespace gdjs {
|
||||
const enum HorizontalAnchor {
|
||||
None = 0,
|
||||
WindowLeft,
|
||||
WindowRight,
|
||||
Proportional,
|
||||
WindowCenter,
|
||||
}
|
||||
const enum VerticalAnchor {
|
||||
None = 0,
|
||||
WindowTop,
|
||||
WindowBottom,
|
||||
Proportional,
|
||||
WindowCenter,
|
||||
}
|
||||
|
||||
export class AnchorRuntimeBehavior extends gdjs.RuntimeBehavior {
|
||||
_relativeToOriginalWindowSize: any;
|
||||
_leftEdgeAnchor: any;
|
||||
_rightEdgeAnchor: any;
|
||||
_leftEdgeAnchor: HorizontalAnchor;
|
||||
_rightEdgeAnchor: HorizontalAnchor;
|
||||
_topEdgeAnchor: any;
|
||||
_bottomEdgeAnchor: any;
|
||||
_invalidDistances: boolean = true;
|
||||
@@ -74,14 +89,25 @@ namespace gdjs {
|
||||
gdjs.AnchorRuntimeBehavior.prototype.doStepPreEvents
|
||||
) as FloatPoint;
|
||||
// TODO EBO Make it work with event based objects or hide this behavior for them.
|
||||
const game = instanceContainer.getGame();
|
||||
let rendererWidth = game.getGameResolutionWidth();
|
||||
let rendererHeight = game.getGameResolutionHeight();
|
||||
let parentMinX = instanceContainer.getUnrotatedViewportMinX();
|
||||
let parentMinY = instanceContainer.getUnrotatedViewportMinY();
|
||||
let parentMaxX = instanceContainer.getUnrotatedViewportMaxX();
|
||||
let parentMaxY = instanceContainer.getUnrotatedViewportMaxY();
|
||||
let parentCenterX = (parentMaxX + parentMinX) / 2;
|
||||
let parentCenterY = (parentMaxY + parentMinY) / 2;
|
||||
let parentWidth = parentMaxX - parentMinX;
|
||||
let parentHeight = parentMaxY - parentMinY;
|
||||
const layer = instanceContainer.getLayer(this.owner.getLayer());
|
||||
if (this._invalidDistances) {
|
||||
if (this._relativeToOriginalWindowSize) {
|
||||
rendererWidth = game.getOriginalWidth();
|
||||
rendererHeight = game.getOriginalHeight();
|
||||
parentMinX = instanceContainer.getInitialUnrotatedViewportMinX();
|
||||
parentMinY = instanceContainer.getInitialUnrotatedViewportMinY();
|
||||
parentMaxX = instanceContainer.getInitialUnrotatedViewportMaxX();
|
||||
parentMaxY = instanceContainer.getInitialUnrotatedViewportMaxY();
|
||||
parentCenterX = (parentMaxX + parentMinX) / 2;
|
||||
parentCenterY = (parentMaxY + parentMinY) / 2;
|
||||
parentWidth = parentMaxX - parentMinX;
|
||||
parentHeight = parentMaxY - parentMinY;
|
||||
}
|
||||
|
||||
//Calculate the distances from the window's bounds.
|
||||
@@ -92,49 +118,28 @@ namespace gdjs {
|
||||
workingPoint
|
||||
);
|
||||
|
||||
//Left edge
|
||||
if (
|
||||
this._leftEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.HorizontalAnchor.WINDOW_LEFT
|
||||
) {
|
||||
this._leftEdgeDistance = topLeftPixel[0];
|
||||
} else {
|
||||
if (
|
||||
this._leftEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.HorizontalAnchor.WINDOW_RIGHT
|
||||
) {
|
||||
this._leftEdgeDistance = rendererWidth - topLeftPixel[0];
|
||||
} else {
|
||||
if (
|
||||
this._leftEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.HorizontalAnchor.PROPORTIONAL
|
||||
) {
|
||||
this._leftEdgeDistance = topLeftPixel[0] / rendererWidth;
|
||||
}
|
||||
}
|
||||
// Left edge
|
||||
if (this._leftEdgeAnchor === HorizontalAnchor.WindowLeft) {
|
||||
this._leftEdgeDistance = topLeftPixel[0] - parentMinX;
|
||||
} else if (this._leftEdgeAnchor === HorizontalAnchor.WindowRight) {
|
||||
this._leftEdgeDistance = topLeftPixel[0] - parentMaxX;
|
||||
} else if (this._leftEdgeAnchor === HorizontalAnchor.Proportional) {
|
||||
this._leftEdgeDistance = (topLeftPixel[0] - parentMinX) / parentWidth;
|
||||
} else if (this._leftEdgeAnchor === HorizontalAnchor.WindowCenter) {
|
||||
this._leftEdgeDistance = topLeftPixel[0] - parentCenterX;
|
||||
}
|
||||
|
||||
//Top edge
|
||||
if (
|
||||
this._topEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.VerticalAnchor.WINDOW_TOP
|
||||
) {
|
||||
this._topEdgeDistance = topLeftPixel[1];
|
||||
} else {
|
||||
if (
|
||||
this._topEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.VerticalAnchor.WINDOW_BOTTOM
|
||||
) {
|
||||
this._topEdgeDistance = rendererHeight - topLeftPixel[1];
|
||||
} else {
|
||||
if (
|
||||
this._topEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.VerticalAnchor.PROPORTIONAL
|
||||
) {
|
||||
this._topEdgeDistance = topLeftPixel[1] / rendererHeight;
|
||||
}
|
||||
}
|
||||
// Top edge
|
||||
if (this._topEdgeAnchor === VerticalAnchor.WindowTop) {
|
||||
this._topEdgeDistance = topLeftPixel[1] - parentMinY;
|
||||
} else if (this._topEdgeAnchor === VerticalAnchor.WindowBottom) {
|
||||
this._topEdgeDistance = topLeftPixel[1] - parentMaxY;
|
||||
} else if (this._topEdgeAnchor === VerticalAnchor.Proportional) {
|
||||
this._topEdgeDistance = (topLeftPixel[1] - parentMinY) / parentHeight;
|
||||
} else if (this._topEdgeAnchor === VerticalAnchor.WindowCenter) {
|
||||
this._topEdgeDistance = topLeftPixel[1] - parentCenterY;
|
||||
}
|
||||
|
||||
// It's fine to reuse workingPoint as topLeftPixel is no longer used.
|
||||
const bottomRightPixel = layer.convertCoords(
|
||||
this.owner.getDrawableX() + this.owner.getWidth(),
|
||||
@@ -143,49 +148,30 @@ namespace gdjs {
|
||||
workingPoint
|
||||
);
|
||||
|
||||
//Right edge
|
||||
if (
|
||||
this._rightEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.HorizontalAnchor.WINDOW_LEFT
|
||||
) {
|
||||
this._rightEdgeDistance = bottomRightPixel[0];
|
||||
} else {
|
||||
if (
|
||||
this._rightEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.HorizontalAnchor.WINDOW_RIGHT
|
||||
) {
|
||||
this._rightEdgeDistance = rendererWidth - bottomRightPixel[0];
|
||||
} else {
|
||||
if (
|
||||
this._rightEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.HorizontalAnchor.PROPORTIONAL
|
||||
) {
|
||||
this._rightEdgeDistance = bottomRightPixel[0] / rendererWidth;
|
||||
}
|
||||
}
|
||||
// Right edge
|
||||
if (this._rightEdgeAnchor === HorizontalAnchor.WindowLeft) {
|
||||
this._rightEdgeDistance = bottomRightPixel[0] - parentMinX;
|
||||
} else if (this._rightEdgeAnchor === HorizontalAnchor.WindowRight) {
|
||||
this._rightEdgeDistance = bottomRightPixel[0] - parentMaxX;
|
||||
} else if (this._rightEdgeAnchor === HorizontalAnchor.Proportional) {
|
||||
this._rightEdgeDistance =
|
||||
(bottomRightPixel[0] - parentMinX) / parentWidth;
|
||||
} else if (this._rightEdgeAnchor === HorizontalAnchor.WindowCenter) {
|
||||
this._rightEdgeDistance = bottomRightPixel[0] - parentCenterX;
|
||||
}
|
||||
|
||||
//Bottom edge
|
||||
if (
|
||||
this._bottomEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.VerticalAnchor.WINDOW_TOP
|
||||
) {
|
||||
this._bottomEdgeDistance = bottomRightPixel[1];
|
||||
} else {
|
||||
if (
|
||||
this._bottomEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.VerticalAnchor.WINDOW_BOTTOM
|
||||
) {
|
||||
this._bottomEdgeDistance = rendererHeight - bottomRightPixel[1];
|
||||
} else {
|
||||
if (
|
||||
this._bottomEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.VerticalAnchor.PROPORTIONAL
|
||||
) {
|
||||
this._bottomEdgeDistance = bottomRightPixel[1] / rendererHeight;
|
||||
}
|
||||
}
|
||||
// Bottom edge
|
||||
if (this._bottomEdgeAnchor === VerticalAnchor.WindowTop) {
|
||||
this._bottomEdgeDistance = bottomRightPixel[1] - parentMinY;
|
||||
} else if (this._bottomEdgeAnchor === VerticalAnchor.WindowBottom) {
|
||||
this._bottomEdgeDistance = bottomRightPixel[1] - parentMaxY;
|
||||
} else if (this._bottomEdgeAnchor === VerticalAnchor.Proportional) {
|
||||
this._bottomEdgeDistance =
|
||||
(bottomRightPixel[1] - parentMinY) / parentHeight;
|
||||
} else if (this._bottomEdgeAnchor === VerticalAnchor.WindowCenter) {
|
||||
this._bottomEdgeDistance = bottomRightPixel[1] - parentCenterY;
|
||||
}
|
||||
|
||||
this._invalidDistances = false;
|
||||
} else {
|
||||
//Move and resize the object if needed
|
||||
@@ -194,93 +180,50 @@ namespace gdjs {
|
||||
let rightPixel = 0;
|
||||
let bottomPixel = 0;
|
||||
|
||||
//Left edge
|
||||
if (
|
||||
this._leftEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.HorizontalAnchor.WINDOW_LEFT
|
||||
) {
|
||||
leftPixel = this._leftEdgeDistance;
|
||||
} else {
|
||||
if (
|
||||
this._leftEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.HorizontalAnchor.WINDOW_RIGHT
|
||||
) {
|
||||
leftPixel = rendererWidth - this._leftEdgeDistance;
|
||||
} else {
|
||||
if (
|
||||
this._leftEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.HorizontalAnchor.PROPORTIONAL
|
||||
) {
|
||||
leftPixel = this._leftEdgeDistance * rendererWidth;
|
||||
}
|
||||
}
|
||||
// Left edge
|
||||
if (this._leftEdgeAnchor === HorizontalAnchor.WindowLeft) {
|
||||
leftPixel = parentMinX + this._leftEdgeDistance;
|
||||
} else if (this._leftEdgeAnchor === HorizontalAnchor.WindowRight) {
|
||||
leftPixel = parentMaxX + this._leftEdgeDistance;
|
||||
} else if (this._leftEdgeAnchor === HorizontalAnchor.Proportional) {
|
||||
leftPixel = parentMinX + this._leftEdgeDistance * parentWidth;
|
||||
} else if (this._leftEdgeAnchor === HorizontalAnchor.WindowCenter) {
|
||||
leftPixel = parentCenterX + this._leftEdgeDistance;
|
||||
}
|
||||
|
||||
//Top edge
|
||||
if (
|
||||
this._topEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.VerticalAnchor.WINDOW_TOP
|
||||
) {
|
||||
topPixel = this._topEdgeDistance;
|
||||
} else {
|
||||
if (
|
||||
this._topEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.VerticalAnchor.WINDOW_BOTTOM
|
||||
) {
|
||||
topPixel = rendererHeight - this._topEdgeDistance;
|
||||
} else {
|
||||
if (
|
||||
this._topEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.VerticalAnchor.PROPORTIONAL
|
||||
) {
|
||||
topPixel = this._topEdgeDistance * rendererHeight;
|
||||
}
|
||||
}
|
||||
// Top edge
|
||||
if (this._topEdgeAnchor === VerticalAnchor.WindowTop) {
|
||||
topPixel = parentMinY + this._topEdgeDistance;
|
||||
} else if (this._topEdgeAnchor === VerticalAnchor.WindowBottom) {
|
||||
topPixel = parentMaxY + this._topEdgeDistance;
|
||||
} else if (this._topEdgeAnchor === VerticalAnchor.Proportional) {
|
||||
topPixel = parentMinY + this._topEdgeDistance * parentHeight;
|
||||
} else if (this._topEdgeAnchor === VerticalAnchor.WindowCenter) {
|
||||
topPixel = parentCenterY + this._topEdgeDistance;
|
||||
}
|
||||
|
||||
//Right edge
|
||||
if (
|
||||
this._rightEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.HorizontalAnchor.WINDOW_LEFT
|
||||
) {
|
||||
rightPixel = this._rightEdgeDistance;
|
||||
} else {
|
||||
if (
|
||||
this._rightEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.HorizontalAnchor.WINDOW_RIGHT
|
||||
) {
|
||||
rightPixel = rendererWidth - this._rightEdgeDistance;
|
||||
} else {
|
||||
if (
|
||||
this._rightEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.HorizontalAnchor.PROPORTIONAL
|
||||
) {
|
||||
rightPixel = this._rightEdgeDistance * rendererWidth;
|
||||
}
|
||||
}
|
||||
// Right edge
|
||||
if (this._rightEdgeAnchor === HorizontalAnchor.WindowLeft) {
|
||||
rightPixel = parentMinX + this._rightEdgeDistance;
|
||||
} else if (this._rightEdgeAnchor === HorizontalAnchor.WindowRight) {
|
||||
rightPixel = parentMaxX + this._rightEdgeDistance;
|
||||
} else if (this._rightEdgeAnchor === HorizontalAnchor.Proportional) {
|
||||
rightPixel = parentMinX + this._rightEdgeDistance * parentWidth;
|
||||
} else if (this._rightEdgeAnchor === HorizontalAnchor.WindowCenter) {
|
||||
rightPixel = parentCenterX + this._rightEdgeDistance;
|
||||
}
|
||||
|
||||
//Bottom edge
|
||||
if (
|
||||
this._bottomEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.VerticalAnchor.WINDOW_TOP
|
||||
) {
|
||||
bottomPixel = this._bottomEdgeDistance;
|
||||
} else {
|
||||
if (
|
||||
this._bottomEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.VerticalAnchor.WINDOW_BOTTOM
|
||||
) {
|
||||
bottomPixel = rendererHeight - this._bottomEdgeDistance;
|
||||
} else {
|
||||
if (
|
||||
this._bottomEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.VerticalAnchor.PROPORTIONAL
|
||||
) {
|
||||
bottomPixel = this._bottomEdgeDistance * rendererHeight;
|
||||
}
|
||||
}
|
||||
// Bottom edge
|
||||
if (this._bottomEdgeAnchor === VerticalAnchor.WindowTop) {
|
||||
bottomPixel = parentMinY + this._bottomEdgeDistance;
|
||||
} else if (this._bottomEdgeAnchor === VerticalAnchor.WindowBottom) {
|
||||
bottomPixel = parentMaxY + this._bottomEdgeDistance;
|
||||
} else if (this._bottomEdgeAnchor === VerticalAnchor.Proportional) {
|
||||
bottomPixel = parentMinY + this._bottomEdgeDistance * parentHeight;
|
||||
} else if (this._bottomEdgeAnchor === VerticalAnchor.WindowCenter) {
|
||||
bottomPixel = parentCenterY + this._bottomEdgeDistance;
|
||||
}
|
||||
|
||||
// It's fine to reuse workingPoint as topLeftPixel is no longer used.
|
||||
const topLeftCoord = layer.convertInverseCoords(
|
||||
leftPixel,
|
||||
@@ -303,27 +246,18 @@ namespace gdjs {
|
||||
// Compatibility with GD <= 5.0.133
|
||||
if (this._useLegacyBottomAndRightAnchors) {
|
||||
//Move and resize the object according to the anchors
|
||||
if (
|
||||
this._rightEdgeAnchor !==
|
||||
AnchorRuntimeBehavior.HorizontalAnchor.NONE
|
||||
) {
|
||||
if (this._rightEdgeAnchor !== HorizontalAnchor.None) {
|
||||
this.owner.setWidth(right - left);
|
||||
}
|
||||
if (
|
||||
this._bottomEdgeAnchor !== AnchorRuntimeBehavior.VerticalAnchor.NONE
|
||||
) {
|
||||
if (this._bottomEdgeAnchor !== VerticalAnchor.None) {
|
||||
this.owner.setHeight(bottom - top);
|
||||
}
|
||||
if (
|
||||
this._leftEdgeAnchor !== AnchorRuntimeBehavior.HorizontalAnchor.NONE
|
||||
) {
|
||||
if (this._leftEdgeAnchor !== HorizontalAnchor.None) {
|
||||
this.owner.setX(
|
||||
left + this.owner.getX() - this.owner.getDrawableX()
|
||||
);
|
||||
}
|
||||
if (
|
||||
this._topEdgeAnchor !== AnchorRuntimeBehavior.VerticalAnchor.NONE
|
||||
) {
|
||||
if (this._topEdgeAnchor !== VerticalAnchor.None) {
|
||||
this.owner.setY(
|
||||
top + this.owner.getY() - this.owner.getDrawableY()
|
||||
);
|
||||
@@ -333,25 +267,18 @@ namespace gdjs {
|
||||
else {
|
||||
// Resize if right and left anchors are set
|
||||
if (
|
||||
this._rightEdgeAnchor !==
|
||||
AnchorRuntimeBehavior.HorizontalAnchor.NONE &&
|
||||
this._leftEdgeAnchor !== AnchorRuntimeBehavior.HorizontalAnchor.NONE
|
||||
this._rightEdgeAnchor !== HorizontalAnchor.None &&
|
||||
this._leftEdgeAnchor !== HorizontalAnchor.None
|
||||
) {
|
||||
this.owner.setWidth(right - left);
|
||||
this.owner.setX(left);
|
||||
} else {
|
||||
if (
|
||||
this._leftEdgeAnchor !==
|
||||
AnchorRuntimeBehavior.HorizontalAnchor.NONE
|
||||
) {
|
||||
if (this._leftEdgeAnchor !== HorizontalAnchor.None) {
|
||||
this.owner.setX(
|
||||
left + this.owner.getX() - this.owner.getDrawableX()
|
||||
);
|
||||
}
|
||||
if (
|
||||
this._rightEdgeAnchor !==
|
||||
AnchorRuntimeBehavior.HorizontalAnchor.NONE
|
||||
) {
|
||||
if (this._rightEdgeAnchor !== HorizontalAnchor.None) {
|
||||
this.owner.setX(
|
||||
right +
|
||||
this.owner.getX() -
|
||||
@@ -362,24 +289,18 @@ namespace gdjs {
|
||||
}
|
||||
// Resize if top and bottom anchors are set
|
||||
if (
|
||||
this._bottomEdgeAnchor !==
|
||||
AnchorRuntimeBehavior.VerticalAnchor.NONE &&
|
||||
this._topEdgeAnchor !== AnchorRuntimeBehavior.VerticalAnchor.NONE
|
||||
this._bottomEdgeAnchor !== VerticalAnchor.None &&
|
||||
this._topEdgeAnchor !== VerticalAnchor.None
|
||||
) {
|
||||
this.owner.setHeight(bottom - top);
|
||||
this.owner.setY(top);
|
||||
} else {
|
||||
if (
|
||||
this._topEdgeAnchor !== AnchorRuntimeBehavior.VerticalAnchor.NONE
|
||||
) {
|
||||
if (this._topEdgeAnchor !== VerticalAnchor.None) {
|
||||
this.owner.setY(
|
||||
top + this.owner.getY() - this.owner.getDrawableY()
|
||||
);
|
||||
}
|
||||
if (
|
||||
this._bottomEdgeAnchor !==
|
||||
AnchorRuntimeBehavior.VerticalAnchor.NONE
|
||||
) {
|
||||
if (this._bottomEdgeAnchor !== VerticalAnchor.None) {
|
||||
this.owner.setY(
|
||||
bottom +
|
||||
this.owner.getY() -
|
||||
@@ -393,19 +314,6 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
doStepPostEvents(instanceContainer: gdjs.RuntimeInstanceContainer) {}
|
||||
|
||||
static HorizontalAnchor = {
|
||||
NONE: 0,
|
||||
WINDOW_LEFT: 1,
|
||||
WINDOW_RIGHT: 2,
|
||||
PROPORTIONAL: 3,
|
||||
};
|
||||
static VerticalAnchor = {
|
||||
NONE: 0,
|
||||
WINDOW_TOP: 1,
|
||||
WINDOW_BOTTOM: 2,
|
||||
PROPORTIONAL: 3,
|
||||
};
|
||||
}
|
||||
gdjs.registerBehavior(
|
||||
'AnchorBehavior::AnchorBehavior',
|
||||
|
@@ -36,6 +36,13 @@ describe('gdjs.AnchorRuntimeBehavior', function () {
|
||||
usedExtensionsWithVariablesData: [],
|
||||
});
|
||||
|
||||
const setGameResolutionSizeAndStep = (width, height) => {
|
||||
runtimeGame.setGameResolutionSize(width, height);
|
||||
// This method is called by the main loop:
|
||||
runtimeScene.onGameResolutionResized();
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
};
|
||||
|
||||
function createObject(behaviorProperties) {
|
||||
const object = new gdjs.TestRuntimeObject(runtimeScene, {
|
||||
name: 'obj1',
|
||||
@@ -67,13 +74,10 @@ describe('gdjs.AnchorRuntimeBehavior', function () {
|
||||
['rightEdgeAnchor', 'leftEdgeAnchor'].forEach((objectEdge) => {
|
||||
it(`anchors the ${objectEdge} edge of object to window left (fixed)`, function () {
|
||||
const object = createObject({ [objectEdge]: 1 });
|
||||
runtimeGame.setGameResolutionSize(1000, 1000);
|
||||
object.setPosition(500, 500);
|
||||
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
runtimeGame.setGameResolutionSize(2000, 2000);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
expect(object.getX()).to.equal(500);
|
||||
expect(object.getY()).to.equal(500);
|
||||
@@ -83,29 +87,36 @@ describe('gdjs.AnchorRuntimeBehavior', function () {
|
||||
['rightEdgeAnchor', 'leftEdgeAnchor'].forEach((objectEdge) => {
|
||||
it(`anchors the ${objectEdge} edge of object to window right (fixed)`, function () {
|
||||
const object = createObject({ [objectEdge]: 2 });
|
||||
runtimeGame.setGameResolutionSize(1000, 1000);
|
||||
object.setPosition(500, 500);
|
||||
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
runtimeGame.setGameResolutionSize(2000, 2000);
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
expect(object.getX()).to.equal(1500);
|
||||
expect(object.getY()).to.equal(500);
|
||||
expect(object.getWidth()).to.equal(10);
|
||||
});
|
||||
});
|
||||
['rightEdgeAnchor', 'leftEdgeAnchor'].forEach((objectEdge) => {
|
||||
it(`anchors the ${objectEdge} edge of object to window center (fixed)`, function () {
|
||||
const object = createObject({ [objectEdge]: 4 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
expect(object.getX()).to.equal(1000);
|
||||
expect(object.getY()).to.equal(500);
|
||||
expect(object.getWidth()).to.equal(10);
|
||||
});
|
||||
});
|
||||
|
||||
it('anchors the right and left edge of object (fixed)', function () {
|
||||
const object = createObject({ leftEdgeAnchor: 1, rightEdgeAnchor: 2 });
|
||||
runtimeGame.setGameResolutionSize(1000, 1000);
|
||||
object.setPosition(500, 500);
|
||||
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
runtimeGame.setGameResolutionSize(2000, 2000);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
expect(object.getX()).to.equal(500);
|
||||
expect(object.getY()).to.equal(500);
|
||||
@@ -114,13 +125,10 @@ describe('gdjs.AnchorRuntimeBehavior', function () {
|
||||
|
||||
it('anchors the left edge of object (proportional)', function () {
|
||||
const object = createObject({ leftEdgeAnchor: 3 });
|
||||
runtimeGame.setGameResolutionSize(1000, 1000);
|
||||
object.setPosition(500, 500);
|
||||
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
runtimeGame.setGameResolutionSize(2000, 2000);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
expect(object.getX()).to.equal(1000);
|
||||
expect(object.getY()).to.equal(500);
|
||||
@@ -132,13 +140,10 @@ describe('gdjs.AnchorRuntimeBehavior', function () {
|
||||
['topEdgeAnchor', 'bottomEdgeAnchor'].forEach((objectEdge) => {
|
||||
it(`anchors the ${objectEdge} edge of object to window top (fixed)`, function () {
|
||||
const object = createObject({ [objectEdge]: 1 });
|
||||
runtimeGame.setGameResolutionSize(1000, 1000);
|
||||
object.setPosition(500, 500);
|
||||
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
runtimeGame.setGameResolutionSize(2000, 2000);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
expect(object.getX()).to.equal(500);
|
||||
expect(object.getY()).to.equal(500);
|
||||
@@ -148,29 +153,36 @@ describe('gdjs.AnchorRuntimeBehavior', function () {
|
||||
['topEdgeAnchor', 'bottomEdgeAnchor'].forEach((objectEdge) => {
|
||||
it(`anchors the ${objectEdge} edge of object to window bottom (fixed)`, function () {
|
||||
const object = createObject({ [objectEdge]: 2 });
|
||||
runtimeGame.setGameResolutionSize(1000, 1000);
|
||||
object.setPosition(500, 500);
|
||||
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
runtimeGame.setGameResolutionSize(2000, 2000);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
expect(object.getX()).to.equal(500);
|
||||
expect(object.getY()).to.equal(1500);
|
||||
expect(object.getWidth()).to.equal(10);
|
||||
});
|
||||
});
|
||||
['topEdgeAnchor', 'bottomEdgeAnchor'].forEach((objectEdge) => {
|
||||
it(`anchors the ${objectEdge} edge of object to window center (fixed)`, function () {
|
||||
const object = createObject({ [objectEdge]: 4 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
expect(object.getX()).to.equal(500);
|
||||
expect(object.getY()).to.equal(1000);
|
||||
expect(object.getWidth()).to.equal(10);
|
||||
});
|
||||
});
|
||||
|
||||
it('anchors the top and bottom edge of object (fixed)', function () {
|
||||
const object = createObject({ topEdgeAnchor: 1, bottomEdgeAnchor: 2 });
|
||||
runtimeGame.setGameResolutionSize(1000, 1000);
|
||||
object.setPosition(500, 500);
|
||||
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
runtimeGame.setGameResolutionSize(2000, 2000);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
expect(object.getX()).to.equal(500);
|
||||
expect(object.getY()).to.equal(500);
|
||||
@@ -179,13 +191,10 @@ describe('gdjs.AnchorRuntimeBehavior', function () {
|
||||
|
||||
it('anchors the top edge of object (proportional)', function () {
|
||||
const object = createObject({ topEdgeAnchor: 3 });
|
||||
runtimeGame.setGameResolutionSize(1000, 1000);
|
||||
object.setPosition(500, 500);
|
||||
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
runtimeGame.setGameResolutionSize(2000, 2000);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
expect(object.getX()).to.equal(500);
|
||||
expect(object.getY()).to.equal(1000);
|
||||
|
@@ -66,13 +66,6 @@ module.exports = {
|
||||
.setLabel(_('Base color'))
|
||||
.setGroup(_('Appearance'));
|
||||
|
||||
objectProperties
|
||||
.getOrCreate('opacity')
|
||||
.setValue(objectContent.opacity.toString())
|
||||
.setType('number')
|
||||
.setLabel(_('Opacity (0-255)'))
|
||||
.setGroup(_('Appearance'));
|
||||
|
||||
objectProperties
|
||||
.getOrCreate('fontSize')
|
||||
.setValue(objectContent.fontSize.toString())
|
||||
@@ -545,9 +538,6 @@ module.exports = {
|
||||
this._pixiObject.text = rawText;
|
||||
}
|
||||
|
||||
const opacity = +properties.get('opacity').getValue();
|
||||
this._pixiObject.alpha = opacity / 255;
|
||||
|
||||
const color = properties.get('color').getValue();
|
||||
this._pixiObject.textStyles.default.fill = objectsRenderingService.rgbOrHexToHexNumber(
|
||||
color
|
||||
@@ -607,6 +597,13 @@ module.exports = {
|
||||
this._pixiObject.dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Do not hide completely an object so it can still be manipulated
|
||||
const alphaForDisplay = Math.max(
|
||||
this._instance.getOpacity() / 255,
|
||||
0.5
|
||||
);
|
||||
this._pixiObject.alpha = alphaForDisplay;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -192,6 +192,9 @@ namespace gdjs {
|
||||
250
|
||||
);
|
||||
}
|
||||
if (initialInstanceData.opacity !== undefined) {
|
||||
this.setOpacity(initialInstanceData.opacity);
|
||||
}
|
||||
}
|
||||
|
||||
onDestroyed(): void {
|
||||
|
@@ -59,13 +59,6 @@ module.exports = {
|
||||
.setType('textarea')
|
||||
.setLabel(_('Text'));
|
||||
|
||||
objectProperties
|
||||
.getOrCreate('opacity')
|
||||
.setValue(objectContent.opacity.toString())
|
||||
.setType('number')
|
||||
.setLabel(_('Opacity (0-255)'))
|
||||
.setGroup(_('Appearance'));
|
||||
|
||||
objectProperties
|
||||
.getOrCreate('align')
|
||||
.setValue(objectContent.align)
|
||||
@@ -673,9 +666,6 @@ module.exports = {
|
||||
const rawText = properties.get('text').getValue();
|
||||
this._pixiObject.text = rawText;
|
||||
|
||||
const opacity = +properties.get('opacity').getValue();
|
||||
this._pixiObject.alpha = opacity / 255;
|
||||
|
||||
const align = properties.get('align').getValue();
|
||||
this._pixiObject.align = align;
|
||||
|
||||
@@ -739,6 +729,13 @@ module.exports = {
|
||||
this._pixiObject.rotation = RenderedInstance.toRad(
|
||||
this._instance.getAngle()
|
||||
);
|
||||
|
||||
// Do not hide completely an object so it can still be manipulated
|
||||
const alphaForDisplay = Math.max(
|
||||
this._instance.getOpacity() / 255,
|
||||
0.5
|
||||
);
|
||||
this._pixiObject.alpha = alphaForDisplay;
|
||||
}
|
||||
|
||||
onRemovedFromScene() {
|
||||
|
@@ -203,6 +203,9 @@ namespace gdjs {
|
||||
if (initialInstanceData.customSize) {
|
||||
this.setWrappingWidth(initialInstanceData.width);
|
||||
}
|
||||
if (initialInstanceData.opacity !== undefined) {
|
||||
this.setOpacity(initialInstanceData.opacity);
|
||||
}
|
||||
}
|
||||
|
||||
onDestroyed(): void {
|
||||
|
@@ -34,7 +34,8 @@ void DeclareDestroyOutsideBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
"CppPlatform/Extensions/destroyoutsideicon.png",
|
||||
"DestroyOutsideBehavior",
|
||||
std::make_shared<DestroyOutsideBehavior>(),
|
||||
std::shared_ptr<gd::BehaviorsSharedData>());
|
||||
std::shared_ptr<gd::BehaviorsSharedData>())
|
||||
.SetQuickCustomizationVisibility(gd::QuickCustomization::Hidden);
|
||||
|
||||
aut.AddCondition("ExtraBorder",
|
||||
_("Additional border"),
|
||||
|
@@ -52,7 +52,7 @@ module.exports = {
|
||||
.setType('number');
|
||||
adjustmentProperties
|
||||
.getOrCreate('saturation')
|
||||
.setValue('2')
|
||||
.setValue('1')
|
||||
.setLabel(_('Saturation (between 0 and 5)'))
|
||||
.setType('number');
|
||||
adjustmentProperties
|
||||
@@ -77,7 +77,7 @@ module.exports = {
|
||||
.setType('number');
|
||||
adjustmentProperties
|
||||
.getOrCreate('blue')
|
||||
.setValue('0.6')
|
||||
.setValue('1')
|
||||
.setLabel(_('Blue (between 0 and 5)'))
|
||||
.setType('number');
|
||||
adjustmentProperties
|
||||
|
@@ -30,7 +30,13 @@ namespace gdjs {
|
||||
if (typeof firebaseConfig !== 'object') return;
|
||||
if (firebase.apps.length !== 0) await firebase.app().delete();
|
||||
firebase.initializeApp(firebaseConfig);
|
||||
for (let func of onAppCreated) func();
|
||||
for (let func of onAppCreated) {
|
||||
try {
|
||||
func();
|
||||
} catch (e) {
|
||||
logger.error('An error occurred while running a callback: ' + e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
gdjs.registerFirstRuntimeSceneLoadedCallback(_setupFirebase);
|
||||
|
@@ -369,6 +369,36 @@ module.exports = {
|
||||
'gdjs.multiplayerMessageManager.hasCustomMessageBeenReceived'
|
||||
);
|
||||
|
||||
extension
|
||||
.addExpressionAndConditionAndAction(
|
||||
'number',
|
||||
'ObjectsSynchronizationRate',
|
||||
_('Objects synchronization rate'),
|
||||
_(
|
||||
'objects synchronization rate (between 1 and 60, default is 30 times per second)'
|
||||
),
|
||||
_('objects synchronization rate'),
|
||||
_('Advanced'),
|
||||
'JsPlatform/Extensions/multiplayer.svg'
|
||||
)
|
||||
.useStandardParameters(
|
||||
'number',
|
||||
gd.ParameterOptions.makeNewOptions().setDescription(_('Sync rate'))
|
||||
)
|
||||
.setIncludeFile('Extensions/Multiplayer/peer.js')
|
||||
.addIncludeFile('Extensions/Multiplayer/peerJsHelper.js')
|
||||
.addIncludeFile(
|
||||
'Extensions/PlayerAuthentication/playerauthenticationcomponents.js'
|
||||
)
|
||||
.addIncludeFile(
|
||||
'Extensions/PlayerAuthentication/playerauthenticationtools.js'
|
||||
)
|
||||
.addIncludeFile('Extensions/Multiplayer/messageManager.js')
|
||||
.addIncludeFile('Extensions/Multiplayer/multiplayerVariablesManager.js')
|
||||
.addIncludeFile('Extensions/Multiplayer/multiplayertools.js')
|
||||
.setFunctionName('gdjs.multiplayer.setObjectsSynchronizationRate')
|
||||
.setGetter('gdjs.multiplayer.getObjectsSynchronizationRate');
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'IsPlayerHost',
|
||||
@@ -392,13 +422,13 @@ module.exports = {
|
||||
.addIncludeFile('Extensions/Multiplayer/messageManager.js')
|
||||
.addIncludeFile('Extensions/Multiplayer/multiplayerVariablesManager.js')
|
||||
.addIncludeFile('Extensions/Multiplayer/multiplayertools.js')
|
||||
.setFunctionName('gdjs.multiplayer.isPlayerHost');
|
||||
.setFunctionName('gdjs.multiplayer.isCurrentPlayerHost');
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'HasAnyPlayerLeft',
|
||||
_('Any player has left'),
|
||||
_('Check if any player has left the lobby.'),
|
||||
_('Check if any player has left the lobby game.'),
|
||||
_('Any player has left'),
|
||||
_('Lobbies'),
|
||||
'JsPlatform/Extensions/multiplayer.svg',
|
||||
@@ -423,7 +453,7 @@ module.exports = {
|
||||
.addCondition(
|
||||
'HasPlayerLeft',
|
||||
_('Player has left'),
|
||||
_('Check if the player has left the lobby.'),
|
||||
_('Check if the player has left the lobby game.'),
|
||||
_('Player _PARAM0_ has left'),
|
||||
_('Lobbies'),
|
||||
'JsPlatform/Extensions/multiplayer.svg',
|
||||
@@ -448,8 +478,10 @@ module.exports = {
|
||||
extension
|
||||
.addExpression(
|
||||
'LastLeftPlayerNumber',
|
||||
_('Last left player number'),
|
||||
_('Returns the number of the player that has just left the lobby.'),
|
||||
_('Player number that just left'),
|
||||
_(
|
||||
'Returns the player number of the player that has just left the lobby.'
|
||||
),
|
||||
_('Lobbies'),
|
||||
'JsPlatform/Extensions/multiplayer.svg'
|
||||
)
|
||||
@@ -524,8 +556,10 @@ module.exports = {
|
||||
extension
|
||||
.addExpression(
|
||||
'LastJoinedPlayerNumber',
|
||||
_('Last joined player number'),
|
||||
_('Returns the number of the player that has just joined the lobby.'),
|
||||
_('Player number that just joined'),
|
||||
_(
|
||||
'Returns the player number of the player that has just joined the lobby.'
|
||||
),
|
||||
_('Lobbies'),
|
||||
'JsPlatform/Extensions/multiplayer.svg'
|
||||
)
|
||||
@@ -546,6 +580,61 @@ module.exports = {
|
||||
'gdjs.multiplayerMessageManager.getLatestPlayerWhoJustJoined'
|
||||
);
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'IsMigratingHost',
|
||||
_('Host is migrating'),
|
||||
_(
|
||||
'Check if the host is migrating, in order to adapt the game state (like pausing the game).'
|
||||
),
|
||||
_('Host is migrating'),
|
||||
_('Lobbies'),
|
||||
'JsPlatform/Extensions/multiplayer.svg',
|
||||
'JsPlatform/Extensions/multiplayer.svg'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/Multiplayer/peer.js')
|
||||
.addIncludeFile('Extensions/Multiplayer/peerJsHelper.js')
|
||||
.addIncludeFile(
|
||||
'Extensions/PlayerAuthentication/playerauthenticationcomponents.js'
|
||||
)
|
||||
.addIncludeFile(
|
||||
'Extensions/PlayerAuthentication/playerauthenticationtools.js'
|
||||
)
|
||||
.addIncludeFile('Extensions/Multiplayer/multiplayercomponents.js')
|
||||
.addIncludeFile('Extensions/Multiplayer/messageManager.js')
|
||||
.addIncludeFile('Extensions/Multiplayer/multiplayerVariablesManager.js')
|
||||
.addIncludeFile('Extensions/Multiplayer/multiplayertools.js')
|
||||
.setFunctionName('gdjs.multiplayer.isMigratingHost');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'EndLobbyWhenHostLeaves',
|
||||
_('Configure lobby game to end when host leaves'),
|
||||
_(
|
||||
'Configure the lobby game to end when the host leaves. This will trigger the "Lobby game has just ended" condition. (Default behavior is to migrate the host)'
|
||||
),
|
||||
_('Configure lobby game to end when host leaves'),
|
||||
_('Advanced'),
|
||||
'JsPlatform/Extensions/multiplayer.svg',
|
||||
'JsPlatform/Extensions/multiplayer.svg'
|
||||
)
|
||||
.addParameter('yesorno', _('End lobby game when host leaves'), '', false)
|
||||
.setHelpPath('/all-features/multiplayer')
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/Multiplayer/peer.js')
|
||||
.addIncludeFile('Extensions/Multiplayer/peerJsHelper.js')
|
||||
.addIncludeFile(
|
||||
'Extensions/PlayerAuthentication/playerauthenticationcomponents.js'
|
||||
)
|
||||
.addIncludeFile(
|
||||
'Extensions/PlayerAuthentication/playerauthenticationtools.js'
|
||||
)
|
||||
.addIncludeFile('Extensions/Multiplayer/messageManager.js')
|
||||
.addIncludeFile('Extensions/Multiplayer/multiplayerVariablesManager.js')
|
||||
.addIncludeFile('Extensions/Multiplayer/multiplayertools.js')
|
||||
.setFunctionName('gdjs.multiplayer.endLobbyWhenHostLeaves');
|
||||
|
||||
extension
|
||||
.addStrExpression(
|
||||
'MessageData',
|
||||
@@ -997,6 +1086,7 @@ module.exports = {
|
||||
multiplayerObjectBehavior,
|
||||
sharedData
|
||||
)
|
||||
.setQuickCustomizationVisibility(gd.QuickCustomization.Hidden)
|
||||
.setIncludeFile('Extensions/Multiplayer/peer.js')
|
||||
.addIncludeFile('Extensions/Multiplayer/peerJsHelper.js')
|
||||
.addIncludeFile(
|
||||
|
@@ -145,7 +145,7 @@ namespace gdjs {
|
||||
} = {};
|
||||
|
||||
// The number of times per second the scene data should be synchronized.
|
||||
const sceneSyncDataTickRate = 1;
|
||||
const sceneSyncDataSyncRate = 1;
|
||||
let lastSceneSyncTimestamp = 0;
|
||||
let lastSentSceneSyncData: LayoutNetworkSyncData | null = null;
|
||||
let numberOfForcedSceneUpdates = 0;
|
||||
@@ -154,7 +154,7 @@ namespace gdjs {
|
||||
>();
|
||||
|
||||
// The number of times per second the game data should be synchronized.
|
||||
const gameSyncDataTickRate = 1;
|
||||
const gameSyncDataSyncRate = 1;
|
||||
let lastGameSyncTimestamp = 0;
|
||||
let lastSentGameSyncData: GameNetworkSyncData | null = null;
|
||||
let numberOfForcedGameUpdates = 0;
|
||||
@@ -164,8 +164,8 @@ namespace gdjs {
|
||||
|
||||
// Send heartbeat messages from host to players, ensuring their connection is still alive,
|
||||
// measure the ping, and send other useful info.
|
||||
const heartbeatTickRate = 1;
|
||||
let lastHeartbeatTimestamp = 0;
|
||||
const heartbeatSyncRate = 1;
|
||||
let lastHeartbeatSentTimestamp = 0;
|
||||
let _playersLastRoundTripTimes: {
|
||||
[playerNumber: number]: number[];
|
||||
} = {};
|
||||
@@ -531,7 +531,10 @@ namespace gdjs {
|
||||
currentPlayerObjectOwnership === previousOwner ||
|
||||
// the object is already owned by the new owner. (may have been changed by another player faster)
|
||||
currentPlayerObjectOwnership === newOwner;
|
||||
if (gdjs.multiplayer.isPlayerHost() && !ownershipChangeIsCoherent) {
|
||||
if (
|
||||
gdjs.multiplayer.isCurrentPlayerHost() &&
|
||||
!ownershipChangeIsCoherent
|
||||
) {
|
||||
// We received an ownership change message for an object which is in an unexpected state.
|
||||
// There may be some lag, and multiple ownership changes may have been sent by the other players.
|
||||
// As the host, let's not change the ownership and let the player revert it.
|
||||
@@ -560,7 +563,7 @@ namespace gdjs {
|
||||
// If we are the host,
|
||||
// so we need to relay the ownership change to others,
|
||||
// and expect an acknowledgment from them.
|
||||
if (gdjs.multiplayer.isPlayerHost()) {
|
||||
if (gdjs.multiplayer.isCurrentPlayerHost()) {
|
||||
const connectedPeerIds = gdjs.multiplayerPeerJsHelper.getAllPeers();
|
||||
// We don't need to send the message to the player who sent the ownership change message.
|
||||
const otherPeerIds = connectedPeerIds.filter(
|
||||
@@ -738,7 +741,7 @@ namespace gdjs {
|
||||
|
||||
// If we are are the host,
|
||||
// we need to relay the position to others except the player who sent the update message.
|
||||
if (gdjs.multiplayer.isPlayerHost()) {
|
||||
if (gdjs.multiplayer.isCurrentPlayerHost()) {
|
||||
const connectedPeerIds = gdjs.multiplayerPeerJsHelper.getAllPeers();
|
||||
const otherPeerIds = connectedPeerIds.filter(
|
||||
(peerId) => peerId !== messageSender
|
||||
@@ -863,7 +866,10 @@ namespace gdjs {
|
||||
currentPlayerVariableOwnership === previousOwner ||
|
||||
// the variable is already owned by the new owner. (may have been changed by another player faster)
|
||||
currentPlayerVariableOwnership === newOwner;
|
||||
if (gdjs.multiplayer.isPlayerHost() && !ownershipChangeIsCoherent) {
|
||||
if (
|
||||
gdjs.multiplayer.isCurrentPlayerHost() &&
|
||||
!ownershipChangeIsCoherent
|
||||
) {
|
||||
// We received an ownership change message for a variable which is in an unexpected state.
|
||||
// There may be some lag, and multiple ownership changes may have been sent by the other players.
|
||||
// As the host, let's not change the ownership and let the player revert it.
|
||||
@@ -892,7 +898,7 @@ namespace gdjs {
|
||||
// If we are the host,
|
||||
// we need to relay the ownership change to others,
|
||||
// and expect an acknowledgment from them.
|
||||
if (gdjs.multiplayer.isPlayerHost()) {
|
||||
if (gdjs.multiplayer.isCurrentPlayerHost()) {
|
||||
const connectedPeerIds = gdjs.multiplayerPeerJsHelper.getAllPeers();
|
||||
// We don't need to send the message to the player who sent the ownership change message.
|
||||
const otherPeerIds = connectedPeerIds.filter(
|
||||
@@ -1336,7 +1342,7 @@ namespace gdjs {
|
||||
|
||||
// If we are the host, we need to relay the destruction to others.
|
||||
// And expect an acknowledgment from everyone else as well.
|
||||
if (gdjs.multiplayer.isPlayerHost()) {
|
||||
if (gdjs.multiplayer.isCurrentPlayerHost()) {
|
||||
const connectedPeerIds = gdjs.multiplayerPeerJsHelper.getAllPeers();
|
||||
// We don't need to send the message to the player who sent the destroy message.
|
||||
const otherPeerIds = connectedPeerIds.filter(
|
||||
@@ -1429,7 +1435,7 @@ namespace gdjs {
|
||||
|
||||
// If we are the host, we can consider this messaged as received
|
||||
// and add it to the list of custom messages to process on top of the messages received.
|
||||
if (gdjs.multiplayer.isPlayerHost()) {
|
||||
if (gdjs.multiplayer.isCurrentPlayerHost()) {
|
||||
const messagesList = gdjs.multiplayerPeerJsHelper.getOrCreateMessagesList(
|
||||
messageName
|
||||
);
|
||||
@@ -1592,7 +1598,7 @@ namespace gdjs {
|
||||
|
||||
// If we are the host,
|
||||
// so we need to relay the message to others.
|
||||
if (gdjs.multiplayer.isPlayerHost()) {
|
||||
if (gdjs.multiplayer.isCurrentPlayerHost()) {
|
||||
// In the case of custom messages, we relay the message to all players, including the sender.
|
||||
// This allows the sender to process it the same way others would, when they receive the event.
|
||||
const connectedPeerIds = gdjs.multiplayerPeerJsHelper.getAllPeers();
|
||||
@@ -1650,7 +1656,7 @@ namespace gdjs {
|
||||
|
||||
const hasSceneBeenSyncedRecently = () => {
|
||||
return (
|
||||
getTimeNow() - lastSceneSyncTimestamp < 1000 / sceneSyncDataTickRate
|
||||
getTimeNow() - lastSceneSyncTimestamp < 1000 / sceneSyncDataSyncRate
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1664,6 +1670,7 @@ namespace gdjs {
|
||||
|
||||
const sceneNetworkSyncData = runtimeScene.getNetworkSyncData({
|
||||
playerNumber: gdjs.multiplayer.getCurrentPlayerNumber(),
|
||||
isHost: gdjs.multiplayer.isCurrentPlayerHost(),
|
||||
});
|
||||
if (!sceneNetworkSyncData) {
|
||||
return;
|
||||
@@ -1737,7 +1744,7 @@ namespace gdjs {
|
||||
|
||||
// If we are are the host,
|
||||
// we need to relay the scene update to others except the player who sent the update message.
|
||||
if (gdjs.multiplayer.isPlayerHost()) {
|
||||
if (gdjs.multiplayer.isCurrentPlayerHost()) {
|
||||
const connectedPeerIds = gdjs.multiplayerPeerJsHelper.getAllPeers();
|
||||
// We don't need to send the message to the player who sent the update message.
|
||||
const otherPeerIds = connectedPeerIds.filter(
|
||||
@@ -1814,7 +1821,7 @@ namespace gdjs {
|
||||
};
|
||||
|
||||
const hasGameBeenSyncedRecently = () => {
|
||||
return getTimeNow() - lastGameSyncTimestamp < 1000 / gameSyncDataTickRate;
|
||||
return getTimeNow() - lastGameSyncTimestamp < 1000 / gameSyncDataSyncRate;
|
||||
};
|
||||
|
||||
const handleUpdateGameMessagesToSend = (
|
||||
@@ -1827,6 +1834,7 @@ namespace gdjs {
|
||||
|
||||
const gameNetworkSyncData = runtimeScene.getGame().getNetworkSyncData({
|
||||
playerNumber: gdjs.multiplayer.getCurrentPlayerNumber(),
|
||||
isHost: gdjs.multiplayer.isCurrentPlayerHost(),
|
||||
});
|
||||
if (!gameNetworkSyncData) {
|
||||
return;
|
||||
@@ -1888,7 +1896,7 @@ namespace gdjs {
|
||||
|
||||
// If we are are the host,
|
||||
// we need to relay the game update to others except the player who sent the update message.
|
||||
if (gdjs.multiplayer.isPlayerHost()) {
|
||||
if (gdjs.multiplayer.isCurrentPlayerHost()) {
|
||||
const connectedPeerIds = gdjs.multiplayerPeerJsHelper.getAllPeers();
|
||||
// We don't need to send the message to the player who sent the update message.
|
||||
const otherPeerIds = connectedPeerIds.filter(
|
||||
@@ -1937,9 +1945,10 @@ namespace gdjs {
|
||||
messageName: string;
|
||||
messageData: any;
|
||||
} => {
|
||||
// Ensure player 1 is correctly set when the first heartbeat is sent.
|
||||
_playersInfo[1] = {
|
||||
ping: 0, // Player 1 is the host, so we don't need to compute the ping.
|
||||
// If we create the heartbeat meassage, we are the host,
|
||||
// Ensure our player number is correctly set when the first heartbeat is sent.
|
||||
_playersInfo[gdjs.multiplayer.getCurrentPlayerNumber()] = {
|
||||
ping: 0, // we are the host, so we don't need to compute the ping.
|
||||
playerId: gdjs.playerAuthentication.getUserId(),
|
||||
username: gdjs.playerAuthentication.getUsername(),
|
||||
};
|
||||
@@ -1976,15 +1985,15 @@ namespace gdjs {
|
||||
};
|
||||
const hasSentHeartbeatRecently = () => {
|
||||
return (
|
||||
!!lastHeartbeatTimestamp &&
|
||||
getTimeNow() - lastHeartbeatTimestamp < 1000 / heartbeatTickRate
|
||||
!!lastHeartbeatSentTimestamp &&
|
||||
getTimeNow() - lastHeartbeatSentTimestamp < 1000 / heartbeatSyncRate
|
||||
);
|
||||
};
|
||||
const handleHeartbeatsToSend = () => {
|
||||
// Only host sends heartbeats to all players regularly:
|
||||
// - it allows them to send a heartbeat back immediately so that the host can compute the ping.
|
||||
// - it allows to pass along the pings of all players to all players.
|
||||
if (!gdjs.multiplayer.isPlayerHost()) {
|
||||
if (!gdjs.multiplayer.isCurrentPlayerHost()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1997,7 +2006,7 @@ namespace gdjs {
|
||||
const { messageName, messageData } = createHeartbeatMessage();
|
||||
sendDataTo(connectedPeerIds, messageName, messageData);
|
||||
|
||||
lastHeartbeatTimestamp = getTimeNow();
|
||||
lastHeartbeatSentTimestamp = getTimeNow();
|
||||
};
|
||||
|
||||
const handleHeartbeatsReceived = () => {
|
||||
@@ -2024,7 +2033,7 @@ namespace gdjs {
|
||||
|
||||
// If we are not the host, save what the host told us about the other players info
|
||||
// and respond with a heartbeat immediately, informing the host of our playerId and username.
|
||||
if (!gdjs.multiplayer.isPlayerHost()) {
|
||||
if (!gdjs.multiplayer.isCurrentPlayerHost()) {
|
||||
const currentPlayerNumber = gdjs.multiplayer.getCurrentPlayerNumber();
|
||||
const currentlyKnownPlayerNumbers = Object.keys(
|
||||
_playersInfo
|
||||
@@ -2134,12 +2143,20 @@ namespace gdjs {
|
||||
const connectedPeerIds = gdjs.multiplayerPeerJsHelper.getAllPeers();
|
||||
const { messageName, messageData } = createHeartbeatMessage();
|
||||
sendDataTo(connectedPeerIds, messageName, messageData);
|
||||
lastHeartbeatTimestamp = getTimeNow();
|
||||
lastHeartbeatSentTimestamp = getTimeNow();
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const hasReceivedHeartbeatFromPlayer = (playerNumber: number) => {
|
||||
// Consider that a player has sent a heartbeat if we have been able to calculate
|
||||
// at least one round trip time for them.
|
||||
const playerLastRoundTripTimes =
|
||||
_playersLastRoundTripTimes[playerNumber] || [];
|
||||
return playerLastRoundTripTimes.length > 0;
|
||||
};
|
||||
|
||||
const getPlayerPing = (playerNumber: number) => {
|
||||
const playerInfo = _playersInfo[playerNumber];
|
||||
if (!playerInfo) {
|
||||
@@ -2153,7 +2170,15 @@ namespace gdjs {
|
||||
return getPlayerPing(currentPlayerNumber);
|
||||
};
|
||||
|
||||
const markPlayerAsDisconnected = (playerNumber: number) => {
|
||||
const markPlayerAsDisconnected = ({
|
||||
runtimeScene,
|
||||
playerNumber,
|
||||
peerId,
|
||||
}: {
|
||||
runtimeScene: gdjs.RuntimeScene;
|
||||
playerNumber: number;
|
||||
peerId?: string;
|
||||
}) => {
|
||||
logger.info(`Marking player ${playerNumber} as disconnected.`);
|
||||
_playerNumbersWhoJustLeft.push(playerNumber);
|
||||
// Temporarily save the username in another variable to be used for the notification,
|
||||
@@ -2161,23 +2186,31 @@ namespace gdjs {
|
||||
_temporaryPlayerNumberToUsername[playerNumber] = getPlayerUsername(
|
||||
playerNumber
|
||||
);
|
||||
clearPlayerTempData(playerNumber);
|
||||
|
||||
// If Player 1 has disconnected, just end the game.
|
||||
if (playerNumber === 1) {
|
||||
logger.info('Host has disconnected, ending the game.');
|
||||
clearAllMessagesTempData();
|
||||
gdjs.multiplayer.handleLobbyGameEnded();
|
||||
return;
|
||||
// If Host has disconnected, either switch host or stop the game.
|
||||
if (peerId && peerId === gdjs.multiplayer.hostPeerId) {
|
||||
const shouldEndLobbyGame = gdjs.multiplayer.shouldEndLobbyWhenHostLeaves();
|
||||
if (shouldEndLobbyGame) {
|
||||
logger.info('Host has disconnected, ending the game.');
|
||||
|
||||
clearAllMessagesTempData();
|
||||
gdjs.multiplayer.handleLobbyGameEnded();
|
||||
} else {
|
||||
logger.info('Host has disconnected, switching host.');
|
||||
|
||||
gdjs.multiplayer.handleHostDisconnected({ runtimeScene });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
clearPlayerTempData(playerNumber);
|
||||
// If we are the host, send a heartbeat right away so that everyone is aware of the disconnection
|
||||
// on approximately the same frame.
|
||||
if (gdjs.multiplayer.isPlayerHost()) {
|
||||
if (gdjs.multiplayer.isCurrentPlayerHost()) {
|
||||
const connectedPeerIds = gdjs.multiplayerPeerJsHelper.getAllPeers();
|
||||
const { messageName, messageData } = createHeartbeatMessage();
|
||||
sendDataTo(connectedPeerIds, messageName, messageData);
|
||||
lastHeartbeatTimestamp = getTimeNow();
|
||||
lastHeartbeatSentTimestamp = getTimeNow();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2200,7 +2233,10 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
// We rely on the p2p helper to know who has disconnected.
|
||||
const justDisconnectedPlayerNumbers: number[] = [];
|
||||
const justDisconnectedPlayers: {
|
||||
playerNumber: number;
|
||||
peerId: string;
|
||||
}[] = [];
|
||||
|
||||
const justDisconnectedPeers = gdjs.multiplayerPeerJsHelper.getJustDisconnectedPeers();
|
||||
if (justDisconnectedPeers.length) {
|
||||
@@ -2212,14 +2248,17 @@ namespace gdjs {
|
||||
return;
|
||||
}
|
||||
logger.info(`Player ${disconnectedPlayerNumber} has disconnected.`);
|
||||
justDisconnectedPlayerNumbers.push(disconnectedPlayerNumber);
|
||||
justDisconnectedPlayers.push({
|
||||
playerNumber: disconnectedPlayerNumber,
|
||||
peerId: disconnectedPeer,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for (const playerNumber of justDisconnectedPlayerNumbers) {
|
||||
for (const { playerNumber, peerId } of justDisconnectedPlayers) {
|
||||
// When a player disconnects, as the host, we look at all the instances
|
||||
// they own and decide what to do with them.
|
||||
if (gdjs.multiplayer.isPlayerHost()) {
|
||||
if (gdjs.multiplayer.isCurrentPlayerHost()) {
|
||||
const instances = runtimeScene.getAdhocListOfAllInstances();
|
||||
for (const instance of instances) {
|
||||
const behavior = instance.getBehavior(
|
||||
@@ -2243,7 +2282,7 @@ namespace gdjs {
|
||||
}
|
||||
}
|
||||
|
||||
markPlayerAsDisconnected(playerNumber);
|
||||
markPlayerAsDisconnected({ runtimeScene, playerNumber, peerId });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2303,6 +2342,10 @@ namespace gdjs {
|
||||
return _playersInfo[playerNumber] !== undefined;
|
||||
};
|
||||
|
||||
const getPlayersInfo = () => {
|
||||
return _playersInfo;
|
||||
};
|
||||
|
||||
const endGameMessageName = '#endGame';
|
||||
const createEndGameMessage = (): {
|
||||
messageName: string;
|
||||
@@ -2315,7 +2358,7 @@ namespace gdjs {
|
||||
};
|
||||
const sendEndGameMessage = () => {
|
||||
// Only the host can end the game.
|
||||
if (!gdjs.multiplayer.isPlayerHost()) {
|
||||
if (!gdjs.multiplayer.isCurrentPlayerHost()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2327,8 +2370,8 @@ namespace gdjs {
|
||||
sendDataTo(connectedPeerIds, messageName, messageData);
|
||||
};
|
||||
|
||||
const handleEndGameMessages = () => {
|
||||
if (gdjs.multiplayer.isPlayerHost()) {
|
||||
const handleEndGameMessagesReceived = () => {
|
||||
if (gdjs.multiplayer.isCurrentPlayerHost()) {
|
||||
// Only other players need to react to the end game message.
|
||||
return;
|
||||
}
|
||||
@@ -2348,6 +2391,50 @@ namespace gdjs {
|
||||
gdjs.multiplayer.handleLobbyGameEnded();
|
||||
};
|
||||
|
||||
const resumeGameMessageName = '#resumeGame';
|
||||
const createResumeGameMessage = (): {
|
||||
messageName: string;
|
||||
messageData: any;
|
||||
} => {
|
||||
return {
|
||||
messageName: resumeGameMessageName,
|
||||
messageData: {},
|
||||
};
|
||||
};
|
||||
const sendResumeGameMessage = () => {
|
||||
// Only the host can inform others that the game is resuming.
|
||||
if (!gdjs.multiplayer.isCurrentPlayerHost()) {
|
||||
return;
|
||||
}
|
||||
|
||||
debugLogger.info(`Sending resumeGame message.`);
|
||||
|
||||
const connectedPeerIds = gdjs.multiplayerPeerJsHelper.getAllPeers();
|
||||
const { messageName, messageData } = createResumeGameMessage();
|
||||
sendDataTo(connectedPeerIds, messageName, messageData);
|
||||
};
|
||||
|
||||
const handleResumeGameMessagesReceived = (
|
||||
runtimeScene: gdjs.RuntimeScene
|
||||
) => {
|
||||
if (gdjs.multiplayer.isCurrentPlayerHost()) {
|
||||
// Only other players need to react to resume game message.
|
||||
return;
|
||||
}
|
||||
|
||||
const p2pMessagesMap = gdjs.multiplayerPeerJsHelper.getAllMessagesMap();
|
||||
const resumeGameMessagesList = p2pMessagesMap.get(resumeGameMessageName);
|
||||
if (!resumeGameMessagesList) {
|
||||
return; // No resume game message received.
|
||||
}
|
||||
const messages = resumeGameMessagesList.getMessages();
|
||||
if (!messages.length) return; // No messages to process.
|
||||
|
||||
logger.info(`Received resumeGame message.`);
|
||||
|
||||
gdjs.multiplayer.resumeGame(runtimeScene);
|
||||
};
|
||||
|
||||
const clearAllMessagesTempData = () => {
|
||||
_playersLastRoundTripTimes = {};
|
||||
_playersInfo = {};
|
||||
@@ -2409,6 +2496,7 @@ namespace gdjs {
|
||||
// Heartbeats.
|
||||
handleHeartbeatsToSend,
|
||||
handleHeartbeatsReceived,
|
||||
hasReceivedHeartbeatFromPlayer,
|
||||
// Pings & usernames.
|
||||
getPlayerPing,
|
||||
getCurrentPlayerPing,
|
||||
@@ -2419,12 +2507,14 @@ namespace gdjs {
|
||||
getConnectedPlayers,
|
||||
getNumberOfConnectedPlayers,
|
||||
isPlayerConnected,
|
||||
getPlayersInfo,
|
||||
// Leaving players.
|
||||
hasAnyPlayerJustLeft,
|
||||
hasPlayerJustLeft,
|
||||
getPlayersWhoJustLeft,
|
||||
getLatestPlayerWhoJustLeft,
|
||||
removePlayerWhoJustLeft,
|
||||
markPlayerAsDisconnected,
|
||||
// Joining players.
|
||||
hasAnyPlayerJustJoined,
|
||||
hasPlayerJustJoined,
|
||||
@@ -2433,8 +2523,11 @@ namespace gdjs {
|
||||
removePlayerWhoJustJoined,
|
||||
// End game.
|
||||
sendEndGameMessage,
|
||||
handleEndGameMessages,
|
||||
handleEndGameMessagesReceived,
|
||||
clearAllMessagesTempData,
|
||||
// Resume game after migration.
|
||||
sendResumeGameMessage,
|
||||
handleResumeGameMessagesReceived,
|
||||
};
|
||||
};
|
||||
|
||||
|
@@ -398,11 +398,12 @@ namespace gdjs {
|
||||
export const displayErrorNotification = function (
|
||||
runtimeScene: gdjs.RuntimeScene
|
||||
) {
|
||||
showNotification(
|
||||
showNotification({
|
||||
runtimeScene,
|
||||
'An error occurred while displaying the game lobbies, please try again.',
|
||||
'error'
|
||||
);
|
||||
content:
|
||||
'An error occurred while displaying the game lobbies, please try again.',
|
||||
type: 'error',
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -412,7 +413,11 @@ namespace gdjs {
|
||||
runtimeScene: gdjs.RuntimeScene,
|
||||
playerName: string
|
||||
) {
|
||||
showNotification(runtimeScene, `${playerName} left.`, 'warning');
|
||||
showNotification({
|
||||
runtimeScene,
|
||||
content: `${playerName} left.`,
|
||||
type: 'warning',
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -422,7 +427,11 @@ namespace gdjs {
|
||||
runtimeScene: gdjs.RuntimeScene,
|
||||
playerName: string
|
||||
) {
|
||||
showNotification(runtimeScene, `${playerName} joined.`, 'success');
|
||||
showNotification({
|
||||
runtimeScene,
|
||||
content: `${playerName} joined.`,
|
||||
type: 'success',
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -431,11 +440,48 @@ namespace gdjs {
|
||||
export const displayConnectionErrorNotification = function (
|
||||
runtimeScene: gdjs.RuntimeScene
|
||||
) {
|
||||
showNotification(
|
||||
showNotification({
|
||||
runtimeScene,
|
||||
'Could not connect to other players.',
|
||||
'error'
|
||||
);
|
||||
content: 'Could not connect to other players.',
|
||||
type: 'error',
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Create, display, and hide a notification when a player leaves the game.
|
||||
*/
|
||||
export const displayHostMigrationNotification = function (
|
||||
runtimeScene: gdjs.RuntimeScene
|
||||
) {
|
||||
showNotification({
|
||||
runtimeScene,
|
||||
content: `Migrating host...`,
|
||||
type: 'warning',
|
||||
id: 'migrating-host',
|
||||
persist: true,
|
||||
});
|
||||
};
|
||||
|
||||
export const showHostMigrationFinishedNotification = function (
|
||||
runtimeScene: gdjs.RuntimeScene
|
||||
) {
|
||||
removeNotificationAndShiftOthers('migrating-host');
|
||||
showNotification({
|
||||
runtimeScene,
|
||||
content: `Host migrated!`,
|
||||
type: 'success',
|
||||
});
|
||||
};
|
||||
|
||||
export const showHostMigrationFailedNotification = function (
|
||||
runtimeScene: gdjs.RuntimeScene
|
||||
) {
|
||||
removeNotificationAndShiftOthers('migrating-host');
|
||||
showNotification({
|
||||
runtimeScene,
|
||||
content: `Host migration failed.`,
|
||||
type: 'error',
|
||||
});
|
||||
};
|
||||
|
||||
const removeNotificationAndShiftOthers = function (
|
||||
@@ -443,7 +489,9 @@ namespace gdjs {
|
||||
) {
|
||||
const notification = document.getElementById(notificationContainerId);
|
||||
if (!notification) {
|
||||
logger.error('Notification not found.');
|
||||
logger.warn(
|
||||
`Notification ${notificationContainerId} not found. skipping`
|
||||
);
|
||||
return;
|
||||
}
|
||||
const index = notificationContainerIds.indexOf(notificationContainerId);
|
||||
@@ -452,8 +500,8 @@ namespace gdjs {
|
||||
}
|
||||
notification.remove();
|
||||
|
||||
// Shift the other notifications up.
|
||||
for (let i = 0; i < notificationContainerIds.length; i++) {
|
||||
// Shift the notifications that are below the one that was removed up.
|
||||
for (let i = index; i < notificationContainerIds.length; i++) {
|
||||
const notification = document.getElementById(
|
||||
notificationContainerIds[i]
|
||||
);
|
||||
@@ -468,11 +516,19 @@ namespace gdjs {
|
||||
/**
|
||||
* Helper to show a notification to the user, that disappears automatically.
|
||||
*/
|
||||
export const showNotification = function (
|
||||
runtimeScene: gdjs.RuntimeScene,
|
||||
content: string,
|
||||
type: 'success' | 'warning' | 'error'
|
||||
) {
|
||||
export const showNotification = function ({
|
||||
runtimeScene,
|
||||
content,
|
||||
type,
|
||||
id,
|
||||
persist,
|
||||
}: {
|
||||
runtimeScene: gdjs.RuntimeScene;
|
||||
content: string;
|
||||
type: 'success' | 'warning' | 'error';
|
||||
id?: string;
|
||||
persist?: boolean;
|
||||
}) {
|
||||
// When we show a notification, we add it below the other ones.
|
||||
// We also remove the oldest one if there are too many > 5.
|
||||
if (notificationContainerIds.length > 5) {
|
||||
@@ -486,7 +542,8 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
// We generate a random ID for the notification, so they can stack.
|
||||
const id = `notification-${Math.random().toString(36).substring(7)}`;
|
||||
const notificationId =
|
||||
id || `notification-${Math.random().toString(36).substring(7)}`;
|
||||
|
||||
const domContainer = runtimeScene
|
||||
.getGame()
|
||||
@@ -498,7 +555,7 @@ namespace gdjs {
|
||||
}
|
||||
const divContainer = document.createElement('div');
|
||||
|
||||
divContainer.id = id;
|
||||
divContainer.id = notificationId;
|
||||
divContainer.style.position = 'absolute';
|
||||
divContainer.style.pointerEvents = 'all';
|
||||
divContainer.style.backgroundColor =
|
||||
@@ -544,10 +601,14 @@ namespace gdjs {
|
||||
|
||||
divContainer.appendChild(loggedText);
|
||||
domContainer.appendChild(divContainer);
|
||||
notificationContainerIds.push(id);
|
||||
notificationContainerIds.push(notificationId);
|
||||
|
||||
if (persist) {
|
||||
return;
|
||||
}
|
||||
|
||||
const animationTime = 700;
|
||||
const notificationTime = 5000;
|
||||
const notificationTime = 3000;
|
||||
setTimeout(() => {
|
||||
try {
|
||||
divContainer.animate(
|
||||
@@ -566,7 +627,7 @@ namespace gdjs {
|
||||
}, notificationTime);
|
||||
// Use timeout because onanimationend listener does not work.
|
||||
setTimeout(() => {
|
||||
removeNotificationAndShiftOthers(id);
|
||||
removeNotificationAndShiftOthers(notificationId);
|
||||
}, notificationTime + animationTime);
|
||||
};
|
||||
|
||||
|
@@ -24,48 +24,46 @@ namespace gdjs {
|
||||
actionOnPlayerDisconnect: string;
|
||||
|
||||
// The last time the object has been synchronized.
|
||||
// This is to avoid synchronizing the object too often, see _objectMaxTickRate.
|
||||
// This is to avoid synchronizing the object too often, see _objectMaxSyncRate.
|
||||
_lastObjectSyncTimestamp: number = 0;
|
||||
// The number of times per second the object should be synchronized if it keeps changing.
|
||||
_objectMaxTickRate: number = 60;
|
||||
|
||||
// The last time the basic object info has been synchronized.
|
||||
_lastBasicObjectSyncTimestamp: number = 0;
|
||||
// The number of times per second the object basic info should be synchronized when it doesn't change.
|
||||
_objectBasicInfoTickRate: number = 5;
|
||||
_objectBasicInfoSyncRate: number = 5;
|
||||
// The last data sent to synchronize the basic info of the object.
|
||||
_lastSentBasicObjectSyncData: BasicObjectNetworkSyncData | undefined;
|
||||
// When we know that the basic info of the object has been updated, we can force sending them
|
||||
// on the max tickrate for a number of times to ensure they are received, without the need of an acknowledgment.
|
||||
// on the max SyncRate for a number of times to ensure they are received, without the need of an acknowledgment.
|
||||
_numberOfForcedBasicObjectUpdates: number = 0;
|
||||
|
||||
// The last time the variables have been synchronized.
|
||||
_lastVariablesSyncTimestamp: number = 0;
|
||||
// The number of times per second the variables should be synchronized.
|
||||
_variablesTickRate: number = 1;
|
||||
_variablesSyncRate: number = 1;
|
||||
// The last data sent to synchronize the variables.
|
||||
_lastSentVariableSyncData: VariableNetworkSyncData[] | undefined;
|
||||
// When we know that the variables have been updated, we can force sending them
|
||||
// on the same tickrate as the object update for a number of times
|
||||
// on the same syncRate as the object update for a number of times
|
||||
// to ensure they are received, without the need of an acknowledgment.
|
||||
_numberOfForcedVariablesUpdates: number = 0;
|
||||
|
||||
// The last time the effects have been synchronized.
|
||||
_lastEffectsSyncTimestamp: number = 0;
|
||||
// The number of times per second the effects should be synchronized.
|
||||
_effectsTickRate: number = 1;
|
||||
_effectsSyncRate: number = 1;
|
||||
// The last data sent to synchronize the effects.
|
||||
_lastSentEffectSyncData:
|
||||
| { [effectName: string]: EffectNetworkSyncData }
|
||||
| undefined;
|
||||
// When we know that the effects have been updated, we can force sending them
|
||||
// on the same tickrate as the object update for a number of times
|
||||
// on the same syncRate as the object update for a number of times
|
||||
// to ensure they are received, without the need of an acknowledgment.
|
||||
_numberOfForcedEffectsUpdates: number = 0;
|
||||
|
||||
// To avoid seeing too many logs.
|
||||
_lastLogTimestamp: number = 0;
|
||||
_logTickRate: number = 1;
|
||||
_logSyncRate: number = 1;
|
||||
// Clock to be incremented every time we send a message, to ensure they are ordered
|
||||
// and old messages are ignored.
|
||||
_clock: number = 0;
|
||||
@@ -131,35 +129,35 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
private _hasObjectBeenSyncedWithinMaxRate() {
|
||||
const objectMaxSyncRate = gdjs.multiplayer.getObjectsSynchronizationRate();
|
||||
return (
|
||||
getTimeNow() - this._lastObjectSyncTimestamp <
|
||||
1000 / this._objectMaxTickRate
|
||||
getTimeNow() - this._lastObjectSyncTimestamp < 1000 / objectMaxSyncRate
|
||||
);
|
||||
}
|
||||
|
||||
private _hasObjectBasicInfoBeenSyncedRecently() {
|
||||
return (
|
||||
getTimeNow() - this._lastBasicObjectSyncTimestamp <
|
||||
1000 / this._objectBasicInfoTickRate
|
||||
1000 / this._objectBasicInfoSyncRate
|
||||
);
|
||||
}
|
||||
|
||||
private _haveVariablesBeenSyncedRecently() {
|
||||
return (
|
||||
getTimeNow() - this._lastVariablesSyncTimestamp <
|
||||
1000 / this._variablesTickRate
|
||||
1000 / this._variablesSyncRate
|
||||
);
|
||||
}
|
||||
|
||||
private _haveEffectsBeenSyncedRecently() {
|
||||
return (
|
||||
getTimeNow() - this._lastEffectsSyncTimestamp <
|
||||
1000 / this._effectsTickRate
|
||||
1000 / this._effectsSyncRate
|
||||
);
|
||||
}
|
||||
|
||||
// private _logToConsoleWithThrottle(message: string) {
|
||||
// if (getTimeNow() - this._lastLogTimestamp > 1000 / this._logTickRate) {
|
||||
// if (getTimeNow() - this._lastLogTimestamp > 1000 / this._logSyncRate) {
|
||||
// logger.info(message);
|
||||
// this._lastLogTimestamp = getTimeNow();
|
||||
// }
|
||||
@@ -415,7 +413,10 @@ namespace gdjs {
|
||||
|
||||
// For destruction of objects, we allow the host to destroy the object even if it is not the owner.
|
||||
// This is particularly helpful when a player disconnects, so the host can destroy the object they were owning.
|
||||
if (!this._isOwnerAsPlayerOrHost() && !gdjs.multiplayer.isPlayerHost()) {
|
||||
if (
|
||||
!this._isOwnerAsPlayerOrHost() &&
|
||||
!gdjs.multiplayer.isCurrentPlayerHost()
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,82 @@
|
||||
namespace gdjs {
|
||||
const logger = new gdjs.Logger('Multiplayer');
|
||||
|
||||
type LobbyChangeHostRequest = {
|
||||
lobbyId: string;
|
||||
gameId: string;
|
||||
peerId: string;
|
||||
playerId: string;
|
||||
ping: number;
|
||||
createdAt: number;
|
||||
ttl: number;
|
||||
newLobbyId?: string;
|
||||
newHostPeerId?: string;
|
||||
newPlayers?: {
|
||||
playerNumber: number;
|
||||
playerId: string;
|
||||
}[];
|
||||
};
|
||||
|
||||
const getTimeNow =
|
||||
window.performance && typeof window.performance.now === 'function'
|
||||
? window.performance.now.bind(window.performance)
|
||||
: Date.now;
|
||||
|
||||
const fetchAsPlayer = async ({
|
||||
relativeUrl,
|
||||
method,
|
||||
body,
|
||||
dev,
|
||||
}: {
|
||||
relativeUrl: string;
|
||||
method: 'GET' | 'POST';
|
||||
body?: string;
|
||||
dev: boolean;
|
||||
}) => {
|
||||
const playerId = gdjs.playerAuthentication.getUserId();
|
||||
const playerToken = gdjs.playerAuthentication.getUserToken();
|
||||
if (!playerId || !playerToken) {
|
||||
logger.warn('Cannot fetch as a player if the player is not connected.');
|
||||
throw new Error(
|
||||
'Cannot fetch as a player if the player is not connected.'
|
||||
);
|
||||
}
|
||||
|
||||
const rootApi = dev
|
||||
? 'https://api-dev.gdevelop.io'
|
||||
: 'https://api.gdevelop.io';
|
||||
const url = new URL(`${rootApi}${relativeUrl}`);
|
||||
url.searchParams.set('playerId', playerId);
|
||||
const formattedUrl = url.toString();
|
||||
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `player-game-token ${playerToken}`,
|
||||
};
|
||||
const response = await fetch(formattedUrl, {
|
||||
method,
|
||||
headers,
|
||||
body,
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`Error while fetching as a player: ${response.status} ${response.statusText}`
|
||||
);
|
||||
}
|
||||
|
||||
// Response can either be 'OK' or a JSON object. Get the content before trying to parse it.
|
||||
const responseText = await response.text();
|
||||
if (responseText === 'OK') {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.parse(responseText);
|
||||
} catch (error) {
|
||||
throw new Error(`Error while parsing the response: ${error}`);
|
||||
}
|
||||
};
|
||||
|
||||
export namespace multiplayer {
|
||||
/** Set to true in testing to avoid relying on the multiplayer extension. */
|
||||
export let disableMultiplayerForTesting = false;
|
||||
@@ -17,20 +93,38 @@ namespace gdjs {
|
||||
let _lobbyId: string | null = null;
|
||||
let _connectionId: string | null = null;
|
||||
|
||||
let _shouldEndLobbyWhenHostLeaves = false;
|
||||
let _lobbyChangeHostRequest: LobbyChangeHostRequest | null = null;
|
||||
let _lobbyChangeHostRequestInitiatedAt: number | null = null;
|
||||
let _isChangingHost = false;
|
||||
let _lobbyNewHostPickedAt: number | null = null;
|
||||
|
||||
// Communication methods.
|
||||
let _lobbiesMessageCallback: ((event: MessageEvent) => void) | null = null;
|
||||
let _websocket: WebSocket | null = null;
|
||||
let _websocketHeartbeatInterval: NodeJS.Timeout | null = null;
|
||||
let _lobbyHeartbeatInterval: NodeJS.Timeout | null = null;
|
||||
let _websocketHeartbeatIntervalFunction: NodeJS.Timeout | null = null;
|
||||
let _lobbyHeartbeatIntervalFunction: NodeJS.Timeout | null = null;
|
||||
|
||||
const DEFAULT_WEBSOCKET_HEARTBEAT_INTERVAL = 10000;
|
||||
const DEFAULT_LOBBY_HEARTBEAT_INTERVAL = 30000;
|
||||
const DEFAULT_COUNTDOWN_SECONDS_TO_START = 5;
|
||||
let currentLobbyHeartbeatInterval = DEFAULT_LOBBY_HEARTBEAT_INTERVAL;
|
||||
const DEFAULT_LOBBY_CHANGE_HOST_REQUEST_CHECK_INTERVAL = 1000;
|
||||
// 10 seconds to be safe, but the backend will answer in less.
|
||||
const DEFAULT_LOBBY_CHANGE_HOST_REQUEST_TIMEOUT = 10000;
|
||||
const DEFAULT_LOBBY_EXPECTED_CONNECTED_PLAYERS_CHECK_INTERVAL = 1000;
|
||||
const DEFAULT_LOBBY_EXPECTED_CONNECTED_PLAYERS_TIMEOUT = 10000;
|
||||
let _resumeTimeout: NodeJS.Timeout | null = null;
|
||||
const DEFAULT_LOBBY_EXPECTED_RESUME_TIMEOUT = 12000;
|
||||
|
||||
export const DEFAULT_OBJECT_MAX_SYNC_RATE = 30;
|
||||
// The number of times per second an object should be synchronized if it keeps changing.
|
||||
export let _objectMaxSyncRate = DEFAULT_OBJECT_MAX_SYNC_RATE;
|
||||
|
||||
// Save if we are on dev environment so we don't need to use the runtimeGame every time.
|
||||
let isUsingGDevelopDevelopmentEnvironment = false;
|
||||
|
||||
export let playerNumber: number | null = null;
|
||||
export let hostPeerId: string | null = null;
|
||||
|
||||
gdjs.registerRuntimeScenePreEventsCallback(
|
||||
(runtimeScene: gdjs.RuntimeScene) => {
|
||||
@@ -88,6 +182,11 @@ namespace gdjs {
|
||||
// Then look at the heartbeats received to know if a new player has joined/left.
|
||||
gdjs.multiplayerMessageManager.handleHeartbeatsReceived();
|
||||
|
||||
gdjs.multiplayerMessageManager.handleEndGameMessagesReceived();
|
||||
gdjs.multiplayerMessageManager.handleResumeGameMessagesReceived(
|
||||
runtimeScene
|
||||
);
|
||||
|
||||
gdjs.multiplayerMessageManager.handleDestroyInstanceMessagesReceived(
|
||||
runtimeScene
|
||||
);
|
||||
@@ -161,6 +260,19 @@ namespace gdjs {
|
||||
return url.toString();
|
||||
};
|
||||
|
||||
export const setObjectsSynchronizationRate = (rate: number) => {
|
||||
if (rate < 1 || rate > 60) {
|
||||
logger.warn(
|
||||
`Invalid rate ${rate} for object synchronization. Defaulting to ${DEFAULT_OBJECT_MAX_SYNC_RATE}.`
|
||||
);
|
||||
_objectMaxSyncRate = DEFAULT_OBJECT_MAX_SYNC_RATE;
|
||||
} else {
|
||||
_objectMaxSyncRate = rate;
|
||||
}
|
||||
};
|
||||
|
||||
export const getObjectsSynchronizationRate = () => _objectMaxSyncRate;
|
||||
|
||||
/**
|
||||
* Returns true if the game has just started,
|
||||
* useful to switch to the game scene.
|
||||
@@ -181,7 +293,7 @@ namespace gdjs {
|
||||
/**
|
||||
* Returns the number of players in the lobby.
|
||||
*/
|
||||
export const getPlayersInLobbyCount = () => {
|
||||
export const getPlayersInLobbyCount = (): number => {
|
||||
// Whether the lobby game has started or not, the number of players in the lobby
|
||||
// is the number of connected players.
|
||||
return gdjs.multiplayerMessageManager.getNumberOfConnectedPlayers();
|
||||
@@ -190,7 +302,7 @@ namespace gdjs {
|
||||
/**
|
||||
* Returns true if the player at this position is connected to the lobby.
|
||||
*/
|
||||
export const isPlayerConnected = (playerNumber: number) => {
|
||||
export const isPlayerConnected = (playerNumber: number): boolean => {
|
||||
return gdjs.multiplayerMessageManager.isPlayerConnected(playerNumber);
|
||||
};
|
||||
|
||||
@@ -199,29 +311,52 @@ namespace gdjs {
|
||||
* Return 0 if the player is not in the lobby.
|
||||
* Returns 1, 2, 3, ... if the player is in the lobby.
|
||||
*/
|
||||
export const getCurrentPlayerNumber = () => {
|
||||
export const getCurrentPlayerNumber = (): number => {
|
||||
return playerNumber || 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if the player is the host in the lobby. Here, player 1.
|
||||
* Returns true if the player is the host in the lobby.
|
||||
* This can change during the game.
|
||||
*/
|
||||
export const isPlayerHost = () => {
|
||||
return playerNumber === 1;
|
||||
export const isCurrentPlayerHost = (): boolean => {
|
||||
return (
|
||||
!!hostPeerId &&
|
||||
hostPeerId === gdjs.multiplayerPeerJsHelper.getCurrentId()
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if the host left and the game is either:
|
||||
* - picking a new host
|
||||
* - waiting for everyone to connect to the new host
|
||||
*/
|
||||
export const isMigratingHost = (): boolean => {
|
||||
return !!_isChangingHost;
|
||||
};
|
||||
|
||||
/**
|
||||
* If this is set, instead of migrating the host, the lobby will end when the host leaves.
|
||||
*/
|
||||
export const endLobbyWhenHostLeaves = (enable: boolean) => {
|
||||
_shouldEndLobbyWhenHostLeaves = enable;
|
||||
};
|
||||
|
||||
export const shouldEndLobbyWhenHostLeaves = () =>
|
||||
_shouldEndLobbyWhenHostLeaves;
|
||||
|
||||
/**
|
||||
* Returns the player username at the given number in the lobby.
|
||||
* The number is shifted by one, so that the first player has number 1.
|
||||
*/
|
||||
export const getPlayerUsername = (playerNumber: number) => {
|
||||
export const getPlayerUsername = (playerNumber: number): string => {
|
||||
return gdjs.multiplayerMessageManager.getPlayerUsername(playerNumber);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the player username of the current player in the lobby.
|
||||
*/
|
||||
export const getCurrentPlayerUsername = () => {
|
||||
export const getCurrentPlayerUsername = (): string => {
|
||||
const currentPlayerNumber = getCurrentPlayerNumber();
|
||||
return getPlayerUsername(currentPlayerNumber);
|
||||
};
|
||||
@@ -241,7 +376,12 @@ namespace gdjs {
|
||||
|
||||
// When a player leaves, we send a heartbeat to the backend so that they're aware of the players in the lobby.
|
||||
// Do not await as we want don't want to block the execution of the of the rest of the logic.
|
||||
sendHeartbeatToBackend();
|
||||
if (
|
||||
isCurrentPlayerHost() &&
|
||||
isReadyToSendOrReceiveGameUpdateMessages()
|
||||
) {
|
||||
sendHeartbeatToBackend();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -253,6 +393,15 @@ namespace gdjs {
|
||||
runtimeScene,
|
||||
playerUsername
|
||||
);
|
||||
|
||||
// We also send a heartbeat to the backend right away, so that they're aware of the players in the lobby.
|
||||
// Do not await as we want don't want to block the execution of the of the rest of the logic.
|
||||
if (
|
||||
isCurrentPlayerHost() &&
|
||||
isReadyToSendOrReceiveGameUpdateMessages()
|
||||
) {
|
||||
sendHeartbeatToBackend();
|
||||
}
|
||||
}
|
||||
// We remove the players who just joined 1 by 1, so that they can be treated in different frames.
|
||||
// This is especially important if the expression to know the latest player who just joined is used,
|
||||
@@ -310,6 +459,7 @@ namespace gdjs {
|
||||
_websocket.close();
|
||||
_connectionId = null;
|
||||
playerNumber = null;
|
||||
hostPeerId = null;
|
||||
_lobbyId = null;
|
||||
_websocket = null;
|
||||
}
|
||||
@@ -339,7 +489,7 @@ namespace gdjs {
|
||||
_websocket.onopen = () => {
|
||||
logger.info('Connected to the lobby.');
|
||||
// Register a heartbeat to keep the connection alive.
|
||||
_websocketHeartbeatInterval = setInterval(() => {
|
||||
_websocketHeartbeatIntervalFunction = setInterval(() => {
|
||||
if (_websocket) {
|
||||
_websocket.send(
|
||||
JSON.stringify({
|
||||
@@ -413,23 +563,21 @@ namespace gdjs {
|
||||
case 'gameCountdownStarted': {
|
||||
const messageData = messageContent.data;
|
||||
const compressionMethod = messageData.compressionMethod || 'none';
|
||||
const secondsToStart =
|
||||
messageData.secondsToStart ||
|
||||
DEFAULT_COUNTDOWN_SECONDS_TO_START;
|
||||
handleGameCountdownStartedEvent({
|
||||
runtimeScene,
|
||||
compressionMethod,
|
||||
secondsToStart,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 'gameStarted': {
|
||||
const messageData = messageContent.data;
|
||||
const heartbeatInterval =
|
||||
currentLobbyHeartbeatInterval =
|
||||
messageData.heartbeatInterval ||
|
||||
DEFAULT_LOBBY_HEARTBEAT_INTERVAL;
|
||||
|
||||
handleGameStartedEvent({ runtimeScene, heartbeatInterval });
|
||||
handleGameStartedEvent({
|
||||
runtimeScene,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 'peerId': {
|
||||
@@ -452,14 +600,14 @@ namespace gdjs {
|
||||
}
|
||||
};
|
||||
_websocket.onclose = () => {
|
||||
logger.info(
|
||||
'Disconnected from the lobby. Either manually or game started.'
|
||||
);
|
||||
if (!_isLobbyGameRunning) {
|
||||
logger.info('Disconnected from the lobby.');
|
||||
}
|
||||
|
||||
_connectionId = null;
|
||||
_websocket = null;
|
||||
if (_websocketHeartbeatInterval) {
|
||||
clearInterval(_websocketHeartbeatInterval);
|
||||
if (_websocketHeartbeatIntervalFunction) {
|
||||
clearInterval(_websocketHeartbeatIntervalFunction);
|
||||
}
|
||||
|
||||
// If the game is running, then all good.
|
||||
@@ -576,6 +724,7 @@ namespace gdjs {
|
||||
}
|
||||
_connectionId = null;
|
||||
playerNumber = null;
|
||||
hostPeerId = null;
|
||||
_lobbyId = null;
|
||||
_websocket = null;
|
||||
};
|
||||
@@ -613,15 +762,14 @@ namespace gdjs {
|
||||
const handleGameCountdownStartedEvent = function ({
|
||||
runtimeScene,
|
||||
compressionMethod,
|
||||
secondsToStart,
|
||||
}: {
|
||||
runtimeScene: gdjs.RuntimeScene;
|
||||
compressionMethod: gdjs.multiplayerPeerJsHelper.CompressionMethod;
|
||||
secondsToStart: number;
|
||||
}) {
|
||||
gdjs.multiplayerPeerJsHelper.setCompressionMethod(compressionMethod);
|
||||
|
||||
// When the countdown starts, if we are player number 1, then send the peerId to others so they can connect via P2P.
|
||||
// When the countdown starts, if we are player number 1, we are chosen as the host.
|
||||
// We then send the peerId to others so they can connect via P2P.
|
||||
if (getCurrentPlayerNumber() === 1) {
|
||||
sendPeerId();
|
||||
}
|
||||
@@ -639,7 +787,6 @@ namespace gdjs {
|
||||
lobbiesIframe.contentWindow.postMessage(
|
||||
{
|
||||
id: 'gameCountdownStarted',
|
||||
secondsToStart,
|
||||
},
|
||||
'*' // We could restrict to GDevelop games platform but it's not necessary as the message is not sensitive, and it allows easy debugging.
|
||||
);
|
||||
@@ -652,43 +799,34 @@ namespace gdjs {
|
||||
|
||||
const sendHeartbeatToBackend = async function () {
|
||||
const gameId = gdjs.projectData.properties.projectUuid;
|
||||
const playerId = gdjs.playerAuthentication.getUserId();
|
||||
const playerToken = gdjs.playerAuthentication.getUserToken();
|
||||
|
||||
if (!gameId || !playerId || !playerToken || !_lobbyId) {
|
||||
if (!gameId || !_lobbyId) {
|
||||
logger.error(
|
||||
'Cannot keep the lobby playing without the game ID or player ID.'
|
||||
'Cannot keep the lobby playing without the game ID or lobby ID.'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const rootApi = isUsingGDevelopDevelopmentEnvironment
|
||||
? 'https://api-dev.gdevelop.io'
|
||||
: 'https://api.gdevelop.io';
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
let heartbeatUrl = `${rootApi}/play/game/${gameId}/public-lobby/${_lobbyId}/action/heartbeat`;
|
||||
headers['Authorization'] = `player-game-token ${playerToken}`;
|
||||
heartbeatUrl += `?playerId=${playerId}`;
|
||||
const heartbeatRelativeUrl = `/play/game/${gameId}/public-lobby/${_lobbyId}/action/heartbeat`;
|
||||
const players = gdjs.multiplayerMessageManager.getConnectedPlayers();
|
||||
try {
|
||||
await fetch(heartbeatUrl, {
|
||||
await fetchAsPlayer({
|
||||
relativeUrl: heartbeatRelativeUrl,
|
||||
method: 'POST',
|
||||
headers,
|
||||
body: JSON.stringify({
|
||||
players,
|
||||
}),
|
||||
dev: isUsingGDevelopDevelopmentEnvironment,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Error while sending heartbeat, retrying:', error);
|
||||
try {
|
||||
await fetch(heartbeatUrl, {
|
||||
await fetchAsPlayer({
|
||||
relativeUrl: heartbeatRelativeUrl,
|
||||
method: 'POST',
|
||||
headers,
|
||||
body: JSON.stringify({
|
||||
players,
|
||||
}),
|
||||
dev: isUsingGDevelopDevelopmentEnvironment,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
@@ -705,16 +843,14 @@ namespace gdjs {
|
||||
*/
|
||||
const handleGameStartedEvent = function ({
|
||||
runtimeScene,
|
||||
heartbeatInterval,
|
||||
}: {
|
||||
runtimeScene: gdjs.RuntimeScene;
|
||||
heartbeatInterval: number;
|
||||
}) {
|
||||
// It is possible the connection to other players didn't work.
|
||||
// If that's the case, show an error message and leave the lobby.
|
||||
// If we are the host, still start the game, as this allows a player to test the game alone.
|
||||
const allConnectedPeers = gdjs.multiplayerPeerJsHelper.getAllPeers();
|
||||
if (!isPlayerHost() && allConnectedPeers.length === 0) {
|
||||
if (!isCurrentPlayerHost() && allConnectedPeers.length === 0) {
|
||||
gdjs.multiplayerComponents.displayConnectionErrorNotification(
|
||||
runtimeScene
|
||||
);
|
||||
@@ -726,10 +862,10 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
// If we are the host, start pinging the backend to let it know the lobby is running.
|
||||
if (isPlayerHost()) {
|
||||
_lobbyHeartbeatInterval = setInterval(async () => {
|
||||
if (isCurrentPlayerHost()) {
|
||||
_lobbyHeartbeatIntervalFunction = setInterval(async () => {
|
||||
await sendHeartbeatToBackend();
|
||||
}, heartbeatInterval);
|
||||
}, currentLobbyHeartbeatInterval);
|
||||
}
|
||||
|
||||
// If we are connected to players, then the game can start.
|
||||
@@ -757,9 +893,11 @@ namespace gdjs {
|
||||
_isLobbyGameRunning = false;
|
||||
_lobbyId = null;
|
||||
playerNumber = null;
|
||||
hostPeerId = null;
|
||||
_isReadyToSendOrReceiveGameUpdateMessages = false;
|
||||
if (_lobbyHeartbeatInterval) {
|
||||
clearInterval(_lobbyHeartbeatInterval);
|
||||
if (_lobbyHeartbeatIntervalFunction) {
|
||||
clearInterval(_lobbyHeartbeatIntervalFunction);
|
||||
_lobbyHeartbeatIntervalFunction = null;
|
||||
}
|
||||
|
||||
// Disconnect from any P2P connections.
|
||||
@@ -795,6 +933,7 @@ namespace gdjs {
|
||||
return;
|
||||
}
|
||||
|
||||
hostPeerId = peerId;
|
||||
gdjs.multiplayerPeerJsHelper.connect(peerId);
|
||||
};
|
||||
|
||||
@@ -878,10 +1017,315 @@ namespace gdjs {
|
||||
action: 'updateConnection',
|
||||
connectionType: 'lobby',
|
||||
status: 'connected',
|
||||
peerId: gdjs.multiplayerPeerJsHelper.getCurrentId(),
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const clearChangeHostRequestData = function (
|
||||
runtimeScene: gdjs.RuntimeScene
|
||||
) {
|
||||
_lobbyChangeHostRequest = null;
|
||||
_lobbyChangeHostRequestInitiatedAt = null;
|
||||
_lobbyNewHostPickedAt = null;
|
||||
if (_resumeTimeout) {
|
||||
clearTimeout(_resumeTimeout);
|
||||
_resumeTimeout = null;
|
||||
}
|
||||
_isChangingHost = false;
|
||||
if (hostPeerId) {
|
||||
gdjs.multiplayerComponents.showHostMigrationFinishedNotification(
|
||||
runtimeScene
|
||||
);
|
||||
} else {
|
||||
gdjs.multiplayerComponents.showHostMigrationFailedNotification(
|
||||
runtimeScene
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const resumeGame = async function (runtimeScene: gdjs.RuntimeScene) {
|
||||
if (isCurrentPlayerHost()) {
|
||||
// Send message to other players to indicate the game is resuming.
|
||||
gdjs.multiplayerMessageManager.sendResumeGameMessage();
|
||||
|
||||
// Start sending heartbeats to the backend.
|
||||
await sendHeartbeatToBackend();
|
||||
_lobbyHeartbeatIntervalFunction = setInterval(async () => {
|
||||
await sendHeartbeatToBackend();
|
||||
}, currentLobbyHeartbeatInterval);
|
||||
}
|
||||
|
||||
// Migration is finished.
|
||||
clearChangeHostRequestData(runtimeScene);
|
||||
};
|
||||
|
||||
/**
|
||||
* When a host is being changed, multiple cases can happen:
|
||||
* - We are the new host and the only one in the lobby. Unpause the game right away.
|
||||
* - We are the new host and there are other players in the new lobby. Wait for them to connect:
|
||||
* - if they are all connected, unpause the game.
|
||||
* - if we reach a timeout, a player may have disconnected at the same time, unpause the game.
|
||||
* - We are not the new host. Connect to the new host peerId.
|
||||
* - If we cannot connect, leave the lobby.
|
||||
* - when we receive a message to unpause the game, unpause it.
|
||||
* - if we reach a timeout without the message, leave the lobby, something wrong happened.
|
||||
*/
|
||||
const checkHostChangeRequestRegularly = async function ({
|
||||
runtimeScene,
|
||||
}: {
|
||||
runtimeScene: gdjs.RuntimeScene;
|
||||
}) {
|
||||
if (!_lobbyChangeHostRequest || !_lobbyChangeHostRequestInitiatedAt) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Refresh the request to get the latest information.
|
||||
try {
|
||||
const changeHostRelativeUrl = `/play/game/${
|
||||
_lobbyChangeHostRequest.gameId
|
||||
}/public-lobby/${
|
||||
_lobbyChangeHostRequest.lobbyId
|
||||
}/lobby-change-host-request?peerId=${gdjs.multiplayerPeerJsHelper.getCurrentId()}`;
|
||||
|
||||
const lobbyChangeHostRequest = await fetchAsPlayer({
|
||||
relativeUrl: changeHostRelativeUrl,
|
||||
method: 'GET',
|
||||
dev: isUsingGDevelopDevelopmentEnvironment,
|
||||
});
|
||||
_lobbyChangeHostRequest = lobbyChangeHostRequest;
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
'Error while trying to retrieve the lobby change host request:',
|
||||
error
|
||||
);
|
||||
handleLobbyGameEnded();
|
||||
clearChangeHostRequestData(runtimeScene);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_lobbyChangeHostRequest) {
|
||||
throw new Error('No lobby change host request received.');
|
||||
}
|
||||
|
||||
const newHostPeerId = _lobbyChangeHostRequest.newHostPeerId;
|
||||
if (!newHostPeerId) {
|
||||
logger.info('No new host picked yet.');
|
||||
if (
|
||||
getTimeNow() - _lobbyChangeHostRequestInitiatedAt >
|
||||
DEFAULT_LOBBY_CHANGE_HOST_REQUEST_TIMEOUT
|
||||
) {
|
||||
logger.error(
|
||||
'Timeout while waiting for the lobby host change. Giving up.'
|
||||
);
|
||||
handleLobbyGameEnded();
|
||||
clearChangeHostRequestData(runtimeScene);
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info('Retrying...');
|
||||
setTimeout(() => {
|
||||
checkHostChangeRequestRegularly({ runtimeScene });
|
||||
}, DEFAULT_LOBBY_CHANGE_HOST_REQUEST_CHECK_INTERVAL);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const newLobbyId = _lobbyChangeHostRequest.newLobbyId;
|
||||
const newPlayers = _lobbyChangeHostRequest.newPlayers;
|
||||
if (!newLobbyId || !newPlayers) {
|
||||
logger.error(
|
||||
'Change host request is incomplete. Cannot change host.'
|
||||
);
|
||||
handleLobbyGameEnded();
|
||||
clearChangeHostRequestData(runtimeScene);
|
||||
return;
|
||||
}
|
||||
hostPeerId = newHostPeerId;
|
||||
_lobbyNewHostPickedAt = getTimeNow();
|
||||
_lobbyId = newLobbyId;
|
||||
|
||||
if (newHostPeerId === gdjs.multiplayerPeerJsHelper.getCurrentId()) {
|
||||
logger.info(
|
||||
`We are the new host. Switching to lobby ${newLobbyId} and awaiting for ${
|
||||
newPlayers.length - 1
|
||||
} player(s) to connect.`
|
||||
);
|
||||
await checkExpectedConnectedPlayersRegularly({
|
||||
runtimeScene,
|
||||
});
|
||||
} else {
|
||||
logger.info(
|
||||
`Connecting to new host and switching lobby to ${newLobbyId}.`
|
||||
);
|
||||
gdjs.multiplayerPeerJsHelper.connect(newHostPeerId);
|
||||
_resumeTimeout = setTimeout(() => {
|
||||
logger.error(
|
||||
'Timeout while waiting for the game to resume. Leaving the lobby.'
|
||||
);
|
||||
handleLobbyGameEnded();
|
||||
clearChangeHostRequestData(runtimeScene);
|
||||
}, DEFAULT_LOBBY_EXPECTED_RESUME_TIMEOUT);
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Error while trying to change host:', error);
|
||||
handleLobbyGameEnded();
|
||||
clearChangeHostRequestData(runtimeScene);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper for the new host, to check if they have all the expected players connected.
|
||||
*/
|
||||
const checkExpectedConnectedPlayersRegularly = async function ({
|
||||
runtimeScene,
|
||||
}: {
|
||||
runtimeScene: gdjs.RuntimeScene;
|
||||
}) {
|
||||
if (!_lobbyChangeHostRequest) {
|
||||
return;
|
||||
}
|
||||
|
||||
const expectedNewPlayers = _lobbyChangeHostRequest.newPlayers;
|
||||
if (!expectedNewPlayers) {
|
||||
logger.error('No expected players in the lobby change host request.');
|
||||
handleLobbyGameEnded();
|
||||
clearChangeHostRequestData(runtimeScene);
|
||||
return;
|
||||
}
|
||||
const expectedNewOtherPlayerNumbers = expectedNewPlayers.map(
|
||||
(player) => player.playerNumber
|
||||
);
|
||||
|
||||
// First look for players who left during the migration.
|
||||
const playerNumbersConnectedBeforeMigration = gdjs.multiplayerMessageManager
|
||||
.getConnectedPlayers()
|
||||
.map((player) => player.playerNumber);
|
||||
const playerNumbersWhoLeftDuringMigration = playerNumbersConnectedBeforeMigration.filter(
|
||||
(playerNumberBeforeMigration) =>
|
||||
!expectedNewOtherPlayerNumbers.includes(playerNumberBeforeMigration)
|
||||
);
|
||||
playerNumbersWhoLeftDuringMigration.map((playerNumberWhoLeft) => {
|
||||
logger.info(
|
||||
`Player ${playerNumberWhoLeft} left during the host migration. Marking as disconnected.`
|
||||
);
|
||||
gdjs.multiplayerMessageManager.markPlayerAsDisconnected({
|
||||
runtimeScene,
|
||||
playerNumber: playerNumberWhoLeft,
|
||||
});
|
||||
});
|
||||
|
||||
// Then check if all expected players are connected.
|
||||
const playerNumbersWhoDidNotConnect = expectedNewOtherPlayerNumbers.filter(
|
||||
(otherPlayerNumber) =>
|
||||
otherPlayerNumber !== playerNumber && // We don't look for ourselves
|
||||
!gdjs.multiplayerMessageManager.hasReceivedHeartbeatFromPlayer(
|
||||
otherPlayerNumber
|
||||
)
|
||||
);
|
||||
|
||||
if (playerNumbersWhoDidNotConnect.length === 0) {
|
||||
logger.info('All expected players are connected. Resuming the game.');
|
||||
await resumeGame(runtimeScene);
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
_lobbyNewHostPickedAt &&
|
||||
getTimeNow() - _lobbyNewHostPickedAt >
|
||||
DEFAULT_LOBBY_EXPECTED_CONNECTED_PLAYERS_TIMEOUT &&
|
||||
playerNumbersWhoDidNotConnect.length > 0
|
||||
) {
|
||||
logger.error(
|
||||
`Timeout while waiting for players ${playerNumbersWhoDidNotConnect.join(
|
||||
', '
|
||||
)} to connect. Assume they disconnected.`
|
||||
);
|
||||
playerNumbersWhoDidNotConnect.map((missingPlayerNumber) => {
|
||||
gdjs.multiplayerMessageManager.markPlayerAsDisconnected({
|
||||
runtimeScene,
|
||||
playerNumber: missingPlayerNumber,
|
||||
});
|
||||
});
|
||||
await resumeGame(runtimeScene);
|
||||
return;
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
checkExpectedConnectedPlayersRegularly({
|
||||
runtimeScene,
|
||||
});
|
||||
}, DEFAULT_LOBBY_EXPECTED_CONNECTED_PLAYERS_CHECK_INTERVAL);
|
||||
};
|
||||
|
||||
/**
|
||||
* When the host disconnects, we inform the backend we lost the connection and we need a new lobby/host.
|
||||
*/
|
||||
export const handleHostDisconnected = async function ({
|
||||
runtimeScene,
|
||||
}: {
|
||||
runtimeScene: gdjs.RuntimeScene;
|
||||
}) {
|
||||
if (!_isLobbyGameRunning) {
|
||||
// This can happen when the game ends. Nothing to do here.
|
||||
return;
|
||||
}
|
||||
|
||||
if (_lobbyChangeHostRequest) {
|
||||
// The new host disconnected while we are already changing host.
|
||||
// Let's end the lobby game to avoid weird situations.
|
||||
handleLobbyGameEnded();
|
||||
clearChangeHostRequestData(runtimeScene);
|
||||
}
|
||||
|
||||
const gameId = gdjs.projectData.properties.projectUuid;
|
||||
|
||||
if (!gameId || !_lobbyId) {
|
||||
logger.error(
|
||||
'Cannot ask for a host change without the game ID or lobby ID.'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
_isChangingHost = true;
|
||||
gdjs.multiplayerComponents.displayHostMigrationNotification(
|
||||
runtimeScene
|
||||
);
|
||||
|
||||
const changeHostRelativeUrl = `/play/game/${gameId}/public-lobby/${_lobbyId}/lobby-change-host-request`;
|
||||
const playersInfo = gdjs.multiplayerMessageManager.getPlayersInfo();
|
||||
const playersInfoForHostChange = Object.keys(playersInfo).map(
|
||||
(playerNumber) => {
|
||||
return {
|
||||
playerNumber: parseInt(playerNumber, 10),
|
||||
playerId: playersInfo[playerNumber].playerId,
|
||||
ping: playersInfo[playerNumber].ping,
|
||||
};
|
||||
}
|
||||
);
|
||||
const body = JSON.stringify({
|
||||
playersInfo: playersInfoForHostChange,
|
||||
peerId: gdjs.multiplayerPeerJsHelper.getCurrentId(),
|
||||
});
|
||||
const lobbyChangeHostRequest = await fetchAsPlayer({
|
||||
relativeUrl: changeHostRelativeUrl,
|
||||
method: 'POST',
|
||||
body,
|
||||
dev: isUsingGDevelopDevelopmentEnvironment,
|
||||
});
|
||||
|
||||
_lobbyChangeHostRequest = lobbyChangeHostRequest;
|
||||
_lobbyChangeHostRequestInitiatedAt = getTimeNow();
|
||||
|
||||
await checkHostChangeRequestRegularly({ runtimeScene });
|
||||
} catch (error) {
|
||||
logger.error('Error while trying to change host:', error);
|
||||
handleLobbyGameEnded();
|
||||
clearChangeHostRequestData(runtimeScene);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Action to end the lobby game.
|
||||
* This will update the lobby status and inform everyone in the lobby that the game has ended.
|
||||
@@ -891,7 +1335,7 @@ namespace gdjs {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isPlayerHost()) {
|
||||
if (!isCurrentPlayerHost()) {
|
||||
logger.error('Only the host can end the game.');
|
||||
return;
|
||||
}
|
||||
@@ -906,31 +1350,18 @@ namespace gdjs {
|
||||
|
||||
// Also call backend to end the game.
|
||||
const gameId = gdjs.projectData.properties.projectUuid;
|
||||
const playerId = gdjs.playerAuthentication.getUserId();
|
||||
const playerToken = gdjs.playerAuthentication.getUserToken();
|
||||
|
||||
if (!gameId || !playerId || !playerToken || !_lobbyId) {
|
||||
logger.error('Cannot end the lobby without the game ID or player ID.');
|
||||
if (!gameId || !_lobbyId) {
|
||||
logger.error('Cannot end the lobby without the game ID or lobby ID.');
|
||||
return;
|
||||
}
|
||||
|
||||
const rootApi = isUsingGDevelopDevelopmentEnvironment
|
||||
? 'https://api-dev.gdevelop.io'
|
||||
: 'https://api.gdevelop.io';
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
let endGameUrl = `${rootApi}/play/game/${gameId}/public-lobby/${_lobbyId}/action/end`;
|
||||
headers['Authorization'] = `player-game-token ${playerToken}`;
|
||||
endGameUrl += `?playerId=${playerId}`;
|
||||
const endGameRelativeUrl = `/play/game/${gameId}/public-lobby/${_lobbyId}/action/end`;
|
||||
try {
|
||||
await fetch(endGameUrl, {
|
||||
await fetchAsPlayer({
|
||||
relativeUrl: endGameRelativeUrl,
|
||||
method: 'POST',
|
||||
headers,
|
||||
body: JSON.stringify({
|
||||
gameId,
|
||||
lobbyId: _lobbyId,
|
||||
}),
|
||||
body: JSON.stringify({}),
|
||||
dev: isUsingGDevelopDevelopmentEnvironment,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Error while ending the game:', error);
|
||||
@@ -966,6 +1397,8 @@ namespace gdjs {
|
||||
peerId,
|
||||
})
|
||||
);
|
||||
// We are the host.
|
||||
hostPeerId = peerId;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@@ -313,9 +313,14 @@ describe('Multiplayer', () => {
|
||||
/**
|
||||
* Helper to fast forward a bit of time in players games, so that heartbeats
|
||||
* are sent and all players are aware of each other.
|
||||
* @param {{ playerNumber: number, peerId: string}[]} players
|
||||
* @param {{ playerNumber: number, peerId: string, isHost?: boolean }[]} players
|
||||
*/
|
||||
const initiateGameWithPlayers = (players) => {
|
||||
// Find the host.
|
||||
const host = players.find((player) => player.isHost);
|
||||
if (!host)
|
||||
throw new Error('No host defined in players, cannot initiate game.');
|
||||
|
||||
// Create the instances of the MultiplayerMessageManager and MultiplayerVariablesManager
|
||||
// for each player.
|
||||
for (const player of players) {
|
||||
@@ -325,6 +330,9 @@ describe('Multiplayer', () => {
|
||||
peerMultiplayerVariablesManager[
|
||||
player.peerId
|
||||
] = gdjs.makeMultiplayerVariablesManager();
|
||||
|
||||
// Define the host for everyone.
|
||||
gdjs.multiplayer.hostPeerId = host.peerId;
|
||||
}
|
||||
|
||||
// Use a scene to simulate the game loop moving forward.
|
||||
@@ -395,12 +403,16 @@ describe('Multiplayer', () => {
|
||||
gdjs.multiplayer.disableMultiplayerForTesting = false;
|
||||
gdjs.multiplayer._isLobbyGameRunning = true;
|
||||
gdjs.multiplayer._isReadyToSendOrReceiveGameUpdateMessages = true;
|
||||
// Sync as fast as possible for tests.
|
||||
gdjs.multiplayer._objectMaxSyncRate = Infinity;
|
||||
});
|
||||
afterEach(() => {
|
||||
gdjs.multiplayerPeerJsHelper = _originalP2pIfAny;
|
||||
gdjs.multiplayer.disableMultiplayerForTesting = true;
|
||||
gdjs.multiplayer._isLobbyGameRunning = false;
|
||||
gdjs.multiplayer._isReadyToSendOrReceiveGameUpdateMessages = false;
|
||||
gdjs.multiplayer._objectMaxSyncRate =
|
||||
gdjs.multiplayer.DEFAULT_OBJECT_MAX_SYNC_RATE;
|
||||
});
|
||||
|
||||
describe('Single scene tests', () => {
|
||||
@@ -411,7 +423,7 @@ describe('Multiplayer', () => {
|
||||
initiateGameWithPlayers,
|
||||
} = createMultiplayerManagersMock();
|
||||
const allConnectedPlayers = [
|
||||
{ playerNumber: 1, peerId: 'player-1' },
|
||||
{ playerNumber: 1, peerId: 'player-1', isHost: true },
|
||||
{ playerNumber: 2, peerId: 'player-2' },
|
||||
];
|
||||
initiateGameWithPlayers(allConnectedPlayers);
|
||||
@@ -607,7 +619,7 @@ describe('Multiplayer', () => {
|
||||
} = createMultiplayerManagersMock();
|
||||
|
||||
const allConnectedPlayers = [
|
||||
{ playerNumber: 1, peerId: 'player-1' },
|
||||
{ playerNumber: 1, peerId: 'player-1', isHost: true },
|
||||
{ playerNumber: 2, peerId: 'player-2' },
|
||||
];
|
||||
initiateGameWithPlayers(allConnectedPlayers);
|
||||
@@ -693,7 +705,7 @@ describe('Multiplayer', () => {
|
||||
} = createMultiplayerManagersMock();
|
||||
|
||||
const allConnectedPlayers = [
|
||||
{ playerNumber: 1, peerId: 'player-1' },
|
||||
{ playerNumber: 1, peerId: 'player-1', isHost: true },
|
||||
{ playerNumber: 2, peerId: 'player-2' },
|
||||
{ playerNumber: 3, peerId: 'player-3' },
|
||||
];
|
||||
@@ -830,7 +842,7 @@ describe('Multiplayer', () => {
|
||||
} = createMultiplayerManagersMock();
|
||||
|
||||
const allConnectedPlayers = [
|
||||
{ playerNumber: 1, peerId: 'player-1' },
|
||||
{ playerNumber: 1, peerId: 'player-1', isHost: true },
|
||||
{ playerNumber: 2, peerId: 'player-2' },
|
||||
{ playerNumber: 3, peerId: 'player-3' },
|
||||
];
|
||||
@@ -1026,7 +1038,7 @@ describe('Multiplayer', () => {
|
||||
} = createMultiplayerManagersMock();
|
||||
|
||||
const allConnectedPlayers = [
|
||||
{ playerNumber: 1, peerId: 'player-1' },
|
||||
{ playerNumber: 1, peerId: 'player-1', isHost: true },
|
||||
{ playerNumber: 2, peerId: 'player-2' },
|
||||
];
|
||||
initiateGameWithPlayers(allConnectedPlayers);
|
||||
@@ -1081,13 +1093,11 @@ describe('Multiplayer', () => {
|
||||
|
||||
const {
|
||||
object: p1SpriteObject,
|
||||
behavior: p1SpriteObjectBehavior,
|
||||
} = getObjectAndMultiplayerBehaviorsFromScene(
|
||||
p1RuntimeScene,
|
||||
'MySpriteObject'
|
||||
)[0];
|
||||
|
||||
p1SpriteObjectBehavior._objectMaxTickRate = Infinity;
|
||||
p1SpriteObject.setX(242);
|
||||
p1SpriteObject.setY(243);
|
||||
p1RuntimeScene.renderAndStep(1000 / 60);
|
||||
@@ -1155,7 +1165,7 @@ describe('Multiplayer', () => {
|
||||
} = createMultiplayerManagersMock();
|
||||
|
||||
const allConnectedPlayers = [
|
||||
{ playerNumber: 1, peerId: 'player-1' },
|
||||
{ playerNumber: 1, peerId: 'player-1', isHost: true },
|
||||
{ playerNumber: 2, peerId: 'player-2' },
|
||||
{ playerNumber: 3, peerId: 'player-3' },
|
||||
];
|
||||
@@ -1237,13 +1247,11 @@ describe('Multiplayer', () => {
|
||||
|
||||
const {
|
||||
object: p2SpriteObject,
|
||||
behavior: p2SpriteObjectBehavior,
|
||||
} = getObjectAndMultiplayerBehaviorsFromScene(
|
||||
p2RuntimeScene,
|
||||
'MySpriteObject'
|
||||
)[0];
|
||||
|
||||
p2SpriteObjectBehavior._objectMaxTickRate = Infinity;
|
||||
p2SpriteObject.setX(242);
|
||||
p2SpriteObject.setY(243);
|
||||
p2RuntimeScene.renderAndStep(1000 / 60);
|
||||
@@ -1358,7 +1366,7 @@ describe('Multiplayer', () => {
|
||||
} = createMultiplayerManagersMock();
|
||||
|
||||
const allConnectedPlayers = [
|
||||
{ playerNumber: 1, peerId: 'player-1' },
|
||||
{ playerNumber: 1, peerId: 'player-1', isHost: true },
|
||||
{ playerNumber: 2, peerId: 'player-2' },
|
||||
{ playerNumber: 3, peerId: 'player-3' },
|
||||
];
|
||||
@@ -1630,7 +1638,7 @@ describe('Multiplayer', () => {
|
||||
} = createMultiplayerManagersMock();
|
||||
|
||||
const allConnectedPlayers = [
|
||||
{ playerNumber: 1, peerId: 'player-1' },
|
||||
{ playerNumber: 1, peerId: 'player-1', isHost: true },
|
||||
{ playerNumber: 2, peerId: 'player-2' },
|
||||
{ playerNumber: 3, peerId: 'player-3' },
|
||||
];
|
||||
@@ -1840,7 +1848,7 @@ describe('Multiplayer', () => {
|
||||
} = createMultiplayerManagersMock();
|
||||
|
||||
const allConnectedPlayers = [
|
||||
{ playerNumber: 1, peerId: 'player-1' },
|
||||
{ playerNumber: 1, peerId: 'player-1', isHost: true },
|
||||
{ playerNumber: 2, peerId: 'player-2' },
|
||||
{ playerNumber: 3, peerId: 'player-3' },
|
||||
];
|
||||
@@ -1899,7 +1907,7 @@ describe('Multiplayer', () => {
|
||||
} = createMultiplayerManagersMock();
|
||||
|
||||
const allConnectedPlayers = [
|
||||
{ playerNumber: 1, peerId: 'player-1' },
|
||||
{ playerNumber: 1, peerId: 'player-1', isHost: true },
|
||||
{ playerNumber: 2, peerId: 'player-2' },
|
||||
];
|
||||
initiateGameWithPlayers(allConnectedPlayers);
|
||||
@@ -1941,7 +1949,7 @@ describe('Multiplayer', () => {
|
||||
} = createMultiplayerManagersMock();
|
||||
|
||||
const allConnectedPlayers = [
|
||||
{ playerNumber: 1, peerId: 'player-1' },
|
||||
{ playerNumber: 1, peerId: 'player-1', isHost: true },
|
||||
{ playerNumber: 2, peerId: 'player-2' },
|
||||
{ playerNumber: 3, peerId: 'player-3' },
|
||||
];
|
||||
@@ -2140,12 +2148,10 @@ describe('Multiplayer', () => {
|
||||
|
||||
const {
|
||||
object: p2SpriteObject,
|
||||
behavior: p2SpriteMultiplayerObjectBehavior,
|
||||
} = getObjectAndMultiplayerBehaviorsFromScene(
|
||||
p2RuntimeScene,
|
||||
'MySpriteObject'
|
||||
)[0];
|
||||
p2SpriteMultiplayerObjectBehavior._objectMaxTickRate = Infinity;
|
||||
p2SpriteObject.setX(242);
|
||||
p2SpriteObject.setY(243);
|
||||
p2RuntimeScene.renderAndStep(1000 / 60);
|
||||
@@ -2202,7 +2208,7 @@ describe('Multiplayer', () => {
|
||||
} = createMultiplayerManagersMock();
|
||||
|
||||
const allConnectedPlayers = [
|
||||
{ playerNumber: 1, peerId: 'player-1' },
|
||||
{ playerNumber: 1, peerId: 'player-1', isHost: true },
|
||||
{ playerNumber: 2, peerId: 'player-2' },
|
||||
];
|
||||
initiateGameWithPlayers(allConnectedPlayers);
|
||||
@@ -2365,7 +2371,7 @@ describe('Multiplayer', () => {
|
||||
} = createMultiplayerManagersMock();
|
||||
|
||||
const allConnectedPlayers = [
|
||||
{ playerNumber: 1, peerId: 'player-1' },
|
||||
{ playerNumber: 1, peerId: 'player-1', isHost: true },
|
||||
{ playerNumber: 2, peerId: 'player-2' },
|
||||
];
|
||||
initiateGameWithPlayers(allConnectedPlayers);
|
||||
@@ -2450,7 +2456,7 @@ describe('Multiplayer', () => {
|
||||
} = createMultiplayerManagersMock();
|
||||
|
||||
const allConnectedPlayers = [
|
||||
{ playerNumber: 1, peerId: 'player-1' },
|
||||
{ playerNumber: 1, peerId: 'player-1', isHost: true },
|
||||
{ playerNumber: 2, peerId: 'player-2' },
|
||||
];
|
||||
initiateGameWithPlayers(allConnectedPlayers);
|
||||
@@ -2582,7 +2588,7 @@ describe('Multiplayer', () => {
|
||||
} = createMultiplayerManagersMock();
|
||||
|
||||
const allConnectedPlayers = [
|
||||
{ playerNumber: 1, peerId: 'player-1' },
|
||||
{ playerNumber: 1, peerId: 'player-1', isHost: true },
|
||||
{ playerNumber: 2, peerId: 'player-2' },
|
||||
];
|
||||
initiateGameWithPlayers(allConnectedPlayers);
|
||||
@@ -2676,7 +2682,7 @@ describe('Multiplayer', () => {
|
||||
} = createMultiplayerManagersMock();
|
||||
|
||||
const allConnectedPlayers = [
|
||||
{ playerNumber: 1, peerId: 'player-1' },
|
||||
{ playerNumber: 1, peerId: 'player-1', isHost: true },
|
||||
{ playerNumber: 2, peerId: 'player-2' },
|
||||
{ playerNumber: 3, peerId: 'player-3' },
|
||||
];
|
||||
@@ -2685,7 +2691,7 @@ describe('Multiplayer', () => {
|
||||
|
||||
// Player 2 leaves.
|
||||
const newConnectedPlayers = [
|
||||
{ playerNumber: 1, peerId: 'player-1' },
|
||||
{ playerNumber: 1, peerId: 'player-1', isHost: true },
|
||||
{ playerNumber: 3, peerId: 'player-3' },
|
||||
];
|
||||
// Host sees the player 2 leaving.
|
||||
@@ -2712,7 +2718,7 @@ describe('Multiplayer', () => {
|
||||
} = createMultiplayerManagersMock();
|
||||
|
||||
const allConnectedPlayers = [
|
||||
{ playerNumber: 1, peerId: 'player-1' },
|
||||
{ playerNumber: 1, peerId: 'player-1', isHost: true },
|
||||
{ playerNumber: 3, peerId: 'player-3' },
|
||||
];
|
||||
initiateGameWithPlayers(allConnectedPlayers);
|
||||
@@ -2723,7 +2729,7 @@ describe('Multiplayer', () => {
|
||||
|
||||
// Player 2 joins.
|
||||
const newConnectedPlayers = [
|
||||
{ playerNumber: 1, peerId: 'player-1' },
|
||||
{ playerNumber: 1, peerId: 'player-1', isHost: true },
|
||||
{ playerNumber: 2, peerId: 'player-2' },
|
||||
{ playerNumber: 3, peerId: 'player-3' },
|
||||
];
|
||||
|
@@ -30,6 +30,102 @@ PanelSpriteObject::PanelSpriteObject()
|
||||
|
||||
PanelSpriteObject::~PanelSpriteObject() {}
|
||||
|
||||
bool PanelSpriteObject::UpdateProperty(const gd::String& propertyName,
|
||||
const gd::String& newValue) {
|
||||
if (propertyName == "texture") {
|
||||
textureName = newValue;
|
||||
return true;
|
||||
}
|
||||
if (propertyName == "width") {
|
||||
SetWidth(newValue.To<double>());
|
||||
return true;
|
||||
}
|
||||
if (propertyName == "height") {
|
||||
SetHeight(newValue.To<double>());
|
||||
return true;
|
||||
}
|
||||
if (propertyName == "leftMargin") {
|
||||
SetLeftMargin(newValue.To<double>());
|
||||
return true;
|
||||
}
|
||||
if (propertyName == "topMargin") {
|
||||
SetTopMargin(newValue.To<double>());
|
||||
return true;
|
||||
}
|
||||
if (propertyName == "rightMargin") {
|
||||
SetRightMargin(newValue.To<double>());
|
||||
return true;
|
||||
}
|
||||
if (propertyName == "bottomMargin") {
|
||||
SetBottomMargin(newValue.To<double>());
|
||||
return true;
|
||||
}
|
||||
if (propertyName == "tiled") {
|
||||
SetTiled(newValue == "1");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::map<gd::String, gd::PropertyDescriptor> PanelSpriteObject::GetProperties() const {
|
||||
std::map<gd::String, gd::PropertyDescriptor> objectProperties;
|
||||
|
||||
objectProperties["texture"]
|
||||
.SetValue(textureName)
|
||||
.SetType("resource")
|
||||
.AddExtraInfo("image")
|
||||
.SetLabel(_("Texture"));
|
||||
|
||||
objectProperties["width"]
|
||||
.SetValue(gd::String::From(width))
|
||||
.SetType("number")
|
||||
.SetLabel(_("Width"))
|
||||
.SetMeasurementUnit(gd::MeasurementUnit::GetPixel())
|
||||
.SetGroup(_("Default size"));
|
||||
|
||||
objectProperties["height"]
|
||||
.SetValue(gd::String::From(height))
|
||||
.SetType("number")
|
||||
.SetLabel(_("Height"))
|
||||
.SetMeasurementUnit(gd::MeasurementUnit::GetPixel())
|
||||
.SetGroup(_("Default size"));
|
||||
|
||||
objectProperties["leftMargin"]
|
||||
.SetValue(gd::String::From(leftMargin))
|
||||
.SetType("number")
|
||||
.SetLabel(_("Left"))
|
||||
.SetMeasurementUnit(gd::MeasurementUnit::GetPixel())
|
||||
.SetGroup(_("Margins"));
|
||||
|
||||
objectProperties["topMargin"]
|
||||
.SetValue(gd::String::From(topMargin))
|
||||
.SetType("number")
|
||||
.SetLabel(_("Top"))
|
||||
.SetMeasurementUnit(gd::MeasurementUnit::GetPixel())
|
||||
.SetGroup(_("Margins"));
|
||||
|
||||
objectProperties["rightMargin"]
|
||||
.SetValue(gd::String::From(rightMargin))
|
||||
.SetType("number")
|
||||
.SetLabel(_("Right"))
|
||||
.SetMeasurementUnit(gd::MeasurementUnit::GetPixel())
|
||||
.SetGroup(_("Margins"));
|
||||
|
||||
objectProperties["bottomMargin"]
|
||||
.SetValue(gd::String::From(bottomMargin))
|
||||
.SetType("number")
|
||||
.SetLabel(_("Bottom"))
|
||||
.SetMeasurementUnit(gd::MeasurementUnit::GetPixel())
|
||||
.SetGroup(_("Margins"));
|
||||
|
||||
objectProperties["tiled"]
|
||||
.SetValue(tiled ? "true" : "false")
|
||||
.SetType("boolean")
|
||||
.SetLabel(_("Repeat borders and center textures (instead of stretching them)"));
|
||||
|
||||
return objectProperties;
|
||||
}
|
||||
void PanelSpriteObject::DoUnserializeFrom(
|
||||
gd::Project& project, const gd::SerializerElement& element) {
|
||||
textureName = element.GetStringAttribute("texture");
|
||||
|
@@ -23,12 +23,18 @@ class GD_EXTENSION_API PanelSpriteObject : public gd::ObjectConfiguration {
|
||||
public:
|
||||
PanelSpriteObject();
|
||||
virtual ~PanelSpriteObject();
|
||||
virtual std::unique_ptr<gd::ObjectConfiguration> Clone() const {
|
||||
virtual std::unique_ptr<gd::ObjectConfiguration> Clone() const override {
|
||||
return std::unique_ptr<gd::ObjectConfiguration>(
|
||||
new PanelSpriteObject(*this));
|
||||
}
|
||||
|
||||
virtual void ExposeResources(gd::ArbitraryResourceWorker &worker);
|
||||
virtual void ExposeResources(gd::ArbitraryResourceWorker &worker) override;
|
||||
|
||||
virtual std::map<gd::String, gd::PropertyDescriptor> GetProperties()
|
||||
const override;
|
||||
|
||||
virtual bool UpdateProperty(const gd::String& name,
|
||||
const gd::String& value) override;
|
||||
|
||||
double GetWidth() const { return width; };
|
||||
double GetHeight() const { return height; };
|
||||
@@ -63,15 +69,15 @@ class GD_EXTENSION_API PanelSpriteObject : public gd::ObjectConfiguration {
|
||||
};
|
||||
const gd::String &GetTexture() const { return textureName; };
|
||||
|
||||
gd::String textureName; ///< deprecated. Use Get/SetTexture instead.
|
||||
|
||||
private:
|
||||
virtual void DoUnserializeFrom(gd::Project &project,
|
||||
const gd::SerializerElement &element);
|
||||
const gd::SerializerElement &element) override;
|
||||
#if defined(GD_IDE_ONLY)
|
||||
virtual void DoSerializeTo(gd::SerializerElement &element) const;
|
||||
virtual void DoSerializeTo(gd::SerializerElement &element) const override;
|
||||
#endif
|
||||
|
||||
gd::String textureName;
|
||||
|
||||
double width;
|
||||
double height;
|
||||
|
||||
|
@@ -32,6 +32,10 @@ namespace gdjs {
|
||||
const StretchedSprite = !tiled ? PIXI.Sprite : PIXI.TilingSprite;
|
||||
this._spritesContainer = new PIXI.Container();
|
||||
this._wrapperContainer = new PIXI.Container();
|
||||
|
||||
// All these textures are going to be replaced in the call to `setTexture`.
|
||||
// But to be safe and preserve the invariant that "these objects own their own
|
||||
// textures", we create a new texture for each sprite.
|
||||
this._centerSprite = new StretchedSprite(
|
||||
new PIXI.Texture(texture.baseTexture)
|
||||
);
|
||||
@@ -39,21 +43,21 @@ namespace gdjs {
|
||||
// Right
|
||||
new StretchedSprite(new PIXI.Texture(texture.baseTexture)),
|
||||
// Top-Right
|
||||
new PIXI.Sprite(texture),
|
||||
new PIXI.Sprite(new PIXI.Texture(texture.baseTexture)),
|
||||
// Top
|
||||
new StretchedSprite(new PIXI.Texture(texture.baseTexture)),
|
||||
// Top-Left
|
||||
new PIXI.Sprite(texture),
|
||||
new PIXI.Sprite(new PIXI.Texture(texture.baseTexture)),
|
||||
// Left
|
||||
new StretchedSprite(new PIXI.Texture(texture.baseTexture)),
|
||||
// Bottom-Left
|
||||
new PIXI.Sprite(texture),
|
||||
new PIXI.Sprite(new PIXI.Texture(texture.baseTexture)),
|
||||
// Bottom
|
||||
new StretchedSprite(new PIXI.Texture(texture.baseTexture)),
|
||||
new PIXI.Sprite(texture),
|
||||
// Bottom-Right
|
||||
new PIXI.Sprite(new PIXI.Texture(texture.baseTexture)),
|
||||
];
|
||||
|
||||
//Bottom-Right
|
||||
this.setTexture(textureName, instanceContainer);
|
||||
this._spritesContainer.removeChildren();
|
||||
this._spritesContainer.addChild(this._centerSprite);
|
||||
@@ -209,7 +213,6 @@ namespace gdjs {
|
||||
this._textureHeight = texture.height;
|
||||
|
||||
function makeInsideTexture(rect) {
|
||||
//TODO
|
||||
if (rect.width < 0) {
|
||||
rect.width = 0;
|
||||
}
|
||||
@@ -236,6 +239,7 @@ namespace gdjs {
|
||||
}
|
||||
return rect;
|
||||
}
|
||||
this._centerSprite.texture.destroy(false);
|
||||
this._centerSprite.texture = new PIXI.Texture(
|
||||
texture,
|
||||
makeInsideTexture(
|
||||
@@ -249,6 +253,7 @@ namespace gdjs {
|
||||
);
|
||||
|
||||
//Top, Bottom, Right, Left borders:
|
||||
this._borderSprites[0].texture.destroy(false);
|
||||
this._borderSprites[0].texture = new PIXI.Texture(
|
||||
texture,
|
||||
makeInsideTexture(
|
||||
@@ -260,6 +265,7 @@ namespace gdjs {
|
||||
)
|
||||
)
|
||||
);
|
||||
this._borderSprites[2].texture.destroy(false);
|
||||
this._borderSprites[2].texture = new PIXI.Texture(
|
||||
texture,
|
||||
makeInsideTexture(
|
||||
@@ -271,6 +277,7 @@ namespace gdjs {
|
||||
)
|
||||
)
|
||||
);
|
||||
this._borderSprites[4].texture.destroy(false);
|
||||
this._borderSprites[4].texture = new PIXI.Texture(
|
||||
texture,
|
||||
makeInsideTexture(
|
||||
@@ -282,6 +289,7 @@ namespace gdjs {
|
||||
)
|
||||
)
|
||||
);
|
||||
this._borderSprites[6].texture.destroy(false);
|
||||
this._borderSprites[6].texture = new PIXI.Texture(
|
||||
texture,
|
||||
makeInsideTexture(
|
||||
@@ -293,6 +301,7 @@ namespace gdjs {
|
||||
)
|
||||
)
|
||||
);
|
||||
this._borderSprites[1].texture.destroy(false);
|
||||
this._borderSprites[1].texture = new PIXI.Texture(
|
||||
texture,
|
||||
makeInsideTexture(
|
||||
@@ -304,10 +313,12 @@ namespace gdjs {
|
||||
)
|
||||
)
|
||||
);
|
||||
this._borderSprites[3].texture.destroy(false);
|
||||
this._borderSprites[3].texture = new PIXI.Texture(
|
||||
texture,
|
||||
makeInsideTexture(new PIXI.Rectangle(0, 0, obj._lBorder, obj._tBorder))
|
||||
);
|
||||
this._borderSprites[5].texture.destroy(false);
|
||||
this._borderSprites[5].texture = new PIXI.Texture(
|
||||
texture,
|
||||
makeInsideTexture(
|
||||
@@ -319,6 +330,7 @@ namespace gdjs {
|
||||
)
|
||||
)
|
||||
);
|
||||
this._borderSprites[7].texture.destroy(false);
|
||||
this._borderSprites[7].texture = new PIXI.Texture(
|
||||
texture,
|
||||
makeInsideTexture(
|
||||
@@ -395,7 +407,8 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
destroy() {
|
||||
// Destroy textures because they are instantiated by this class.
|
||||
// Destroy textures because they are instantiated by this class:
|
||||
// all textures of borderSprites and centerSprite are "owned" by them.
|
||||
for (const borderSprite of this._borderSprites) {
|
||||
borderSprite.destroy({ texture: true });
|
||||
}
|
||||
|
@@ -172,6 +172,9 @@ namespace gdjs {
|
||||
this.setWidth(initialInstanceData.width);
|
||||
this.setHeight(initialInstanceData.height);
|
||||
}
|
||||
if (initialInstanceData.opacity !== undefined) {
|
||||
this.setOpacity(initialInstanceData.opacity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -182,6 +182,7 @@ module.exports = {
|
||||
.setValue(behaviorContent.getChild('bodyType').getStringValue())
|
||||
.setType('Choice')
|
||||
.setLabel('Type')
|
||||
.setQuickCustomizationVisibility(gd.QuickCustomization.Hidden)
|
||||
.addExtraInfo('Static')
|
||||
.addExtraInfo('Dynamic')
|
||||
.addExtraInfo('Kinematic');
|
||||
@@ -190,6 +191,7 @@ module.exports = {
|
||||
.setValue(
|
||||
behaviorContent.getChild('bullet').getBoolValue() ? 'true' : 'false'
|
||||
)
|
||||
.setQuickCustomizationVisibility(gd.QuickCustomization.Hidden)
|
||||
.setType('Boolean')
|
||||
.setLabel('Bullet');
|
||||
behaviorProperties
|
||||
@@ -199,6 +201,7 @@ module.exports = {
|
||||
? 'true'
|
||||
: 'false'
|
||||
)
|
||||
.setQuickCustomizationVisibility(gd.QuickCustomization.Hidden)
|
||||
.setType('Boolean')
|
||||
.setLabel('Fixed Rotation');
|
||||
behaviorProperties
|
||||
@@ -206,6 +209,7 @@ module.exports = {
|
||||
.setValue(
|
||||
behaviorContent.getChild('canSleep').getBoolValue() ? 'true' : 'false'
|
||||
)
|
||||
.setQuickCustomizationVisibility(gd.QuickCustomization.Hidden)
|
||||
.setType('Boolean')
|
||||
.setLabel('Can Sleep');
|
||||
behaviorProperties
|
||||
@@ -213,6 +217,7 @@ module.exports = {
|
||||
.setValue(behaviorContent.getChild('shape').getStringValue())
|
||||
.setType('Choice')
|
||||
.setLabel('Shape')
|
||||
.setQuickCustomizationVisibility(gd.QuickCustomization.Hidden)
|
||||
.addExtraInfo('Box')
|
||||
.addExtraInfo('Circle')
|
||||
.addExtraInfo('Edge')
|
||||
@@ -227,7 +232,8 @@ module.exports = {
|
||||
)
|
||||
.setType('Number')
|
||||
.setMeasurementUnit(gd.MeasurementUnit.getPixel())
|
||||
.setLabel('Shape Dimension A');
|
||||
.setLabel('Shape Dimension A')
|
||||
.setQuickCustomizationVisibility(gd.QuickCustomization.Hidden);
|
||||
behaviorProperties
|
||||
.getOrCreate('shapeDimensionB')
|
||||
.setValue(
|
||||
@@ -238,7 +244,8 @@ module.exports = {
|
||||
)
|
||||
.setType('Number')
|
||||
.setMeasurementUnit(gd.MeasurementUnit.getPixel())
|
||||
.setLabel('Shape Dimension B');
|
||||
.setLabel('Shape Dimension B')
|
||||
.setQuickCustomizationVisibility(gd.QuickCustomization.Hidden);
|
||||
behaviorProperties
|
||||
.getOrCreate('shapeOffsetX')
|
||||
.setValue(
|
||||
@@ -246,7 +253,8 @@ module.exports = {
|
||||
)
|
||||
.setType('Number')
|
||||
.setMeasurementUnit(gd.MeasurementUnit.getPixel())
|
||||
.setLabel('Shape Offset X');
|
||||
.setLabel('Shape Offset X')
|
||||
.setQuickCustomizationVisibility(gd.QuickCustomization.Hidden);
|
||||
behaviorProperties
|
||||
.getOrCreate('shapeOffsetY')
|
||||
.setValue(
|
||||
@@ -254,7 +262,8 @@ module.exports = {
|
||||
)
|
||||
.setType('Number')
|
||||
.setMeasurementUnit(gd.MeasurementUnit.getPixel())
|
||||
.setLabel('Shape Offset Y');
|
||||
.setLabel('Shape Offset Y')
|
||||
.setQuickCustomizationVisibility(gd.QuickCustomization.Hidden);
|
||||
behaviorProperties
|
||||
.getOrCreate('polygonOrigin')
|
||||
.setValue(
|
||||
@@ -266,7 +275,8 @@ module.exports = {
|
||||
.setLabel('Polygon Origin')
|
||||
.addExtraInfo('Center')
|
||||
.addExtraInfo('Origin')
|
||||
.addExtraInfo('TopLeft');
|
||||
.addExtraInfo('TopLeft')
|
||||
.setQuickCustomizationVisibility(gd.QuickCustomization.Hidden);
|
||||
behaviorProperties
|
||||
.getOrCreate('vertices')
|
||||
.setValue(
|
||||
@@ -274,7 +284,8 @@ module.exports = {
|
||||
? gd.Serializer.toJSON(behaviorContent.getChild('vertices'))
|
||||
: '[]'
|
||||
)
|
||||
.setLabel('Vertices');
|
||||
.setLabel('Vertices')
|
||||
.setQuickCustomizationVisibility(gd.QuickCustomization.Hidden);
|
||||
behaviorProperties
|
||||
.getOrCreate('density')
|
||||
.setValue(
|
||||
@@ -315,24 +326,28 @@ module.exports = {
|
||||
.toString(10)
|
||||
)
|
||||
.setType('Number')
|
||||
.setLabel('Angular Damping');
|
||||
.setLabel('Angular Damping')
|
||||
.setQuickCustomizationVisibility(gd.QuickCustomization.Hidden);
|
||||
behaviorProperties
|
||||
.getOrCreate('gravityScale')
|
||||
.setValue(
|
||||
behaviorContent.getChild('gravityScale').getDoubleValue().toString(10)
|
||||
)
|
||||
.setType('Number')
|
||||
.setLabel('Gravity Scale');
|
||||
.setLabel('Gravity Scale')
|
||||
.setQuickCustomizationVisibility(gd.QuickCustomization.Hidden);
|
||||
behaviorProperties
|
||||
.getOrCreate('layers')
|
||||
.setValue(behaviorContent.getChild('layers').getIntValue().toString(10))
|
||||
.setType('Number')
|
||||
.setLabel('Layers');
|
||||
.setLabel('Layers')
|
||||
.setQuickCustomizationVisibility(gd.QuickCustomization.Hidden);
|
||||
behaviorProperties
|
||||
.getOrCreate('masks')
|
||||
.setValue(behaviorContent.getChild('masks').getIntValue().toString(10))
|
||||
.setType('Number')
|
||||
.setLabel('Masks');
|
||||
.setLabel('Masks')
|
||||
.setQuickCustomizationVisibility(gd.QuickCustomization.Hidden);
|
||||
|
||||
return behaviorProperties;
|
||||
};
|
||||
@@ -379,16 +394,15 @@ module.exports = {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (propertyName === 'scaleX') {
|
||||
if (propertyName === 'worldScale') {
|
||||
const newValueAsNumber = parseInt(newValue, 10);
|
||||
if (newValueAsNumber !== newValueAsNumber) return false;
|
||||
if (!sharedContent.hasChild('worldScale')) {
|
||||
sharedContent.addChild('worldScale');
|
||||
}
|
||||
sharedContent.getChild('worldScale').setDoubleValue(newValueAsNumber);
|
||||
// Set deprecated properties for compatibility with 5.4.209-
|
||||
sharedContent.getChild('scaleX').setDoubleValue(newValueAsNumber);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (propertyName === 'scaleY') {
|
||||
const newValueAsNumber = parseInt(newValue, 10);
|
||||
if (newValueAsNumber !== newValueAsNumber) return false;
|
||||
sharedContent.getChild('scaleY').setDoubleValue(newValueAsNumber);
|
||||
return true;
|
||||
}
|
||||
@@ -412,16 +426,22 @@ module.exports = {
|
||||
)
|
||||
.setType('Number')
|
||||
.setMeasurementUnit(gd.MeasurementUnit.getNewton());
|
||||
|
||||
if (!sharedContent.hasChild('worldScale')) {
|
||||
sharedContent.addChild('worldScale');
|
||||
sharedContent
|
||||
.getChild('worldScale')
|
||||
.setDoubleValue(
|
||||
Math.sqrt(
|
||||
sharedContent.getChild('scaleX').getDoubleValue() *
|
||||
sharedContent.getChild('scaleY').getDoubleValue()
|
||||
)
|
||||
);
|
||||
}
|
||||
sharedProperties
|
||||
.getOrCreate('scaleX')
|
||||
.getOrCreate('worldScale')
|
||||
.setValue(
|
||||
sharedContent.getChild('scaleX').getDoubleValue().toString(10)
|
||||
)
|
||||
.setType('Number');
|
||||
sharedProperties
|
||||
.getOrCreate('scaleY')
|
||||
.setValue(
|
||||
sharedContent.getChild('scaleY').getDoubleValue().toString(10)
|
||||
sharedContent.getChild('worldScale').getDoubleValue().toString(10)
|
||||
)
|
||||
.setType('Number');
|
||||
|
||||
@@ -430,6 +450,8 @@ module.exports = {
|
||||
sharedData.initializeContent = function (behaviorContent) {
|
||||
behaviorContent.addChild('gravityX').setDoubleValue(0);
|
||||
behaviorContent.addChild('gravityY').setDoubleValue(9.8);
|
||||
behaviorContent.addChild('worldScale').setDoubleValue(100);
|
||||
// Set deprecated properties for compatibility with 5.4.209-
|
||||
behaviorContent.addChild('scaleX').setDoubleValue(100);
|
||||
behaviorContent.addChild('scaleY').setDoubleValue(100);
|
||||
};
|
||||
@@ -457,6 +479,19 @@ module.exports = {
|
||||
);
|
||||
|
||||
// Global
|
||||
aut
|
||||
.addExpression(
|
||||
'WorldScale',
|
||||
_('World scale'),
|
||||
_('Return the world scale.'),
|
||||
_('Global'),
|
||||
'res/physics32.png'
|
||||
)
|
||||
.addParameter('object', _('Object'), '', false)
|
||||
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('getWorldScale');
|
||||
|
||||
aut
|
||||
.addCondition(
|
||||
'GravityX',
|
||||
@@ -1742,7 +1777,7 @@ module.exports = {
|
||||
)
|
||||
.addParameter('object', _('Object'), '', false)
|
||||
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
|
||||
.addParameter('expression', _('Angular impulse (N·m·s'))
|
||||
.addParameter('expression', _('Angular impulse (N·m·s)'))
|
||||
.setParameterLongDescription(
|
||||
_(
|
||||
'An impulse is like a rotation speed addition but depends on the mass.'
|
||||
@@ -1769,7 +1804,7 @@ module.exports = {
|
||||
'Inertia',
|
||||
_('Inertia'),
|
||||
_(
|
||||
'Return the rotational inertia of the object (in kilograms * meters * meters)'
|
||||
'Return the rotational inertia of the object (in kilograms · meters²)'
|
||||
),
|
||||
'',
|
||||
'res/physics32.png'
|
||||
|
@@ -15,6 +15,8 @@ namespace gdjs {
|
||||
lvy: number | undefined;
|
||||
av: number | undefined;
|
||||
aw: boolean | undefined;
|
||||
layers: number;
|
||||
masks: number;
|
||||
}
|
||||
|
||||
export interface Physics2NetworkSyncData extends BehaviorNetworkSyncData {
|
||||
@@ -23,9 +25,15 @@ namespace gdjs {
|
||||
export class Physics2SharedData {
|
||||
gravityX: float;
|
||||
gravityY: float;
|
||||
worldScale: float;
|
||||
worldInvScale: float;
|
||||
/** @deprecated Use `worldScale` instead */
|
||||
scaleX: float;
|
||||
/** @deprecated Use `worldScale` instead */
|
||||
scaleY: float;
|
||||
/** @deprecated Use `worldInvScale` instead */
|
||||
invScaleX: float;
|
||||
/** @deprecated Use `worldInvScale` instead */
|
||||
invScaleY: float;
|
||||
timeStep: float;
|
||||
frameTime: float = 0;
|
||||
@@ -52,10 +60,13 @@ namespace gdjs {
|
||||
this._registeredBehaviors = new Set();
|
||||
this.gravityX = sharedData.gravityX;
|
||||
this.gravityY = sharedData.gravityY;
|
||||
this.scaleX = sharedData.scaleX === 0 ? 100 : sharedData.scaleX;
|
||||
this.scaleY = sharedData.scaleY === 0 ? 100 : sharedData.scaleY;
|
||||
this.scaleX = sharedData.scaleX || 100;
|
||||
this.scaleY = sharedData.scaleY || 100;
|
||||
this.invScaleX = 1 / this.scaleX;
|
||||
this.invScaleY = 1 / this.scaleY;
|
||||
this.worldScale =
|
||||
sharedData.worldScale || Math.sqrt(this.scaleX * this.scaleY);
|
||||
this.worldInvScale = 1 / this.worldScale;
|
||||
this.timeStep = 1 / 60;
|
||||
this.world = new Box2D.b2World(
|
||||
new Box2D.b2Vec2(this.gravityX, this.gravityY)
|
||||
@@ -513,6 +524,8 @@ namespace gdjs {
|
||||
...super.getNetworkSyncData(),
|
||||
props: {
|
||||
...bodyProps,
|
||||
layers: this.layers,
|
||||
masks: this.masks,
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -556,6 +569,14 @@ namespace gdjs {
|
||||
this._body.SetAwake(behaviorSpecificProps.aw);
|
||||
}
|
||||
}
|
||||
|
||||
if (behaviorSpecificProps.layers !== undefined) {
|
||||
this.layers = behaviorSpecificProps.layers;
|
||||
}
|
||||
|
||||
if (behaviorSpecificProps.masks !== undefined) {
|
||||
this.masks = behaviorSpecificProps.masks;
|
||||
}
|
||||
}
|
||||
|
||||
onDeActivate() {
|
||||
@@ -642,10 +663,10 @@ namespace gdjs {
|
||||
createShape(): Box2D.b2FixtureDef {
|
||||
// Get the scaled offset
|
||||
const offsetX = this.shapeOffsetX
|
||||
? this.shapeOffsetX * this.shapeScale * this._sharedData.invScaleX
|
||||
? this.shapeOffsetX * this.shapeScale * this._sharedData.worldInvScale
|
||||
: 0;
|
||||
const offsetY = this.shapeOffsetY
|
||||
? this.shapeOffsetY * this.shapeScale * this._sharedData.invScaleY
|
||||
? this.shapeOffsetY * this.shapeScale * this._sharedData.worldInvScale
|
||||
: 0;
|
||||
|
||||
// Generate the base shape
|
||||
@@ -657,12 +678,14 @@ namespace gdjs {
|
||||
// Average radius from width and height
|
||||
if (this.shapeDimensionA > 0) {
|
||||
shape.set_m_radius(
|
||||
this.shapeDimensionA * this.shapeScale * this._sharedData.invScaleX
|
||||
this.shapeDimensionA *
|
||||
this.shapeScale *
|
||||
this._sharedData.worldInvScale
|
||||
);
|
||||
} else {
|
||||
const radius =
|
||||
(this.owner.getWidth() * this._sharedData.invScaleX +
|
||||
this.owner.getHeight() * this._sharedData.invScaleY) /
|
||||
(this.owner.getWidth() * this._sharedData.worldInvScale +
|
||||
this.owner.getHeight() * this._sharedData.worldInvScale) /
|
||||
4;
|
||||
shape.set_m_radius(radius > 0 ? radius : 1);
|
||||
}
|
||||
@@ -680,10 +703,10 @@ namespace gdjs {
|
||||
) {
|
||||
let width =
|
||||
(this.owner.getWidth() > 0 ? this.owner.getWidth() : 1) *
|
||||
this._sharedData.invScaleX;
|
||||
this._sharedData.worldInvScale;
|
||||
let height =
|
||||
(this.owner.getHeight() > 0 ? this.owner.getHeight() : 1) *
|
||||
this._sharedData.invScaleY;
|
||||
this._sharedData.worldInvScale;
|
||||
|
||||
// Set the shape box
|
||||
shape.SetAsBox(
|
||||
@@ -728,12 +751,12 @@ namespace gdjs {
|
||||
Box2D.HEAPF32[(this._verticesBuffer + offset) >> 2] =
|
||||
(this.polygon.vertices[i][0] * this.shapeScale +
|
||||
originOffsetX) *
|
||||
this._sharedData.invScaleX +
|
||||
this._sharedData.worldInvScale +
|
||||
offsetX;
|
||||
Box2D.HEAPF32[(this._verticesBuffer + (offset + 4)) >> 2] =
|
||||
(this.polygon.vertices[i][1] * this.shapeScale +
|
||||
originOffsetY) *
|
||||
this._sharedData.invScaleY +
|
||||
this._sharedData.worldInvScale +
|
||||
offsetY;
|
||||
offset += 8;
|
||||
}
|
||||
@@ -755,10 +778,10 @@ namespace gdjs {
|
||||
? this.shapeDimensionA * this.shapeScale
|
||||
: this.owner.getWidth() > 0
|
||||
? this.owner.getWidth()
|
||||
: 1) * this._sharedData.invScaleX;
|
||||
: 1) * this._sharedData.worldInvScale;
|
||||
let height =
|
||||
this.owner.getHeight() > 0
|
||||
? this.owner.getHeight() * this._sharedData.invScaleY
|
||||
? this.owner.getHeight() * this._sharedData.worldInvScale
|
||||
: 0;
|
||||
|
||||
// Angle from custom dimension, otherwise is 0
|
||||
@@ -787,13 +810,13 @@ namespace gdjs {
|
||||
? this.shapeDimensionA * this.shapeScale
|
||||
: this.owner.getWidth() > 0
|
||||
? this.owner.getWidth()
|
||||
: 1) * this._sharedData.invScaleX;
|
||||
: 1) * this._sharedData.worldInvScale;
|
||||
let height =
|
||||
(this.shapeDimensionB > 0
|
||||
? this.shapeDimensionB * this.shapeScale
|
||||
: this.owner.getHeight() > 0
|
||||
? this.owner.getHeight()
|
||||
: 1) * this._sharedData.invScaleY;
|
||||
: 1) * this._sharedData.worldInvScale;
|
||||
|
||||
// Set the shape box, the offset must be added here too
|
||||
shape.SetAsBox(
|
||||
@@ -878,9 +901,9 @@ namespace gdjs {
|
||||
bodyDef.set_position(
|
||||
this.b2Vec2(
|
||||
(this.owner.getDrawableX() + this.owner.getWidth() / 2) *
|
||||
this._sharedData.invScaleX,
|
||||
this._sharedData.worldInvScale,
|
||||
(this.owner.getDrawableY() + this.owner.getHeight() / 2) *
|
||||
this._sharedData.invScaleY
|
||||
this._sharedData.worldInvScale
|
||||
)
|
||||
);
|
||||
bodyDef.set_angle(gdjs.toRad(this.owner.getAngle()));
|
||||
@@ -934,13 +957,13 @@ namespace gdjs {
|
||||
// don't do anything (but still run the physics simulation - this is independent).
|
||||
if (this._body !== null) {
|
||||
this.owner.setX(
|
||||
this._body.GetPosition().get_x() * this._sharedData.scaleX -
|
||||
this._body.GetPosition().get_x() * this._sharedData.worldScale -
|
||||
this.owner.getWidth() / 2 +
|
||||
this.owner.getX() -
|
||||
this.owner.getDrawableX()
|
||||
);
|
||||
this.owner.setY(
|
||||
this._body.GetPosition().get_y() * this._sharedData.scaleY -
|
||||
this._body.GetPosition().get_y() * this._sharedData.worldScale -
|
||||
this.owner.getHeight() / 2 +
|
||||
this.owner.getY() -
|
||||
this.owner.getDrawableY()
|
||||
@@ -993,15 +1016,19 @@ namespace gdjs {
|
||||
) {
|
||||
const pos = this.b2Vec2(
|
||||
(this.owner.getDrawableX() + this.owner.getWidth() / 2) *
|
||||
this._sharedData.invScaleX,
|
||||
this._sharedData.worldInvScale,
|
||||
(this.owner.getDrawableY() + this.owner.getHeight() / 2) *
|
||||
this._sharedData.invScaleY
|
||||
this._sharedData.worldInvScale
|
||||
);
|
||||
body.SetTransform(pos, gdjs.toRad(this.owner.getAngle()));
|
||||
body.SetAwake(true);
|
||||
}
|
||||
}
|
||||
|
||||
getWorldScale(): float {
|
||||
return this._sharedData.worldScale;
|
||||
}
|
||||
|
||||
getGravityX(): float {
|
||||
return this._sharedData.gravityX;
|
||||
}
|
||||
@@ -1435,7 +1462,7 @@ namespace gdjs {
|
||||
const body = this._body!;
|
||||
|
||||
// Get the linear velocity on X
|
||||
return body.GetLinearVelocity().get_x() * this._sharedData.scaleX;
|
||||
return body.GetLinearVelocity().get_x() * this._sharedData.worldScale;
|
||||
}
|
||||
|
||||
setLinearVelocityX(linearVelocityX: float): void {
|
||||
@@ -1448,7 +1475,7 @@ namespace gdjs {
|
||||
// Set the linear velocity on X
|
||||
body.SetLinearVelocity(
|
||||
this.b2Vec2(
|
||||
linearVelocityX * this._sharedData.invScaleX,
|
||||
linearVelocityX * this._sharedData.worldInvScale,
|
||||
body.GetLinearVelocity().get_y()
|
||||
)
|
||||
);
|
||||
@@ -1462,7 +1489,7 @@ namespace gdjs {
|
||||
const body = this._body!;
|
||||
|
||||
// Get the linear velocity on Y
|
||||
return body.GetLinearVelocity().get_y() * this._sharedData.scaleY;
|
||||
return body.GetLinearVelocity().get_y() * this._sharedData.worldScale;
|
||||
}
|
||||
|
||||
setLinearVelocityY(linearVelocityY: float): void {
|
||||
@@ -1476,7 +1503,7 @@ namespace gdjs {
|
||||
body.SetLinearVelocity(
|
||||
this.b2Vec2(
|
||||
body.GetLinearVelocity().get_x(),
|
||||
linearVelocityY * this._sharedData.invScaleY
|
||||
linearVelocityY * this._sharedData.worldInvScale
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -1490,8 +1517,8 @@ namespace gdjs {
|
||||
|
||||
// Get the linear velocity length
|
||||
return this.b2Vec2(
|
||||
body.GetLinearVelocity().get_x() * this._sharedData.scaleX,
|
||||
body.GetLinearVelocity().get_y() * this._sharedData.scaleY
|
||||
body.GetLinearVelocity().get_x() * this._sharedData.worldScale,
|
||||
body.GetLinearVelocity().get_y() * this._sharedData.worldScale
|
||||
).Length();
|
||||
}
|
||||
|
||||
@@ -1505,8 +1532,8 @@ namespace gdjs {
|
||||
// Get the linear velocity angle
|
||||
return gdjs.toDegrees(
|
||||
Math.atan2(
|
||||
body.GetLinearVelocity().get_y() * this._sharedData.scaleY,
|
||||
body.GetLinearVelocity().get_x() * this._sharedData.scaleX
|
||||
body.GetLinearVelocity().get_y() * this._sharedData.worldScale,
|
||||
body.GetLinearVelocity().get_x() * this._sharedData.worldScale
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -1522,8 +1549,8 @@ namespace gdjs {
|
||||
angle = gdjs.toRad(angle);
|
||||
body.SetLinearVelocity(
|
||||
this.b2Vec2(
|
||||
linearVelocity * Math.cos(angle) * this._sharedData.invScaleX,
|
||||
linearVelocity * Math.sin(angle) * this._sharedData.invScaleY
|
||||
linearVelocity * Math.cos(angle) * this._sharedData.worldInvScale,
|
||||
linearVelocity * Math.sin(angle) * this._sharedData.worldInvScale
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -1580,8 +1607,8 @@ namespace gdjs {
|
||||
body.ApplyForce(
|
||||
this.b2Vec2(forceX, forceY),
|
||||
this.b2Vec2Sec(
|
||||
positionX * this._sharedData.invScaleX,
|
||||
positionY * this._sharedData.invScaleY
|
||||
positionX * this._sharedData.worldInvScale,
|
||||
positionY * this._sharedData.worldInvScale
|
||||
),
|
||||
// TODO Should let Box2d awake the object itself.
|
||||
false
|
||||
@@ -1608,8 +1635,8 @@ namespace gdjs {
|
||||
body.ApplyForce(
|
||||
this.b2Vec2(length * Math.cos(angle), length * Math.sin(angle)),
|
||||
this.b2Vec2Sec(
|
||||
positionX * this._sharedData.invScaleX,
|
||||
positionY * this._sharedData.invScaleY
|
||||
positionX * this._sharedData.worldInvScale,
|
||||
positionY * this._sharedData.worldInvScale
|
||||
),
|
||||
// TODO Should let Box2d awake the object itself.
|
||||
false
|
||||
@@ -1632,16 +1659,17 @@ namespace gdjs {
|
||||
// Wake up the object
|
||||
body.SetAwake(true);
|
||||
|
||||
// TODO Optimize this using a unit vector instead of trigonometry.
|
||||
// Apply the force
|
||||
const angle = Math.atan2(
|
||||
towardY * this._sharedData.invScaleY - body.GetPosition().get_y(),
|
||||
towardX * this._sharedData.invScaleX - body.GetPosition().get_x()
|
||||
towardY * this._sharedData.worldInvScale - body.GetPosition().get_y(),
|
||||
towardX * this._sharedData.worldInvScale - body.GetPosition().get_x()
|
||||
);
|
||||
body.ApplyForce(
|
||||
this.b2Vec2(length * Math.cos(angle), length * Math.sin(angle)),
|
||||
this.b2Vec2Sec(
|
||||
positionX * this._sharedData.invScaleX,
|
||||
positionY * this._sharedData.invScaleY
|
||||
positionX * this._sharedData.worldInvScale,
|
||||
positionY * this._sharedData.worldInvScale
|
||||
),
|
||||
// TODO Should let Box2d awake the object itself.
|
||||
false
|
||||
@@ -1667,8 +1695,8 @@ namespace gdjs {
|
||||
body.ApplyLinearImpulse(
|
||||
this.b2Vec2(impulseX, impulseY),
|
||||
this.b2Vec2Sec(
|
||||
positionX * this._sharedData.invScaleX,
|
||||
positionY * this._sharedData.invScaleY
|
||||
positionX * this._sharedData.worldInvScale,
|
||||
positionY * this._sharedData.worldInvScale
|
||||
),
|
||||
// TODO Should let Box2d awake the object itself.
|
||||
false
|
||||
@@ -1695,8 +1723,8 @@ namespace gdjs {
|
||||
body.ApplyLinearImpulse(
|
||||
this.b2Vec2(length * Math.cos(angle), length * Math.sin(angle)),
|
||||
this.b2Vec2Sec(
|
||||
positionX * this._sharedData.invScaleX,
|
||||
positionY * this._sharedData.invScaleY
|
||||
positionX * this._sharedData.worldInvScale,
|
||||
positionY * this._sharedData.worldInvScale
|
||||
),
|
||||
// TODO Should let Box2d awake the object itself.
|
||||
false
|
||||
@@ -1719,16 +1747,17 @@ namespace gdjs {
|
||||
// Wake up the object
|
||||
body.SetAwake(true);
|
||||
|
||||
// TODO Optimize this using a unit vector instead of trigonometry.
|
||||
// Apply the impulse
|
||||
const angle = Math.atan2(
|
||||
towardY * this._sharedData.invScaleY - body.GetPosition().get_y(),
|
||||
towardX * this._sharedData.invScaleX - body.GetPosition().get_x()
|
||||
towardY * this._sharedData.worldInvScale - body.GetPosition().get_y(),
|
||||
towardX * this._sharedData.worldInvScale - body.GetPosition().get_x()
|
||||
);
|
||||
body.ApplyLinearImpulse(
|
||||
this.b2Vec2(length * Math.cos(angle), length * Math.sin(angle)),
|
||||
this.b2Vec2Sec(
|
||||
positionX * this._sharedData.invScaleX,
|
||||
positionY * this._sharedData.invScaleY
|
||||
positionX * this._sharedData.worldInvScale,
|
||||
positionY * this._sharedData.worldInvScale
|
||||
),
|
||||
// TODO Should let Box2d awake the object itself.
|
||||
false
|
||||
@@ -1805,7 +1834,7 @@ namespace gdjs {
|
||||
const body = this._body!;
|
||||
|
||||
// Get the mass center on X
|
||||
return body.GetWorldCenter().get_x() * this._sharedData.scaleX;
|
||||
return body.GetWorldCenter().get_x() * this._sharedData.worldScale;
|
||||
}
|
||||
|
||||
getMassCenterY(): float {
|
||||
@@ -1816,7 +1845,7 @@ namespace gdjs {
|
||||
const body = this._body!;
|
||||
|
||||
// Get the mass center on Y
|
||||
return body.GetWorldCenter().get_y() * this._sharedData.scaleY;
|
||||
return body.GetWorldCenter().get_y() * this._sharedData.worldScale;
|
||||
}
|
||||
|
||||
// Joints
|
||||
@@ -1983,8 +2012,8 @@ namespace gdjs {
|
||||
jointDef.set_localAnchorA(
|
||||
body.GetLocalPoint(
|
||||
this.b2Vec2(
|
||||
x1 * this._sharedData.invScaleX,
|
||||
y1 * this._sharedData.invScaleY
|
||||
x1 * this._sharedData.worldInvScale,
|
||||
y1 * this._sharedData.worldInvScale
|
||||
)
|
||||
)
|
||||
);
|
||||
@@ -1992,17 +2021,17 @@ namespace gdjs {
|
||||
jointDef.set_localAnchorB(
|
||||
otherBody.GetLocalPoint(
|
||||
this.b2Vec2(
|
||||
x2 * this._sharedData.invScaleX,
|
||||
y2 * this._sharedData.invScaleY
|
||||
x2 * this._sharedData.worldInvScale,
|
||||
y2 * this._sharedData.worldInvScale
|
||||
)
|
||||
)
|
||||
);
|
||||
jointDef.set_length(
|
||||
length > 0
|
||||
? length * this._sharedData.invScaleX
|
||||
? length * this._sharedData.worldInvScale
|
||||
: this.b2Vec2(
|
||||
(x2 - x1) * this._sharedData.invScaleX,
|
||||
(y2 - y1) * this._sharedData.invScaleY
|
||||
(x2 - x1) * this._sharedData.worldInvScale,
|
||||
(y2 - y1) * this._sharedData.worldInvScale
|
||||
).Length()
|
||||
);
|
||||
jointDef.set_frequencyHz(frequency >= 0 ? frequency : 0);
|
||||
@@ -2031,7 +2060,7 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
// Get the joint length
|
||||
return joint.GetLength() * this._sharedData.scaleX;
|
||||
return joint.GetLength() * this._sharedData.worldScale;
|
||||
}
|
||||
|
||||
setDistanceJointLength(jointId: integer | string, length: float): void {
|
||||
@@ -2049,7 +2078,7 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
// Set the joint length
|
||||
joint.SetLength(length * this._sharedData.invScaleX);
|
||||
joint.SetLength(length * this._sharedData.worldInvScale);
|
||||
|
||||
// Awake the bodies
|
||||
joint.GetBodyA().SetAwake(true);
|
||||
@@ -2148,8 +2177,8 @@ namespace gdjs {
|
||||
jointDef.set_localAnchorA(
|
||||
this._sharedData.staticBody.GetLocalPoint(
|
||||
this.b2Vec2(
|
||||
x * this._sharedData.invScaleX,
|
||||
y * this._sharedData.invScaleY
|
||||
x * this._sharedData.worldInvScale,
|
||||
y * this._sharedData.worldInvScale
|
||||
)
|
||||
)
|
||||
);
|
||||
@@ -2157,8 +2186,8 @@ namespace gdjs {
|
||||
jointDef.set_localAnchorB(
|
||||
body.GetLocalPoint(
|
||||
this.b2Vec2(
|
||||
x * this._sharedData.invScaleX,
|
||||
y * this._sharedData.invScaleY
|
||||
x * this._sharedData.worldInvScale,
|
||||
y * this._sharedData.worldInvScale
|
||||
)
|
||||
)
|
||||
);
|
||||
@@ -2233,8 +2262,8 @@ namespace gdjs {
|
||||
jointDef.set_localAnchorA(
|
||||
body.GetLocalPoint(
|
||||
this.b2Vec2(
|
||||
x1 * this._sharedData.invScaleX,
|
||||
y1 * this._sharedData.invScaleY
|
||||
x1 * this._sharedData.worldInvScale,
|
||||
y1 * this._sharedData.worldInvScale
|
||||
)
|
||||
)
|
||||
);
|
||||
@@ -2242,8 +2271,8 @@ namespace gdjs {
|
||||
jointDef.set_localAnchorB(
|
||||
otherBody.GetLocalPoint(
|
||||
this.b2Vec2(
|
||||
x2 * this._sharedData.invScaleX,
|
||||
y2 * this._sharedData.invScaleY
|
||||
x2 * this._sharedData.worldInvScale,
|
||||
y2 * this._sharedData.worldInvScale
|
||||
)
|
||||
)
|
||||
);
|
||||
@@ -2534,8 +2563,8 @@ namespace gdjs {
|
||||
jointDef.set_localAnchorA(
|
||||
body.GetLocalPoint(
|
||||
this.b2Vec2(
|
||||
x1 * this._sharedData.invScaleX,
|
||||
y1 * this._sharedData.invScaleY
|
||||
x1 * this._sharedData.worldInvScale,
|
||||
y1 * this._sharedData.worldInvScale
|
||||
)
|
||||
)
|
||||
);
|
||||
@@ -2543,8 +2572,8 @@ namespace gdjs {
|
||||
jointDef.set_localAnchorB(
|
||||
otherBody.GetLocalPoint(
|
||||
this.b2Vec2(
|
||||
x2 * this._sharedData.invScaleX,
|
||||
y2 * this._sharedData.invScaleY
|
||||
x2 * this._sharedData.worldInvScale,
|
||||
y2 * this._sharedData.worldInvScale
|
||||
)
|
||||
)
|
||||
);
|
||||
@@ -2564,13 +2593,17 @@ namespace gdjs {
|
||||
|
||||
// The translation range must include zero
|
||||
jointDef.set_lowerTranslation(
|
||||
lowerTranslation < 0 ? lowerTranslation * this._sharedData.invScaleX : 0
|
||||
lowerTranslation < 0
|
||||
? lowerTranslation * this._sharedData.worldInvScale
|
||||
: 0
|
||||
);
|
||||
jointDef.set_upperTranslation(
|
||||
upperTranslation > 0 ? upperTranslation * this._sharedData.invScaleX : 0
|
||||
upperTranslation > 0
|
||||
? upperTranslation * this._sharedData.worldInvScale
|
||||
: 0
|
||||
);
|
||||
jointDef.set_enableMotor(enableMotor);
|
||||
jointDef.set_motorSpeed(motorSpeed * this._sharedData.invScaleX);
|
||||
jointDef.set_motorSpeed(motorSpeed * this._sharedData.worldInvScale);
|
||||
jointDef.set_maxMotorForce(maxMotorForce);
|
||||
jointDef.set_collideConnected(collideConnected);
|
||||
|
||||
@@ -2633,7 +2666,7 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
// Get the joint current translation
|
||||
return joint.GetJointTranslation() * this._sharedData.scaleX;
|
||||
return joint.GetJointTranslation() * this._sharedData.worldScale;
|
||||
}
|
||||
|
||||
getPrismaticJointSpeed(jointId: integer | string): float {
|
||||
@@ -2648,7 +2681,7 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
// Get the joint speed
|
||||
return joint.GetJointSpeed() * this._sharedData.scaleX;
|
||||
return joint.GetJointSpeed() * this._sharedData.worldScale;
|
||||
}
|
||||
|
||||
isPrismaticJointLimitsEnabled(jointId: integer | string): boolean {
|
||||
@@ -2696,7 +2729,7 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
// Get the joint lower limit
|
||||
return joint.GetLowerLimit() * this._sharedData.scaleX;
|
||||
return joint.GetLowerLimit() * this._sharedData.worldScale;
|
||||
}
|
||||
|
||||
getPrismaticJointMaxTranslation(jointId: integer | string): float {
|
||||
@@ -2711,7 +2744,7 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
// Get the joint upper angle
|
||||
return joint.GetUpperLimit() * this._sharedData.scaleX;
|
||||
return joint.GetUpperLimit() * this._sharedData.worldScale;
|
||||
}
|
||||
|
||||
setPrismaticJointLimits(
|
||||
@@ -2742,8 +2775,8 @@ namespace gdjs {
|
||||
|
||||
// Set the joint limits
|
||||
joint.SetLimits(
|
||||
lowerTranslation * this._sharedData.invScaleX,
|
||||
upperTranslation * this._sharedData.invScaleX
|
||||
lowerTranslation * this._sharedData.worldInvScale,
|
||||
upperTranslation * this._sharedData.worldInvScale
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2792,7 +2825,7 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
// Get the joint motor speed
|
||||
return joint.GetMotorSpeed() * this._sharedData.scaleX;
|
||||
return joint.GetMotorSpeed() * this._sharedData.worldScale;
|
||||
}
|
||||
|
||||
setPrismaticJointMotorSpeed(jointId: integer | string, speed): void {
|
||||
@@ -2807,7 +2840,7 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
// Set the joint motor speed
|
||||
joint.SetMotorSpeed(speed * this._sharedData.invScaleX);
|
||||
joint.SetMotorSpeed(speed * this._sharedData.worldInvScale);
|
||||
}
|
||||
|
||||
getPrismaticJointMaxMotorForce(jointId: integer | string): float {
|
||||
@@ -2904,8 +2937,8 @@ namespace gdjs {
|
||||
jointDef.set_localAnchorA(
|
||||
body.GetLocalPoint(
|
||||
this.b2Vec2(
|
||||
x1 * this._sharedData.invScaleX,
|
||||
y1 * this._sharedData.invScaleY
|
||||
x1 * this._sharedData.worldInvScale,
|
||||
y1 * this._sharedData.worldInvScale
|
||||
)
|
||||
)
|
||||
);
|
||||
@@ -2913,37 +2946,37 @@ namespace gdjs {
|
||||
jointDef.set_localAnchorB(
|
||||
otherBody.GetLocalPoint(
|
||||
this.b2Vec2(
|
||||
x2 * this._sharedData.invScaleX,
|
||||
y2 * this._sharedData.invScaleY
|
||||
x2 * this._sharedData.worldInvScale,
|
||||
y2 * this._sharedData.worldInvScale
|
||||
)
|
||||
)
|
||||
);
|
||||
jointDef.set_groundAnchorA(
|
||||
this.b2Vec2(
|
||||
groundX1 * this._sharedData.invScaleX,
|
||||
groundY1 * this._sharedData.invScaleY
|
||||
groundX1 * this._sharedData.worldInvScale,
|
||||
groundY1 * this._sharedData.worldInvScale
|
||||
)
|
||||
);
|
||||
jointDef.set_groundAnchorB(
|
||||
this.b2Vec2(
|
||||
groundX2 * this._sharedData.invScaleX,
|
||||
groundY2 * this._sharedData.invScaleY
|
||||
groundX2 * this._sharedData.worldInvScale,
|
||||
groundY2 * this._sharedData.worldInvScale
|
||||
)
|
||||
);
|
||||
jointDef.set_lengthA(
|
||||
lengthA > 0
|
||||
? lengthA * this._sharedData.invScaleX
|
||||
? lengthA * this._sharedData.worldInvScale
|
||||
: this.b2Vec2(
|
||||
(groundX1 - x1) * this._sharedData.invScaleX,
|
||||
(groundY1 - y1) * this._sharedData.invScaleY
|
||||
(groundX1 - x1) * this._sharedData.worldInvScale,
|
||||
(groundY1 - y1) * this._sharedData.worldInvScale
|
||||
).Length()
|
||||
);
|
||||
jointDef.set_lengthB(
|
||||
lengthB > 0
|
||||
? lengthB * this._sharedData.invScaleX
|
||||
? lengthB * this._sharedData.worldInvScale
|
||||
: this.b2Vec2(
|
||||
(groundX2 - x2) * this._sharedData.invScaleX,
|
||||
(groundY2 - y2) * this._sharedData.invScaleY
|
||||
(groundX2 - x2) * this._sharedData.worldInvScale,
|
||||
(groundY2 - y2) * this._sharedData.worldInvScale
|
||||
).Length()
|
||||
);
|
||||
jointDef.set_ratio(ratio > 0 ? ratio : 1);
|
||||
@@ -2971,7 +3004,7 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
// Get the joint ground anchor
|
||||
return joint.GetGroundAnchorA().get_x() * this._sharedData.scaleX;
|
||||
return joint.GetGroundAnchorA().get_x() * this._sharedData.worldScale;
|
||||
}
|
||||
|
||||
getPulleyJointFirstGroundAnchorY(jointId: integer | string): float {
|
||||
@@ -2984,7 +3017,7 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
// Get the joint ground anchor
|
||||
return joint.GetGroundAnchorA().get_y() * this._sharedData.scaleY;
|
||||
return joint.GetGroundAnchorA().get_y() * this._sharedData.worldScale;
|
||||
}
|
||||
|
||||
getPulleyJointSecondGroundAnchorX(jointId: integer | string): float {
|
||||
@@ -2997,7 +3030,7 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
// Get the joint ground anchor
|
||||
return joint.GetGroundAnchorB().get_x() * this._sharedData.scaleX;
|
||||
return joint.GetGroundAnchorB().get_x() * this._sharedData.worldScale;
|
||||
}
|
||||
|
||||
getPulleyJointSecondGroundAnchorY(jointId: integer | string): float {
|
||||
@@ -3010,7 +3043,7 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
// Get the joint ground anchor
|
||||
return joint.GetGroundAnchorB().get_y() * this._sharedData.scaleY;
|
||||
return joint.GetGroundAnchorB().get_y() * this._sharedData.worldScale;
|
||||
}
|
||||
|
||||
getPulleyJointFirstLength(jointId: integer | string): float {
|
||||
@@ -3023,7 +3056,7 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
// Get the joint length
|
||||
return joint.GetCurrentLengthA() * this._sharedData.scaleX;
|
||||
return joint.GetCurrentLengthA() * this._sharedData.worldScale;
|
||||
}
|
||||
|
||||
getPulleyJointSecondLength(jointId: integer | string): float {
|
||||
@@ -3036,7 +3069,7 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
// Get the joint length
|
||||
return joint.GetCurrentLengthB() * this._sharedData.scaleX;
|
||||
return joint.GetCurrentLengthB() * this._sharedData.worldScale;
|
||||
}
|
||||
|
||||
getPulleyJointRatio(jointId: integer | string): float {
|
||||
@@ -3188,8 +3221,8 @@ namespace gdjs {
|
||||
jointDef.set_bodyB(body);
|
||||
jointDef.set_target(
|
||||
this.b2Vec2(
|
||||
targetX * this._sharedData.invScaleX,
|
||||
targetY * this._sharedData.invScaleY
|
||||
targetX * this._sharedData.worldInvScale,
|
||||
targetY * this._sharedData.worldInvScale
|
||||
)
|
||||
);
|
||||
jointDef.set_maxForce(maxForce >= 0 ? maxForce : 0);
|
||||
@@ -3217,7 +3250,7 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
// Get the joint target X
|
||||
return joint.GetTarget().get_x() * this._sharedData.scaleX;
|
||||
return joint.GetTarget().get_x() * this._sharedData.worldScale;
|
||||
}
|
||||
|
||||
getMouseJointTargetY(jointId: integer | string): float {
|
||||
@@ -3229,7 +3262,7 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
// Get the joint target Y
|
||||
return joint.GetTarget().get_y() * this._sharedData.scaleY;
|
||||
return joint.GetTarget().get_y() * this._sharedData.worldScale;
|
||||
}
|
||||
|
||||
setMouseJointTarget(
|
||||
@@ -3247,8 +3280,8 @@ namespace gdjs {
|
||||
// Set the joint target
|
||||
joint.SetTarget(
|
||||
this.b2Vec2(
|
||||
targetX * this._sharedData.invScaleX,
|
||||
targetY * this._sharedData.invScaleY
|
||||
targetX * this._sharedData.worldInvScale,
|
||||
targetY * this._sharedData.worldInvScale
|
||||
)
|
||||
);
|
||||
|
||||
@@ -3386,8 +3419,8 @@ namespace gdjs {
|
||||
jointDef.set_localAnchorA(
|
||||
body.GetLocalPoint(
|
||||
this.b2Vec2(
|
||||
x1 * this._sharedData.invScaleX,
|
||||
y1 * this._sharedData.invScaleY
|
||||
x1 * this._sharedData.worldInvScale,
|
||||
y1 * this._sharedData.worldInvScale
|
||||
)
|
||||
)
|
||||
);
|
||||
@@ -3395,8 +3428,8 @@ namespace gdjs {
|
||||
jointDef.set_localAnchorB(
|
||||
otherBody.GetLocalPoint(
|
||||
this.b2Vec2(
|
||||
x2 * this._sharedData.invScaleX,
|
||||
y2 * this._sharedData.invScaleY
|
||||
x2 * this._sharedData.worldInvScale,
|
||||
y2 * this._sharedData.worldInvScale
|
||||
)
|
||||
)
|
||||
);
|
||||
@@ -3451,7 +3484,7 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
// Get the joint current translation
|
||||
return joint.GetJointTranslation() * this._sharedData.scaleX;
|
||||
return joint.GetJointTranslation() * this._sharedData.worldScale;
|
||||
}
|
||||
|
||||
getWheelJointSpeed(jointId: integer | string): float {
|
||||
@@ -3668,8 +3701,8 @@ namespace gdjs {
|
||||
jointDef.set_localAnchorA(
|
||||
body.GetLocalPoint(
|
||||
this.b2Vec2(
|
||||
x1 * this._sharedData.invScaleX,
|
||||
y1 * this._sharedData.invScaleY
|
||||
x1 * this._sharedData.worldInvScale,
|
||||
y1 * this._sharedData.worldInvScale
|
||||
)
|
||||
)
|
||||
);
|
||||
@@ -3677,8 +3710,8 @@ namespace gdjs {
|
||||
jointDef.set_localAnchorB(
|
||||
otherBody.GetLocalPoint(
|
||||
this.b2Vec2(
|
||||
x2 * this._sharedData.invScaleX,
|
||||
y2 * this._sharedData.invScaleY
|
||||
x2 * this._sharedData.worldInvScale,
|
||||
y2 * this._sharedData.worldInvScale
|
||||
)
|
||||
)
|
||||
);
|
||||
@@ -3817,8 +3850,8 @@ namespace gdjs {
|
||||
jointDef.set_localAnchorA(
|
||||
body.GetLocalPoint(
|
||||
this.b2Vec2(
|
||||
x1 * this._sharedData.invScaleX,
|
||||
y1 * this._sharedData.invScaleY
|
||||
x1 * this._sharedData.worldInvScale,
|
||||
y1 * this._sharedData.worldInvScale
|
||||
)
|
||||
)
|
||||
);
|
||||
@@ -3826,17 +3859,17 @@ namespace gdjs {
|
||||
jointDef.set_localAnchorB(
|
||||
otherBody.GetLocalPoint(
|
||||
this.b2Vec2(
|
||||
x2 * this._sharedData.invScaleX,
|
||||
y2 * this._sharedData.invScaleY
|
||||
x2 * this._sharedData.worldInvScale,
|
||||
y2 * this._sharedData.worldInvScale
|
||||
)
|
||||
)
|
||||
);
|
||||
jointDef.set_maxLength(
|
||||
maxLength > 0
|
||||
? maxLength * this._sharedData.invScaleX
|
||||
? maxLength * this._sharedData.worldInvScale
|
||||
: this.b2Vec2(
|
||||
(x2 - x1) * this._sharedData.invScaleX,
|
||||
(y2 - y1) * this._sharedData.invScaleY
|
||||
(x2 - x1) * this._sharedData.worldInvScale,
|
||||
(y2 - y1) * this._sharedData.worldInvScale
|
||||
).Length()
|
||||
);
|
||||
jointDef.set_collideConnected(collideConnected);
|
||||
@@ -3863,7 +3896,7 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
// Get the joint maximum length
|
||||
return joint.GetMaxLength() * this._sharedData.scaleX;
|
||||
return joint.GetMaxLength() * this._sharedData.worldScale;
|
||||
}
|
||||
|
||||
setRopeJointMaxLength(jointId: integer | string, maxLength: float): void {
|
||||
@@ -3881,7 +3914,7 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
// Set the joint maximum length
|
||||
joint.SetMaxLength(maxLength * this._sharedData.invScaleX);
|
||||
joint.SetMaxLength(maxLength * this._sharedData.worldInvScale);
|
||||
|
||||
// Awake the bodies
|
||||
joint.GetBodyA().SetAwake(true);
|
||||
@@ -3927,8 +3960,8 @@ namespace gdjs {
|
||||
jointDef.set_localAnchorA(
|
||||
body.GetLocalPoint(
|
||||
this.b2Vec2(
|
||||
x1 * this._sharedData.invScaleX,
|
||||
y1 * this._sharedData.invScaleY
|
||||
x1 * this._sharedData.worldInvScale,
|
||||
y1 * this._sharedData.worldInvScale
|
||||
)
|
||||
)
|
||||
);
|
||||
@@ -3936,8 +3969,8 @@ namespace gdjs {
|
||||
jointDef.set_localAnchorB(
|
||||
otherBody.GetLocalPoint(
|
||||
this.b2Vec2(
|
||||
x2 * this._sharedData.invScaleX,
|
||||
y2 * this._sharedData.invScaleY
|
||||
x2 * this._sharedData.worldInvScale,
|
||||
y2 * this._sharedData.worldInvScale
|
||||
)
|
||||
)
|
||||
);
|
||||
@@ -4061,8 +4094,8 @@ namespace gdjs {
|
||||
jointDef.set_bodyB(otherBody);
|
||||
jointDef.set_linearOffset(
|
||||
this.b2Vec2(
|
||||
offsetX * this._sharedData.invScaleX,
|
||||
offsetY * this._sharedData.invScaleY
|
||||
offsetX * this._sharedData.worldInvScale,
|
||||
offsetY * this._sharedData.worldInvScale
|
||||
)
|
||||
);
|
||||
jointDef.set_angularOffset(gdjs.toRad(offsetAngle));
|
||||
@@ -4095,7 +4128,7 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
// Get the joint offset
|
||||
return joint.GetLinearOffset().get_x() * this._sharedData.scaleX;
|
||||
return joint.GetLinearOffset().get_x() * this._sharedData.worldScale;
|
||||
}
|
||||
|
||||
getMotorJointOffsetY(jointId: integer | string): float {
|
||||
@@ -4108,7 +4141,7 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
// Get the joint offset
|
||||
return joint.GetLinearOffset().get_y() * this._sharedData.scaleY;
|
||||
return joint.GetLinearOffset().get_y() * this._sharedData.worldScale;
|
||||
}
|
||||
|
||||
setMotorJointOffset(
|
||||
@@ -4127,8 +4160,8 @@ namespace gdjs {
|
||||
// Set the joint offset
|
||||
joint.SetLinearOffset(
|
||||
this.b2Vec2(
|
||||
offsetX * this._sharedData.invScaleX,
|
||||
offsetY * this._sharedData.invScaleY
|
||||
offsetX * this._sharedData.worldInvScale,
|
||||
offsetY * this._sharedData.worldInvScale
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@@ -843,7 +843,8 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
"CppPlatform/Extensions/platformicon.png",
|
||||
"PlatformBehavior",
|
||||
std::make_shared<PlatformBehavior>(),
|
||||
std::make_shared<gd::BehaviorsSharedData>());
|
||||
std::make_shared<gd::BehaviorsSharedData>())
|
||||
.SetQuickCustomizationVisibility(gd::QuickCustomization::Hidden);
|
||||
|
||||
aut.AddAction("ChangePlatformType",
|
||||
_("Platform type"),
|
||||
|
@@ -61,6 +61,7 @@ PlatformerObjectBehavior::GetProperties(
|
||||
gd::String::From(behaviorContent.GetDoubleAttribute("jumpSpeed")));
|
||||
properties["JumpSustainTime"]
|
||||
.SetLabel(_("Jump sustain time"))
|
||||
.SetQuickCustomizationVisibility(gd::QuickCustomization::Hidden)
|
||||
.SetGroup(_("Jump"))
|
||||
.SetType("Number")
|
||||
.SetMeasurementUnit(gd::MeasurementUnit::GetSecond())
|
||||
@@ -79,6 +80,7 @@ PlatformerObjectBehavior::GetProperties(
|
||||
behaviorContent.GetDoubleAttribute("maxFallingSpeed")));
|
||||
properties["LadderClimbingSpeed"]
|
||||
.SetLabel(_("Ladder climbing speed"))
|
||||
.SetQuickCustomizationVisibility(gd::QuickCustomization::Hidden)
|
||||
.SetGroup(_("Ladder"))
|
||||
.SetType("Number")
|
||||
.SetMeasurementUnit(gd::MeasurementUnit::GetPixelSpeed())
|
||||
@@ -107,12 +109,14 @@ PlatformerObjectBehavior::GetProperties(
|
||||
gd::String::From(behaviorContent.GetDoubleAttribute("maxSpeed")));
|
||||
properties["IgnoreDefaultControls"]
|
||||
.SetLabel(_("Default controls"))
|
||||
.SetQuickCustomizationVisibility(gd::QuickCustomization::Hidden)
|
||||
.SetValue(behaviorContent.GetBoolAttribute("ignoreDefaultControls")
|
||||
? "false"
|
||||
: "true")
|
||||
.SetType("Boolean");
|
||||
properties["SlopeMaxAngle"]
|
||||
.SetLabel(_("Slope max. angle"))
|
||||
.SetQuickCustomizationVisibility(gd::QuickCustomization::Hidden)
|
||||
.SetGroup(_("Walk"))
|
||||
.SetType("Number")
|
||||
.SetMeasurementUnit(gd::MeasurementUnit::GetDegreeAngle())
|
||||
@@ -120,6 +124,7 @@ PlatformerObjectBehavior::GetProperties(
|
||||
behaviorContent.GetDoubleAttribute("slopeMaxAngle")));
|
||||
properties["CanGrabPlatforms"]
|
||||
.SetLabel(_("Can grab platform ledges"))
|
||||
.SetQuickCustomizationVisibility(gd::QuickCustomization::Hidden)
|
||||
.SetGroup(_("Ledge"))
|
||||
.SetValue(behaviorContent.GetBoolAttribute("canGrabPlatforms", false)
|
||||
? "true"
|
||||
@@ -128,6 +133,7 @@ PlatformerObjectBehavior::GetProperties(
|
||||
properties["CanGrabWithoutMoving"]
|
||||
.SetLabel(_("Automatically grab platform ledges without having to move "
|
||||
"horizontally"))
|
||||
.SetQuickCustomizationVisibility(gd::QuickCustomization::Hidden)
|
||||
.SetGroup(_("Ledge"))
|
||||
.SetValue(behaviorContent.GetBoolAttribute("canGrabWithoutMoving", false)
|
||||
? "true"
|
||||
@@ -135,6 +141,7 @@ PlatformerObjectBehavior::GetProperties(
|
||||
.SetType("Boolean");
|
||||
properties["YGrabOffset"]
|
||||
.SetLabel(_("Grab offset on Y axis"))
|
||||
.SetQuickCustomizationVisibility(gd::QuickCustomization::Hidden)
|
||||
.SetGroup(_("Ledge"))
|
||||
.SetType("Number")
|
||||
.SetMeasurementUnit(gd::MeasurementUnit::GetPixel())
|
||||
@@ -142,6 +149,7 @@ PlatformerObjectBehavior::GetProperties(
|
||||
gd::String::From(behaviorContent.GetDoubleAttribute("yGrabOffset")));
|
||||
properties["XGrabTolerance"]
|
||||
.SetLabel(_("Grab tolerance on X axis"))
|
||||
.SetQuickCustomizationVisibility(gd::QuickCustomization::Hidden)
|
||||
.SetGroup(_("Ledge"))
|
||||
.SetType("Number")
|
||||
.SetMeasurementUnit(gd::MeasurementUnit::GetPixel())
|
||||
@@ -158,6 +166,7 @@ PlatformerObjectBehavior::GetProperties(
|
||||
.SetType("Boolean");
|
||||
properties["CanGoDownFromJumpthru"]
|
||||
.SetLabel(_("Can go down from jumpthru platforms"))
|
||||
.SetQuickCustomizationVisibility(gd::QuickCustomization::Hidden)
|
||||
.SetGroup(_("Walk"))
|
||||
.SetValue(behaviorContent.GetBoolAttribute("canGoDownFromJumpthru", false)
|
||||
? "true"
|
||||
|
@@ -166,6 +166,19 @@ namespace gdjs {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the extra parameters that could be set for an instance.
|
||||
* @param initialInstanceData The extra parameters
|
||||
*/
|
||||
extraInitializationFromInitialInstance(initialInstanceData: InstanceData) {
|
||||
if (initialInstanceData.flippedX) {
|
||||
this.flipX(initialInstanceData.flippedX);
|
||||
}
|
||||
if (initialInstanceData.flippedY) {
|
||||
this.flipY(initialInstanceData.flippedY);
|
||||
}
|
||||
}
|
||||
|
||||
stepBehaviorsPreEvents(instanceContainer: gdjs.RuntimeInstanceContainer) {
|
||||
//We redefine stepBehaviorsPreEvents just to clear the graphics before running events.
|
||||
if (this._clearBetweenFrames) {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user