mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
Compare commits
145 Commits
cursor/ext
...
add-setbod
Author | SHA1 | Date | |
---|---|---|---|
![]() |
7b6f6e4683 | ||
![]() |
add042ec16 | ||
![]() |
e174136fb4 | ||
![]() |
ff8697ed71 | ||
![]() |
542a841791 | ||
![]() |
b9640f0049 | ||
![]() |
a6cc3dc85b | ||
![]() |
6083c71e0e | ||
![]() |
e844ea819b | ||
![]() |
27f0300bc7 | ||
![]() |
35cea2eaba | ||
![]() |
7c4617da99 | ||
![]() |
d6d7c5c1fb | ||
![]() |
b7f7a39aa7 | ||
![]() |
8c04771a87 | ||
![]() |
febf15b279 | ||
![]() |
a05e4b7ecc | ||
![]() |
3568a999f9 | ||
![]() |
207097bf03 | ||
![]() |
0e3d2b9570 | ||
![]() |
87fac429e8 | ||
![]() |
3b1097931b | ||
![]() |
ec7e408cd1 | ||
![]() |
f8a99b9cfa | ||
![]() |
4c7231e6ae | ||
![]() |
883d32515c | ||
![]() |
f0f3c257fa | ||
![]() |
42fce7d9ce | ||
![]() |
facac37fff | ||
![]() |
1272b601c6 | ||
![]() |
58e35cfaf5 | ||
![]() |
2befc9781b | ||
![]() |
8fb2872c36 | ||
![]() |
98033515c8 | ||
![]() |
25c02cea2e | ||
![]() |
6d5be78fec | ||
![]() |
f6e60085db | ||
![]() |
a61648af70 | ||
![]() |
2b496c6fd3 | ||
![]() |
1e984f0965 | ||
![]() |
37bed36315 | ||
![]() |
5394cc5201 | ||
![]() |
5e3dfb0e9c | ||
![]() |
f6a6c981f8 | ||
![]() |
415c1bfd2f | ||
![]() |
e4a911db25 | ||
![]() |
5fcd67d77b | ||
![]() |
86db08ac3f | ||
![]() |
8d735fc726 | ||
![]() |
1e1f4bb2a3 | ||
![]() |
d8000aca10 | ||
![]() |
a2660ff0dc | ||
![]() |
000d5785cf | ||
![]() |
9fe04712a9 | ||
![]() |
846afd9e0a | ||
![]() |
6125ff0f90 | ||
![]() |
a5428a8843 | ||
![]() |
19be45cda6 | ||
![]() |
889c97cb27 | ||
![]() |
1d83da41a9 | ||
![]() |
a65f2174eb | ||
![]() |
9db493e87e | ||
![]() |
49a3a18b51 | ||
![]() |
0489e7036b | ||
![]() |
794d5a781c | ||
![]() |
c21dfbcc1f | ||
![]() |
cc75db6d09 | ||
![]() |
48d35a50b5 | ||
![]() |
3a0888046f | ||
![]() |
7917994835 | ||
![]() |
9e2bab43f7 | ||
![]() |
7e03f47f08 | ||
![]() |
7c6137a4fc | ||
![]() |
0cbd6e2fe9 | ||
![]() |
5acc1f5560 | ||
![]() |
887693a90d | ||
![]() |
fbb985833f | ||
![]() |
1b3734ff6b | ||
![]() |
6288b30ac3 | ||
![]() |
ee435f7081 | ||
![]() |
d75b4eb2a9 | ||
![]() |
5eeb505807 | ||
![]() |
30566e35ce | ||
![]() |
e058b7f295 | ||
![]() |
902a30a9f8 | ||
![]() |
8669b94fb0 | ||
![]() |
7fb08aea62 | ||
![]() |
e7a1548b0e | ||
![]() |
bdcb6f0533 | ||
![]() |
97849ce6f1 | ||
![]() |
d1c937caf4 | ||
![]() |
5ffe6279a2 | ||
![]() |
9260e2b77a | ||
![]() |
593465e2ec | ||
![]() |
8820350760 | ||
![]() |
7e1668229a | ||
![]() |
387b96b9a0 | ||
![]() |
5f52d786c6 | ||
![]() |
e0db597f9d | ||
![]() |
41b0315ec6 | ||
![]() |
a930a4085e | ||
![]() |
d0dbbfac07 | ||
![]() |
3dc24b46f4 | ||
![]() |
8e44a357b4 | ||
![]() |
dd462310cc | ||
![]() |
a1935fa0cd | ||
![]() |
b45c57246b | ||
![]() |
c481ecd6b5 | ||
![]() |
e0898dd9b0 | ||
![]() |
5561334efa | ||
![]() |
6c4bb4f79e | ||
![]() |
8b2d2e2fe7 | ||
![]() |
49d128c964 | ||
![]() |
f24d1e0916 | ||
![]() |
9faa4c0c69 | ||
![]() |
a04b8f65db | ||
![]() |
e1cf7d23cd | ||
![]() |
b74b221844 | ||
![]() |
38affc15b4 | ||
![]() |
948488d92b | ||
![]() |
f5902d0346 | ||
![]() |
f28dc8e88a | ||
![]() |
1f41749fa3 | ||
![]() |
a4908a4d42 | ||
![]() |
aa7754e658 | ||
![]() |
58ea9387aa | ||
![]() |
775266c974 | ||
![]() |
eb9794cd1f | ||
![]() |
130732adde | ||
![]() |
7a98e73d61 | ||
![]() |
1f26b72b4b | ||
![]() |
a15ffb5b47 | ||
![]() |
1a5f72283a | ||
![]() |
0460b283ba | ||
![]() |
e212e7c780 | ||
![]() |
84100fc7cf | ||
![]() |
11a8682b07 | ||
![]() |
d3a0bbdfb1 | ||
![]() |
15f3a45d6a | ||
![]() |
f0a4f352cc | ||
![]() |
d16b3e8154 | ||
![]() |
614fb97288 | ||
![]() |
8a40d3645a | ||
![]() |
2b7dadf2a8 | ||
![]() |
c338e16e4f |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -33,3 +33,4 @@
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
Thumbs.db
|
||||
.claude
|
||||
|
3
.vscode/tasks.json
vendored
3
.vscode/tasks.json
vendored
@@ -38,8 +38,7 @@
|
||||
"presentation": {
|
||||
"reveal": "silent"
|
||||
},
|
||||
"isBackground": true,
|
||||
"runOptions": { "instanceLimit": 1, "runOn": "folderOpen" }
|
||||
"isBackground": true
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
|
@@ -42,15 +42,19 @@ gd::String EventsCodeGenerator::GenerateRelationalOperatorCall(
|
||||
const vector<gd::String>& arguments,
|
||||
const gd::String& callStartString,
|
||||
std::size_t startFromArgument) {
|
||||
std::size_t relationalOperatorIndex = instrInfos.parameters.GetParametersCount();
|
||||
for (std::size_t i = startFromArgument; i < instrInfos.parameters.GetParametersCount();
|
||||
std::size_t relationalOperatorIndex =
|
||||
instrInfos.parameters.GetParametersCount();
|
||||
for (std::size_t i = startFromArgument;
|
||||
i < instrInfos.parameters.GetParametersCount();
|
||||
++i) {
|
||||
if (instrInfos.parameters.GetParameter(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.GetParametersCount()) {
|
||||
if (relationalOperatorIndex + 1 >=
|
||||
instrInfos.parameters.GetParametersCount()) {
|
||||
ReportError();
|
||||
return "";
|
||||
}
|
||||
@@ -87,20 +91,23 @@ gd::String EventsCodeGenerator::GenerateRelationalOperation(
|
||||
const gd::String& relationalOperator,
|
||||
const gd::String& lhs,
|
||||
const gd::String& rhs) {
|
||||
return lhs + " " + GenerateRelationalOperatorCodes(relationalOperator) + " " + rhs;
|
||||
return lhs + " " + GenerateRelationalOperatorCodes(relationalOperator) + " " +
|
||||
rhs;
|
||||
}
|
||||
|
||||
const gd::String EventsCodeGenerator::GenerateRelationalOperatorCodes(const gd::String &operatorString) {
|
||||
if (operatorString == "=") {
|
||||
return "==";
|
||||
}
|
||||
if (operatorString != "<" && operatorString != ">" &&
|
||||
operatorString != "<=" && operatorString != ">=" && operatorString != "!=" &&
|
||||
operatorString != "startsWith" && operatorString != "endsWith" && operatorString != "contains") {
|
||||
cout << "Warning: Bad relational operator: Set to == by default." << endl;
|
||||
return "==";
|
||||
}
|
||||
return operatorString;
|
||||
const gd::String EventsCodeGenerator::GenerateRelationalOperatorCodes(
|
||||
const gd::String& operatorString) {
|
||||
if (operatorString == "=") {
|
||||
return "==";
|
||||
}
|
||||
if (operatorString != "<" && operatorString != ">" &&
|
||||
operatorString != "<=" && operatorString != ">=" &&
|
||||
operatorString != "!=" && operatorString != "startsWith" &&
|
||||
operatorString != "endsWith" && operatorString != "contains") {
|
||||
cout << "Warning: Bad relational operator: Set to == by default." << endl;
|
||||
return "==";
|
||||
}
|
||||
return operatorString;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -124,7 +131,8 @@ gd::String EventsCodeGenerator::GenerateOperatorCall(
|
||||
const gd::String& getterStartString,
|
||||
std::size_t startFromArgument) {
|
||||
std::size_t operatorIndex = instrInfos.parameters.GetParametersCount();
|
||||
for (std::size_t i = startFromArgument; i < instrInfos.parameters.GetParametersCount();
|
||||
for (std::size_t i = startFromArgument;
|
||||
i < instrInfos.parameters.GetParametersCount();
|
||||
++i) {
|
||||
if (instrInfos.parameters.GetParameter(i).GetType() == "operator") {
|
||||
operatorIndex = i;
|
||||
@@ -195,7 +203,8 @@ gd::String EventsCodeGenerator::GenerateCompoundOperatorCall(
|
||||
const gd::String& callStartString,
|
||||
std::size_t startFromArgument) {
|
||||
std::size_t operatorIndex = instrInfos.parameters.GetParametersCount();
|
||||
for (std::size_t i = startFromArgument; i < instrInfos.parameters.GetParametersCount();
|
||||
for (std::size_t i = startFromArgument;
|
||||
i < instrInfos.parameters.GetParametersCount();
|
||||
++i) {
|
||||
if (instrInfos.parameters.GetParameter(i).GetType() == "operator") {
|
||||
operatorIndex = i;
|
||||
@@ -248,7 +257,8 @@ gd::String EventsCodeGenerator::GenerateMutatorCall(
|
||||
const gd::String& callStartString,
|
||||
std::size_t startFromArgument) {
|
||||
std::size_t operatorIndex = instrInfos.parameters.GetParametersCount();
|
||||
for (std::size_t i = startFromArgument; i < instrInfos.parameters.GetParametersCount();
|
||||
for (std::size_t i = startFromArgument;
|
||||
i < instrInfos.parameters.GetParametersCount();
|
||||
++i) {
|
||||
if (instrInfos.parameters.GetParameter(i).GetType() == "operator") {
|
||||
operatorIndex = i;
|
||||
@@ -323,83 +333,97 @@ gd::String EventsCodeGenerator::GenerateConditionCode(
|
||||
}
|
||||
|
||||
// Insert code only parameters and be sure there is no lack of parameter.
|
||||
while (condition.GetParameters().size() < instrInfos.parameters.GetParametersCount()) {
|
||||
while (condition.GetParameters().size() <
|
||||
instrInfos.parameters.GetParametersCount()) {
|
||||
vector<gd::Expression> parameters = condition.GetParameters();
|
||||
parameters.push_back(gd::Expression(""));
|
||||
condition.SetParameters(parameters);
|
||||
}
|
||||
|
||||
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.GetParametersCount(); ++pNb) {
|
||||
if (ParameterMetadata::IsObject(instrInfos.parameters.GetParameter(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 =
|
||||
const auto& expectedObjectType =
|
||||
instrInfos.parameters.GetParameter(pNb).GetExtraInfo();
|
||||
const auto &actualObjectType =
|
||||
const auto& actualObjectType =
|
||||
GetObjectsContainersList().GetTypeOfObject(objectInParameter);
|
||||
if (!GetObjectsContainersList().HasObjectOrGroupNamed(
|
||||
objectInParameter)) {
|
||||
gd::ProjectDiagnostic projectDiagnostic(
|
||||
gd::ProjectDiagnostic::ErrorType::UnknownObject, "",
|
||||
objectInParameter, "");
|
||||
gd::ProjectDiagnostic::ErrorType::UnknownObject,
|
||||
"",
|
||||
objectInParameter,
|
||||
"");
|
||||
if (diagnosticReport) diagnosticReport->Add(projectDiagnostic);
|
||||
return "/* Unknown object - skipped. */";
|
||||
} else if (!expectedObjectType.empty() &&
|
||||
actualObjectType != expectedObjectType) {
|
||||
gd::ProjectDiagnostic projectDiagnostic(
|
||||
gd::ProjectDiagnostic::ErrorType::MismatchedObjectType, "",
|
||||
actualObjectType, expectedObjectType, objectInParameter);
|
||||
gd::ProjectDiagnostic::ErrorType::MismatchedObjectType,
|
||||
"",
|
||||
actualObjectType,
|
||||
expectedObjectType,
|
||||
objectInParameter);
|
||||
if (diagnosticReport) diagnosticReport->Add(projectDiagnostic);
|
||||
return "/* Mismatched object type - skipped. */";
|
||||
}
|
||||
}
|
||||
}
|
||||
bool isAnyBehaviorMissing =
|
||||
gd::EventsCodeGenerator::CheckBehaviorParameters(condition, instrInfos);
|
||||
if (isAnyBehaviorMissing) {
|
||||
return "/* Missing behavior - skipped. */";
|
||||
}
|
||||
|
||||
if (instrInfos.IsObjectInstruction()) {
|
||||
gd::String objectName = condition.GetParameter(0).GetPlainString();
|
||||
if (!objectName.empty() && instrInfos.parameters.GetParametersCount() > 0) {
|
||||
std::vector<gd::String> realObjects =
|
||||
GetObjectsContainersList().ExpandObjectName(objectName, context.GetCurrentObject());
|
||||
GetObjectsContainersList().ExpandObjectName(
|
||||
objectName, context.GetCurrentObject());
|
||||
for (std::size_t i = 0; i < realObjects.size(); ++i) {
|
||||
// Set up the context
|
||||
gd::String objectType = GetObjectsContainersList().GetTypeOfObject(realObjects[i]);
|
||||
gd::String objectType =
|
||||
GetObjectsContainersList().GetTypeOfObject(realObjects[i]);
|
||||
const ObjectMetadata& objInfo =
|
||||
MetadataProvider::GetObjectMetadata(platform, objectType);
|
||||
|
||||
AddIncludeFiles(objInfo.includeFiles);
|
||||
context.SetCurrentObject(realObjects[i]);
|
||||
context.ObjectsListNeeded(realObjects[i]);
|
||||
AddIncludeFiles(objInfo.includeFiles);
|
||||
context.SetCurrentObject(realObjects[i]);
|
||||
context.ObjectsListNeeded(realObjects[i]);
|
||||
|
||||
// Prepare arguments and generate the condition whole code
|
||||
vector<gd::String> arguments = GenerateParametersCodes(
|
||||
condition.GetParameters(), instrInfos.parameters, context);
|
||||
conditionCode += GenerateObjectCondition(realObjects[i],
|
||||
objInfo,
|
||||
arguments,
|
||||
instrInfos,
|
||||
returnBoolean,
|
||||
condition.IsInverted(),
|
||||
context);
|
||||
// Prepare arguments and generate the condition whole code
|
||||
vector<gd::String> arguments = GenerateParametersCodes(
|
||||
condition.GetParameters(), instrInfos.parameters, context);
|
||||
conditionCode += GenerateObjectCondition(realObjects[i],
|
||||
objInfo,
|
||||
arguments,
|
||||
instrInfos,
|
||||
returnBoolean,
|
||||
condition.IsInverted(),
|
||||
context);
|
||||
|
||||
context.SetNoCurrentObject();
|
||||
context.SetNoCurrentObject();
|
||||
}
|
||||
}
|
||||
} else if (instrInfos.IsBehaviorInstruction()) {
|
||||
if (instrInfos.parameters.GetParametersCount() >= 2) {
|
||||
const gd::String &objectName = condition.GetParameter(0).GetPlainString();
|
||||
const gd::String &behaviorName =
|
||||
const gd::String& objectName = condition.GetParameter(0).GetPlainString();
|
||||
const gd::String& behaviorName =
|
||||
condition.GetParameter(1).GetPlainString();
|
||||
const gd::String &actualBehaviorType =
|
||||
const gd::String& actualBehaviorType =
|
||||
GetObjectsContainersList().GetTypeOfBehavior(behaviorName);
|
||||
|
||||
std::vector<gd::String> realObjects =
|
||||
GetObjectsContainersList().ExpandObjectName(
|
||||
objectName, context.GetCurrentObject());
|
||||
|
||||
const BehaviorMetadata &autoInfo =
|
||||
const BehaviorMetadata& autoInfo =
|
||||
MetadataProvider::GetBehaviorMetadata(platform, actualBehaviorType);
|
||||
|
||||
for (std::size_t i = 0; i < realObjects.size(); ++i) {
|
||||
@@ -411,15 +435,14 @@ gd::String EventsCodeGenerator::GenerateConditionCode(
|
||||
// Prepare arguments and generate the whole condition code
|
||||
vector<gd::String> arguments = GenerateParametersCodes(
|
||||
condition.GetParameters(), instrInfos.parameters, context);
|
||||
conditionCode += GenerateBehaviorCondition(
|
||||
realObjects[i],
|
||||
behaviorName,
|
||||
autoInfo,
|
||||
arguments,
|
||||
instrInfos,
|
||||
returnBoolean,
|
||||
condition.IsInverted(),
|
||||
context);
|
||||
conditionCode += GenerateBehaviorCondition(realObjects[i],
|
||||
behaviorName,
|
||||
autoInfo,
|
||||
arguments,
|
||||
instrInfos,
|
||||
returnBoolean,
|
||||
condition.IsInverted(),
|
||||
context);
|
||||
|
||||
context.SetNoCurrentObject();
|
||||
}
|
||||
@@ -488,31 +511,50 @@ gd::String EventsCodeGenerator::GenerateConditionsListCode(
|
||||
return outputCode;
|
||||
}
|
||||
|
||||
void EventsCodeGenerator::CheckBehaviorParameters(
|
||||
const gd::Instruction &instruction,
|
||||
const gd::InstructionMetadata &instrInfos) {
|
||||
gd::ParameterMetadataTools::IterateOverParameters(
|
||||
instruction.GetParameters(), instrInfos.parameters,
|
||||
[this](const gd::ParameterMetadata ¶meterMetadata,
|
||||
const gd::Expression ¶meterValue,
|
||||
const gd::String &lastObjectName) {
|
||||
bool EventsCodeGenerator::CheckBehaviorParameters(
|
||||
const gd::Instruction& instruction,
|
||||
const gd::InstructionMetadata& instrInfos) {
|
||||
bool isAnyBehaviorMissing = false;
|
||||
gd::ParameterMetadataTools::IterateOverParametersWithIndex(
|
||||
instruction.GetParameters(),
|
||||
instrInfos.parameters,
|
||||
[this, &isAnyBehaviorMissing, &instrInfos](
|
||||
const gd::ParameterMetadata& parameterMetadata,
|
||||
const gd::Expression& parameterValue,
|
||||
size_t parameterIndex,
|
||||
const gd::String& lastObjectName,
|
||||
size_t lastObjectIndex) {
|
||||
if (ParameterMetadata::IsBehavior(parameterMetadata.GetType())) {
|
||||
const gd::String &behaviorName = parameterValue.GetPlainString();
|
||||
const gd::String &actualBehaviorType =
|
||||
const gd::String& behaviorName = parameterValue.GetPlainString();
|
||||
const gd::String& actualBehaviorType =
|
||||
GetObjectsContainersList().GetTypeOfBehaviorInObjectOrGroup(
|
||||
lastObjectName, behaviorName);
|
||||
const gd::String &expectedBehaviorType =
|
||||
const gd::String& expectedBehaviorType =
|
||||
parameterMetadata.GetExtraInfo();
|
||||
|
||||
if (!expectedBehaviorType.empty() &&
|
||||
actualBehaviorType != expectedBehaviorType) {
|
||||
const auto& objectParameterMetadata =
|
||||
instrInfos.GetParameter(lastObjectIndex);
|
||||
// Event functions crash if some objects in a group are missing
|
||||
// the required behaviors, since they lose reference to the original
|
||||
// objects. Missing behaviors are considered "fatal" only for
|
||||
// ObjectList parameters, in order to minimize side effects on
|
||||
// built-in functions.
|
||||
if (objectParameterMetadata.GetType() == "objectList") {
|
||||
isAnyBehaviorMissing = true;
|
||||
}
|
||||
gd::ProjectDiagnostic projectDiagnostic(
|
||||
gd::ProjectDiagnostic::ErrorType::MissingBehavior, "",
|
||||
actualBehaviorType, expectedBehaviorType, lastObjectName);
|
||||
gd::ProjectDiagnostic::ErrorType::MissingBehavior,
|
||||
"",
|
||||
actualBehaviorType,
|
||||
expectedBehaviorType,
|
||||
lastObjectName);
|
||||
if (diagnosticReport) diagnosticReport->Add(projectDiagnostic);
|
||||
}
|
||||
}
|
||||
});
|
||||
return isAnyBehaviorMissing;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -521,7 +563,8 @@ void EventsCodeGenerator::CheckBehaviorParameters(
|
||||
gd::String EventsCodeGenerator::GenerateActionCode(
|
||||
gd::Instruction& action,
|
||||
EventsCodeGenerationContext& context,
|
||||
const gd::String& optionalAsyncCallbackName) {
|
||||
const gd::String& optionalAsyncCallbackName,
|
||||
const gd::String& optionalAsyncCallbackId) {
|
||||
gd::String actionCode;
|
||||
|
||||
const gd::InstructionMetadata& instrInfos =
|
||||
@@ -546,39 +589,51 @@ gd::String EventsCodeGenerator::GenerateActionCode(
|
||||
: instrInfos.codeExtraInformation.functionCallName;
|
||||
|
||||
// Be sure there is no lack of parameter.
|
||||
while (action.GetParameters().size() < instrInfos.parameters.GetParametersCount()) {
|
||||
while (action.GetParameters().size() <
|
||||
instrInfos.parameters.GetParametersCount()) {
|
||||
vector<gd::Expression> parameters = action.GetParameters();
|
||||
parameters.push_back(gd::Expression(""));
|
||||
action.SetParameters(parameters);
|
||||
}
|
||||
|
||||
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.GetParametersCount(); ++pNb) {
|
||||
if (ParameterMetadata::IsObject(instrInfos.parameters.GetParameter(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 =
|
||||
const auto& expectedObjectType =
|
||||
instrInfos.parameters.GetParameter(pNb).GetExtraInfo();
|
||||
const auto &actualObjectType =
|
||||
const auto& actualObjectType =
|
||||
GetObjectsContainersList().GetTypeOfObject(objectInParameter);
|
||||
if (!GetObjectsContainersList().HasObjectOrGroupNamed(
|
||||
objectInParameter)) {
|
||||
gd::ProjectDiagnostic projectDiagnostic(
|
||||
gd::ProjectDiagnostic::ErrorType::UnknownObject, "",
|
||||
objectInParameter, "");
|
||||
gd::ProjectDiagnostic::ErrorType::UnknownObject,
|
||||
"",
|
||||
objectInParameter,
|
||||
"");
|
||||
if (diagnosticReport) diagnosticReport->Add(projectDiagnostic);
|
||||
return "/* Unknown object - skipped. */";
|
||||
} else if (!expectedObjectType.empty() &&
|
||||
actualObjectType != expectedObjectType) {
|
||||
gd::ProjectDiagnostic projectDiagnostic(
|
||||
gd::ProjectDiagnostic::ErrorType::MismatchedObjectType, "",
|
||||
actualObjectType, expectedObjectType, objectInParameter);
|
||||
gd::ProjectDiagnostic::ErrorType::MismatchedObjectType,
|
||||
"",
|
||||
actualObjectType,
|
||||
expectedObjectType,
|
||||
objectInParameter);
|
||||
if (diagnosticReport) diagnosticReport->Add(projectDiagnostic);
|
||||
return "/* Mismatched object type - skipped. */";
|
||||
}
|
||||
}
|
||||
}
|
||||
bool isAnyBehaviorMissing =
|
||||
gd::EventsCodeGenerator::CheckBehaviorParameters(action, instrInfos);
|
||||
if (isAnyBehaviorMissing) {
|
||||
return "/* Missing behavior - skipped. */";
|
||||
}
|
||||
|
||||
// Call free function first if available
|
||||
if (instrInfos.IsObjectInstruction()) {
|
||||
@@ -586,43 +641,46 @@ gd::String EventsCodeGenerator::GenerateActionCode(
|
||||
|
||||
if (instrInfos.parameters.GetParametersCount() > 0) {
|
||||
std::vector<gd::String> realObjects =
|
||||
GetObjectsContainersList().ExpandObjectName(objectName, context.GetCurrentObject());
|
||||
GetObjectsContainersList().ExpandObjectName(
|
||||
objectName, context.GetCurrentObject());
|
||||
for (std::size_t i = 0; i < realObjects.size(); ++i) {
|
||||
// Setup context
|
||||
gd::String objectType = GetObjectsContainersList().GetTypeOfObject(realObjects[i]);
|
||||
gd::String objectType =
|
||||
GetObjectsContainersList().GetTypeOfObject(realObjects[i]);
|
||||
const ObjectMetadata& objInfo =
|
||||
MetadataProvider::GetObjectMetadata(platform, objectType);
|
||||
|
||||
AddIncludeFiles(objInfo.includeFiles);
|
||||
context.SetCurrentObject(realObjects[i]);
|
||||
context.ObjectsListNeeded(realObjects[i]);
|
||||
AddIncludeFiles(objInfo.includeFiles);
|
||||
context.SetCurrentObject(realObjects[i]);
|
||||
context.ObjectsListNeeded(realObjects[i]);
|
||||
|
||||
// Prepare arguments and generate the whole action code
|
||||
vector<gd::String> arguments = GenerateParametersCodes(
|
||||
action.GetParameters(), instrInfos.parameters, context);
|
||||
actionCode += GenerateObjectAction(realObjects[i],
|
||||
objInfo,
|
||||
functionCallName,
|
||||
arguments,
|
||||
instrInfos,
|
||||
context,
|
||||
optionalAsyncCallbackName);
|
||||
// Prepare arguments and generate the whole action code
|
||||
vector<gd::String> arguments = GenerateParametersCodes(
|
||||
action.GetParameters(), instrInfos.parameters, context);
|
||||
actionCode += GenerateObjectAction(realObjects[i],
|
||||
objInfo,
|
||||
functionCallName,
|
||||
arguments,
|
||||
instrInfos,
|
||||
context,
|
||||
optionalAsyncCallbackName,
|
||||
optionalAsyncCallbackId);
|
||||
|
||||
context.SetNoCurrentObject();
|
||||
context.SetNoCurrentObject();
|
||||
}
|
||||
}
|
||||
} else if (instrInfos.IsBehaviorInstruction()) {
|
||||
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 =
|
||||
const gd::String& objectName = action.GetParameter(0).GetPlainString();
|
||||
const gd::String& behaviorName = action.GetParameter(1).GetPlainString();
|
||||
const gd::String& actualBehaviorType =
|
||||
GetObjectsContainersList().GetTypeOfBehavior(behaviorName);
|
||||
|
||||
std::vector<gd::String> realObjects =
|
||||
GetObjectsContainersList().ExpandObjectName(
|
||||
objectName, context.GetCurrentObject());
|
||||
|
||||
const BehaviorMetadata &autoInfo =
|
||||
const BehaviorMetadata& autoInfo =
|
||||
MetadataProvider::GetBehaviorMetadata(platform, actualBehaviorType);
|
||||
|
||||
AddIncludeFiles(autoInfo.includeFiles);
|
||||
@@ -634,15 +692,15 @@ gd::String EventsCodeGenerator::GenerateActionCode(
|
||||
// Prepare arguments and generate the whole action code
|
||||
vector<gd::String> arguments = GenerateParametersCodes(
|
||||
action.GetParameters(), instrInfos.parameters, context);
|
||||
actionCode +=
|
||||
GenerateBehaviorAction(realObjects[i],
|
||||
behaviorName,
|
||||
autoInfo,
|
||||
functionCallName,
|
||||
arguments,
|
||||
instrInfos,
|
||||
context,
|
||||
optionalAsyncCallbackName);
|
||||
actionCode += GenerateBehaviorAction(realObjects[i],
|
||||
behaviorName,
|
||||
autoInfo,
|
||||
functionCallName,
|
||||
arguments,
|
||||
instrInfos,
|
||||
context,
|
||||
optionalAsyncCallbackName,
|
||||
optionalAsyncCallbackId);
|
||||
|
||||
context.SetNoCurrentObject();
|
||||
}
|
||||
@@ -654,7 +712,8 @@ gd::String EventsCodeGenerator::GenerateActionCode(
|
||||
arguments,
|
||||
instrInfos,
|
||||
context,
|
||||
optionalAsyncCallbackName);
|
||||
optionalAsyncCallbackName,
|
||||
optionalAsyncCallbackId);
|
||||
}
|
||||
|
||||
return actionCode;
|
||||
@@ -667,8 +726,8 @@ gd::String EventsCodeGenerator::GenerateLocalVariablesStackAccessor() {
|
||||
}
|
||||
|
||||
gd::String EventsCodeGenerator::GenerateAnyOrSceneVariableGetter(
|
||||
const gd::Expression &variableExpression,
|
||||
EventsCodeGenerationContext &context) {
|
||||
const gd::Expression& variableExpression,
|
||||
EventsCodeGenerationContext& context) {
|
||||
const auto variableName = gd::ExpressionVariableNameFinder::GetVariableName(
|
||||
*variableExpression.GetRootNode());
|
||||
|
||||
@@ -679,8 +738,12 @@ gd::String EventsCodeGenerator::GenerateAnyOrSceneVariableGetter(
|
||||
: "scenevar";
|
||||
|
||||
return gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
*this, context, variableParameterType,
|
||||
variableExpression.GetPlainString(), "", "AllowUndeclaredVariable");
|
||||
*this,
|
||||
context,
|
||||
variableParameterType,
|
||||
variableExpression.GetPlainString(),
|
||||
"",
|
||||
"AllowUndeclaredVariable");
|
||||
}
|
||||
|
||||
const EventsCodeGenerator::CallbackDescriptor
|
||||
@@ -727,6 +790,11 @@ EventsCodeGenerator::GenerateCallback(
|
||||
|
||||
AddCustomCodeOutsideMain(callbackCode);
|
||||
|
||||
const gd::String idToCallbackMapUpdate = GetCodeNamespaceAccessor() +
|
||||
"idToCallbackMap.set(" + callbackID +
|
||||
", " + callbackFunctionName + ");\n";
|
||||
AddCustomCodeOutsideMain(idToCallbackMapUpdate);
|
||||
|
||||
std::set<gd::String> requiredObjects;
|
||||
// Build the list of all objects required by the callback. Any object that has
|
||||
// already been declared could have gone through previous object picking, so
|
||||
@@ -769,7 +837,7 @@ gd::String EventsCodeGenerator::GenerateActionsListCode(
|
||||
} else {
|
||||
outputCode += actionCode;
|
||||
}
|
||||
outputCode += "}";
|
||||
outputCode += "}\n";
|
||||
}
|
||||
|
||||
return outputCode;
|
||||
@@ -786,13 +854,28 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
|
||||
|
||||
if (ParameterMetadata::IsExpression("number", metadata.GetType())) {
|
||||
argOutput = gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
*this, context, "number", parameter, lastObjectName, metadata.GetExtraInfo());
|
||||
*this,
|
||||
context,
|
||||
"number",
|
||||
parameter,
|
||||
lastObjectName,
|
||||
metadata.GetExtraInfo());
|
||||
} else if (ParameterMetadata::IsExpression("string", metadata.GetType())) {
|
||||
argOutput = gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
*this, context, "string", parameter, lastObjectName, metadata.GetExtraInfo());
|
||||
*this,
|
||||
context,
|
||||
"string",
|
||||
parameter,
|
||||
lastObjectName,
|
||||
metadata.GetExtraInfo());
|
||||
} else if (ParameterMetadata::IsExpression("variable", metadata.GetType())) {
|
||||
argOutput = gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
*this, context, metadata.GetType(), parameter, lastObjectName, metadata.GetExtraInfo());
|
||||
*this,
|
||||
context,
|
||||
metadata.GetType(),
|
||||
parameter,
|
||||
lastObjectName,
|
||||
metadata.GetExtraInfo());
|
||||
} else if (ParameterMetadata::IsObject(metadata.GetType())) {
|
||||
// It would be possible to run a gd::ExpressionCodeGenerator if later
|
||||
// objects can have nested objects, or function returning objects.
|
||||
@@ -827,7 +910,8 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
|
||||
metadata.GetType() == "atlasResource" ||
|
||||
metadata.GetType() == "spineResource" ||
|
||||
// Deprecated, old parameter names:
|
||||
metadata.GetType() == "password" || metadata.GetType() == "musicfile" ||
|
||||
metadata.GetType() == "password" ||
|
||||
metadata.GetType() == "musicfile" ||
|
||||
metadata.GetType() == "soundfile") {
|
||||
argOutput = "\"" + ConvertToString(parameter.GetPlainString()) + "\"";
|
||||
} else if (metadata.GetType() == "mouse") {
|
||||
@@ -977,7 +1061,8 @@ gd::String EventsCodeGenerator::GenerateEventsListCode(
|
||||
for (std::size_t eId = 0; eId < events.size(); ++eId) {
|
||||
auto& event = events[eId];
|
||||
if (event.HasVariables()) {
|
||||
GetProjectScopedContainers().GetVariablesContainersList().Push(event.GetVariables());
|
||||
GetProjectScopedContainers().GetVariablesContainersList().Push(
|
||||
event.GetVariables());
|
||||
}
|
||||
|
||||
// Each event has its own context : Objects picked in an event are totally
|
||||
@@ -1102,7 +1187,7 @@ gd::String EventsCodeGenerator::GenerateFreeCondition(
|
||||
instrInfos.codeExtraInformation.functionCallName);
|
||||
} else {
|
||||
predicate = instrInfos.codeExtraInformation.functionCallName + "(" +
|
||||
GenerateArgumentsList(arguments, 0) + ")";
|
||||
GenerateArgumentsList(arguments, 0) + ")";
|
||||
}
|
||||
|
||||
// Add logical not if needed
|
||||
@@ -1146,7 +1231,7 @@ gd::String EventsCodeGenerator::GenerateObjectCondition(
|
||||
instrInfos, arguments, objectFunctionCallNamePart, 1);
|
||||
} else {
|
||||
predicate = objectFunctionCallNamePart + "(" +
|
||||
GenerateArgumentsList(arguments, 1) + ")";
|
||||
GenerateArgumentsList(arguments, 1) + ")";
|
||||
}
|
||||
if (conditionInverted) predicate = GenerateNegatedPredicate(predicate);
|
||||
|
||||
@@ -1178,18 +1263,20 @@ gd::String EventsCodeGenerator::GenerateBehaviorCondition(
|
||||
}
|
||||
|
||||
gd::String EventsCodeGenerator::GenerateFreeAction(
|
||||
const gd::String& functionCallName,
|
||||
const gd::String& functionCallName,
|
||||
const std::vector<gd::String>& arguments,
|
||||
const gd::InstructionMetadata& instrInfos,
|
||||
gd::EventsCodeGenerationContext& context,
|
||||
const gd::String& optionalAsyncCallbackName) {
|
||||
const gd::String& optionalAsyncCallbackName,
|
||||
const gd::String& optionalAsyncCallbackId) {
|
||||
// Generate call
|
||||
gd::String call;
|
||||
if (instrInfos.codeExtraInformation.type == "number" ||
|
||||
instrInfos.codeExtraInformation.type == "string" ||
|
||||
// Boolean actions declared with addExpressionAndConditionAndAction uses
|
||||
// MutatorAndOrAccessor even though they don't declare an operator parameter.
|
||||
// Boolean operators are only used with SetMutators or SetCustomCodeGenerator.
|
||||
// MutatorAndOrAccessor even though they don't declare an operator
|
||||
// parameter. Boolean operators are only used with SetMutators or
|
||||
// SetCustomCodeGenerator.
|
||||
(instrInfos.codeExtraInformation.type == "boolean" &&
|
||||
instrInfos.codeExtraInformation.accessType ==
|
||||
gd::InstructionMetadata::ExtraInformation::AccessType::Mutators)) {
|
||||
@@ -1202,23 +1289,19 @@ gd::String EventsCodeGenerator::GenerateFreeAction(
|
||||
instrInfos.codeExtraInformation.optionalAssociatedInstruction);
|
||||
else if (instrInfos.codeExtraInformation.accessType ==
|
||||
gd::InstructionMetadata::ExtraInformation::Mutators)
|
||||
call =
|
||||
GenerateMutatorCall(instrInfos,
|
||||
arguments,
|
||||
functionCallName);
|
||||
call = GenerateMutatorCall(instrInfos, arguments, functionCallName);
|
||||
else
|
||||
call = GenerateCompoundOperatorCall(
|
||||
instrInfos,
|
||||
arguments,
|
||||
functionCallName);
|
||||
call =
|
||||
GenerateCompoundOperatorCall(instrInfos, arguments, functionCallName);
|
||||
} else {
|
||||
call = functionCallName + "(" +
|
||||
GenerateArgumentsList(arguments) + ")";
|
||||
call = functionCallName + "(" + GenerateArgumentsList(arguments) + ")";
|
||||
}
|
||||
|
||||
if (!optionalAsyncCallbackName.empty())
|
||||
if (!optionalAsyncCallbackName.empty() && !optionalAsyncCallbackId.empty()) {
|
||||
call = "runtimeScene.getAsyncTasksManager().addTask(" + call + ", " +
|
||||
optionalAsyncCallbackName + ")";
|
||||
optionalAsyncCallbackName + ", " + optionalAsyncCallbackId +
|
||||
", asyncObjectsList)";
|
||||
}
|
||||
|
||||
return call + ";\n";
|
||||
}
|
||||
@@ -1230,7 +1313,8 @@ gd::String EventsCodeGenerator::GenerateObjectAction(
|
||||
const std::vector<gd::String>& arguments,
|
||||
const gd::InstructionMetadata& instrInfos,
|
||||
gd::EventsCodeGenerationContext& context,
|
||||
const gd::String& optionalAsyncCallbackName) {
|
||||
const gd::String& optionalAsyncCallbackName,
|
||||
const gd::String& optionalAsyncCallbackId) {
|
||||
// Create call
|
||||
gd::String call;
|
||||
if ((instrInfos.codeExtraInformation.type == "number" ||
|
||||
@@ -1271,7 +1355,8 @@ gd::String EventsCodeGenerator::GenerateBehaviorAction(
|
||||
const std::vector<gd::String>& arguments,
|
||||
const gd::InstructionMetadata& instrInfos,
|
||||
gd::EventsCodeGenerationContext& context,
|
||||
const gd::String& optionalAsyncCallbackName) {
|
||||
const gd::String& optionalAsyncCallbackName,
|
||||
const gd::String& optionalAsyncCallbackId) {
|
||||
// Create call
|
||||
gd::String call;
|
||||
if ((instrInfos.codeExtraInformation.type == "number" ||
|
||||
@@ -1286,17 +1371,13 @@ gd::String EventsCodeGenerator::GenerateBehaviorAction(
|
||||
2);
|
||||
else
|
||||
call = GenerateCompoundOperatorCall(
|
||||
instrInfos,
|
||||
arguments,
|
||||
functionCallName,
|
||||
2);
|
||||
instrInfos, arguments, functionCallName, 2);
|
||||
return "For each picked object \"" + objectName + "\", call " + call +
|
||||
" for behavior \"" + behaviorName + "\".\n";
|
||||
} else {
|
||||
gd::String argumentsStr = GenerateArgumentsList(arguments, 2);
|
||||
|
||||
call = functionCallName + "(" +
|
||||
argumentsStr + ")";
|
||||
call = functionCallName + "(" + argumentsStr + ")";
|
||||
|
||||
return "For each picked object \"" + objectName + "\", call " + call + "(" +
|
||||
argumentsStr + ")" + " for behavior \"" + behaviorName + "\"" +
|
||||
@@ -1351,42 +1432,47 @@ gd::String EventsCodeGenerator::GenerateArgumentsList(
|
||||
return argumentsStr;
|
||||
}
|
||||
|
||||
gd::String EventsCodeGenerator::GeneratePropertyGetter(const gd::PropertiesContainer& propertiesContainer,
|
||||
const gd::NamedPropertyDescriptor& property,
|
||||
const gd::String& type,
|
||||
gd::EventsCodeGenerationContext& context) {
|
||||
gd::String EventsCodeGenerator::GeneratePropertyGetter(
|
||||
const gd::PropertiesContainer& propertiesContainer,
|
||||
const gd::NamedPropertyDescriptor& property,
|
||||
const gd::String& type,
|
||||
gd::EventsCodeGenerationContext& context) {
|
||||
return "getProperty" + property.GetName() + "As" + type + "()";
|
||||
}
|
||||
|
||||
gd::String EventsCodeGenerator::GeneratePropertyGetterWithoutCasting(
|
||||
const gd::PropertiesContainer &propertiesContainer,
|
||||
const gd::NamedPropertyDescriptor &property) {
|
||||
const gd::PropertiesContainer& propertiesContainer,
|
||||
const gd::NamedPropertyDescriptor& property) {
|
||||
return "getProperty" + property.GetName() + "()";
|
||||
}
|
||||
|
||||
gd::String EventsCodeGenerator::GeneratePropertySetterWithoutCasting(
|
||||
const gd::PropertiesContainer &propertiesContainer,
|
||||
const gd::NamedPropertyDescriptor &property,
|
||||
const gd::String &operandCode) {
|
||||
const gd::PropertiesContainer& propertiesContainer,
|
||||
const gd::NamedPropertyDescriptor& property,
|
||||
const gd::String& operandCode) {
|
||||
return "setProperty" + property.GetName() + "(" + operandCode + ")";
|
||||
}
|
||||
}
|
||||
|
||||
gd::String EventsCodeGenerator::GenerateParameterGetter(const gd::ParameterMetadata& parameter,
|
||||
const gd::String& type,
|
||||
gd::EventsCodeGenerationContext& context) {
|
||||
gd::String EventsCodeGenerator::GenerateParameterGetter(
|
||||
const gd::ParameterMetadata& parameter,
|
||||
const gd::String& type,
|
||||
gd::EventsCodeGenerationContext& context) {
|
||||
return "getParameter" + parameter.GetName() + "As" + type + "()";
|
||||
}
|
||||
|
||||
gd::String EventsCodeGenerator::GenerateParameterGetterWithoutCasting(
|
||||
const gd::ParameterMetadata ¶meter) {
|
||||
return "getParameter" + parameter.GetName() + "()";
|
||||
}
|
||||
const gd::ParameterMetadata& parameter) {
|
||||
return "getParameter" + parameter.GetName() + "()";
|
||||
}
|
||||
|
||||
EventsCodeGenerator::EventsCodeGenerator(const gd::Project& project_,
|
||||
const gd::Layout& layout,
|
||||
const gd::Platform& platform_)
|
||||
: platform(platform_),
|
||||
projectScopedContainers(gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project_, layout)),
|
||||
projectScopedContainers(
|
||||
gd::ProjectScopedContainers::
|
||||
MakeNewProjectScopedContainersForProjectAndLayout(project_,
|
||||
layout)),
|
||||
hasProjectAndLayout(true),
|
||||
project(&project_),
|
||||
scene(&layout),
|
||||
@@ -1395,7 +1481,7 @@ EventsCodeGenerator::EventsCodeGenerator(const gd::Project& project_,
|
||||
maxCustomConditionsDepth(0),
|
||||
maxConditionsListsSize(0),
|
||||
eventsListNextUniqueId(0),
|
||||
diagnosticReport(nullptr){};
|
||||
diagnosticReport(nullptr) {};
|
||||
|
||||
EventsCodeGenerator::EventsCodeGenerator(
|
||||
const gd::Platform& platform_,
|
||||
@@ -1410,6 +1496,6 @@ EventsCodeGenerator::EventsCodeGenerator(
|
||||
maxCustomConditionsDepth(0),
|
||||
maxConditionsListsSize(0),
|
||||
eventsListNextUniqueId(0),
|
||||
diagnosticReport(nullptr){};
|
||||
diagnosticReport(nullptr) {};
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -9,9 +9,9 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "GDCore/Events/CodeGeneration/DiagnosticReport.h"
|
||||
#include "GDCore/Events/Event.h"
|
||||
#include "GDCore/Events/Instruction.h"
|
||||
#include "GDCore/Events/CodeGeneration/DiagnosticReport.h"
|
||||
#include "GDCore/Project/ProjectScopedContainers.h"
|
||||
#include "GDCore/String.h"
|
||||
|
||||
@@ -62,7 +62,7 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
EventsCodeGenerator(
|
||||
const gd::Platform& platform,
|
||||
const gd::ProjectScopedContainers& projectScopedContainers_);
|
||||
virtual ~EventsCodeGenerator(){};
|
||||
virtual ~EventsCodeGenerator() {};
|
||||
|
||||
/**
|
||||
* \brief Preprocess an events list (replacing for example links with the
|
||||
@@ -160,7 +160,8 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
gd::String GenerateActionCode(
|
||||
gd::Instruction& action,
|
||||
EventsCodeGenerationContext& context,
|
||||
const gd::String& optionalAsyncCallbackName = "");
|
||||
const gd::String& optionalAsyncCallbackName = "",
|
||||
const gd::String& optionalAsyncCallbackId = "");
|
||||
|
||||
struct CallbackDescriptor {
|
||||
CallbackDescriptor(const gd::String functionName_,
|
||||
@@ -168,7 +169,7 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
const std::set<gd::String> requiredObjects_)
|
||||
: functionName(functionName_),
|
||||
argumentsList(argumentsList_),
|
||||
requiredObjects(requiredObjects_){};
|
||||
requiredObjects(requiredObjects_) {};
|
||||
/**
|
||||
* The name by which the function can be invoked.
|
||||
*/
|
||||
@@ -338,9 +339,9 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Give access to the project scoped containers as code generation might
|
||||
* push and pop variable containers (for local variables).
|
||||
* This could be passed as a parameter recursively in code generation, but this requires
|
||||
* @brief Give access to the project scoped containers as code generation
|
||||
* might push and pop variable containers (for local variables). This could be
|
||||
* passed as a parameter recursively in code generation, but this requires
|
||||
* heavy refactoring. Instead, we use this single instance.
|
||||
*/
|
||||
gd::ProjectScopedContainers& GetProjectScopedContainers() {
|
||||
@@ -387,9 +388,7 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
diagnosticReport = diagnosticReport_;
|
||||
}
|
||||
|
||||
gd::DiagnosticReport* GetDiagnosticReport() {
|
||||
return diagnosticReport;
|
||||
}
|
||||
gd::DiagnosticReport* GetDiagnosticReport() { return diagnosticReport; }
|
||||
|
||||
/**
|
||||
* \brief Generate the full name for accessing to a boolean variable used for
|
||||
@@ -513,16 +512,16 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
* \brief Generate an any variable getter that fallbacks on scene variable for
|
||||
* compatibility reason.
|
||||
*/
|
||||
gd::String
|
||||
GenerateAnyOrSceneVariableGetter(const gd::Expression &variableExpression,
|
||||
EventsCodeGenerationContext &context);
|
||||
gd::String GenerateAnyOrSceneVariableGetter(
|
||||
const gd::Expression& variableExpression,
|
||||
EventsCodeGenerationContext& context);
|
||||
|
||||
virtual gd::String GeneratePropertySetterWithoutCasting(
|
||||
const gd::PropertiesContainer &propertiesContainer,
|
||||
const gd::NamedPropertyDescriptor &property,
|
||||
const gd::String &operandCode);
|
||||
const gd::PropertiesContainer& propertiesContainer,
|
||||
const gd::NamedPropertyDescriptor& property,
|
||||
const gd::String& operandCode);
|
||||
|
||||
protected:
|
||||
protected:
|
||||
virtual const gd::String GenerateRelationalOperatorCodes(
|
||||
const gd::String& operatorString);
|
||||
|
||||
@@ -643,16 +642,16 @@ protected:
|
||||
gd::EventsCodeGenerationContext& context);
|
||||
|
||||
virtual gd::String GeneratePropertyGetterWithoutCasting(
|
||||
const gd::PropertiesContainer &propertiesContainer,
|
||||
const gd::NamedPropertyDescriptor &property);
|
||||
const gd::PropertiesContainer& propertiesContainer,
|
||||
const gd::NamedPropertyDescriptor& property);
|
||||
|
||||
virtual gd::String GenerateParameterGetter(
|
||||
const gd::ParameterMetadata& parameter,
|
||||
const gd::String& type,
|
||||
gd::EventsCodeGenerationContext& context);
|
||||
|
||||
virtual gd::String
|
||||
GenerateParameterGetterWithoutCasting(const gd::ParameterMetadata ¶meter);
|
||||
virtual gd::String GenerateParameterGetterWithoutCasting(
|
||||
const gd::ParameterMetadata& parameter);
|
||||
|
||||
/**
|
||||
* \brief Generate the code to reference an object which is
|
||||
@@ -769,7 +768,8 @@ protected:
|
||||
const std::vector<gd::String>& arguments,
|
||||
const gd::InstructionMetadata& instrInfos,
|
||||
gd::EventsCodeGenerationContext& context,
|
||||
const gd::String& optionalAsyncCallbackName = "");
|
||||
const gd::String& optionalAsyncCallbackName = "",
|
||||
const gd::String& optionalAsyncCallbackId = "");
|
||||
|
||||
virtual gd::String GenerateObjectAction(
|
||||
const gd::String& objectName,
|
||||
@@ -778,7 +778,8 @@ protected:
|
||||
const std::vector<gd::String>& arguments,
|
||||
const gd::InstructionMetadata& instrInfos,
|
||||
gd::EventsCodeGenerationContext& context,
|
||||
const gd::String& optionalAsyncCallbackName = "");
|
||||
const gd::String& optionalAsyncCallbackName = "",
|
||||
const gd::String& optionalAsyncCallbackId = "");
|
||||
|
||||
virtual gd::String GenerateBehaviorAction(
|
||||
const gd::String& objectName,
|
||||
@@ -788,7 +789,8 @@ protected:
|
||||
const std::vector<gd::String>& arguments,
|
||||
const gd::InstructionMetadata& instrInfos,
|
||||
gd::EventsCodeGenerationContext& context,
|
||||
const gd::String& optionalAsyncCallbackName = "");
|
||||
const gd::String& optionalAsyncCallbackName = "",
|
||||
const gd::String& optionalAsyncCallbackId = "");
|
||||
|
||||
gd::String GenerateRelationalOperatorCall(
|
||||
const gd::InstructionMetadata& instrInfos,
|
||||
@@ -837,9 +839,8 @@ protected:
|
||||
virtual gd::String GenerateGetBehaviorNameCode(
|
||||
const gd::String& behaviorName);
|
||||
|
||||
void CheckBehaviorParameters(
|
||||
const gd::Instruction &instruction,
|
||||
const gd::InstructionMetadata &instrInfos);
|
||||
bool CheckBehaviorParameters(const gd::Instruction& instruction,
|
||||
const gd::InstructionMetadata& instrInfos);
|
||||
|
||||
const gd::Platform& platform; ///< The platform being used.
|
||||
|
||||
@@ -876,4 +877,3 @@ protected:
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
||||
|
@@ -18,21 +18,21 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAnimatableExtension(
|
||||
gd::PlatformExtension& extension) {
|
||||
extension
|
||||
.SetExtensionInformation("AnimatableCapability",
|
||||
_("Animatable capability"),
|
||||
_("Animate objects."),
|
||||
_("Objects with animations"),
|
||||
_("Actions and conditions for objects having animations (sprite, 3D models...)."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/objects");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Animatable capability"))
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Objects with animations"))
|
||||
.SetIcon("res/actions/animation24.png");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Animations and images"))
|
||||
.SetIcon("res/actions/animation24.png");
|
||||
|
||||
gd::BehaviorMetadata& aut = extension.AddBehavior(
|
||||
"AnimatableBehavior",
|
||||
_("Animatable capability"),
|
||||
_("Objects with animations"),
|
||||
"Animation",
|
||||
_("Animate objects."),
|
||||
_("Actions and conditions for objects having animations (sprite, 3D models...).."),
|
||||
"",
|
||||
"res/actions/animation24.png",
|
||||
"AnimatableBehavior",
|
||||
|
@@ -18,8 +18,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsEffectExtension(
|
||||
gd::PlatformExtension& extension) {
|
||||
extension
|
||||
.SetExtensionInformation("EffectCapability",
|
||||
_("Effect capability"),
|
||||
_("Apply visual effects to objects."),
|
||||
_("Objects with effects"),
|
||||
_("Actions/conditions to enable/disable and change parameters of visual effects applied on objects."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/objects");
|
||||
@@ -28,9 +28,9 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsEffectExtension(
|
||||
|
||||
gd::BehaviorMetadata& aut = extension.AddBehavior(
|
||||
"EffectBehavior",
|
||||
_("Effect capability"),
|
||||
_("Objects with effects"),
|
||||
"Effect",
|
||||
_("Apply visual effects to objects."),
|
||||
_("Actions/conditions to enable/disable and change parameters of visual effects applied on objects."),
|
||||
"",
|
||||
"res/actions/effect_black.svg",
|
||||
"EffectBehavior",
|
||||
|
@@ -18,8 +18,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsFlippableExtension(
|
||||
gd::PlatformExtension& extension) {
|
||||
extension
|
||||
.SetExtensionInformation("FlippableCapability",
|
||||
_("Flippable capability"),
|
||||
_("Flip objects."),
|
||||
_("Flippable objects"),
|
||||
_("Actions/conditions for objects which can be flipped horizontally or vertically."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/objects");
|
||||
@@ -28,9 +28,9 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsFlippableExtension(
|
||||
|
||||
gd::BehaviorMetadata& aut = extension.AddBehavior(
|
||||
"FlippableBehavior",
|
||||
_("Flippable capability"),
|
||||
_("Flippable objects"),
|
||||
"Flippable",
|
||||
_("Flip objects."),
|
||||
_("Actions/conditions for objects which can be flipped horizontally or vertically."),
|
||||
"",
|
||||
"res/actions/flipX24.png",
|
||||
"FlippableBehavior",
|
||||
|
@@ -18,27 +18,30 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsOpacityExtension(
|
||||
gd::PlatformExtension& extension) {
|
||||
extension
|
||||
.SetExtensionInformation("OpacityCapability",
|
||||
_("Opacity capability"),
|
||||
_("Change the object opacity."),
|
||||
_("Objects with opacity"),
|
||||
_("Action/condition/expression to change or "
|
||||
"check the opacity of an object (0-255)."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/objects");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Opacity capability"))
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Objects with opacity"))
|
||||
.SetIcon("res/actions/opacity24.png");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Visibility"))
|
||||
.SetIcon("res/actions/opacity24.png");
|
||||
|
||||
gd::BehaviorMetadata& aut = extension.AddBehavior(
|
||||
"OpacityBehavior",
|
||||
_("Opacity capability"),
|
||||
"Opacity",
|
||||
_("Change the object opacity."),
|
||||
"",
|
||||
"res/actions/opacity24.png",
|
||||
"OpacityBehavior",
|
||||
std::make_shared<gd::Behavior>(),
|
||||
std::make_shared<gd::BehaviorsSharedData>())
|
||||
.SetHidden();
|
||||
gd::BehaviorMetadata& aut =
|
||||
extension
|
||||
.AddBehavior("OpacityBehavior",
|
||||
_("Objects with opacity"),
|
||||
"Opacity",
|
||||
_("Action/condition/expression to change or check the "
|
||||
"opacity of an object (0-255)."),
|
||||
"",
|
||||
"res/actions/opacity24.png",
|
||||
"OpacityBehavior",
|
||||
std::make_shared<gd::Behavior>(),
|
||||
std::make_shared<gd::BehaviorsSharedData>())
|
||||
.SetHidden();
|
||||
|
||||
aut.AddExpressionAndConditionAndAction(
|
||||
"number",
|
||||
@@ -52,8 +55,9 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsOpacityExtension(
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "OpacityBehavior")
|
||||
.UseStandardParameters(
|
||||
"number", gd::ParameterOptions::MakeNewOptions().SetDescription(
|
||||
_("Opacity (0-255)")))
|
||||
"number",
|
||||
gd::ParameterOptions::MakeNewOptions().SetDescription(
|
||||
_("Opacity (0-255)")))
|
||||
.SetFunctionName("setOpacity")
|
||||
.SetGetter("getOpacity");
|
||||
aut.GetAllExpressions()["Value"].SetGroup("");
|
||||
|
@@ -16,11 +16,13 @@ namespace gd {
|
||||
void GD_CORE_API BuiltinExtensionsImplementer::ImplementsResizableExtension(
|
||||
gd::PlatformExtension &extension) {
|
||||
extension
|
||||
.SetExtensionInformation("ResizableCapability",
|
||||
_("Resizable capability"),
|
||||
_("Change the object dimensions."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionInformation(
|
||||
"ResizableCapability",
|
||||
_("Resizable objects"),
|
||||
_("Change or compare the size (width/height) of an object which can "
|
||||
"be resized (i.e: most objects)."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/objects");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Size")).SetIcon(
|
||||
"res/actions/scale24_black.png");
|
||||
@@ -28,9 +30,10 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsResizableExtension(
|
||||
gd::BehaviorMetadata &aut =
|
||||
extension
|
||||
.AddBehavior("ResizableBehavior",
|
||||
_("Resizable capability"),
|
||||
_("Resizable objects"),
|
||||
"Resizable",
|
||||
_("Change the object dimensions."),
|
||||
_("Change or compare the size (width/height) of an "
|
||||
"object which can be resized (i.e: most objects)."),
|
||||
"",
|
||||
"res/actions/scale24_black.png",
|
||||
"ResizableBehavior",
|
||||
|
@@ -18,27 +18,30 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsScalableExtension(
|
||||
gd::PlatformExtension& extension) {
|
||||
extension
|
||||
.SetExtensionInformation("ScalableCapability",
|
||||
_("Scalable capability"),
|
||||
_("Change the object scale."),
|
||||
_("Scalable objects"),
|
||||
_("Actions/conditions/expression to change or "
|
||||
"check the scale of an object (default: 1)."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/objects");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Scalable capability"))
|
||||
.SetIcon("res/actions/scale24_black.png");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Size"))
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Scalable objects"))
|
||||
.SetIcon("res/actions/scale24_black.png");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Size")).SetIcon(
|
||||
"res/actions/scale24_black.png");
|
||||
|
||||
gd::BehaviorMetadata& aut = extension.AddBehavior(
|
||||
"ScalableBehavior",
|
||||
_("Scalable capability"),
|
||||
"Scale",
|
||||
_("Change the object scale."),
|
||||
"",
|
||||
"res/actions/scale24_black.png",
|
||||
"ResizableBehavior",
|
||||
std::make_shared<gd::Behavior>(),
|
||||
std::make_shared<gd::BehaviorsSharedData>())
|
||||
.SetHidden();
|
||||
gd::BehaviorMetadata& aut =
|
||||
extension
|
||||
.AddBehavior("ScalableBehavior",
|
||||
_("Scalable objects"),
|
||||
"Scale",
|
||||
_("Actions/conditions/expression to change or check the "
|
||||
"scale of an object (default: 1)."),
|
||||
"",
|
||||
"res/actions/scale24_black.png",
|
||||
"ResizableBehavior",
|
||||
std::make_shared<gd::Behavior>(),
|
||||
std::make_shared<gd::BehaviorsSharedData>())
|
||||
.SetHidden();
|
||||
|
||||
aut.AddExpressionAndConditionAndAction(
|
||||
"number",
|
||||
|
@@ -18,17 +18,17 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTextContainerExtension(
|
||||
gd::PlatformExtension& extension) {
|
||||
extension
|
||||
.SetExtensionInformation("TextContainerCapability",
|
||||
_("Text capability"),
|
||||
_("Objects containing a text"),
|
||||
_("Allows an object to contain a text, usually shown on screen, that can be modified."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/objects");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Text capability"))
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Objects containing a text"))
|
||||
.SetIcon("res/conditions/text24_black.png");
|
||||
|
||||
gd::BehaviorMetadata& aut = extension.AddBehavior(
|
||||
"TextContainerBehavior",
|
||||
_("Text capability"),
|
||||
_("Objects containing a text"),
|
||||
"Text",
|
||||
_("Allows an object to contain a text, usually shown on screen, that can be modified."),
|
||||
"",
|
||||
|
@@ -16,7 +16,9 @@ BuiltinExtensionsImplementer::ImplementsCommonConversionsExtension(
|
||||
.SetExtensionInformation(
|
||||
"BuiltinCommonConversions",
|
||||
_("Conversion"),
|
||||
"Expressions to convert number, texts and quantities.",
|
||||
"Expressions to convert numbers to string, strings to numbers, "
|
||||
"angles (degrees from/to radians) and a GDevelop variable to/from a "
|
||||
"JSON string.",
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/all-features/common-conversions");
|
||||
@@ -41,7 +43,7 @@ BuiltinExtensionsImplementer::ImplementsCommonConversionsExtension(
|
||||
|
||||
extension
|
||||
.AddStrExpression("LargeNumberToString",
|
||||
_("Number > Text ( without scientific notation )"),
|
||||
_("Number > Text (without scientific notation)"),
|
||||
_("Convert the result of the expression to text, "
|
||||
"without using the scientific notation"),
|
||||
"",
|
||||
@@ -72,7 +74,8 @@ BuiltinExtensionsImplementer::ImplementsCommonConversionsExtension(
|
||||
_("Convert a variable to JSON"),
|
||||
_("JSON"),
|
||||
"res/conditions/toujours24_black.png")
|
||||
.AddParameter("variable", _("The variable to be stringified"),
|
||||
.AddParameter("variable",
|
||||
_("The variable to be stringified"),
|
||||
"AllowUndeclaredVariable");
|
||||
|
||||
// Deprecated
|
||||
|
@@ -42,13 +42,17 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsFileExtension(
|
||||
extension
|
||||
.AddAction(
|
||||
"LoadFile",
|
||||
_("Load a storage in memory"),
|
||||
_("This action loads the specified storage in memory, so you can "
|
||||
"write and read it.\nYou can open and write without using this "
|
||||
"action, but it will be slower.\nIf you use this action, do not "
|
||||
"forget to unload the storage from memory."),
|
||||
_("Load storage _PARAM0_ in memory"),
|
||||
"",
|
||||
_("Manually preload a storage in memory"),
|
||||
_("Forces the specified storage to be loaded and kept in "
|
||||
"memory, allowing faster reads/writes. "
|
||||
"However, it requires manual management: if you use this "
|
||||
"action, you *must* also unload the storage manually when "
|
||||
"it's no longer needed to ensure data is persisted.\n\n"
|
||||
"Unless you have a specific performance need, avoid using this "
|
||||
"action. The system already handles loading/unloading "
|
||||
"automatically."),
|
||||
_("Load data storage _PARAM0_ in memory"),
|
||||
_("Advanced"),
|
||||
"res/actions/fichier24.png",
|
||||
"res/actions/fichier.png")
|
||||
.AddParameter("string", _("Storage name"))
|
||||
@@ -56,11 +60,11 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsFileExtension(
|
||||
|
||||
extension
|
||||
.AddAction("UnloadFile",
|
||||
_("Close a storage"),
|
||||
_("This action closes the structured data previously loaded "
|
||||
_("Manually unload and persist a storage"),
|
||||
_("Close the specified storage previously loaded "
|
||||
"in memory, saving all changes made."),
|
||||
_("Close structured data _PARAM0_"),
|
||||
"",
|
||||
_("Unload and persist data storage _PARAM0_"),
|
||||
_("Advanced"),
|
||||
"res/actions/fichier24.png",
|
||||
"res/actions/fichier.png")
|
||||
.AddParameter("string", _("Storage name"))
|
||||
|
@@ -15,10 +15,11 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsKeyboardExtension(
|
||||
.SetExtensionInformation(
|
||||
"BuiltinKeyboard",
|
||||
_("Keyboard"),
|
||||
_("Allows your game to respond to keyboard input. Note that this "
|
||||
_("Conditions to check keys pressed on a keyboard. Note that this "
|
||||
"does not work with on-screen keyboard on touch devices: use "
|
||||
"instead conditions related to touch when making a game for "
|
||||
"mobile/touchscreen devices."),
|
||||
"instead mouse/touch conditions when making a game for "
|
||||
"mobile/touchscreen devices or when making a new game from "
|
||||
"scratch."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/all-features/keyboard")
|
||||
@@ -51,10 +52,25 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsKeyboardExtension(
|
||||
.SetHidden();
|
||||
|
||||
extension
|
||||
.AddCondition("KeyFromTextPressed",
|
||||
_("Key pressed"),
|
||||
_("Check if a key is pressed"),
|
||||
_("_PARAM1_ key is pressed"),
|
||||
.AddCondition(
|
||||
"KeyFromTextPressed",
|
||||
_("Key pressed"),
|
||||
_("Check if a key is pressed. This stays true as long as "
|
||||
"the key is held down. To check if a key was pressed during "
|
||||
"the frame, use \"Key just pressed\" instead."),
|
||||
_("_PARAM1_ key is pressed"),
|
||||
"",
|
||||
"res/conditions/keyboard24.png",
|
||||
"res/conditions/keyboard.png")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter("keyboardKey", _("Key to check"))
|
||||
.MarkAsSimple();
|
||||
|
||||
extension
|
||||
.AddCondition("KeyFromTextJustPressed",
|
||||
_("Key just pressed"),
|
||||
_("Check if a key was just pressed."),
|
||||
_("_PARAM1_ key was just pressed"),
|
||||
"",
|
||||
"res/conditions/keyboard24.png",
|
||||
"res/conditions/keyboard.png")
|
||||
@@ -65,7 +81,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsKeyboardExtension(
|
||||
extension
|
||||
.AddCondition("KeyFromTextReleased",
|
||||
_("Key released"),
|
||||
_("Check if a key was just released"),
|
||||
_("Check if a key was just released."),
|
||||
_("_PARAM1_ key is released"),
|
||||
"",
|
||||
"res/conditions/keyboard24.png",
|
||||
@@ -84,7 +100,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsKeyboardExtension(
|
||||
"res/conditions/keyboard.png")
|
||||
.AddCodeOnlyParameter("currentScene", "");
|
||||
|
||||
extension
|
||||
extension
|
||||
.AddCondition("AnyKeyReleased",
|
||||
_("Any key released"),
|
||||
_("Check if any key is released"),
|
||||
|
@@ -16,8 +16,11 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsMouseExtension(
|
||||
.SetExtensionInformation(
|
||||
"BuiltinMouse",
|
||||
_("Mouse and touch"),
|
||||
"Conditions and actions to handle either the mouse or touches on "
|
||||
"touchscreen. By default, conditions related to the mouse will also "
|
||||
"Conditions, actions and expressions to handle either the mouse or "
|
||||
"touches on a touchscreen. Notably: cursor position, mouse wheel, "
|
||||
"mouse buttons, touch positions, started/end touches, etc...\n"
|
||||
"\n"
|
||||
"By default, conditions related to the mouse will also "
|
||||
"handle the touches - so that it's easier to handle both in your "
|
||||
"game. You can disable this behavior if you want to handle them "
|
||||
"separately in different events.",
|
||||
@@ -273,28 +276,26 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsMouseExtension(
|
||||
.SetHidden();
|
||||
|
||||
extension
|
||||
.AddCondition(
|
||||
"MouseButtonFromTextPressed",
|
||||
_("Mouse button pressed or touch held"),
|
||||
_("Check if the specified mouse button is pressed or "
|
||||
"if a touch is in contact with the screen."),
|
||||
_("Touch or _PARAM1_ mouse button is down"),
|
||||
"",
|
||||
"res/conditions/mouse24.png",
|
||||
"res/conditions/mouse.png")
|
||||
.AddCondition("MouseButtonFromTextPressed",
|
||||
_("Mouse button pressed or touch held"),
|
||||
_("Check if the specified mouse button is pressed or "
|
||||
"if a touch is in contact with the screen."),
|
||||
_("Touch or _PARAM1_ mouse button is down"),
|
||||
"",
|
||||
"res/conditions/mouse24.png",
|
||||
"res/conditions/mouse.png")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter("mouseButton", _("Button to check"))
|
||||
.MarkAsSimple();
|
||||
|
||||
extension
|
||||
.AddCondition(
|
||||
"MouseButtonFromTextReleased",
|
||||
_("Mouse button released"),
|
||||
_("Check if the specified mouse button was released."),
|
||||
_("Touch or _PARAM1_ mouse button is released"),
|
||||
"",
|
||||
"res/conditions/mouse24.png",
|
||||
"res/conditions/mouse.png")
|
||||
.AddCondition("MouseButtonFromTextReleased",
|
||||
_("Mouse button released"),
|
||||
_("Check if the specified mouse button was released."),
|
||||
_("Touch or _PARAM1_ mouse button is released"),
|
||||
"",
|
||||
"res/conditions/mouse24.png",
|
||||
"res/conditions/mouse.png")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter("mouseButton", _("Button to check"))
|
||||
.MarkAsSimple();
|
||||
|
@@ -15,8 +15,9 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsNetworkExtension(
|
||||
.SetExtensionInformation(
|
||||
"BuiltinNetwork",
|
||||
_("Network"),
|
||||
_("Features to send web requests, communicate with external \"APIs\" "
|
||||
"and other network related tasks."),
|
||||
_("Actions to send web requests, communicate with external \"APIs\" "
|
||||
"and other network related tasks. Also contains an action to open "
|
||||
"a URL on the device browser."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/all-features/network")
|
||||
|
@@ -4,8 +4,8 @@
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#include "AllBuiltinExtensions.h"
|
||||
#include "GDCore/Tools/Localization.h"
|
||||
#include "GDCore/Extensions/Metadata/MultipleInstructionMetadata.h"
|
||||
#include "GDCore/Tools/Localization.h"
|
||||
|
||||
using namespace std;
|
||||
namespace gd {
|
||||
@@ -16,7 +16,11 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSceneExtension(
|
||||
.SetExtensionInformation(
|
||||
"BuiltinScene",
|
||||
_("Scene"),
|
||||
_("Actions and conditions to manipulate the scenes during the game."),
|
||||
_("Actions/conditions to change the current scene (or pause it and "
|
||||
"launch another one, or go back to the previous one), check if a "
|
||||
"scene or the game has just started/resumed, preload assets of a "
|
||||
"scene, get the current scene name or loading progress, quit the "
|
||||
"game, set background color, or disable input when focus is lost."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("" /*TODO: Add a documentation page for this */);
|
||||
@@ -166,25 +170,28 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSceneExtension(
|
||||
.AddCodeOnlyParameter("currentScene", "");
|
||||
|
||||
extension
|
||||
.AddAction("PrioritizeLoadingOfScene",
|
||||
_("Preload scene"),
|
||||
_("Preload a scene resources as soon as possible in background."),
|
||||
_("Preload scene _PARAM1_ in background"),
|
||||
"",
|
||||
"res/actions/hourglass_black.svg",
|
||||
"res/actions/hourglass_black.svg")
|
||||
.AddAction(
|
||||
"PrioritizeLoadingOfScene",
|
||||
_("Preload scene"),
|
||||
_("Preload a scene resources as soon as possible in background."),
|
||||
_("Preload scene _PARAM1_ in background"),
|
||||
"",
|
||||
"res/actions/hourglass_black.svg",
|
||||
"res/actions/hourglass_black.svg")
|
||||
.SetHelpPath("/all-features/resources-loading")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter("sceneName", _("Name of the new scene"))
|
||||
.MarkAsAdvanced();
|
||||
|
||||
extension.AddExpressionAndCondition("number",
|
||||
"SceneLoadingProgress",
|
||||
_("Scene loading progress"),
|
||||
_("The progress of resources loading in background for a scene (between 0 and 1)."),
|
||||
_("_PARAM1_ loading progress"),
|
||||
_(""),
|
||||
"res/actions/hourglass_black.svg")
|
||||
extension
|
||||
.AddExpressionAndCondition("number",
|
||||
"SceneLoadingProgress",
|
||||
_("Scene loading progress"),
|
||||
_("The progress of resources loading in "
|
||||
"background for a scene (between 0 and 1)."),
|
||||
_("_PARAM1_ loading progress"),
|
||||
_(""),
|
||||
"res/actions/hourglass_black.svg")
|
||||
.SetHelpPath("/all-features/resources-loading")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter("sceneName", _("Scene name"))
|
||||
@@ -192,13 +199,14 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSceneExtension(
|
||||
.MarkAsAdvanced();
|
||||
|
||||
extension
|
||||
.AddCondition("AreSceneAssetsLoaded",
|
||||
_("Scene preloaded"),
|
||||
_("Check if scene resources have finished to load in background."),
|
||||
_("Scene _PARAM1_ was preloaded in background"),
|
||||
"",
|
||||
"res/actions/hourglass_black.svg",
|
||||
"res/actions/hourglass_black.svg")
|
||||
.AddCondition(
|
||||
"AreSceneAssetsLoaded",
|
||||
_("Scene preloaded"),
|
||||
_("Check if scene resources have finished to load in background."),
|
||||
_("Scene _PARAM1_ was preloaded in background"),
|
||||
"",
|
||||
"res/actions/hourglass_black.svg",
|
||||
"res/actions/hourglass_black.svg")
|
||||
.SetHelpPath("/all-features/resources-loading")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter("sceneName", _("Scene name"))
|
||||
|
@@ -15,12 +15,13 @@ namespace gd {
|
||||
void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
|
||||
gd::PlatformExtension& extension) {
|
||||
extension
|
||||
.SetExtensionInformation("Sprite",
|
||||
_("Sprite"),
|
||||
_("Sprite are animated object which can be used "
|
||||
"for most elements of a game."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionInformation(
|
||||
"Sprite",
|
||||
_("Sprite"),
|
||||
_("Sprite are animated objects which can be used "
|
||||
"for most elements of a 2D game."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/objects/sprite");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Sprite"))
|
||||
.SetIcon("CppPlatform/Extensions/spriteicon.png");
|
||||
@@ -30,7 +31,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
|
||||
.AddObject<SpriteObject>("Sprite",
|
||||
_("Sprite"),
|
||||
_("Animated object which can be used for "
|
||||
"most elements of a game."),
|
||||
"most elements of a 2D game."),
|
||||
"CppPlatform/Extensions/spriteicon.png")
|
||||
.SetCategoryFullName(_("General"))
|
||||
.SetOpenFullEditorLabel(_("Edit animations"))
|
||||
@@ -645,11 +646,12 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
|
||||
"res/actions/sprite.png")
|
||||
.AddParameter("object", _("Object"), "Sprite");
|
||||
|
||||
obj.AddExpression("AnimationFrameCount",
|
||||
_("Number of frames"),
|
||||
_("Number of frames in the current animation of the object"),
|
||||
_("Animations and images"),
|
||||
"res/actions/sprite.png")
|
||||
obj.AddExpression(
|
||||
"AnimationFrameCount",
|
||||
_("Number of frames"),
|
||||
_("Number of frames in the current animation of the object"),
|
||||
_("Animations and images"),
|
||||
"res/actions/sprite.png")
|
||||
.AddParameter("object", _("Object"), "Sprite");
|
||||
|
||||
// Deprecated
|
||||
|
@@ -16,7 +16,8 @@ BuiltinExtensionsImplementer::ImplementsStringInstructionsExtension(
|
||||
.SetExtensionInformation(
|
||||
"BuiltinStringInstructions",
|
||||
_("Text manipulation"),
|
||||
"Provides expressions to manipulate strings (also called texts).",
|
||||
"Provides expressions to manipulate strings (also called texts): new "
|
||||
"line, upper/lowercase, substring, find, replace, etc...",
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("" /*TODO: Add a documentation page for this */);
|
||||
@@ -191,7 +192,8 @@ BuiltinExtensionsImplementer::ImplementsStringInstructionsExtension(
|
||||
"res/conditions/toujours24_black.png")
|
||||
.AddParameter("string", _("Text in which the replacement must be done"))
|
||||
.AddParameter("string", _("Text to find inside the first text"))
|
||||
.AddParameter("string", _("Replacement to put instead of the text to find"));
|
||||
.AddParameter("string",
|
||||
_("Replacement to put instead of the text to find"));
|
||||
|
||||
extension
|
||||
.AddStrExpression("StrReplaceAll",
|
||||
@@ -199,10 +201,11 @@ BuiltinExtensionsImplementer::ImplementsStringInstructionsExtension(
|
||||
_("Replace all occurrences of a text by another."),
|
||||
"",
|
||||
"res/conditions/toujours24_black.png")
|
||||
.AddParameter("string", _("Text in which the replacement(s) must be done"))
|
||||
.AddParameter("string",
|
||||
_("Text in which the replacement(s) must be done"))
|
||||
.AddParameter("string", _("Text to find inside the first text"))
|
||||
.AddParameter("string", _("Replacement to put instead of the text to find"));
|
||||
|
||||
.AddParameter("string",
|
||||
_("Replacement to put instead of the text to find"));
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -15,9 +15,12 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
|
||||
.SetExtensionInformation(
|
||||
"BuiltinTime",
|
||||
_("Timers and time"),
|
||||
"Actions and conditions to run timers, get the current time or "
|
||||
"modify the time scale (speed at which the game is running - useful "
|
||||
"for slow motion effects).",
|
||||
"Actions and conditions to start, pause or reset scene timers, "
|
||||
"modify the time scale (speed at which the game "
|
||||
"is running - useful for slow motion effects). Also contains an "
|
||||
"action that wait for a delay before running the next actions and "
|
||||
"sub-events and expressions to read the time scale, time delta of "
|
||||
"the last frame or timer elapsed time.",
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/all-features/timers-and-time");
|
||||
@@ -192,26 +195,28 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
|
||||
extension
|
||||
.AddExpression("TimerElapsedTime",
|
||||
_("Scene timer value"),
|
||||
_("Value of a scene timer"),
|
||||
_("Value of a scene timer (in seconds)"),
|
||||
"",
|
||||
"res/actions/time.png")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter("identifier", _("Timer's name"), "sceneTimer");
|
||||
|
||||
extension
|
||||
.AddExpression("TimeFromStart",
|
||||
_("Time elapsed since the beginning of the scene"),
|
||||
_("Time elapsed since the beginning of the scene"),
|
||||
"",
|
||||
"res/actions/time.png")
|
||||
.AddExpression(
|
||||
"TimeFromStart",
|
||||
_("Time elapsed since the beginning of the scene (in seconds)."),
|
||||
_("Time elapsed since the beginning of the scene (in seconds)."),
|
||||
"",
|
||||
"res/actions/time.png")
|
||||
.AddCodeOnlyParameter("currentScene", "");
|
||||
|
||||
extension
|
||||
.AddExpression("TempsDebut",
|
||||
_("Time elapsed since the beginning of the scene"),
|
||||
_("Time elapsed since the beginning of the scene"),
|
||||
"",
|
||||
"res/actions/time.png")
|
||||
.AddExpression(
|
||||
"TempsDebut",
|
||||
_("Time elapsed since the beginning of the scene (in seconds)."),
|
||||
_("Time elapsed since the beginning of the scene (in seconds)."),
|
||||
"",
|
||||
"res/actions/time.png")
|
||||
.SetHidden()
|
||||
.AddCodeOnlyParameter("currentScene", "");
|
||||
|
||||
@@ -226,16 +231,21 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
|
||||
extension
|
||||
.AddExpression("Time",
|
||||
_("Current time"),
|
||||
_("Current time"),
|
||||
_("Gives the current time"),
|
||||
"",
|
||||
"res/actions/time.png")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter(
|
||||
"stringWithSelector",
|
||||
_("Hour: hour - Minutes: min - Seconds: sec - Day of month: "
|
||||
"mday - Months since January: mon - Year since 1900: year - Days "
|
||||
"since Sunday: wday - Days since Jan 1st: yday - Timestamp (ms): "
|
||||
"timestamp\""),
|
||||
_("- Hour of the day: \"hour\"\n"
|
||||
"- Minutes: \"min\"\n"
|
||||
"- Seconds: \"sec\"\n"
|
||||
"- Day of month: \"mday\"\n"
|
||||
"- Months since January: \"mon\"\n"
|
||||
"- Year since 1900: \"year\"\n"
|
||||
"- Days since Sunday: \"wday\"\n"
|
||||
"- Days since Jan 1st: \"yday\"\n"
|
||||
"- Timestamp (ms): \"timestamp\""),
|
||||
"[\"hour\", \"min\", \"sec\", \"mon\", \"year\", \"wday\", \"mday\", "
|
||||
"\"yday\", \"timestamp\"]");
|
||||
}
|
||||
|
@@ -15,16 +15,17 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsWindowExtension(
|
||||
.SetExtensionInformation(
|
||||
"BuiltinWindow",
|
||||
_("Game window and resolution"),
|
||||
"Provides actions and conditions to manipulate the game window. "
|
||||
"Actions and conditions to manipulate the game window or change how "
|
||||
"the game is resized according to the screen size. "
|
||||
"Depending on the platform on which the game is running, not all of "
|
||||
"these features can be applied.",
|
||||
"these features can be applied.\n"
|
||||
"Also contains expressions to read the screen size.",
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetCategory("User interface")
|
||||
.SetExtensionHelpPath("/all-features/window");
|
||||
extension
|
||||
.AddInstructionOrExpressionGroupMetadata(
|
||||
_("Game window and resolution"))
|
||||
.AddInstructionOrExpressionGroupMetadata(_("Game window and resolution"))
|
||||
.SetIcon("res/actions/window24.png");
|
||||
|
||||
extension
|
||||
|
@@ -298,6 +298,19 @@ class GD_CORE_API BehaviorMetadata : public InstructionOrExpressionContainerMeta
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the behavior can be used on objects from event-based objects.
|
||||
*/
|
||||
bool IsRelevantForChildObjects() const { return isRelevantForChildObjects; }
|
||||
|
||||
/**
|
||||
* Set that behavior can't be used on objects from event-based objects.
|
||||
*/
|
||||
BehaviorMetadata &MarkAsIrrelevantForChildObjects() {
|
||||
isRelevantForChildObjects = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
QuickCustomization::Visibility GetQuickCustomizationVisibility() const {
|
||||
return quickCustomizationVisibility;
|
||||
}
|
||||
@@ -393,6 +406,7 @@ class GD_CORE_API BehaviorMetadata : public InstructionOrExpressionContainerMeta
|
||||
mutable std::vector<gd::String> requiredBehaviors;
|
||||
bool isPrivate = false;
|
||||
bool isHidden = false;
|
||||
bool isRelevantForChildObjects = true;
|
||||
gd::String openFullEditorLabel;
|
||||
QuickCustomization::Visibility quickCustomizationVisibility = QuickCustomization::Visibility::Default;
|
||||
|
||||
|
@@ -277,6 +277,10 @@ class GD_CORE_API MetadataProvider {
|
||||
return &metadata == &badObjectInfo;
|
||||
}
|
||||
|
||||
static bool IsBadEffectMetadata(const gd::EffectMetadata& metadata) {
|
||||
return &metadata == &badEffectMetadata;
|
||||
}
|
||||
|
||||
virtual ~MetadataProvider();
|
||||
|
||||
private:
|
||||
|
@@ -194,7 +194,8 @@ void ParameterMetadataTools::IterateOverParameters(
|
||||
[&fn](const gd::ParameterMetadata& parameterMetadata,
|
||||
const gd::Expression& parameterValue,
|
||||
size_t parameterIndex,
|
||||
const gd::String& lastObjectName) {
|
||||
const gd::String& lastObjectName,
|
||||
size_t lastObjectIndex) {
|
||||
fn(parameterMetadata, parameterValue, lastObjectName);
|
||||
});
|
||||
}
|
||||
@@ -205,8 +206,10 @@ void ParameterMetadataTools::IterateOverParametersWithIndex(
|
||||
std::function<void(const gd::ParameterMetadata& parameterMetadata,
|
||||
const gd::Expression& parameterValue,
|
||||
size_t parameterIndex,
|
||||
const gd::String& lastObjectName)> fn) {
|
||||
const gd::String& lastObjectName,
|
||||
size_t lastObjectIndex)> fn) {
|
||||
gd::String lastObjectName = "";
|
||||
size_t lastObjectIndex = 0;
|
||||
for (std::size_t pNb = 0; pNb < parametersMetadata.GetParametersCount();
|
||||
++pNb) {
|
||||
const gd::ParameterMetadata ¶meterMetadata =
|
||||
@@ -218,15 +221,17 @@ void ParameterMetadataTools::IterateOverParametersWithIndex(
|
||||
? Expression(parameterMetadata.GetDefaultValue())
|
||||
: parameterValue;
|
||||
|
||||
fn(parameterMetadata, parameterValueOrDefault, pNb, lastObjectName);
|
||||
fn(parameterMetadata, parameterValueOrDefault, pNb, lastObjectName, lastObjectIndex);
|
||||
|
||||
// Memorize the last object name. 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.
|
||||
if (gd::ParameterMetadata::IsObject(parameterMetadata.GetType()))
|
||||
if (gd::ParameterMetadata::IsObject(parameterMetadata.GetType())) {
|
||||
lastObjectName = parameterValueOrDefault.GetPlainString();
|
||||
lastObjectIndex = pNb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -64,7 +64,8 @@ class GD_CORE_API ParameterMetadataTools {
|
||||
std::function<void(const gd::ParameterMetadata& parameterMetadata,
|
||||
const gd::Expression& parameterValue,
|
||||
size_t parameterIndex,
|
||||
const gd::String& lastObjectName)> fn);
|
||||
const gd::String& lastObjectName,
|
||||
size_t lastObjectIndex)> fn);
|
||||
|
||||
/**
|
||||
* Iterate over the parameters of a FunctionCallNode.
|
||||
|
@@ -29,7 +29,7 @@ bool BehaviorParametersFiller::DoVisitInstruction(gd::Instruction &instruction,
|
||||
instruction.GetParameters(), metadata.GetParameters(),
|
||||
[&](const gd::ParameterMetadata ¶meterMetadata,
|
||||
const gd::Expression ¶meterValue, size_t parameterIndex,
|
||||
const gd::String &lastObjectName) {
|
||||
const gd::String &lastObjectName, size_t lastObjectIndex) {
|
||||
if (parameterMetadata.GetValueTypeMetadata().IsBehavior() &&
|
||||
parameterValue.GetPlainString().length() == 0) {
|
||||
|
||||
|
@@ -108,12 +108,10 @@ bool EventsBehaviorRenamer::DoVisitInstruction(gd::Instruction& instruction,
|
||||
platform, instruction.GetType());
|
||||
|
||||
gd::ParameterMetadataTools::IterateOverParametersWithIndex(
|
||||
instruction.GetParameters(),
|
||||
metadata.GetParameters(),
|
||||
[&](const gd::ParameterMetadata& parameterMetadata,
|
||||
const gd::Expression& parameterValue,
|
||||
size_t parameterIndex,
|
||||
const gd::String& lastObjectName) {
|
||||
instruction.GetParameters(), metadata.GetParameters(),
|
||||
[&](const gd::ParameterMetadata ¶meterMetadata,
|
||||
const gd::Expression ¶meterValue, size_t parameterIndex,
|
||||
const gd::String &lastObjectName, size_t lastObjectIndex) {
|
||||
const gd::String& type = parameterMetadata.GetType();
|
||||
|
||||
if (gd::ParameterMetadata::IsBehavior(type)) {
|
||||
|
@@ -183,12 +183,10 @@ bool EventsParameterReplacer::DoVisitInstruction(gd::Instruction& instruction,
|
||||
platform, instruction.GetType());
|
||||
|
||||
gd::ParameterMetadataTools::IterateOverParametersWithIndex(
|
||||
instruction.GetParameters(),
|
||||
metadata.GetParameters(),
|
||||
[&](const gd::ParameterMetadata& parameterMetadata,
|
||||
const gd::Expression& parameterValue,
|
||||
size_t parameterIndex,
|
||||
const gd::String& lastObjectName) {
|
||||
instruction.GetParameters(), metadata.GetParameters(),
|
||||
[&](const gd::ParameterMetadata ¶meterMetadata,
|
||||
const gd::Expression ¶meterValue, size_t parameterIndex,
|
||||
const gd::String &lastObjectName, size_t lastObjectIndex) {
|
||||
if (!gd::EventsParameterReplacer::CanContainParameter(
|
||||
parameterMetadata.GetValueTypeMetadata())) {
|
||||
return;
|
||||
|
@@ -217,12 +217,10 @@ bool EventsPropertyReplacer::DoVisitInstruction(gd::Instruction& instruction,
|
||||
bool shouldDeleteInstruction = false;
|
||||
|
||||
gd::ParameterMetadataTools::IterateOverParametersWithIndex(
|
||||
instruction.GetParameters(),
|
||||
metadata.GetParameters(),
|
||||
[&](const gd::ParameterMetadata& parameterMetadata,
|
||||
const gd::Expression& parameterValue,
|
||||
size_t parameterIndex,
|
||||
const gd::String& lastObjectName) {
|
||||
instruction.GetParameters(), metadata.GetParameters(),
|
||||
[&](const gd::ParameterMetadata ¶meterMetadata,
|
||||
const gd::Expression ¶meterValue, size_t parameterIndex,
|
||||
const gd::String &lastObjectName, size_t lastObjectIndex) {
|
||||
if (!gd::EventsPropertyReplacer::CanContainProperty(
|
||||
parameterMetadata.GetValueTypeMetadata())) {
|
||||
return;
|
||||
|
@@ -334,7 +334,7 @@ private:
|
||||
instruction.GetParameters(), metadata.GetParameters(),
|
||||
[&](const gd::ParameterMetadata ¶meterMetadata,
|
||||
const gd::Expression ¶meterValue, size_t parameterIndex,
|
||||
const gd::String &lastObjectName) {
|
||||
const gd::String &lastObjectName, size_t lastObjectIndex) {
|
||||
if (!gd::EventsObjectReplacer::CanContainObject(
|
||||
parameterMetadata.GetValueTypeMetadata())) {
|
||||
return;
|
||||
|
@@ -42,18 +42,16 @@ bool EventsVariableInstructionTypeSwitcher::DoVisitInstruction(gd::Instruction&
|
||||
platform, instruction.GetType());
|
||||
|
||||
gd::ParameterMetadataTools::IterateOverParametersWithIndex(
|
||||
instruction.GetParameters(),
|
||||
metadata.GetParameters(),
|
||||
[&](const gd::ParameterMetadata& parameterMetadata,
|
||||
const gd::Expression& parameterValue,
|
||||
size_t parameterIndex,
|
||||
const gd::String& lastObjectName) {
|
||||
instruction.GetParameters(), metadata.GetParameters(),
|
||||
[&](const gd::ParameterMetadata ¶meterMetadata,
|
||||
const gd::Expression ¶meterValue, size_t parameterIndex,
|
||||
const gd::String &lastObjectName, size_t lastObjectIndex) {
|
||||
const gd::String& type = parameterMetadata.GetType();
|
||||
|
||||
if (!gd::ParameterMetadata::IsExpression("variable", type) ||
|
||||
!gd::VariableInstructionSwitcher::IsSwitchableVariableInstruction(
|
||||
instruction.GetType())) {
|
||||
return;
|
||||
return;
|
||||
}
|
||||
const auto variableName =
|
||||
gd::ExpressionVariableNameFinder::GetVariableName(
|
||||
@@ -72,10 +70,11 @@ bool EventsVariableInstructionTypeSwitcher::DoVisitInstruction(gd::Instruction&
|
||||
.GetObjectOrGroupVariablesContainer(lastObjectName);
|
||||
}
|
||||
} else if (type == "variableOrProperty") {
|
||||
variablesContainer =
|
||||
&GetProjectScopedContainers()
|
||||
.GetVariablesContainersList()
|
||||
.GetVariablesContainerFromVariableOrPropertyName(variableName);
|
||||
variablesContainer =
|
||||
&GetProjectScopedContainers()
|
||||
.GetVariablesContainersList()
|
||||
.GetVariablesContainerFromVariableOrPropertyName(
|
||||
variableName);
|
||||
} else {
|
||||
if (GetProjectScopedContainers().GetVariablesContainersList().Has(
|
||||
variableName)) {
|
||||
|
@@ -448,12 +448,10 @@ bool EventsVariableReplacer::DoVisitInstruction(gd::Instruction& instruction,
|
||||
bool shouldDeleteInstruction = false;
|
||||
|
||||
gd::ParameterMetadataTools::IterateOverParametersWithIndex(
|
||||
instruction.GetParameters(),
|
||||
metadata.GetParameters(),
|
||||
[&](const gd::ParameterMetadata& parameterMetadata,
|
||||
const gd::Expression& parameterValue,
|
||||
size_t parameterIndex,
|
||||
const gd::String& lastObjectName) {
|
||||
instruction.GetParameters(), metadata.GetParameters(),
|
||||
[&](const gd::ParameterMetadata ¶meterMetadata,
|
||||
const gd::Expression ¶meterValue, size_t parameterIndex,
|
||||
const gd::String &lastObjectName, size_t lastObjectIndex) {
|
||||
const gd::String& type = parameterMetadata.GetType();
|
||||
|
||||
if (!gd::ParameterMetadata::IsExpression("variable", type) &&
|
||||
|
@@ -150,7 +150,7 @@ bool ProjectElementRenamer::DoVisitInstruction(gd::Instruction &instruction,
|
||||
instruction.GetParameters(), metadata.GetParameters(),
|
||||
[&](const gd::ParameterMetadata ¶meterMetadata,
|
||||
const gd::Expression ¶meterValue, size_t parameterIndex,
|
||||
const gd::String &lastObjectName) {
|
||||
const gd::String &lastObjectName, size_t lastObjectIndex) {
|
||||
if (parameterMetadata.GetType() == "layer") {
|
||||
if (parameterValue.GetPlainString().length() < 2) {
|
||||
// This is either the base layer or an invalid layer name.
|
||||
|
@@ -63,7 +63,6 @@ void EventsBasedObjectVariantHelper::ComplyVariantsToEventsBasedObject(
|
||||
}
|
||||
|
||||
// Copy missing behaviors
|
||||
auto &behaviors = object.GetAllBehaviorContents();
|
||||
for (const auto &pair : defaultBehaviors) {
|
||||
const auto &behaviorName = pair.first;
|
||||
const auto &defaultBehavior = pair.second;
|
||||
@@ -82,11 +81,9 @@ void EventsBasedObjectVariantHelper::ComplyVariantsToEventsBasedObject(
|
||||
}
|
||||
}
|
||||
// Delete extra behaviors
|
||||
for (auto it = behaviors.begin(); it != behaviors.end(); ++it) {
|
||||
const auto &behaviorName = it->first;
|
||||
for (auto &behaviorName : object.GetAllBehaviorNames()) {
|
||||
if (!defaultObject->HasBehaviorNamed(behaviorName)) {
|
||||
object.RemoveBehavior(behaviorName);
|
||||
--it;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -76,6 +76,7 @@ void ObjectAssetSerializer::SerializeTo(
|
||||
|
||||
double width = 0;
|
||||
double height = 0;
|
||||
std::unordered_set<gd::String> alreadyUsedVariantIdentifiers;
|
||||
if (project.HasEventsBasedObject(object.GetType())) {
|
||||
SerializerElement &variantsElement =
|
||||
objectAssetElement.AddChild("variants");
|
||||
@@ -87,7 +88,6 @@ void ObjectAssetSerializer::SerializeTo(
|
||||
height = variant->GetAreaMaxY() - variant->GetAreaMinY();
|
||||
}
|
||||
|
||||
std::unordered_set<gd::String> alreadyUsedVariantIdentifiers;
|
||||
gd::ObjectAssetSerializer::SerializeUsedVariantsTo(
|
||||
project, object, variantsElement, alreadyUsedVariantIdentifiers);
|
||||
}
|
||||
@@ -114,14 +114,24 @@ void ObjectAssetSerializer::SerializeTo(
|
||||
resourceElement.SetAttribute("name", resource.GetName());
|
||||
}
|
||||
|
||||
std::unordered_set<gd::String> usedExtensionNames;
|
||||
usedExtensionNames.insert(extensionName);
|
||||
for (auto &usedVariantIdentifier : alreadyUsedVariantIdentifiers) {
|
||||
usedExtensionNames.insert(PlatformExtension::GetExtensionFromFullObjectType(
|
||||
usedVariantIdentifier));
|
||||
}
|
||||
SerializerElement &requiredExtensionsElement =
|
||||
objectAssetElement.AddChild("requiredExtensions");
|
||||
requiredExtensionsElement.ConsiderAsArrayOf("requiredExtension");
|
||||
if (project.HasEventsFunctionsExtensionNamed(extensionName)) {
|
||||
SerializerElement &requiredExtensionElement =
|
||||
requiredExtensionsElement.AddChild("requiredExtension");
|
||||
requiredExtensionElement.SetAttribute("extensionName", extensionName);
|
||||
requiredExtensionElement.SetAttribute("extensionVersion", "1.0.0");
|
||||
for (auto &usedExtensionName : usedExtensionNames) {
|
||||
if (project.HasEventsFunctionsExtensionNamed(usedExtensionName)) {
|
||||
auto &extension = project.GetEventsFunctionsExtension(usedExtensionName);
|
||||
SerializerElement &requiredExtensionElement =
|
||||
requiredExtensionsElement.AddChild("requiredExtension");
|
||||
requiredExtensionElement.SetAttribute("extensionName", usedExtensionName);
|
||||
requiredExtensionElement.SetAttribute("extensionVersion",
|
||||
extension.GetVersion());
|
||||
}
|
||||
}
|
||||
|
||||
// TODO This can be removed when the asset script no longer require it.
|
||||
|
@@ -227,12 +227,11 @@ bool ResourceWorkerInEventsWorker::DoVisitInstruction(gd::Instruction& instructi
|
||||
platform, instruction.GetType());
|
||||
|
||||
gd::ParameterMetadataTools::IterateOverParametersWithIndex(
|
||||
instruction.GetParameters(),
|
||||
metadata.GetParameters(),
|
||||
[this, &instruction](const gd::ParameterMetadata& parameterMetadata,
|
||||
const gd::Expression& parameterExpression,
|
||||
size_t parameterIndex,
|
||||
const gd::String& lastObjectName) {
|
||||
instruction.GetParameters(), metadata.GetParameters(),
|
||||
[this, &instruction](
|
||||
const gd::ParameterMetadata ¶meterMetadata,
|
||||
const gd::Expression ¶meterExpression, size_t parameterIndex,
|
||||
const gd::String &lastObjectName, size_t lastObjectIndex) {
|
||||
const String& parameterValue = parameterExpression.GetPlainString();
|
||||
if (parameterMetadata.GetType() == "fontResource") {
|
||||
gd::String updatedParameterValue = parameterValue;
|
||||
|
@@ -248,12 +248,13 @@ gd::String PropertyFunctionGenerator::GetStringifiedExtraInfo(
|
||||
gd::String arrayString;
|
||||
arrayString += "[";
|
||||
bool isFirst = true;
|
||||
for (const gd::String &choice : property.GetExtraInfo()) {
|
||||
for (const auto &choice : property.GetChoices()) {
|
||||
if (!isFirst) {
|
||||
arrayString += ",";
|
||||
}
|
||||
isFirst = false;
|
||||
arrayString += "\"" + choice + "\"";
|
||||
// TODO Handle labels (and search "choice label")
|
||||
arrayString += "\"" + choice.GetValue() + "\"";
|
||||
}
|
||||
arrayString += "]";
|
||||
return arrayString;
|
||||
|
@@ -75,6 +75,17 @@ void ResourceExposer::ExposeProjectResources(
|
||||
// Expose global objects configuration resources
|
||||
auto objectWorker = gd::GetResourceWorkerOnObjects(project, worker);
|
||||
objectWorker.Launch(project.GetObjects());
|
||||
|
||||
// Exposed extension event resources
|
||||
// Note that using resources in extensions is very unlikely and probably not
|
||||
// worth the effort of something smart.
|
||||
auto eventWorker = gd::GetResourceWorkerOnEvents(project, worker);
|
||||
for (std::size_t e = 0; e < project.GetEventsFunctionsExtensionsCount();
|
||||
e++) {
|
||||
auto &eventsFunctionsExtension = project.GetEventsFunctionsExtension(e);
|
||||
gd::ProjectBrowserHelper::ExposeEventsFunctionsExtensionEvents(
|
||||
project, eventsFunctionsExtension, eventWorker);
|
||||
}
|
||||
}
|
||||
|
||||
void ResourceExposer::ExposeLayoutResources(
|
||||
@@ -103,16 +114,6 @@ void ResourceExposer::ExposeLayoutResources(
|
||||
auto eventWorker = gd::GetResourceWorkerOnEvents(project, worker);
|
||||
gd::ProjectBrowserHelper::ExposeLayoutEventsAndDependencies(
|
||||
project, layout, eventWorker);
|
||||
|
||||
// Exposed extension event resources
|
||||
// Note that using resources in extensions is very unlikely and probably not
|
||||
// worth the effort of something smart.
|
||||
for (std::size_t e = 0; e < project.GetEventsFunctionsExtensionsCount();
|
||||
e++) {
|
||||
auto &eventsFunctionsExtension = project.GetEventsFunctionsExtension(e);
|
||||
gd::ProjectBrowserHelper::ExposeEventsFunctionsExtensionEvents(
|
||||
project, eventsFunctionsExtension, eventWorker);
|
||||
}
|
||||
}
|
||||
|
||||
void ResourceExposer::ExposeEffectResources(
|
||||
|
@@ -8,6 +8,8 @@
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
gd::String Effect::badStringParameterValue;
|
||||
|
||||
void Effect::SerializeTo(SerializerElement& element) const {
|
||||
element.SetAttribute("name", GetName());
|
||||
|
@@ -34,39 +34,42 @@ class GD_CORE_API Effect {
|
||||
void SetFolded(bool fold = true) { folded = fold; }
|
||||
bool IsFolded() const { return folded; }
|
||||
|
||||
void SetDoubleParameter(const gd::String& name, double value) {
|
||||
void SetDoubleParameter(const gd::String &name, double value) {
|
||||
doubleParameters[name] = value;
|
||||
}
|
||||
|
||||
double GetDoubleParameter(const gd::String& name) {
|
||||
return doubleParameters[name];
|
||||
double GetDoubleParameter(const gd::String &name) const {
|
||||
auto itr = doubleParameters.find(name);
|
||||
return itr == doubleParameters.end() ? 0 : itr->second;
|
||||
}
|
||||
|
||||
bool HasDoubleParameter(const gd::String& name) {
|
||||
bool HasDoubleParameter(const gd::String &name) const {
|
||||
return doubleParameters.find(name) != doubleParameters.end();
|
||||
}
|
||||
|
||||
void SetStringParameter(const gd::String& name, const gd::String& value) {
|
||||
void SetStringParameter(const gd::String &name, const gd::String &value) {
|
||||
stringParameters[name] = value;
|
||||
}
|
||||
|
||||
const gd::String& GetStringParameter(const gd::String& name) {
|
||||
return stringParameters[name];
|
||||
const gd::String &GetStringParameter(const gd::String &name) const {
|
||||
auto itr = stringParameters.find(name);
|
||||
return itr == stringParameters.end() ? badStringParameterValue : itr->second;
|
||||
}
|
||||
|
||||
bool HasStringParameter(const gd::String& name) {
|
||||
bool HasStringParameter(const gd::String &name) const {
|
||||
return stringParameters.find(name) != stringParameters.end();
|
||||
}
|
||||
|
||||
void SetBooleanParameter(const gd::String& name, bool value) {
|
||||
void SetBooleanParameter(const gd::String &name, bool value) {
|
||||
booleanParameters[name] = value;
|
||||
}
|
||||
|
||||
bool GetBooleanParameter(const gd::String& name) {
|
||||
return booleanParameters[name];
|
||||
bool GetBooleanParameter(const gd::String &name) const {
|
||||
auto itr = booleanParameters.find(name);
|
||||
return itr == booleanParameters.end() ? false : itr->second;
|
||||
}
|
||||
|
||||
bool HasBooleanParameter(const gd::String& name) {
|
||||
bool HasBooleanParameter(const gd::String &name) const {
|
||||
return booleanParameters.find(name) != booleanParameters.end();
|
||||
}
|
||||
|
||||
@@ -105,6 +108,9 @@ class GD_CORE_API Effect {
|
||||
std::map<gd::String, double> doubleParameters; ///< Values of parameters being doubles, keyed by names.
|
||||
std::map<gd::String, gd::String> stringParameters; ///< Values of parameters being strings, keyed by names.
|
||||
std::map<gd::String, bool> booleanParameters; ///< Values of parameters being booleans, keyed by names.
|
||||
|
||||
static gd::String badStringParameterValue; ///< Empty string returned by
|
||||
///< GeStringParameter
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -34,6 +34,20 @@ void PropertyDescriptor::SerializeTo(SerializerElement& element) const {
|
||||
}
|
||||
}
|
||||
|
||||
if (!choices.empty()
|
||||
// Compatibility with GD <= 5.5.239
|
||||
|| !extraInformation.empty()
|
||||
// end of compatibility code
|
||||
) {
|
||||
SerializerElement &choicesElement = element.AddChild("choices");
|
||||
choicesElement.ConsiderAsArrayOf("choice");
|
||||
for (const auto &choice : choices) {
|
||||
auto &choiceElement = choicesElement.AddChild("Choice");
|
||||
choiceElement.SetStringAttribute("value", choice.GetValue());
|
||||
choiceElement.SetStringAttribute("label", choice.GetLabel());
|
||||
}
|
||||
}
|
||||
|
||||
if (hidden) {
|
||||
element.AddChild("hidden").SetBoolValue(hidden);
|
||||
}
|
||||
@@ -80,6 +94,26 @@ void PropertyDescriptor::UnserializeFrom(const SerializerElement& element) {
|
||||
extraInformationElement.GetChild(i).GetStringValue());
|
||||
}
|
||||
|
||||
if (element.HasChild("choices")) {
|
||||
choices.clear();
|
||||
const SerializerElement &choicesElement = element.GetChild("choices");
|
||||
choicesElement.ConsiderAsArrayOf("choice");
|
||||
for (std::size_t i = 0; i < choicesElement.GetChildrenCount(); ++i) {
|
||||
auto &choiceElement = choicesElement.GetChild(i);
|
||||
AddChoice(choiceElement.GetStringAttribute("value"),
|
||||
choiceElement.GetStringAttribute("label"));
|
||||
}
|
||||
}
|
||||
// Compatibility with GD <= 5.5.239
|
||||
else if (type == "Choice") {
|
||||
choices.clear();
|
||||
for (auto &choiceValue : extraInformation) {
|
||||
AddChoice(choiceValue, choiceValue);
|
||||
}
|
||||
extraInformation.clear();
|
||||
}
|
||||
// end of compatibility code
|
||||
|
||||
hidden = element.HasChild("hidden")
|
||||
? element.GetChild("hidden").GetBoolValue()
|
||||
: false;
|
||||
|
@@ -116,6 +116,11 @@ class GD_CORE_API PropertyDescriptor {
|
||||
return *this;
|
||||
}
|
||||
|
||||
PropertyDescriptor& ClearChoices() {
|
||||
choices.clear();
|
||||
return *this;
|
||||
}
|
||||
|
||||
PropertyDescriptor& AddChoice(const gd::String& value,
|
||||
const gd::String& label) {
|
||||
choices.push_back(PropertyDescriptorChoice(value, label));
|
||||
|
@@ -764,129 +764,6 @@ TEST_CASE("ArbitraryResourceWorker", "[common][resources]") {
|
||||
REQUIRE(worker.audios[0] == "res4");
|
||||
}
|
||||
|
||||
SECTION("Can find resource usages in event-based functions") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
|
||||
project.GetResourcesManager().AddResource(
|
||||
"res1", "path/to/file1.png", "image");
|
||||
project.GetResourcesManager().AddResource(
|
||||
"res2", "path/to/file2.png", "image");
|
||||
project.GetResourcesManager().AddResource(
|
||||
"res3", "path/to/file3.png", "image");
|
||||
ArbitraryResourceWorkerTest worker(project.GetResourcesManager());
|
||||
|
||||
auto& extension = project.InsertNewEventsFunctionsExtension("MyEventExtension", 0);
|
||||
auto &function = extension.GetEventsFunctions().InsertNewEventsFunction(
|
||||
"MyFreeFunction", 0);
|
||||
|
||||
gd::StandardEvent standardEvent;
|
||||
gd::Instruction instruction;
|
||||
instruction.SetType("MyExtension::DoSomethingWithResources");
|
||||
instruction.SetParametersCount(3);
|
||||
instruction.SetParameter(0, "res3");
|
||||
instruction.SetParameter(1, "res1");
|
||||
instruction.SetParameter(2, "res4");
|
||||
standardEvent.GetActions().Insert(instruction);
|
||||
function.GetEvents().InsertEvent(standardEvent);
|
||||
|
||||
auto& layout = project.InsertNewLayout("MyScene", 0);
|
||||
|
||||
// MyEventExtension::MyFreeFunction doesn't need to be actually used in
|
||||
// events because the implementation is naive.
|
||||
|
||||
gd::ResourceExposer::ExposeLayoutResources(project, layout, worker);
|
||||
REQUIRE(worker.bitmapFonts.size() == 1);
|
||||
REQUIRE(worker.bitmapFonts[0] == "res3");
|
||||
REQUIRE(worker.images.size() == 1);
|
||||
REQUIRE(worker.images[0] == "res1");
|
||||
REQUIRE(worker.audios.size() == 1);
|
||||
REQUIRE(worker.audios[0] == "res4");
|
||||
}
|
||||
|
||||
SECTION("Can find resource usages in event-based behavior functions") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
|
||||
project.GetResourcesManager().AddResource(
|
||||
"res1", "path/to/file1.png", "image");
|
||||
project.GetResourcesManager().AddResource(
|
||||
"res2", "path/to/file2.png", "image");
|
||||
project.GetResourcesManager().AddResource(
|
||||
"res3", "path/to/file3.png", "image");
|
||||
ArbitraryResourceWorkerTest worker(project.GetResourcesManager());
|
||||
|
||||
auto& extension = project.InsertNewEventsFunctionsExtension("MyEventExtension", 0);
|
||||
auto& behavior = extension.GetEventsBasedBehaviors().InsertNew("MyBehavior", 0);
|
||||
auto& function = behavior.GetEventsFunctions().InsertNewEventsFunction("MyFunction", 0);
|
||||
|
||||
gd::StandardEvent standardEvent;
|
||||
gd::Instruction instruction;
|
||||
instruction.SetType("MyExtension::DoSomethingWithResources");
|
||||
instruction.SetParametersCount(3);
|
||||
instruction.SetParameter(0, "res3");
|
||||
instruction.SetParameter(1, "res1");
|
||||
instruction.SetParameter(2, "res4");
|
||||
standardEvent.GetActions().Insert(instruction);
|
||||
function.GetEvents().InsertEvent(standardEvent);
|
||||
|
||||
auto& layout = project.InsertNewLayout("MyScene", 0);
|
||||
|
||||
// MyEventExtension::MyBehavior::MyFunction doesn't need to be actually used in
|
||||
// events because the implementation is naive.
|
||||
|
||||
gd::ResourceExposer::ExposeLayoutResources(project, layout, worker);
|
||||
REQUIRE(worker.bitmapFonts.size() == 1);
|
||||
REQUIRE(worker.bitmapFonts[0] == "res3");
|
||||
REQUIRE(worker.images.size() == 1);
|
||||
REQUIRE(worker.images[0] == "res1");
|
||||
REQUIRE(worker.audios.size() == 1);
|
||||
REQUIRE(worker.audios[0] == "res4");
|
||||
}
|
||||
|
||||
SECTION("Can find resource usages in event-based object functions") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
|
||||
project.GetResourcesManager().AddResource(
|
||||
"res1", "path/to/file1.png", "image");
|
||||
project.GetResourcesManager().AddResource(
|
||||
"res2", "path/to/file2.png", "image");
|
||||
project.GetResourcesManager().AddResource(
|
||||
"res3", "path/to/file3.png", "image");
|
||||
ArbitraryResourceWorkerTest worker(project.GetResourcesManager());
|
||||
|
||||
auto& extension = project.InsertNewEventsFunctionsExtension("MyEventExtension", 0);
|
||||
auto& object = extension.GetEventsBasedObjects().InsertNew("MyObject", 0);
|
||||
auto& function = object.GetEventsFunctions().InsertNewEventsFunction("MyFunction", 0);
|
||||
|
||||
gd::StandardEvent standardEvent;
|
||||
gd::Instruction instruction;
|
||||
instruction.SetType("MyExtension::DoSomethingWithResources");
|
||||
instruction.SetParametersCount(3);
|
||||
instruction.SetParameter(0, "res3");
|
||||
instruction.SetParameter(1, "res1");
|
||||
instruction.SetParameter(2, "res4");
|
||||
standardEvent.GetActions().Insert(instruction);
|
||||
function.GetEvents().InsertEvent(standardEvent);
|
||||
|
||||
auto& layout = project.InsertNewLayout("MyScene", 0);
|
||||
|
||||
// MyEventExtension::MyObject::MyFunction doesn't need to be actually used in
|
||||
// events because the implementation is naive.
|
||||
|
||||
gd::ResourceExposer::ExposeLayoutResources(project, layout, worker);
|
||||
REQUIRE(worker.bitmapFonts.size() == 1);
|
||||
REQUIRE(worker.bitmapFonts[0] == "res3");
|
||||
REQUIRE(worker.images.size() == 1);
|
||||
REQUIRE(worker.images[0] == "res1");
|
||||
REQUIRE(worker.audios.size() == 1);
|
||||
REQUIRE(worker.audios[0] == "res4");
|
||||
}
|
||||
|
||||
SECTION("Can find resource usages in layer effects") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
|
@@ -139,8 +139,8 @@ TEST_CASE("PropertyFunctionGenerator", "[common]") {
|
||||
.SetLabel("Dot shape")
|
||||
.SetDescription("The shape is used for collision.")
|
||||
.SetGroup("Movement");
|
||||
property.GetExtraInfo().push_back("Dot shape");
|
||||
property.GetExtraInfo().push_back("Bounding disk");
|
||||
property.AddChoice("DotShape", "Dot shape");
|
||||
property.AddChoice("BoundingDisk", "Bounding disk");
|
||||
|
||||
gd::PropertyFunctionGenerator::GenerateBehaviorGetterAndSetter(
|
||||
project, extension, behavior, property, false);
|
||||
@@ -157,7 +157,7 @@ TEST_CASE("PropertyFunctionGenerator", "[common]") {
|
||||
gd::EventsFunction::ExpressionAndCondition);
|
||||
REQUIRE(getter.GetExpressionType().GetName() == "stringWithSelector");
|
||||
REQUIRE(getter.GetExpressionType().GetExtraInfo() ==
|
||||
"[\"Dot shape\",\"Bounding disk\"]");
|
||||
"[\"DotShape\",\"BoundingDisk\"]");
|
||||
}
|
||||
|
||||
SECTION("Can generate functions for a boolean property in a behavior") {
|
||||
@@ -386,8 +386,8 @@ TEST_CASE("PropertyFunctionGenerator", "[common]") {
|
||||
.SetLabel("Dot shape")
|
||||
.SetDescription("The shape is used for collision.")
|
||||
.SetGroup("Movement");
|
||||
property.GetExtraInfo().push_back("Dot shape");
|
||||
property.GetExtraInfo().push_back("Bounding disk");
|
||||
property.AddChoice("DotShape", "Dot shape");
|
||||
property.AddChoice("BoundingDisk", "Bounding disk");
|
||||
|
||||
gd::PropertyFunctionGenerator::GenerateObjectGetterAndSetter(
|
||||
project, extension, object, property);
|
||||
@@ -404,7 +404,7 @@ TEST_CASE("PropertyFunctionGenerator", "[common]") {
|
||||
gd::EventsFunction::ExpressionAndCondition);
|
||||
REQUIRE(getter.GetExpressionType().GetName() == "stringWithSelector");
|
||||
REQUIRE(getter.GetExpressionType().GetExtraInfo() ==
|
||||
"[\"Dot shape\",\"Bounding disk\"]");
|
||||
"[\"DotShape\",\"BoundingDisk\"]");
|
||||
}
|
||||
|
||||
SECTION("Can generate functions for a boolean property in an object") {
|
||||
|
@@ -5,8 +5,6 @@ namespace gdjs {
|
||||
type Object3DNetworkSyncDataType = {
|
||||
// z is position on the Z axis, different from zo, which is Z order
|
||||
z: number;
|
||||
w: number;
|
||||
h: number;
|
||||
d: number;
|
||||
rx: number;
|
||||
ry: number;
|
||||
@@ -112,12 +110,12 @@ namespace gdjs {
|
||||
return true;
|
||||
}
|
||||
|
||||
getNetworkSyncData(): Object3DNetworkSyncData {
|
||||
getNetworkSyncData(
|
||||
syncOptions: GetNetworkSyncDataOptions
|
||||
): Object3DNetworkSyncData {
|
||||
return {
|
||||
...super.getNetworkSyncData(),
|
||||
...super.getNetworkSyncData(syncOptions),
|
||||
z: this.getZ(),
|
||||
w: this.getWidth(),
|
||||
h: this.getHeight(),
|
||||
d: this.getDepth(),
|
||||
rx: this.getRotationX(),
|
||||
ry: this.getRotationY(),
|
||||
@@ -127,11 +125,12 @@ namespace gdjs {
|
||||
};
|
||||
}
|
||||
|
||||
updateFromNetworkSyncData(networkSyncData: Object3DNetworkSyncData) {
|
||||
super.updateFromNetworkSyncData(networkSyncData);
|
||||
updateFromNetworkSyncData(
|
||||
networkSyncData: Object3DNetworkSyncData,
|
||||
options: UpdateFromNetworkSyncDataOptions
|
||||
) {
|
||||
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||
if (networkSyncData.z !== undefined) this.setZ(networkSyncData.z);
|
||||
if (networkSyncData.w !== undefined) this.setWidth(networkSyncData.w);
|
||||
if (networkSyncData.h !== undefined) this.setHeight(networkSyncData.h);
|
||||
if (networkSyncData.d !== undefined) this.setDepth(networkSyncData.d);
|
||||
if (networkSyncData.rx !== undefined)
|
||||
this.setRotationX(networkSyncData.rx);
|
||||
|
@@ -452,9 +452,11 @@ namespace gdjs {
|
||||
return true;
|
||||
}
|
||||
|
||||
getNetworkSyncData(): Cube3DObjectNetworkSyncData {
|
||||
getNetworkSyncData(
|
||||
syncOptions: GetNetworkSyncDataOptions
|
||||
): Cube3DObjectNetworkSyncData {
|
||||
return {
|
||||
...super.getNetworkSyncData(),
|
||||
...super.getNetworkSyncData(syncOptions),
|
||||
mt: this._materialType,
|
||||
fo: this._facesOrientation,
|
||||
bfu: this._backFaceUpThroughWhichAxisRotation,
|
||||
@@ -466,9 +468,10 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
updateFromNetworkSyncData(
|
||||
networkSyncData: Cube3DObjectNetworkSyncData
|
||||
networkSyncData: Cube3DObjectNetworkSyncData,
|
||||
options: UpdateFromNetworkSyncDataOptions
|
||||
): void {
|
||||
super.updateFromNetworkSyncData(networkSyncData);
|
||||
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||
|
||||
if (networkSyncData.mt !== undefined) {
|
||||
this._materialType = networkSyncData.mt;
|
||||
|
@@ -1,4 +1,16 @@
|
||||
namespace gdjs {
|
||||
type CustomObject3DNetworkSyncDataType = {
|
||||
z: float;
|
||||
d: float;
|
||||
rx: float;
|
||||
ry: float;
|
||||
ifz: boolean;
|
||||
ccz: float;
|
||||
};
|
||||
|
||||
type CustomObject3DNetworkSyncData = CustomObjectNetworkSyncData &
|
||||
CustomObject3DNetworkSyncDataType;
|
||||
|
||||
/**
|
||||
* Base class for 3D custom objects.
|
||||
*/
|
||||
@@ -77,6 +89,36 @@ namespace gdjs {
|
||||
}
|
||||
}
|
||||
|
||||
getNetworkSyncData(
|
||||
syncOptions: GetNetworkSyncDataOptions
|
||||
): CustomObject3DNetworkSyncData {
|
||||
return {
|
||||
...super.getNetworkSyncData(syncOptions),
|
||||
z: this.getZ(),
|
||||
d: this.getDepth(),
|
||||
rx: this.getRotationX(),
|
||||
ry: this.getRotationY(),
|
||||
ifz: this.isFlippedZ(),
|
||||
ccz: this._customCenterZ,
|
||||
};
|
||||
}
|
||||
|
||||
updateFromNetworkSyncData(
|
||||
networkSyncData: CustomObject3DNetworkSyncData,
|
||||
options: UpdateFromNetworkSyncDataOptions
|
||||
): void {
|
||||
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||
if (networkSyncData.z !== undefined) this.setZ(networkSyncData.z);
|
||||
if (networkSyncData.d !== undefined) this.setDepth(networkSyncData.d);
|
||||
if (networkSyncData.rx !== undefined)
|
||||
this.setRotationX(networkSyncData.rx);
|
||||
if (networkSyncData.ry !== undefined)
|
||||
this.setRotationY(networkSyncData.ry);
|
||||
if (networkSyncData.ifz !== undefined) this.flipZ(networkSyncData.ifz);
|
||||
if (networkSyncData.ccz !== undefined)
|
||||
this._customCenterZ = networkSyncData.ccz;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the object position on the Z axis.
|
||||
*/
|
||||
|
@@ -162,7 +162,12 @@ module.exports = {
|
||||
)
|
||||
.addParameter('object', _('3D object'), '', false)
|
||||
.addParameter('behavior', _('Behavior'), 'Base3DBehavior')
|
||||
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
|
||||
.useStandardParameters(
|
||||
'number',
|
||||
gd.ParameterOptions.makeNewOptions().setDescription(
|
||||
_('Angle (in degrees)')
|
||||
)
|
||||
)
|
||||
.setFunctionName('setRotationX')
|
||||
.setGetter('getRotationX');
|
||||
|
||||
@@ -178,7 +183,12 @@ module.exports = {
|
||||
)
|
||||
.addParameter('object', _('3D object'), '', false)
|
||||
.addParameter('behavior', _('Behavior'), 'Base3DBehavior')
|
||||
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
|
||||
.useStandardParameters(
|
||||
'number',
|
||||
gd.ParameterOptions.makeNewOptions().setDescription(
|
||||
_('Angle (in degrees)')
|
||||
)
|
||||
)
|
||||
.setFunctionName('setRotationY')
|
||||
.setGetter('getRotationY');
|
||||
|
||||
@@ -196,7 +206,7 @@ module.exports = {
|
||||
)
|
||||
.addParameter('object', _('3D object'), '', false)
|
||||
.addParameter('behavior', _('Behavior'), 'Base3DBehavior')
|
||||
.addParameter('number', _('Rotation angle'), '', false)
|
||||
.addParameter('number', _('Angle to add (in degrees)'), '', false)
|
||||
.markAsAdvanced()
|
||||
.setFunctionName('turnAroundX');
|
||||
|
||||
@@ -214,7 +224,7 @@ module.exports = {
|
||||
)
|
||||
.addParameter('object', _('3D object'), '', false)
|
||||
.addParameter('behavior', _('Behavior'), 'Base3DBehavior')
|
||||
.addParameter('number', _('Rotation angle'), '', false)
|
||||
.addParameter('number', _('Angle to add (in degrees)'), '', false)
|
||||
.markAsAdvanced()
|
||||
.setFunctionName('turnAroundY');
|
||||
|
||||
@@ -232,7 +242,7 @@ module.exports = {
|
||||
)
|
||||
.addParameter('object', _('3D object'), '', false)
|
||||
.addParameter('behavior', _('Behavior'), 'Base3DBehavior')
|
||||
.addParameter('number', _('Rotation angle'), '', false)
|
||||
.addParameter('number', _('Angle to add (in degrees)'), '', false)
|
||||
.markAsAdvanced()
|
||||
.setFunctionName('turnAroundZ');
|
||||
}
|
||||
@@ -242,7 +252,7 @@ module.exports = {
|
||||
.addObject(
|
||||
'Model3DObject',
|
||||
_('3D Model'),
|
||||
_('An animated 3D model.'),
|
||||
_('An animated 3D model, useful for most elements of a 3D game.'),
|
||||
'JsPlatform/Extensions/3d_model.svg',
|
||||
new gd.Model3DObjectConfiguration()
|
||||
)
|
||||
@@ -594,7 +604,12 @@ module.exports = {
|
||||
'res/conditions/3d_box.svg'
|
||||
)
|
||||
.addParameter('object', _('3D model'), 'Model3DObject', false)
|
||||
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
|
||||
.useStandardParameters(
|
||||
'number',
|
||||
gd.ParameterOptions.makeNewOptions().setDescription(
|
||||
_('Angle (in degrees)')
|
||||
)
|
||||
)
|
||||
.setHidden()
|
||||
.setFunctionName('setRotationX')
|
||||
.setGetter('getRotationX');
|
||||
@@ -611,7 +626,12 @@ module.exports = {
|
||||
'res/conditions/3d_box.svg'
|
||||
)
|
||||
.addParameter('object', _('3D model'), 'Model3DObject', false)
|
||||
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
|
||||
.useStandardParameters(
|
||||
'number',
|
||||
gd.ParameterOptions.makeNewOptions().setDescription(
|
||||
_('Angle (in degrees)')
|
||||
)
|
||||
)
|
||||
.setHidden()
|
||||
.setFunctionName('setRotationY')
|
||||
.setGetter('getRotationY');
|
||||
@@ -630,7 +650,7 @@ module.exports = {
|
||||
'res/conditions/3d_box.svg'
|
||||
)
|
||||
.addParameter('object', _('3D model'), 'Model3DObject', false)
|
||||
.addParameter('number', _('Rotation angle'), '', false)
|
||||
.addParameter('number', _('Angle to add (in degrees)'), '', false)
|
||||
.markAsAdvanced()
|
||||
.setHidden()
|
||||
.setFunctionName('turnAroundX');
|
||||
@@ -649,7 +669,7 @@ module.exports = {
|
||||
'res/conditions/3d_box.svg'
|
||||
)
|
||||
.addParameter('object', _('3D model'), 'Model3DObject', false)
|
||||
.addParameter('number', _('Rotation angle'), '', false)
|
||||
.addParameter('number', _('Angle to add (in degrees)'), '', false)
|
||||
.markAsAdvanced()
|
||||
.setHidden()
|
||||
.setFunctionName('turnAroundY');
|
||||
@@ -668,7 +688,7 @@ module.exports = {
|
||||
'res/conditions/3d_box.svg'
|
||||
)
|
||||
.addParameter('object', _('3D model'), 'Model3DObject', false)
|
||||
.addParameter('number', _('Rotation angle'), '', false)
|
||||
.addParameter('number', _('Angle to add (in degrees)'), '', false)
|
||||
.markAsAdvanced()
|
||||
.setHidden()
|
||||
.setFunctionName('turnAroundZ');
|
||||
@@ -1125,7 +1145,7 @@ module.exports = {
|
||||
topFaceResourceName: '',
|
||||
bottomFaceResourceName: '',
|
||||
frontFaceVisible: true,
|
||||
backFaceVisible: false,
|
||||
backFaceVisible: true,
|
||||
leftFaceVisible: true,
|
||||
rightFaceVisible: true,
|
||||
topFaceVisible: true,
|
||||
@@ -1493,7 +1513,12 @@ module.exports = {
|
||||
'res/conditions/3d_box.svg'
|
||||
)
|
||||
.addParameter('object', _('3D cube'), 'Cube3DObject', false)
|
||||
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
|
||||
.useStandardParameters(
|
||||
'number',
|
||||
gd.ParameterOptions.makeNewOptions().setDescription(
|
||||
_('Angle (in degrees)')
|
||||
)
|
||||
)
|
||||
.setFunctionName('setRotationX')
|
||||
.setHidden()
|
||||
.setGetter('getRotationX');
|
||||
@@ -1510,7 +1535,12 @@ module.exports = {
|
||||
'res/conditions/3d_box.svg'
|
||||
)
|
||||
.addParameter('object', _('3D cube'), 'Cube3DObject', false)
|
||||
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
|
||||
.useStandardParameters(
|
||||
'number',
|
||||
gd.ParameterOptions.makeNewOptions().setDescription(
|
||||
_('Angle (in degrees)')
|
||||
)
|
||||
)
|
||||
.setFunctionName('setRotationY')
|
||||
.setHidden()
|
||||
.setGetter('getRotationY');
|
||||
@@ -1878,7 +1908,9 @@ module.exports = {
|
||||
.addEffect('AmbientLight')
|
||||
.setFullName(_('Ambient light'))
|
||||
.setDescription(
|
||||
_('A light that illuminates all objects from every direction.')
|
||||
_(
|
||||
'A light that illuminates all objects from every direction. Often used along with a Directional light (though a Hemisphere light can be used instead of an Ambient light).'
|
||||
)
|
||||
)
|
||||
.markAsNotWorkingForObjects()
|
||||
.markAsOnlyWorkingFor3D()
|
||||
@@ -1899,7 +1931,11 @@ module.exports = {
|
||||
const effect = extension
|
||||
.addEffect('DirectionalLight')
|
||||
.setFullName(_('Directional light'))
|
||||
.setDescription(_('A very far light source like the sun.'))
|
||||
.setDescription(
|
||||
_(
|
||||
"A very far light source like the sun. This is the light to use for casting shadows for 3D objects (other lights won't emit shadows). Often used along with a Hemisphere light."
|
||||
)
|
||||
)
|
||||
.markAsNotWorkingForObjects()
|
||||
.markAsOnlyWorkingFor3D()
|
||||
.addIncludeFile('Extensions/3D/DirectionalLight.js');
|
||||
@@ -1983,7 +2019,7 @@ module.exports = {
|
||||
.setFullName(_('Hemisphere light'))
|
||||
.setDescription(
|
||||
_(
|
||||
'A light that illuminates objects from every direction with a gradient.'
|
||||
'A light that illuminates objects from every direction with a gradient. Often used along with a Directional light.'
|
||||
)
|
||||
)
|
||||
.markAsNotWorkingForObjects()
|
||||
@@ -2027,6 +2063,48 @@ module.exports = {
|
||||
.setType('number')
|
||||
.setGroup(_('Orientation'));
|
||||
}
|
||||
{
|
||||
const effect = extension
|
||||
.addEffect('Skybox')
|
||||
.setFullName(_('Skybox'))
|
||||
.setDescription(
|
||||
_('Display a background on a cube surrounding the scene.')
|
||||
)
|
||||
.markAsNotWorkingForObjects()
|
||||
.markAsOnlyWorkingFor3D()
|
||||
.addIncludeFile('Extensions/3D/Skybox.js');
|
||||
const properties = effect.getProperties();
|
||||
properties
|
||||
.getOrCreate('rightFaceResourceName')
|
||||
.setType('resource')
|
||||
.addExtraInfo('image')
|
||||
.setLabel(_('Right face (X+)'));
|
||||
properties
|
||||
.getOrCreate('leftFaceResourceName')
|
||||
.setType('resource')
|
||||
.addExtraInfo('image')
|
||||
.setLabel(_('Left face (X-)'));
|
||||
properties
|
||||
.getOrCreate('bottomFaceResourceName')
|
||||
.setType('resource')
|
||||
.addExtraInfo('image')
|
||||
.setLabel(_('Bottom face (Y+)'));
|
||||
properties
|
||||
.getOrCreate('topFaceResourceName')
|
||||
.setType('resource')
|
||||
.addExtraInfo('image')
|
||||
.setLabel(_('Top face (Y-)'));
|
||||
properties
|
||||
.getOrCreate('frontFaceResourceName')
|
||||
.setType('resource')
|
||||
.addExtraInfo('image')
|
||||
.setLabel(_('Front face (Z+)'));
|
||||
properties
|
||||
.getOrCreate('backFaceResourceName')
|
||||
.setType('resource')
|
||||
.addExtraInfo('image')
|
||||
.setLabel(_('Back face (Z-)'));
|
||||
}
|
||||
{
|
||||
const effect = extension
|
||||
.addEffect('HueAndSaturation')
|
||||
|
@@ -8,6 +8,7 @@ namespace gdjs {
|
||||
anis: Model3DAnimation[];
|
||||
ai: integer;
|
||||
ass: float;
|
||||
aet: float;
|
||||
ap: boolean;
|
||||
cfd: float;
|
||||
};
|
||||
@@ -216,24 +217,28 @@ namespace gdjs {
|
||||
return true;
|
||||
}
|
||||
|
||||
getNetworkSyncData(): Model3DObjectNetworkSyncData {
|
||||
getNetworkSyncData(
|
||||
syncOptions: GetNetworkSyncDataOptions
|
||||
): Model3DObjectNetworkSyncData {
|
||||
return {
|
||||
...super.getNetworkSyncData(),
|
||||
...super.getNetworkSyncData(syncOptions),
|
||||
mt: this._materialType,
|
||||
op: this._originPoint,
|
||||
cp: this._centerPoint,
|
||||
anis: this._animations,
|
||||
ai: this._currentAnimationIndex,
|
||||
ass: this._animationSpeedScale,
|
||||
aet: this.getAnimationElapsedTime(),
|
||||
ap: this._animationPaused,
|
||||
cfd: this._crossfadeDuration,
|
||||
};
|
||||
}
|
||||
|
||||
updateFromNetworkSyncData(
|
||||
networkSyncData: Model3DObjectNetworkSyncData
|
||||
networkSyncData: Model3DObjectNetworkSyncData,
|
||||
options: UpdateFromNetworkSyncDataOptions
|
||||
): void {
|
||||
super.updateFromNetworkSyncData(networkSyncData);
|
||||
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||
|
||||
if (networkSyncData.mt !== undefined) {
|
||||
this._materialType = networkSyncData.mt;
|
||||
@@ -247,11 +252,14 @@ namespace gdjs {
|
||||
if (networkSyncData.anis !== undefined) {
|
||||
this._animations = networkSyncData.anis;
|
||||
}
|
||||
if (networkSyncData.ass !== undefined) {
|
||||
this.setAnimationSpeedScale(networkSyncData.ass);
|
||||
}
|
||||
if (networkSyncData.ai !== undefined) {
|
||||
this.setAnimationIndex(networkSyncData.ai);
|
||||
}
|
||||
if (networkSyncData.ass !== undefined) {
|
||||
this.setAnimationSpeedScale(networkSyncData.ass);
|
||||
if (networkSyncData.aet !== undefined) {
|
||||
this.setAnimationElapsedTime(networkSyncData.aet);
|
||||
}
|
||||
if (networkSyncData.ap !== undefined) {
|
||||
if (networkSyncData.ap !== this.isAnimationPaused()) {
|
||||
|
102
Extensions/3D/Skybox.ts
Normal file
102
Extensions/3D/Skybox.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
namespace gdjs {
|
||||
interface SkyboxFilterNetworkSyncData {}
|
||||
gdjs.PixiFiltersTools.registerFilterCreator(
|
||||
'Scene3D::Skybox',
|
||||
new (class implements gdjs.PixiFiltersTools.FilterCreator {
|
||||
makeFilter(
|
||||
target: EffectsTarget,
|
||||
effectData: EffectData
|
||||
): gdjs.PixiFiltersTools.Filter {
|
||||
if (typeof THREE === 'undefined') {
|
||||
return new gdjs.PixiFiltersTools.EmptyFilter();
|
||||
}
|
||||
return new (class implements gdjs.PixiFiltersTools.Filter {
|
||||
_cubeTexture: THREE.CubeTexture;
|
||||
_oldBackground:
|
||||
| THREE.CubeTexture
|
||||
| THREE.Texture
|
||||
| THREE.Color
|
||||
| null = null;
|
||||
_isEnabled: boolean = false;
|
||||
|
||||
constructor() {
|
||||
this._cubeTexture = target
|
||||
.getRuntimeScene()
|
||||
.getGame()
|
||||
.getImageManager()
|
||||
.getThreeCubeTexture(
|
||||
effectData.stringParameters.rightFaceResourceName,
|
||||
effectData.stringParameters.leftFaceResourceName,
|
||||
effectData.stringParameters.topFaceResourceName,
|
||||
effectData.stringParameters.bottomFaceResourceName,
|
||||
effectData.stringParameters.frontFaceResourceName,
|
||||
effectData.stringParameters.backFaceResourceName
|
||||
);
|
||||
}
|
||||
|
||||
isEnabled(target: EffectsTarget): boolean {
|
||||
return this._isEnabled;
|
||||
}
|
||||
setEnabled(target: EffectsTarget, enabled: boolean): boolean {
|
||||
if (this._isEnabled === enabled) {
|
||||
return true;
|
||||
}
|
||||
if (enabled) {
|
||||
return this.applyEffect(target);
|
||||
} else {
|
||||
return this.removeEffect(target);
|
||||
}
|
||||
}
|
||||
applyEffect(target: EffectsTarget): boolean {
|
||||
const scene = target.get3DRendererObject() as
|
||||
| THREE.Scene
|
||||
| null
|
||||
| undefined;
|
||||
if (!scene) {
|
||||
return false;
|
||||
}
|
||||
// TODO Add a background stack in LayerPixiRenderer to allow
|
||||
// filters to stack them.
|
||||
this._oldBackground = scene.background;
|
||||
scene.background = this._cubeTexture;
|
||||
if (!scene.environment) {
|
||||
scene.environment = this._cubeTexture;
|
||||
}
|
||||
this._isEnabled = true;
|
||||
return true;
|
||||
}
|
||||
removeEffect(target: EffectsTarget): boolean {
|
||||
const scene = target.get3DRendererObject() as
|
||||
| THREE.Scene
|
||||
| null
|
||||
| undefined;
|
||||
if (!scene) {
|
||||
return false;
|
||||
}
|
||||
scene.background = this._oldBackground;
|
||||
scene.environment = null;
|
||||
this._isEnabled = false;
|
||||
return true;
|
||||
}
|
||||
updatePreRender(target: gdjs.EffectsTarget): any {}
|
||||
updateDoubleParameter(parameterName: string, value: number): void {}
|
||||
getDoubleParameter(parameterName: string): number {
|
||||
return 0;
|
||||
}
|
||||
updateStringParameter(parameterName: string, value: string): void {}
|
||||
updateColorParameter(parameterName: string, value: number): void {}
|
||||
getColorParameter(parameterName: string): number {
|
||||
return 0;
|
||||
}
|
||||
updateBooleanParameter(parameterName: string, value: boolean): void {}
|
||||
getNetworkSyncData(): SkyboxFilterNetworkSyncData {
|
||||
return {};
|
||||
}
|
||||
updateFromNetworkSyncData(
|
||||
syncData: SkyboxFilterNetworkSyncData
|
||||
): void {}
|
||||
})();
|
||||
}
|
||||
})()
|
||||
);
|
||||
}
|
@@ -473,7 +473,14 @@ namespace gdjs {
|
||||
this._parentOldMaxY = instanceContainer.getUnrotatedViewportMaxY();
|
||||
}
|
||||
|
||||
doStepPostEvents(instanceContainer: gdjs.RuntimeInstanceContainer) {}
|
||||
doStepPostEvents(instanceContainer: gdjs.RuntimeInstanceContainer) {
|
||||
// Custom objects can be resized during the events step.
|
||||
// The anchor constraints must be applied on child-objects after the parent events.
|
||||
const isChildObject = instanceContainer !== instanceContainer.getScene();
|
||||
if (isChildObject) {
|
||||
this.doStepPreEvents(instanceContainer);
|
||||
}
|
||||
}
|
||||
|
||||
private _convertCoords(
|
||||
instanceContainer: gdjs.RuntimeInstanceContainer,
|
||||
|
@@ -508,7 +508,7 @@ module.exports = {
|
||||
associatedObjectConfiguration,
|
||||
pixiContainer,
|
||||
pixiResourcesLoader,
|
||||
propertyOverridings
|
||||
getPropertyOverridings
|
||||
) {
|
||||
super(
|
||||
project,
|
||||
@@ -516,7 +516,7 @@ module.exports = {
|
||||
associatedObjectConfiguration,
|
||||
pixiContainer,
|
||||
pixiResourcesLoader,
|
||||
propertyOverridings
|
||||
getPropertyOverridings
|
||||
);
|
||||
|
||||
const bbTextStyles = {
|
||||
@@ -555,9 +555,11 @@ module.exports = {
|
||||
gd.ObjectJsImplementation
|
||||
);
|
||||
|
||||
const rawText = this._propertyOverridings.has('Text')
|
||||
? this._propertyOverridings.get('Text')
|
||||
: object.content.text;
|
||||
const propertyOverridings = this.getPropertyOverridings();
|
||||
const rawText =
|
||||
propertyOverridings && propertyOverridings.has('Text')
|
||||
? propertyOverridings.get('Text')
|
||||
: object.content.text;
|
||||
if (rawText !== this._pixiObject.text) {
|
||||
this._pixiObject.text = rawText;
|
||||
}
|
||||
|
@@ -145,9 +145,11 @@ namespace gdjs {
|
||||
return true;
|
||||
}
|
||||
|
||||
override getNetworkSyncData(): BBTextObjectNetworkSyncData {
|
||||
override getNetworkSyncData(
|
||||
syncOptions: GetNetworkSyncDataOptions
|
||||
): BBTextObjectNetworkSyncData {
|
||||
return {
|
||||
...super.getNetworkSyncData(),
|
||||
...super.getNetworkSyncData(syncOptions),
|
||||
text: this._text,
|
||||
o: this._opacity,
|
||||
c: this._color,
|
||||
@@ -162,9 +164,10 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
override updateFromNetworkSyncData(
|
||||
networkSyncData: BBTextObjectNetworkSyncData
|
||||
networkSyncData: BBTextObjectNetworkSyncData,
|
||||
options: UpdateFromNetworkSyncDataOptions
|
||||
): void {
|
||||
super.updateFromNetworkSyncData(networkSyncData);
|
||||
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||
if (this._text !== undefined) {
|
||||
this.setBBText(networkSyncData.text);
|
||||
}
|
||||
@@ -383,6 +386,10 @@ namespace gdjs {
|
||||
return this._renderer.getHeight();
|
||||
}
|
||||
|
||||
override setWidth(width: float): void {
|
||||
this.setWrappingWidth(width);
|
||||
}
|
||||
|
||||
override getDrawableY(): float {
|
||||
return (
|
||||
this.getY() -
|
||||
|
@@ -631,7 +631,7 @@ module.exports = {
|
||||
associatedObjectConfiguration,
|
||||
pixiContainer,
|
||||
pixiResourcesLoader,
|
||||
propertyOverridings
|
||||
getPropertyOverridings
|
||||
) {
|
||||
super(
|
||||
project,
|
||||
@@ -639,7 +639,7 @@ module.exports = {
|
||||
associatedObjectConfiguration,
|
||||
pixiContainer,
|
||||
pixiResourcesLoader,
|
||||
propertyOverridings
|
||||
getPropertyOverridings
|
||||
);
|
||||
|
||||
// We'll track changes of the font to trigger the loading of the new font.
|
||||
@@ -665,9 +665,11 @@ module.exports = {
|
||||
|
||||
// Update the rendered text properties (note: Pixi is only
|
||||
// applying changes if there were changed).
|
||||
this._pixiObject.text = this._propertyOverridings.has('Text')
|
||||
? this._propertyOverridings.get('Text')
|
||||
: object.content.text;
|
||||
const propertyOverridings = this.getPropertyOverridings();
|
||||
this._pixiObject.text =
|
||||
propertyOverridings && propertyOverridings.has('Text')
|
||||
? propertyOverridings.get('Text')
|
||||
: object.content.text;
|
||||
|
||||
const align = object.content.align;
|
||||
this._pixiObject.align = align;
|
||||
|
@@ -155,9 +155,11 @@ namespace gdjs {
|
||||
return true;
|
||||
}
|
||||
|
||||
override getNetworkSyncData(): BitmapTextObjectNetworkSyncData {
|
||||
override getNetworkSyncData(
|
||||
syncOptions: GetNetworkSyncDataOptions
|
||||
): BitmapTextObjectNetworkSyncData {
|
||||
return {
|
||||
...super.getNetworkSyncData(),
|
||||
...super.getNetworkSyncData(syncOptions),
|
||||
text: this._text,
|
||||
opa: this._opacity,
|
||||
tint: this._tint,
|
||||
@@ -172,9 +174,10 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
override updateFromNetworkSyncData(
|
||||
networkSyncData: BitmapTextObjectNetworkSyncData
|
||||
networkSyncData: BitmapTextObjectNetworkSyncData,
|
||||
options: UpdateFromNetworkSyncDataOptions
|
||||
): void {
|
||||
super.updateFromNetworkSyncData(networkSyncData);
|
||||
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||
if (this._text !== undefined) {
|
||||
this.setText(networkSyncData.text);
|
||||
}
|
||||
@@ -426,6 +429,10 @@ namespace gdjs {
|
||||
return this._renderer.getHeight();
|
||||
}
|
||||
|
||||
override setWidth(width: float): void {
|
||||
this.setWrappingWidth(width);
|
||||
}
|
||||
|
||||
override getDrawableY(): float {
|
||||
return (
|
||||
this.getY() -
|
||||
|
@@ -21,7 +21,9 @@ module.exports = {
|
||||
.setExtensionInformation(
|
||||
'FileSystem',
|
||||
_('File system'),
|
||||
_('Access the filesystem of the operating system.'),
|
||||
_(
|
||||
'Access the filesystem of the operating system - only works on native, desktop games exported to Windows, Linux or macOS.'
|
||||
),
|
||||
'Matthias Meike',
|
||||
'Open source (MIT License)'
|
||||
)
|
||||
|
@@ -5,23 +5,25 @@ Copyright (c) 2008-2016 Florian Rival (Florian.Rival@gmail.com)
|
||||
This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "GDCore/Extensions/PlatformExtension.h"
|
||||
#include "GDCore/Tools/Localization.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
void DeclareInventoryExtension(gd::PlatformExtension& extension) {
|
||||
extension.SetExtensionInformation(
|
||||
"Inventory",
|
||||
_("Inventories"),
|
||||
_("Provides actions and conditions to add an inventory to your game, "
|
||||
"with items in memory."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
extension
|
||||
.SetExtensionInformation(
|
||||
"Inventory",
|
||||
_("Inventories"),
|
||||
_("Actions and conditions to store named inventories in memory, "
|
||||
"with items (indexed by their name), a count for each of them, "
|
||||
"a maximum count and an equipped state. Can be loaded/saved "
|
||||
"from/to a GDevelop variable."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/all-features/inventory")
|
||||
.SetCategory("Game mechanic");
|
||||
extension
|
||||
.AddInstructionOrExpressionGroupMetadata(_("Inventories"))
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Inventories"))
|
||||
.SetIcon("CppPlatform/Extensions/Inventoryicon.png");
|
||||
|
||||
extension
|
||||
@@ -164,14 +166,15 @@ void DeclareInventoryExtension(gd::PlatformExtension& extension) {
|
||||
.SetFunctionName("InventoryTools::IsEquipped");
|
||||
|
||||
extension
|
||||
.AddAction("SerializeToVariable",
|
||||
_("Save an inventory in a scene variable"),
|
||||
_("Save all the items of the inventory in a scene variable, so that "
|
||||
"it can be restored later."),
|
||||
_("Save inventory _PARAM1_ in variable _PARAM2_"),
|
||||
_("Variables"),
|
||||
"CppPlatform/Extensions/Inventoryicon.png",
|
||||
"CppPlatform/Extensions/Inventoryicon.png")
|
||||
.AddAction(
|
||||
"SerializeToVariable",
|
||||
_("Save an inventory in a scene variable"),
|
||||
_("Save all the items of the inventory in a scene variable, so that "
|
||||
"it can be restored later."),
|
||||
_("Save inventory _PARAM1_ in variable _PARAM2_"),
|
||||
_("Variables"),
|
||||
"CppPlatform/Extensions/Inventoryicon.png",
|
||||
"CppPlatform/Extensions/Inventoryicon.png")
|
||||
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter("string", _("Inventory name"))
|
||||
@@ -204,13 +207,14 @@ void DeclareInventoryExtension(gd::PlatformExtension& extension) {
|
||||
.SetFunctionName("InventoryTools::Count");
|
||||
|
||||
extension
|
||||
.AddExpression("Maximum",
|
||||
_("Item maximum"),
|
||||
_("Get the maximum of an item in the inventory, or 0 if it is unlimited"),
|
||||
"",
|
||||
"CppPlatform/Extensions/Inventoryicon.png")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter("string", _("Inventory name"))
|
||||
.AddParameter("string", _("Item name"))
|
||||
.SetFunctionName("InventoryTools::Maximum");
|
||||
.AddExpression("Maximum",
|
||||
_("Item maximum"),
|
||||
_("Get the maximum of an item in the inventory, or 0 if "
|
||||
"it is unlimited"),
|
||||
"",
|
||||
"CppPlatform/Extensions/Inventoryicon.png")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter("string", _("Inventory name"))
|
||||
.AddParameter("string", _("Item name"))
|
||||
.SetFunctionName("InventoryTools::Maximum");
|
||||
}
|
||||
|
9
Extensions/JsExtensionTypes.d.ts
vendored
9
Extensions/JsExtensionTypes.d.ts
vendored
@@ -27,7 +27,7 @@ class RenderedInstance {
|
||||
associatedObjectConfiguration: gdObjectConfiguration,
|
||||
pixiContainer: PIXI.Container,
|
||||
pixiResourcesLoader: Class<PixiResourcesLoader>,
|
||||
propertyOverridings: Map<string, string> = new Map<string, string>()
|
||||
getPropertyOverridings: (() => Map<string, string>) | null = null
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -80,6 +80,8 @@ class RenderedInstance {
|
||||
getDefaultHeight(): number;
|
||||
|
||||
getDefaultDepth(): number;
|
||||
|
||||
getPropertyOverridings(): Map<string, string> | null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -107,7 +109,8 @@ class Rendered3DInstance {
|
||||
associatedObjectConfiguration: gdObjectConfiguration,
|
||||
pixiContainer: PIXI.Container,
|
||||
threeGroup: THREE.Group,
|
||||
pixiResourcesLoader: Class<PixiResourcesLoader>
|
||||
pixiResourcesLoader: Class<PixiResourcesLoader>,
|
||||
getPropertyOverridings: (() => Map<string, string>) | null = null
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -174,6 +177,8 @@ class Rendered3DInstance {
|
||||
* Return the depth of the instance when the instance doesn't have a custom size.
|
||||
*/
|
||||
getDefaultDepth(): number;
|
||||
|
||||
getPropertyOverridings(): Map<string, string> | null;
|
||||
}
|
||||
|
||||
declare type ObjectsRenderingService = {
|
||||
|
@@ -21,7 +21,9 @@ module.exports = {
|
||||
.setExtensionInformation(
|
||||
'Leaderboards',
|
||||
_('Leaderboards'),
|
||||
_('Allow your game to send scores to your leaderboards.'),
|
||||
_(
|
||||
'Allow your game to send scores to your leaderboards (anonymously or from the logged-in player) or display existing leaderboards to the player.'
|
||||
),
|
||||
'Florian Rival',
|
||||
'Open source (MIT License)'
|
||||
)
|
||||
@@ -30,6 +32,12 @@ module.exports = {
|
||||
.addInstructionOrExpressionGroupMetadata(_('Leaderboards'))
|
||||
.setIcon('JsPlatform/Extensions/leaderboard.svg');
|
||||
|
||||
extension
|
||||
.addDependency()
|
||||
.setName('Safari View Controller Cordova plugin')
|
||||
.setDependencyType('cordova')
|
||||
.setExportName('@gdevelop/cordova-plugin-safariviewcontroller');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'SavePlayerScore',
|
||||
|
@@ -22,7 +22,7 @@ module.exports = {
|
||||
'Lighting',
|
||||
_('Lights'),
|
||||
|
||||
'This provides a light object, and a behavior to mark other objects as being obstacles for the lights. This is a great way to create a special atmosphere to your game, along with effects, make it more realistic or to create gameplays based on lights.',
|
||||
'This provides a 2D light object, and a behavior to mark other 2D objects as being obstacles for the lights. This is a great way to create a special atmosphere to your game, along with effects, make it more realistic or to create gameplays based on lights.',
|
||||
'Harsimran Virk',
|
||||
'MIT'
|
||||
)
|
||||
@@ -51,7 +51,7 @@ module.exports = {
|
||||
_('Light Obstacle Behavior'),
|
||||
'LightObstacleBehavior',
|
||||
_(
|
||||
'Flag objects as being obstacles to light. The light emitted by light objects will be stopped by the object.'
|
||||
'Flag objects as being obstacles to 2D lights. The light emitted by light objects will be stopped by the object. This does not work on 3D objects and 3D games.'
|
||||
),
|
||||
'',
|
||||
'CppPlatform/Extensions/lightObstacleIcon32.png',
|
||||
@@ -164,7 +164,7 @@ module.exports = {
|
||||
'LightObject',
|
||||
_('Light'),
|
||||
_(
|
||||
'Displays a light on the scene, with a customizable radius and color. Add then the Light Obstacle behavior to the objects that must act as obstacle to the lights.'
|
||||
'Displays a 2D light on the scene, with a customizable radius and color. Add then the Light Obstacle behavior to the objects that must act as obstacle to the lights.'
|
||||
),
|
||||
'CppPlatform/Extensions/lightIcon32.png',
|
||||
lightObject
|
||||
|
@@ -87,16 +87,21 @@ namespace gdjs {
|
||||
return true;
|
||||
}
|
||||
|
||||
getNetworkSyncData(): LightNetworkSyncData {
|
||||
getNetworkSyncData(
|
||||
syncOptions: GetNetworkSyncDataOptions
|
||||
): LightNetworkSyncData {
|
||||
return {
|
||||
...super.getNetworkSyncData(),
|
||||
...super.getNetworkSyncData(syncOptions),
|
||||
rad: this.getRadius(),
|
||||
col: this.getColor(),
|
||||
};
|
||||
}
|
||||
|
||||
updateFromNetworkSyncData(networkSyncData: LightNetworkSyncData): void {
|
||||
super.updateFromNetworkSyncData(networkSyncData);
|
||||
updateFromNetworkSyncData(
|
||||
networkSyncData: LightNetworkSyncData,
|
||||
options: UpdateFromNetworkSyncDataOptions
|
||||
): void {
|
||||
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||
|
||||
if (networkSyncData.rad !== undefined) {
|
||||
this.setRadius(networkSyncData.rad);
|
||||
|
@@ -239,7 +239,7 @@ namespace gdjs {
|
||||
instanceContainer: gdjs.RuntimeInstanceContainer,
|
||||
objectsLists: Hashtable<gdjs.RuntimeObject[]>,
|
||||
obj: gdjs.RuntimeObject | null,
|
||||
eventsFunctionContext: EventsFunctionContext | undefined
|
||||
eventsFunctionContext: EventsFunctionContext | null | undefined
|
||||
) {
|
||||
if (obj === null) {
|
||||
return false;
|
||||
|
@@ -21,7 +21,9 @@ module.exports = {
|
||||
.setExtensionInformation(
|
||||
'Multiplayer',
|
||||
_('Multiplayer'),
|
||||
_('Allow players to connect to lobbies and play together.'),
|
||||
_(
|
||||
'This allows players to join online lobbies and synchronize gameplay across devices without needing to manage servers or networking.\n\nUse the "Open game lobbies" action to let players join a game, and use conditions like "Lobby game has just started" to begin gameplay. Add the "Multiplayer object" behavior to game objects that should be synchronized, and assign or change their ownership using player numbers. Variables and game state (like scenes, scores, or timers) are automatically synced by the host, with options to change ownership or disable sync when needed. Common multiplayer logic —like handling joins/leaves, collisions, and host migration— is supported out-of-the-box for up to 8 players per game.'
|
||||
),
|
||||
'Florian Rival',
|
||||
'Open source (MIT License)'
|
||||
)
|
||||
@@ -31,6 +33,79 @@ module.exports = {
|
||||
.addInstructionOrExpressionGroupMetadata(_('Multiplayer'))
|
||||
.setIcon('JsPlatform/Extensions/multiplayer.svg');
|
||||
|
||||
extension
|
||||
.addDependency()
|
||||
.setName('Safari View Controller Cordova plugin')
|
||||
.setDependencyType('cordova')
|
||||
.setExportName('@gdevelop/cordova-plugin-safariviewcontroller');
|
||||
|
||||
extension
|
||||
.addStrExpression(
|
||||
'CurrentLobbyID',
|
||||
_('Current lobby ID'),
|
||||
_('Returns current lobby ID.'),
|
||||
_('Lobbies'),
|
||||
'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.getLobbyID');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'QuickJoinWithLobbyID',
|
||||
_('Join a specific lobby by its ID'),
|
||||
_(
|
||||
'Join a specific lobby. The player will join the game instantly if this is possible.'
|
||||
),
|
||||
_('Join a specific lobby by its ID _PARAM1_'),
|
||||
_('Lobbies'),
|
||||
'JsPlatform/Extensions/multiplayer.svg',
|
||||
'JsPlatform/Extensions/multiplayer.svg'
|
||||
)
|
||||
.addCodeOnlyParameter('currentScene', '')
|
||||
.addParameter('string', _('Lobby ID'), '', false)
|
||||
.addParameter(
|
||||
'yesorno',
|
||||
_('Display loader while joining a lobby.'),
|
||||
'',
|
||||
true
|
||||
)
|
||||
.setDefaultValue('yes')
|
||||
.addParameter(
|
||||
'yesorno',
|
||||
_('Display game lobbies if unable to join a specific one.'),
|
||||
'',
|
||||
true
|
||||
)
|
||||
.setDefaultValue('yes')
|
||||
.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/multiplayercomponents.js')
|
||||
.addIncludeFile('Extensions/Multiplayer/messageManager.js')
|
||||
.addIncludeFile('Extensions/Multiplayer/multiplayerVariablesManager.js')
|
||||
.addIncludeFile('Extensions/Multiplayer/multiplayertools.js')
|
||||
.setFunctionName('gdjs.multiplayer.authenticateAndQuickJoinWithLobbyID');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'QuickJoinLobby',
|
||||
|
@@ -729,7 +729,9 @@ namespace gdjs {
|
||||
behavior.playerNumber = ownerPlayerNumber;
|
||||
}
|
||||
|
||||
instance.updateFromNetworkSyncData(messageData);
|
||||
instance.updateFromNetworkSyncData(messageData, {
|
||||
clearInputs: false,
|
||||
});
|
||||
|
||||
setLastClockReceivedForInstanceOnScene({
|
||||
sceneNetworkId,
|
||||
@@ -1737,7 +1739,7 @@ namespace gdjs {
|
||||
return;
|
||||
}
|
||||
|
||||
runtimeScene.updateFromNetworkSyncData(messageData);
|
||||
runtimeScene.updateFromNetworkSyncData(messageData, {});
|
||||
} else {
|
||||
// If the game is not ready to receive game update messages, we need to save the data for later use.
|
||||
// This can happen when joining a game that is already running.
|
||||
@@ -1890,7 +1892,7 @@ namespace gdjs {
|
||||
const messageData = message.getData();
|
||||
const messageSender = message.getSender();
|
||||
if (gdjs.multiplayer.isReadyToSendOrReceiveGameUpdateMessages()) {
|
||||
runtimeScene.getGame().updateFromNetworkSyncData(messageData);
|
||||
runtimeScene.getGame().updateFromNetworkSyncData(messageData, {});
|
||||
} else {
|
||||
// If the game is not ready to receive game update messages, we need to save the data for later use.
|
||||
// This can happen when joining a game that is already running.
|
||||
@@ -1918,7 +1920,7 @@ namespace gdjs {
|
||||
// Reapply the game saved updates.
|
||||
lastReceivedGameSyncDataUpdates.getUpdates().forEach((messageData) => {
|
||||
debugLogger.info(`Reapplying saved update of game.`);
|
||||
runtimeScene.getGame().updateFromNetworkSyncData(messageData);
|
||||
runtimeScene.getGame().updateFromNetworkSyncData(messageData, {});
|
||||
});
|
||||
// Game updates are always applied properly, so we can clear them.
|
||||
lastReceivedGameSyncDataUpdates.clear();
|
||||
@@ -1937,7 +1939,7 @@ namespace gdjs {
|
||||
|
||||
debugLogger.info(`Reapplying saved update of scene ${sceneNetworkId}.`);
|
||||
|
||||
runtimeScene.updateFromNetworkSyncData(messageData);
|
||||
runtimeScene.updateFromNetworkSyncData(messageData, {});
|
||||
// We only remove the message if it was successfully applied, so it can be reapplied later,
|
||||
// in case we were not on the right scene.
|
||||
lastReceivedSceneSyncDataUpdates.remove(messageData);
|
||||
|
@@ -278,7 +278,7 @@ namespace gdjs {
|
||||
|
||||
const instanceNetworkId = this._getOrCreateInstanceNetworkId();
|
||||
const objectName = this.owner.getName();
|
||||
const objectNetworkSyncData = this.owner.getNetworkSyncData();
|
||||
const objectNetworkSyncData = this.owner.getNetworkSyncData({});
|
||||
|
||||
// this._logToConsoleWithThrottle(
|
||||
// `Synchronizing object ${this.owner.getName()} (instance ${
|
||||
@@ -293,6 +293,8 @@ namespace gdjs {
|
||||
x: objectNetworkSyncData.x,
|
||||
y: objectNetworkSyncData.y,
|
||||
z: objectNetworkSyncData.z,
|
||||
w: objectNetworkSyncData.w,
|
||||
h: objectNetworkSyncData.h,
|
||||
zo: objectNetworkSyncData.zo,
|
||||
a: objectNetworkSyncData.a,
|
||||
hid: objectNetworkSyncData.hid,
|
||||
@@ -369,6 +371,9 @@ namespace gdjs {
|
||||
this._lastSentBasicObjectSyncData = {
|
||||
x: objectNetworkSyncData.x,
|
||||
y: objectNetworkSyncData.y,
|
||||
z: objectNetworkSyncData.z,
|
||||
w: objectNetworkSyncData.w,
|
||||
h: objectNetworkSyncData.h,
|
||||
zo: objectNetworkSyncData.zo,
|
||||
a: objectNetworkSyncData.a,
|
||||
hid: objectNetworkSyncData.hid,
|
||||
@@ -443,7 +448,7 @@ namespace gdjs {
|
||||
objectOwner: this.playerNumber,
|
||||
objectName,
|
||||
instanceNetworkId,
|
||||
objectNetworkSyncData: this.owner.getNetworkSyncData(),
|
||||
objectNetworkSyncData: this.owner.getNetworkSyncData({}),
|
||||
sceneNetworkId,
|
||||
});
|
||||
this._sendDataToPeersWithIncreasedClock(
|
||||
@@ -593,7 +598,7 @@ namespace gdjs {
|
||||
debugLogger.info(
|
||||
'Sending update message to move the object immediately.'
|
||||
);
|
||||
const objectNetworkSyncData = this.owner.getNetworkSyncData();
|
||||
const objectNetworkSyncData = this.owner.getNetworkSyncData({});
|
||||
const {
|
||||
messageName: updateMessageName,
|
||||
messageData: updateMessageData,
|
||||
|
@@ -17,9 +17,29 @@ namespace gdjs {
|
||||
}[];
|
||||
};
|
||||
|
||||
type LobbyStatus =
|
||||
| 'waiting'
|
||||
| 'starting'
|
||||
| 'playing'
|
||||
| 'migrating'
|
||||
| 'migrated';
|
||||
type LobbyConnectionStatus = 'waiting' | 'ready' | 'connected';
|
||||
type InGamePlayerStatus = 'playing' | 'left';
|
||||
type PlayerStatus = LobbyConnectionStatus | InGamePlayerStatus;
|
||||
|
||||
type LobbyPlayer = {
|
||||
playerId: string;
|
||||
status: PlayerStatus;
|
||||
playerNumber: number;
|
||||
};
|
||||
|
||||
type Lobby = {
|
||||
id: string;
|
||||
status: 'waiting' | 'starting' | 'playing' | 'migrating' | 'migrated';
|
||||
minPlayers: number;
|
||||
maxPlayers: number;
|
||||
canJoinAfterStart: boolean;
|
||||
players: LobbyPlayer[];
|
||||
status: LobbyStatus;
|
||||
};
|
||||
|
||||
type QuickJoinLobbyResponse =
|
||||
@@ -105,6 +125,7 @@ namespace gdjs {
|
||||
let _quickJoinLobbyFailureReason:
|
||||
| 'FULL'
|
||||
| 'NOT_ENOUGH_PLAYERS'
|
||||
| 'DOES_NOT_EXIST'
|
||||
| 'UNKNOWN'
|
||||
| null = null;
|
||||
let _lobbyId: string | null = null;
|
||||
@@ -1697,11 +1718,87 @@ namespace gdjs {
|
||||
}
|
||||
};
|
||||
|
||||
export const authenticateAndQuickJoinLobby = async (
|
||||
export const getLobbyID = (): string => {
|
||||
return _lobbyId || '';
|
||||
};
|
||||
|
||||
const quickJoinWithLobbyID = async (
|
||||
runtimeScene: gdjs.RuntimeScene,
|
||||
lobbyID: string,
|
||||
displayLoader: boolean,
|
||||
openLobbiesPageIfFailure: boolean
|
||||
) => {
|
||||
if (_isQuickJoiningOrStartingAGame) return;
|
||||
const _gameId = gdjs.projectData.properties.projectUuid;
|
||||
if (!_gameId) {
|
||||
logger.error(
|
||||
'The game ID is missing, the quick join lobby action cannot continue.'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
_quickJoinLobbyFailureReason = null;
|
||||
_isQuickJoiningOrStartingAGame = true;
|
||||
if (displayLoader) {
|
||||
gdjs.multiplayerComponents.displayLoader(runtimeScene, true);
|
||||
}
|
||||
|
||||
const quickJoinWithLobbyIDRelativeUrl = `/play/game/${_gameId}/public-lobby/${lobbyID}`;
|
||||
|
||||
try {
|
||||
const lobby: Lobby = await gdjs.evtTools.network.retryIfFailed(
|
||||
{ times: 2 },
|
||||
() =>
|
||||
fetchAsPlayer({
|
||||
relativeUrl: quickJoinWithLobbyIDRelativeUrl,
|
||||
method: 'GET',
|
||||
dev: isUsingGDevelopDevelopmentEnvironment,
|
||||
})
|
||||
);
|
||||
|
||||
const isFull = lobby.players.length === lobby.maxPlayers;
|
||||
if (isFull) {
|
||||
logger.error('Lobby is full - cannot quick join it.');
|
||||
_quickJoinLobbyJustFailed = true;
|
||||
_quickJoinLobbyFailureReason = 'FULL';
|
||||
onLobbyQuickJoinFinished(runtimeScene);
|
||||
if (openLobbiesPageIfFailure) {
|
||||
openLobbiesWindow(runtimeScene);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (lobby.status === 'playing') {
|
||||
_actionAfterJoiningLobby = 'JOIN_GAME';
|
||||
} else if (lobby.status === 'waiting') {
|
||||
if (lobby.players.length === 0) {
|
||||
_actionAfterJoiningLobby = 'START_GAME';
|
||||
} else {
|
||||
_actionAfterJoiningLobby = 'OPEN_LOBBY_PAGE';
|
||||
}
|
||||
} else {
|
||||
throw new Error(`Lobby in wrong status: ${lobby.status}`);
|
||||
}
|
||||
|
||||
handleJoinLobbyEvent(runtimeScene, lobbyID);
|
||||
} catch (error) {
|
||||
const errorCode = parseInt(error.message.match(/\d{3}/)?.[0]);
|
||||
if (errorCode === 404) {
|
||||
logger.error('Lobby does not exist.');
|
||||
_quickJoinLobbyFailureReason = 'DOES_NOT_EXIST';
|
||||
} else {
|
||||
logger.error('An error occurred while joining a lobby:', error);
|
||||
_quickJoinLobbyFailureReason = 'UNKNOWN';
|
||||
}
|
||||
_quickJoinLobbyJustFailed = true;
|
||||
onLobbyQuickJoinFinished(runtimeScene);
|
||||
if (openLobbiesPageIfFailure) {
|
||||
openLobbiesWindow(runtimeScene);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const isQuickJoiningTooFast = () => {
|
||||
const requestDoneAt = Date.now();
|
||||
if (_lastQuickJoinRequestDoneAt) {
|
||||
if (requestDoneAt - _lastQuickJoinRequestDoneAt < 500) {
|
||||
@@ -1709,12 +1806,18 @@ namespace gdjs {
|
||||
logger.warn(
|
||||
'Last request to quick join a lobby was sent too little time ago. Ignoring this one.'
|
||||
);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
_lastQuickJoinRequestDoneAt = requestDoneAt;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
const isNotCorrectlyAuthenticatedForQuickJoin = async (
|
||||
runtimeScene: RuntimeScene
|
||||
) => {
|
||||
const playerId = gdjs.playerAuthentication.getUserId();
|
||||
const playerToken = gdjs.playerAuthentication.getUserToken();
|
||||
if (!playerId || !playerToken) {
|
||||
@@ -1724,14 +1827,43 @@ namespace gdjs {
|
||||
.promise;
|
||||
_isWaitingForLogin = false;
|
||||
|
||||
if (status === 'logged') {
|
||||
await quickJoinLobby(
|
||||
runtimeScene,
|
||||
displayLoader,
|
||||
openLobbiesPageIfFailure
|
||||
);
|
||||
if (status !== 'logged') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
export const authenticateAndQuickJoinWithLobbyID = async (
|
||||
runtimeScene: gdjs.RuntimeScene,
|
||||
lobbyID: string,
|
||||
displayLoader: boolean,
|
||||
openLobbiesPageIfFailure: boolean
|
||||
) => {
|
||||
if (isQuickJoiningTooFast()) {
|
||||
return;
|
||||
}
|
||||
if (await isNotCorrectlyAuthenticatedForQuickJoin(runtimeScene)) {
|
||||
return;
|
||||
}
|
||||
await quickJoinWithLobbyID(
|
||||
runtimeScene,
|
||||
lobbyID,
|
||||
displayLoader,
|
||||
openLobbiesPageIfFailure
|
||||
);
|
||||
};
|
||||
|
||||
export const authenticateAndQuickJoinLobby = async (
|
||||
runtimeScene: gdjs.RuntimeScene,
|
||||
displayLoader: boolean,
|
||||
openLobbiesPageIfFailure: boolean
|
||||
) => {
|
||||
if (isQuickJoiningTooFast()) {
|
||||
return;
|
||||
}
|
||||
if (await isNotCorrectlyAuthenticatedForQuickJoin(runtimeScene)) {
|
||||
return;
|
||||
}
|
||||
await quickJoinLobby(
|
||||
|
@@ -389,26 +389,15 @@ namespace gdjs {
|
||||
this.updatePosition();
|
||||
}
|
||||
|
||||
setColor(rgbColor): void {
|
||||
const colors = rgbColor.split(';');
|
||||
if (colors.length < 3) {
|
||||
return;
|
||||
}
|
||||
this._centerSprite.tint = gdjs.rgbToHexNumber(
|
||||
parseInt(colors[0], 10),
|
||||
parseInt(colors[1], 10),
|
||||
parseInt(colors[2], 10)
|
||||
);
|
||||
setColor(rgbOrHexColor: string): void {
|
||||
const tint = gdjs.rgbOrHexStringToNumber(rgbOrHexColor);
|
||||
this._centerSprite.tint = tint;
|
||||
for (
|
||||
let borderCounter = 0;
|
||||
borderCounter < this._borderSprites.length;
|
||||
borderCounter++
|
||||
) {
|
||||
this._borderSprites[borderCounter].tint = gdjs.rgbToHexNumber(
|
||||
parseInt(colors[0], 10),
|
||||
parseInt(colors[1], 10),
|
||||
parseInt(colors[2], 10)
|
||||
);
|
||||
this._borderSprites[borderCounter].tint = tint;
|
||||
}
|
||||
this._spritesContainer.cacheAsBitmap = false;
|
||||
}
|
||||
@@ -416,11 +405,11 @@ namespace gdjs {
|
||||
getColor() {
|
||||
const rgb = new PIXI.Color(this._centerSprite.tint).toRgbArray();
|
||||
return (
|
||||
Math.floor(rgb[0] * 255) +
|
||||
Math.round(rgb[0] * 255) +
|
||||
';' +
|
||||
Math.floor(rgb[1] * 255) +
|
||||
Math.round(rgb[1] * 255) +
|
||||
';' +
|
||||
Math.floor(rgb[2] * 255)
|
||||
Math.round(rgb[2] * 255)
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -25,8 +25,6 @@ namespace gdjs {
|
||||
export type PanelSpriteObjectData = ObjectData & PanelSpriteObjectDataType;
|
||||
|
||||
export type PanelSpriteNetworkSyncDataType = {
|
||||
wid: number;
|
||||
hei: number;
|
||||
op: number;
|
||||
color: string;
|
||||
};
|
||||
@@ -121,29 +119,24 @@ namespace gdjs {
|
||||
return true;
|
||||
}
|
||||
|
||||
getNetworkSyncData(): PanelSpriteNetworkSyncData {
|
||||
getNetworkSyncData(
|
||||
syncOptions: GetNetworkSyncDataOptions
|
||||
): PanelSpriteNetworkSyncData {
|
||||
return {
|
||||
...super.getNetworkSyncData(),
|
||||
wid: this.getWidth(),
|
||||
hei: this.getHeight(),
|
||||
...super.getNetworkSyncData(syncOptions),
|
||||
op: this.getOpacity(),
|
||||
color: this.getColor(),
|
||||
};
|
||||
}
|
||||
|
||||
updateFromNetworkSyncData(
|
||||
networkSyncData: PanelSpriteNetworkSyncData
|
||||
networkSyncData: PanelSpriteNetworkSyncData,
|
||||
options: UpdateFromNetworkSyncDataOptions
|
||||
): void {
|
||||
super.updateFromNetworkSyncData(networkSyncData);
|
||||
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||
|
||||
// Texture is not synchronized, see if this is asked or not.
|
||||
|
||||
if (networkSyncData.wid !== undefined) {
|
||||
this.setWidth(networkSyncData.wid);
|
||||
}
|
||||
if (networkSyncData.hei !== undefined) {
|
||||
this.setHeight(networkSyncData.hei);
|
||||
}
|
||||
if (networkSyncData.op !== undefined) {
|
||||
this.setOpacity(networkSyncData.op);
|
||||
}
|
||||
|
@@ -370,9 +370,11 @@ namespace gdjs {
|
||||
return true;
|
||||
}
|
||||
|
||||
getNetworkSyncData(): ParticleEmitterObjectNetworkSyncData {
|
||||
getNetworkSyncData(
|
||||
syncOptions: GetNetworkSyncDataOptions
|
||||
): ParticleEmitterObjectNetworkSyncData {
|
||||
return {
|
||||
...super.getNetworkSyncData(),
|
||||
...super.getNetworkSyncData(syncOptions),
|
||||
prms: this.particleRotationMinSpeed,
|
||||
prmx: this.particleRotationMaxSpeed,
|
||||
mpc: this.maxParticlesCount,
|
||||
@@ -399,9 +401,10 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
updateFromNetworkSyncData(
|
||||
syncData: ParticleEmitterObjectNetworkSyncData
|
||||
syncData: ParticleEmitterObjectNetworkSyncData,
|
||||
options: UpdateFromNetworkSyncDataOptions
|
||||
): void {
|
||||
super.updateFromNetworkSyncData(syncData);
|
||||
super.updateFromNetworkSyncData(syncData, options);
|
||||
if (syncData.x !== undefined) {
|
||||
this.setX(syncData.x);
|
||||
}
|
||||
|
@@ -6,7 +6,7 @@ namespace gdjs {
|
||||
const logger = new gdjs.Logger('Pathfinding behavior');
|
||||
|
||||
interface PathfindingNetworkSyncDataType {
|
||||
// Syncing the path should be enough to have a good prediction.
|
||||
// Syncing the path and its position on it should be enough to have a good prediction.
|
||||
path: FloatPoint[];
|
||||
pf: boolean;
|
||||
sp: number;
|
||||
@@ -15,6 +15,7 @@ namespace gdjs {
|
||||
tss: number;
|
||||
re: boolean;
|
||||
ma: number;
|
||||
dos: number;
|
||||
}
|
||||
|
||||
export interface PathfindingNetworkSyncData extends BehaviorNetworkSyncData {
|
||||
@@ -133,9 +134,11 @@ namespace gdjs {
|
||||
return true;
|
||||
}
|
||||
|
||||
getNetworkSyncData(): PathfindingNetworkSyncData {
|
||||
getNetworkSyncData(
|
||||
options: GetNetworkSyncDataOptions
|
||||
): PathfindingNetworkSyncData {
|
||||
return {
|
||||
...super.getNetworkSyncData(),
|
||||
...super.getNetworkSyncData(options),
|
||||
props: {
|
||||
path: this._path,
|
||||
pf: this._pathFound,
|
||||
@@ -145,14 +148,16 @@ namespace gdjs {
|
||||
tss: this._totalSegmentDistance,
|
||||
re: this._reachedEnd,
|
||||
ma: this._movementAngle,
|
||||
dos: this._distanceOnSegment,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
updateFromNetworkSyncData(
|
||||
networkSyncData: PathfindingNetworkSyncData
|
||||
networkSyncData: PathfindingNetworkSyncData,
|
||||
options: UpdateFromNetworkSyncDataOptions
|
||||
): void {
|
||||
super.updateFromNetworkSyncData(networkSyncData);
|
||||
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||
const behaviorSpecificProps = networkSyncData.props;
|
||||
if (behaviorSpecificProps.path !== undefined) {
|
||||
this._path = behaviorSpecificProps.path;
|
||||
@@ -181,6 +186,9 @@ namespace gdjs {
|
||||
if (behaviorSpecificProps.ma !== undefined) {
|
||||
this._movementAngle = behaviorSpecificProps.ma;
|
||||
}
|
||||
if (behaviorSpecificProps.dos !== undefined) {
|
||||
this._distanceOnSegment = behaviorSpecificProps.dos;
|
||||
}
|
||||
}
|
||||
|
||||
setCellWidth(width: float): void {
|
||||
|
@@ -21,7 +21,11 @@ module.exports = {
|
||||
.setExtensionInformation(
|
||||
'Physics2',
|
||||
_('2D Physics Engine'),
|
||||
"The 2D physics engine simulates realistic object physics, with gravity, forces, collisions, joints, etc. It's perfect for 2D games that need to have realistic behaving objects and a gameplay centered around it.",
|
||||
"The 2D physics engine simulates realistic object physics, with gravity, forces, collisions, joints, etc. It's perfect for 2D games that need to have realistic behaving objects and a gameplay centered around it.\n" +
|
||||
'\n' +
|
||||
'Objects like floors or wall objects should usually be set to "Static" as type. Objects that should be moveable are usually "Dynamic" (default). "Kinematic" objects (typically, players or controlled characters) are only moved by their "linear velocity" and "angular velocity" - they can interact with other objects but only these other objects will move.\n' +
|
||||
'\n' +
|
||||
'Forces (and impulses) are expressed in all conditions/expressions/actions of the 2D physics engine in Newtons (N). Typical values for a force are 10-200 N. One meter is 100 pixels by default in the game (check the world scale). Mass is expressed in kilograms (kg).',
|
||||
'Florian Rival, Franco Maciel',
|
||||
'MIT'
|
||||
)
|
||||
@@ -527,6 +531,7 @@ module.exports = {
|
||||
physics2Behavior,
|
||||
sharedData
|
||||
)
|
||||
.markAsIrrelevantForChildObjects()
|
||||
.setIncludeFile('Extensions/Physics2Behavior/physics2runtimebehavior.js')
|
||||
.addIncludeFile('Extensions/Physics2Behavior/Box2D_v2.3.1_min.wasm.js')
|
||||
.addRequiredFile('Extensions/Physics2Behavior/Box2D_v2.3.1_min.wasm.wasm')
|
||||
@@ -789,6 +794,27 @@ module.exports = {
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('setKinematic');
|
||||
|
||||
aut
|
||||
.addAction(
|
||||
'SetBodyType',
|
||||
_('Set body type'),
|
||||
_('Set the body type of an object.'),
|
||||
_('Set the body type of _PARAM0_ to _PARAM2_'),
|
||||
_('Dynamics'),
|
||||
'res/physics32.png',
|
||||
'res/physics32.png'
|
||||
)
|
||||
.addParameter('object', _('Object'), '', false)
|
||||
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
|
||||
.addParameter(
|
||||
'stringWithSelector',
|
||||
_('Body type'),
|
||||
'["Static", "Dynamic", "Kinematic"]',
|
||||
false
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('setBodyType');
|
||||
|
||||
aut
|
||||
.addCondition(
|
||||
'IsBullet',
|
||||
|
@@ -499,7 +499,9 @@ namespace gdjs {
|
||||
return true;
|
||||
}
|
||||
|
||||
getNetworkSyncData(): Physics2NetworkSyncData {
|
||||
getNetworkSyncData(
|
||||
options: GetNetworkSyncDataOptions
|
||||
): Physics2NetworkSyncData {
|
||||
const bodyProps = this._body
|
||||
? {
|
||||
tpx: this._body.GetTransform().get_p().get_x(),
|
||||
@@ -520,7 +522,7 @@ namespace gdjs {
|
||||
aw: undefined,
|
||||
};
|
||||
return {
|
||||
...super.getNetworkSyncData(),
|
||||
...super.getNetworkSyncData(options),
|
||||
props: {
|
||||
...bodyProps,
|
||||
layers: this.layers,
|
||||
@@ -529,45 +531,13 @@ namespace gdjs {
|
||||
};
|
||||
}
|
||||
|
||||
updateFromNetworkSyncData(networkSyncData: Physics2NetworkSyncData) {
|
||||
super.updateFromNetworkSyncData(networkSyncData);
|
||||
updateFromNetworkSyncData(
|
||||
networkSyncData: Physics2NetworkSyncData,
|
||||
options: UpdateFromNetworkSyncDataOptions
|
||||
) {
|
||||
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||
|
||||
const behaviorSpecificProps = networkSyncData.props;
|
||||
if (
|
||||
behaviorSpecificProps.tpx !== undefined &&
|
||||
behaviorSpecificProps.tpy !== undefined &&
|
||||
behaviorSpecificProps.tqa !== undefined
|
||||
) {
|
||||
if (this._body) {
|
||||
this._body.SetTransform(
|
||||
this.b2Vec2(behaviorSpecificProps.tpx, behaviorSpecificProps.tpy),
|
||||
behaviorSpecificProps.tqa
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
behaviorSpecificProps.lvx !== undefined &&
|
||||
behaviorSpecificProps.lvy !== undefined
|
||||
) {
|
||||
if (this._body) {
|
||||
this._body.SetLinearVelocity(
|
||||
this.b2Vec2(behaviorSpecificProps.lvx, behaviorSpecificProps.lvy)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (behaviorSpecificProps.av !== undefined) {
|
||||
if (this._body) {
|
||||
this._body.SetAngularVelocity(behaviorSpecificProps.av);
|
||||
}
|
||||
}
|
||||
|
||||
if (behaviorSpecificProps.aw !== undefined) {
|
||||
if (this._body) {
|
||||
this._body.SetAwake(behaviorSpecificProps.aw);
|
||||
}
|
||||
}
|
||||
|
||||
if (behaviorSpecificProps.layers !== undefined) {
|
||||
this.layers = behaviorSpecificProps.layers;
|
||||
@@ -576,6 +546,38 @@ namespace gdjs {
|
||||
if (behaviorSpecificProps.masks !== undefined) {
|
||||
this.masks = behaviorSpecificProps.masks;
|
||||
}
|
||||
|
||||
this.updateBodyFromObject();
|
||||
|
||||
if (!this._body) return;
|
||||
|
||||
if (
|
||||
behaviorSpecificProps.tpx !== undefined &&
|
||||
behaviorSpecificProps.tpy !== undefined &&
|
||||
behaviorSpecificProps.tqa !== undefined
|
||||
) {
|
||||
this._body.SetTransform(
|
||||
this.b2Vec2(behaviorSpecificProps.tpx, behaviorSpecificProps.tpy),
|
||||
behaviorSpecificProps.tqa
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
behaviorSpecificProps.lvx !== undefined &&
|
||||
behaviorSpecificProps.lvy !== undefined
|
||||
) {
|
||||
this._body.SetLinearVelocity(
|
||||
this.b2Vec2(behaviorSpecificProps.lvx, behaviorSpecificProps.lvy)
|
||||
);
|
||||
}
|
||||
|
||||
if (behaviorSpecificProps.av !== undefined) {
|
||||
this._body.SetAngularVelocity(behaviorSpecificProps.av);
|
||||
}
|
||||
|
||||
if (behaviorSpecificProps.aw !== undefined) {
|
||||
this._body.SetAwake(behaviorSpecificProps.aw);
|
||||
}
|
||||
}
|
||||
|
||||
onDeActivate() {
|
||||
@@ -1137,6 +1139,21 @@ namespace gdjs {
|
||||
return this.bodyType === 'Kinematic';
|
||||
}
|
||||
|
||||
setBodyType(bodyType: string): void {
|
||||
switch (bodyType) {
|
||||
case 'Static':
|
||||
this.setStatic();
|
||||
break;
|
||||
case 'Kinematic':
|
||||
this.setKinematic();
|
||||
break;
|
||||
case 'Dynamic':
|
||||
default:
|
||||
this.setDynamic();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
setKinematic(): void {
|
||||
// Check if there is no modification
|
||||
if (this.bodyType === 'Kinematic') {
|
||||
|
@@ -21,7 +21,11 @@ module.exports = {
|
||||
.setExtensionInformation(
|
||||
'Physics3D',
|
||||
_('3D physics engine'),
|
||||
"The 3D physics engine simulates realistic object physics, with gravity, forces, collisions, joints, etc. It's perfect for almost all 3D games.",
|
||||
"The 3D physics engine simulates realistic object physics, with gravity, forces, collisions, joints, etc. It's perfect for almost all 3D games.\n" +
|
||||
'\n' +
|
||||
'Objects like floors or wall objects should usually be set to "Static" as type. Objects that should be moveable are usually "Dynamic" (default). "Kinematic" objects (typically, players or controlled characters) are only moved by their "linear velocity" and "angular velocity" - they can interact with other objects but only these other objects will move.\n' +
|
||||
'\n' +
|
||||
'Forces (and impulses) are expressed in all conditions/expressions/actions of the 3D physics engine in Newtons (N). Typical values for a force are 10-200 N. One meter is 100 pixels by default in the game (check the world scale). Mass is expressed in kilograms (kg).',
|
||||
'Florian Rival',
|
||||
'MIT'
|
||||
)
|
||||
@@ -675,6 +679,7 @@ module.exports = {
|
||||
behavior,
|
||||
sharedData
|
||||
)
|
||||
.markAsIrrelevantForChildObjects()
|
||||
.addIncludeFile(
|
||||
'Extensions/Physics3DBehavior/Physics3DRuntimeBehavior.js'
|
||||
)
|
||||
@@ -816,6 +821,27 @@ module.exports = {
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('isKinematic');
|
||||
|
||||
aut
|
||||
.addScopedAction(
|
||||
'SetBodyType',
|
||||
_('Set body type'),
|
||||
_('Set the body type of an object.'),
|
||||
_('Set the body type of _PARAM0_ to _PARAM2_'),
|
||||
_('Dynamics'),
|
||||
'JsPlatform/Extensions/physics3d.svg',
|
||||
'JsPlatform/Extensions/physics3d.svg'
|
||||
)
|
||||
.addParameter('object', _('Object'), '', false)
|
||||
.addParameter('behavior', _('Behavior'), 'Physics3DBehavior')
|
||||
.addParameter(
|
||||
'stringWithSelector',
|
||||
_('Body type'),
|
||||
'["Static", "Dynamic", "Kinematic"]',
|
||||
false
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('setBodyType');
|
||||
|
||||
aut
|
||||
.addScopedCondition(
|
||||
'IsBullet',
|
||||
@@ -927,6 +953,54 @@ module.exports = {
|
||||
.setFunctionName('setDensity')
|
||||
.setGetter('getDensity');
|
||||
|
||||
aut
|
||||
.addExpressionAndConditionAndAction(
|
||||
'number',
|
||||
'ShapeOffsetX',
|
||||
_('Shape offset X'),
|
||||
_('the object shape offset on X.'),
|
||||
_('the shape offset on X'),
|
||||
_('Body settings'),
|
||||
'JsPlatform/Extensions/physics3d.svg'
|
||||
)
|
||||
.addParameter('object', _('Object'), '', false)
|
||||
.addParameter('behavior', _('Behavior'), 'Physics3DBehavior')
|
||||
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
|
||||
.setFunctionName('setShapeOffsetX')
|
||||
.setGetter('getShapeOffsetX');
|
||||
|
||||
aut
|
||||
.addExpressionAndConditionAndAction(
|
||||
'number',
|
||||
'ShapeOffsetY',
|
||||
_('Shape offset Y'),
|
||||
_('the object shape offset on Y.'),
|
||||
_('the shape offset on Y'),
|
||||
_('Body settings'),
|
||||
'JsPlatform/Extensions/physics3d.svg'
|
||||
)
|
||||
.addParameter('object', _('Object'), '', false)
|
||||
.addParameter('behavior', _('Behavior'), 'Physics3DBehavior')
|
||||
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
|
||||
.setFunctionName('setShapeOffsetY')
|
||||
.setGetter('getShapeOffsetY');
|
||||
|
||||
aut
|
||||
.addExpressionAndConditionAndAction(
|
||||
'number',
|
||||
'ShapeOffsetZ',
|
||||
_('Shape offset Z'),
|
||||
_('the object shape offset on Z.'),
|
||||
_('the shape offset on Z'),
|
||||
_('Body settings'),
|
||||
'JsPlatform/Extensions/physics3d.svg'
|
||||
)
|
||||
.addParameter('object', _('Object'), '', false)
|
||||
.addParameter('behavior', _('Behavior'), 'Physics3DBehavior')
|
||||
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
|
||||
.setFunctionName('setShapeOffsetZ')
|
||||
.setGetter('getShapeOffsetZ');
|
||||
|
||||
aut
|
||||
.addExpressionAndConditionAndAction(
|
||||
'number',
|
||||
@@ -1995,7 +2069,12 @@ module.exports = {
|
||||
'PhysicsCharacter3D',
|
||||
_('3D physics character'),
|
||||
'PhysicsCharacter3D',
|
||||
_('Jump and run on platforms.'),
|
||||
_(
|
||||
'Allow an object to jump and run on platforms that have the 3D physics behavior' +
|
||||
'(and which are generally set to "Static" as type, unless the platform is animated/moved in events).\n' +
|
||||
'\n' +
|
||||
'This behavior is usually used with one or more "mapper" behavior to let the player move it.'
|
||||
),
|
||||
'',
|
||||
'JsPlatform/Extensions/physics_character3d.svg',
|
||||
'PhysicsCharacter3D',
|
||||
@@ -2564,7 +2643,7 @@ module.exports = {
|
||||
'JumpSustainTime',
|
||||
_('Jump sustain time'),
|
||||
_(
|
||||
'the jump sustain time of an object. This is the time during which keeping the jump button held allow the initial jump speed to be maintained.'
|
||||
'the jump sustain time of an object. This is the time during which keeping the jump button held allow the initial jump speed to be maintained'
|
||||
),
|
||||
_('the jump sustain time'),
|
||||
_('Character configuration'),
|
||||
@@ -3252,7 +3331,11 @@ module.exports = {
|
||||
'PhysicsCar3D',
|
||||
_('3D physics car'),
|
||||
'PhysicsCar3D',
|
||||
_('Simulate a realistic car using the 3D physics engine.'),
|
||||
_(
|
||||
"Simulate a realistic car using the 3D physics engine. This is mostly useful for the car controlled by the player (it's usually too complex for other cars in a game).\n" +
|
||||
'\n' +
|
||||
'This behavior is usually used with one or more "mapper" behavior to let the player move it.'
|
||||
),
|
||||
'',
|
||||
'JsPlatform/Extensions/physics_car3d.svg',
|
||||
'PhysicsCar3D',
|
||||
|
@@ -495,7 +495,9 @@ namespace gdjs {
|
||||
return true;
|
||||
}
|
||||
|
||||
override getNetworkSyncData(): Physics3DNetworkSyncData {
|
||||
override getNetworkSyncData(
|
||||
options: GetNetworkSyncDataOptions
|
||||
): Physics3DNetworkSyncData {
|
||||
let bodyProps;
|
||||
if (this._body) {
|
||||
const position = this._body.GetPosition();
|
||||
@@ -537,7 +539,7 @@ namespace gdjs {
|
||||
};
|
||||
}
|
||||
return {
|
||||
...super.getNetworkSyncData(),
|
||||
...super.getNetworkSyncData(options),
|
||||
props: {
|
||||
...bodyProps,
|
||||
layers: this.layers,
|
||||
@@ -547,27 +549,40 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
override updateFromNetworkSyncData(
|
||||
networkSyncData: Physics3DNetworkSyncData
|
||||
networkSyncData: Physics3DNetworkSyncData,
|
||||
options: UpdateFromNetworkSyncDataOptions
|
||||
) {
|
||||
super.updateFromNetworkSyncData(networkSyncData);
|
||||
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||
|
||||
const behaviorSpecificProps = networkSyncData.props;
|
||||
|
||||
if (behaviorSpecificProps.layers !== undefined) {
|
||||
this.layers = behaviorSpecificProps.layers;
|
||||
}
|
||||
if (behaviorSpecificProps.masks !== undefined) {
|
||||
this.masks = behaviorSpecificProps.masks;
|
||||
}
|
||||
|
||||
this._needToRecreateShape = true;
|
||||
this._needToRecreateBody = true;
|
||||
this.updateBodyFromObject();
|
||||
|
||||
if (!this._body) return;
|
||||
|
||||
if (
|
||||
behaviorSpecificProps.px !== undefined &&
|
||||
behaviorSpecificProps.py !== undefined &&
|
||||
behaviorSpecificProps.pz !== undefined
|
||||
) {
|
||||
if (this._body) {
|
||||
this._sharedData.bodyInterface.SetPosition(
|
||||
this._body.GetID(),
|
||||
this.getRVec3(
|
||||
behaviorSpecificProps.px,
|
||||
behaviorSpecificProps.py,
|
||||
behaviorSpecificProps.pz
|
||||
),
|
||||
Jolt.EActivation_DontActivate
|
||||
);
|
||||
}
|
||||
this._sharedData.bodyInterface.SetPosition(
|
||||
this._body.GetID(),
|
||||
this.getRVec3(
|
||||
behaviorSpecificProps.px,
|
||||
behaviorSpecificProps.py,
|
||||
behaviorSpecificProps.pz
|
||||
),
|
||||
Jolt.EActivation_DontActivate
|
||||
);
|
||||
}
|
||||
if (
|
||||
behaviorSpecificProps.rx !== undefined &&
|
||||
@@ -575,79 +590,77 @@ namespace gdjs {
|
||||
behaviorSpecificProps.rz !== undefined &&
|
||||
behaviorSpecificProps.rw !== undefined
|
||||
) {
|
||||
if (this._body) {
|
||||
this._sharedData.bodyInterface.SetRotation(
|
||||
this._body.GetID(),
|
||||
this.getQuat(
|
||||
behaviorSpecificProps.rx,
|
||||
behaviorSpecificProps.ry,
|
||||
behaviorSpecificProps.rz,
|
||||
behaviorSpecificProps.rw
|
||||
),
|
||||
Jolt.EActivation_DontActivate
|
||||
);
|
||||
}
|
||||
this._sharedData.bodyInterface.SetRotation(
|
||||
this._body.GetID(),
|
||||
this.getQuat(
|
||||
behaviorSpecificProps.rx,
|
||||
behaviorSpecificProps.ry,
|
||||
behaviorSpecificProps.rz,
|
||||
behaviorSpecificProps.rw
|
||||
),
|
||||
Jolt.EActivation_DontActivate
|
||||
);
|
||||
}
|
||||
if (
|
||||
behaviorSpecificProps.lvx !== undefined &&
|
||||
behaviorSpecificProps.lvy !== undefined &&
|
||||
behaviorSpecificProps.lvz !== undefined
|
||||
) {
|
||||
if (this._body) {
|
||||
this._sharedData.bodyInterface.SetLinearVelocity(
|
||||
this._body.GetID(),
|
||||
this.getVec3(
|
||||
behaviorSpecificProps.lvx,
|
||||
behaviorSpecificProps.lvy,
|
||||
behaviorSpecificProps.lvz
|
||||
)
|
||||
);
|
||||
}
|
||||
this._sharedData.bodyInterface.SetLinearVelocity(
|
||||
this._body.GetID(),
|
||||
this.getVec3(
|
||||
behaviorSpecificProps.lvx,
|
||||
behaviorSpecificProps.lvy,
|
||||
behaviorSpecificProps.lvz
|
||||
)
|
||||
);
|
||||
}
|
||||
if (
|
||||
behaviorSpecificProps.avx !== undefined &&
|
||||
behaviorSpecificProps.avy !== undefined &&
|
||||
behaviorSpecificProps.avz !== undefined
|
||||
) {
|
||||
if (this._body) {
|
||||
this._sharedData.bodyInterface.SetAngularVelocity(
|
||||
this._body.GetID(),
|
||||
this.getVec3(
|
||||
behaviorSpecificProps.avx,
|
||||
behaviorSpecificProps.avy,
|
||||
behaviorSpecificProps.avz
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
if (behaviorSpecificProps.layers !== undefined) {
|
||||
this.layers = behaviorSpecificProps.layers;
|
||||
}
|
||||
if (behaviorSpecificProps.masks !== undefined) {
|
||||
this.masks = behaviorSpecificProps.masks;
|
||||
this._sharedData.bodyInterface.SetAngularVelocity(
|
||||
this._body.GetID(),
|
||||
this.getVec3(
|
||||
behaviorSpecificProps.avx,
|
||||
behaviorSpecificProps.avy,
|
||||
behaviorSpecificProps.avz
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
override onDeActivate() {
|
||||
this._sharedData.removeFromBehaviorsList(this);
|
||||
this._destroyBody();
|
||||
}
|
||||
|
||||
override onActivate() {
|
||||
this._sharedData.addToBehaviorsList(this);
|
||||
}
|
||||
|
||||
override onDestroy() {
|
||||
this._destroyedDuringFrameLogic = true;
|
||||
this.onDeActivate();
|
||||
}
|
||||
|
||||
_destroyBody() {
|
||||
this.bodyUpdater.destroyBody();
|
||||
this._contactsEndedThisFrame.length = 0;
|
||||
this._contactsStartedThisFrame.length = 0;
|
||||
this._currentContacts.length = 0;
|
||||
}
|
||||
|
||||
override onActivate() {
|
||||
this._sharedData.addToBehaviorsList(this);
|
||||
|
||||
this._contactsEndedThisFrame.length = 0;
|
||||
this._contactsStartedThisFrame.length = 0;
|
||||
this._currentContacts.length = 0;
|
||||
this.updateBodyFromObject();
|
||||
resetToDefaultBodyUpdater() {
|
||||
this.bodyUpdater = new gdjs.Physics3DRuntimeBehavior.DefaultBodyUpdater(
|
||||
this
|
||||
);
|
||||
}
|
||||
|
||||
override onDestroy() {
|
||||
this._destroyedDuringFrameLogic = true;
|
||||
this.onDeActivate();
|
||||
resetToDefaultCollisionChecker() {
|
||||
this.collisionChecker =
|
||||
new gdjs.Physics3DRuntimeBehavior.DefaultCollisionChecker(this);
|
||||
}
|
||||
|
||||
createShape(): Jolt.Shape {
|
||||
@@ -911,31 +924,58 @@ namespace gdjs {
|
||||
this.updateBodyFromObject();
|
||||
}
|
||||
|
||||
recreateBody() {
|
||||
if (!this._body) {
|
||||
this._createBody();
|
||||
return;
|
||||
}
|
||||
|
||||
recreateBody(previousBodyData?: {
|
||||
linearVelocityX: float;
|
||||
linearVelocityY: float;
|
||||
linearVelocityZ: float;
|
||||
angularVelocityX: float;
|
||||
angularVelocityY: float;
|
||||
angularVelocityZ: float;
|
||||
}) {
|
||||
const bodyInterface = this._sharedData.bodyInterface;
|
||||
const linearVelocity = this._body.GetLinearVelocity();
|
||||
const linearVelocityX = linearVelocity.GetX();
|
||||
const linearVelocityY = linearVelocity.GetY();
|
||||
const linearVelocityZ = linearVelocity.GetZ();
|
||||
const angularVelocity = this._body.GetAngularVelocity();
|
||||
const angularVelocityX = angularVelocity.GetX();
|
||||
const angularVelocityY = angularVelocity.GetY();
|
||||
const angularVelocityZ = angularVelocity.GetZ();
|
||||
const linearVelocityX = previousBodyData
|
||||
? previousBodyData.linearVelocityX
|
||||
: this._body
|
||||
? this._body.GetLinearVelocity().GetX()
|
||||
: 0;
|
||||
const linearVelocityY = previousBodyData
|
||||
? previousBodyData.linearVelocityY
|
||||
: this._body
|
||||
? this._body.GetLinearVelocity().GetY()
|
||||
: 0;
|
||||
const linearVelocityZ = previousBodyData
|
||||
? previousBodyData.linearVelocityZ
|
||||
: this._body
|
||||
? this._body.GetLinearVelocity().GetZ()
|
||||
: 0;
|
||||
const angularVelocityX = previousBodyData
|
||||
? previousBodyData.angularVelocityX
|
||||
: this._body
|
||||
? this._body.GetAngularVelocity().GetX()
|
||||
: 0;
|
||||
const angularVelocityY = previousBodyData
|
||||
? previousBodyData.angularVelocityY
|
||||
: this._body
|
||||
? this._body.GetAngularVelocity().GetY()
|
||||
: 0;
|
||||
const angularVelocityZ = previousBodyData
|
||||
? previousBodyData.angularVelocityZ
|
||||
: this._body
|
||||
? this._body.GetAngularVelocity().GetZ()
|
||||
: 0;
|
||||
|
||||
this.bodyUpdater.destroyBody();
|
||||
this._contactsEndedThisFrame.length = 0;
|
||||
this._contactsStartedThisFrame.length = 0;
|
||||
this._currentContacts.length = 0;
|
||||
if (this._body) {
|
||||
this.bodyUpdater.destroyBody();
|
||||
this._contactsEndedThisFrame.length = 0;
|
||||
this._contactsStartedThisFrame.length = 0;
|
||||
this._currentContacts.length = 0;
|
||||
}
|
||||
|
||||
this._createBody();
|
||||
if (!this._body) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bodyID = this._body.GetID();
|
||||
bodyInterface.SetLinearVelocity(
|
||||
bodyID,
|
||||
@@ -1113,6 +1153,52 @@ namespace gdjs {
|
||||
return this.bodyType === 'Kinematic';
|
||||
}
|
||||
|
||||
setBodyType(bodyType: string): void {
|
||||
if (
|
||||
bodyType !== 'Static' &&
|
||||
bodyType !== 'Dynamic' &&
|
||||
bodyType !== 'Kinematic'
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.bodyType === bodyType) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.bodyType = bodyType;
|
||||
|
||||
if (this._body === null) {
|
||||
if (!this._createBody()) return;
|
||||
}
|
||||
|
||||
if (this._body === null) {
|
||||
return;
|
||||
}
|
||||
const body = this._body!;
|
||||
const bodyInterface = this._sharedData.bodyInterface;
|
||||
let motionType: Jolt.EMotionType;
|
||||
switch (this.bodyType) {
|
||||
case 'Static':
|
||||
motionType = Jolt.EMotionType_Static;
|
||||
break;
|
||||
case 'Kinematic':
|
||||
motionType = Jolt.EMotionType_Kinematic;
|
||||
break;
|
||||
case 'Dynamic':
|
||||
default:
|
||||
motionType = Jolt.EMotionType_Dynamic;
|
||||
break;
|
||||
}
|
||||
|
||||
bodyInterface.SetMotionType(
|
||||
body.GetID(),
|
||||
motionType,
|
||||
Jolt.EActivation_Activate
|
||||
);
|
||||
bodyInterface.SetObjectLayer(body.GetID(), this.getBodyLayer());
|
||||
}
|
||||
|
||||
isBullet(): boolean {
|
||||
return this.bullet;
|
||||
}
|
||||
@@ -1176,6 +1262,33 @@ namespace gdjs {
|
||||
this._needToRecreateBody = true;
|
||||
}
|
||||
|
||||
getShapeOffsetX(): float {
|
||||
return this.shapeOffsetX;
|
||||
}
|
||||
|
||||
setShapeOffsetX(shapeOffsetX: float): void {
|
||||
this.shapeOffsetX = shapeOffsetX;
|
||||
this._needToRecreateShape = true;
|
||||
}
|
||||
|
||||
getShapeOffsetY(): float {
|
||||
return this.shapeOffsetY;
|
||||
}
|
||||
|
||||
setShapeOffsetY(shapeOffsetY: float): void {
|
||||
this.shapeOffsetY = shapeOffsetY;
|
||||
this._needToRecreateShape = true;
|
||||
}
|
||||
|
||||
getShapeOffsetZ(): float {
|
||||
return this.shapeOffsetZ;
|
||||
}
|
||||
|
||||
setShapeOffsetZ(shapeOffsetZ: float): void {
|
||||
this.shapeOffsetZ = shapeOffsetZ;
|
||||
this._needToRecreateShape = true;
|
||||
}
|
||||
|
||||
getFriction(): float {
|
||||
return this.friction;
|
||||
}
|
||||
@@ -1540,9 +1653,9 @@ namespace gdjs {
|
||||
}
|
||||
const body = this._body!;
|
||||
|
||||
const deltaX = towardX - body.GetPosition().GetX();
|
||||
const deltaY = towardY - body.GetPosition().GetY();
|
||||
const deltaZ = towardZ - body.GetPosition().GetZ();
|
||||
const deltaX = towardX - this.owner3D.getX();
|
||||
const deltaY = towardY - this.owner3D.getY();
|
||||
const deltaZ = towardZ - this.owner3D.getZ();
|
||||
const distanceSq = deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ;
|
||||
if (distanceSq === 0) {
|
||||
return;
|
||||
@@ -1600,19 +1713,16 @@ namespace gdjs {
|
||||
length: float,
|
||||
towardX: float,
|
||||
towardY: float,
|
||||
towardZ: float,
|
||||
originX: float,
|
||||
originY: float,
|
||||
originZ: float
|
||||
towardZ: float
|
||||
): void {
|
||||
if (this._body === null) {
|
||||
if (!this._createBody()) return;
|
||||
}
|
||||
const body = this._body!;
|
||||
|
||||
const deltaX = towardX - originX;
|
||||
const deltaY = towardY - originY;
|
||||
const deltaZ = towardZ - originZ;
|
||||
const deltaX = towardX - this.owner3D.getX();
|
||||
const deltaY = towardY - this.owner3D.getY();
|
||||
const deltaZ = towardZ - this.owner3D.getZ();
|
||||
const distanceSq = deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ;
|
||||
if (distanceSq === 0) {
|
||||
return;
|
||||
@@ -1621,12 +1731,7 @@ namespace gdjs {
|
||||
|
||||
this._sharedData.bodyInterface.AddImpulse(
|
||||
body.GetID(),
|
||||
this.getVec3(deltaX * ratio, deltaY * ratio, deltaZ * ratio),
|
||||
this.getRVec3(
|
||||
originX * this._sharedData.worldInvScale,
|
||||
originY * this._sharedData.worldInvScale,
|
||||
originZ * this._sharedData.worldInvScale
|
||||
)
|
||||
this.getVec3(deltaX * ratio, deltaY * ratio, deltaZ * ratio)
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -12,6 +12,7 @@ namespace gdjs {
|
||||
etm: float;
|
||||
esm: float;
|
||||
ei: float;
|
||||
es: float;
|
||||
}
|
||||
|
||||
export interface PhysicsCar3DNetworkSyncData extends BehaviorNetworkSyncData {
|
||||
@@ -29,6 +30,7 @@ namespace gdjs {
|
||||
owner3D: gdjs.RuntimeObject3D;
|
||||
private _physics3DBehaviorName: string;
|
||||
private _physics3D: Physics3D | null = null;
|
||||
private _isHookedToPhysicsStep = false;
|
||||
_vehicleController: Jolt.WheeledVehicleController | null = null;
|
||||
_stepListener: Jolt.VehicleConstraintStepListener | null = null;
|
||||
_vehicleCollisionTester: Jolt.VehicleCollisionTesterCastCylinder | null =
|
||||
@@ -95,7 +97,7 @@ namespace gdjs {
|
||||
// This is useful when the object is synchronized by an external source
|
||||
// like in a multiplayer game, and we want to be able to predict the
|
||||
// movement of the object, even if the inputs are not updated every frame.
|
||||
private _dontClearInputsBetweenFrames: boolean = false;
|
||||
private _clearInputsBetweenFrames: boolean = true;
|
||||
|
||||
constructor(
|
||||
instanceContainer: gdjs.RuntimeInstanceContainer,
|
||||
@@ -153,20 +155,51 @@ namespace gdjs {
|
||||
const behavior = this.owner.getBehavior(
|
||||
this._physics3DBehaviorName
|
||||
) as gdjs.Physics3DRuntimeBehavior;
|
||||
if (!behavior.activated()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const sharedData = behavior._sharedData;
|
||||
|
||||
this._physics3D = {
|
||||
behavior,
|
||||
};
|
||||
sharedData.registerHook(this);
|
||||
if (!this._isHookedToPhysicsStep) {
|
||||
sharedData.registerHook(this);
|
||||
this._isHookedToPhysicsStep = true;
|
||||
}
|
||||
|
||||
// Destroy the body before switching the bodyUpdater,
|
||||
// to ensure the body of the previous bodyUpdater is not left alive.
|
||||
// (would be a memory leak and would create a phantom body in the physics world)
|
||||
// But transfer the linear and angular velocity to the new body,
|
||||
// so the body doesn't stop when it is recreated.
|
||||
let previousBodyData = {
|
||||
linearVelocityX: 0,
|
||||
linearVelocityY: 0,
|
||||
linearVelocityZ: 0,
|
||||
angularVelocityX: 0,
|
||||
angularVelocityY: 0,
|
||||
angularVelocityZ: 0,
|
||||
};
|
||||
if (behavior._body) {
|
||||
const linearVelocity = behavior._body.GetLinearVelocity();
|
||||
previousBodyData.linearVelocityX = linearVelocity.GetX();
|
||||
previousBodyData.linearVelocityY = linearVelocity.GetY();
|
||||
previousBodyData.linearVelocityZ = linearVelocity.GetZ();
|
||||
const angularVelocity = behavior._body.GetAngularVelocity();
|
||||
previousBodyData.angularVelocityX = angularVelocity.GetX();
|
||||
previousBodyData.angularVelocityY = angularVelocity.GetY();
|
||||
previousBodyData.angularVelocityZ = angularVelocity.GetZ();
|
||||
behavior.bodyUpdater.destroyBody();
|
||||
}
|
||||
|
||||
behavior.bodyUpdater =
|
||||
new gdjs.PhysicsCar3DRuntimeBehavior.VehicleBodyUpdater(
|
||||
this,
|
||||
behavior.bodyUpdater
|
||||
);
|
||||
behavior.recreateBody();
|
||||
behavior.recreateBody(previousBodyData);
|
||||
|
||||
return this._physics3D;
|
||||
}
|
||||
@@ -266,13 +299,15 @@ namespace gdjs {
|
||||
return true;
|
||||
}
|
||||
|
||||
override getNetworkSyncData(): PhysicsCar3DNetworkSyncData {
|
||||
override getNetworkSyncData(
|
||||
syncOptions: GetNetworkSyncDataOptions
|
||||
): PhysicsCar3DNetworkSyncData {
|
||||
// This method is called, so we are synchronizing this object.
|
||||
// Let's clear the inputs between frames as we control it.
|
||||
this._dontClearInputsBetweenFrames = false;
|
||||
this._clearInputsBetweenFrames = true;
|
||||
|
||||
return {
|
||||
...super.getNetworkSyncData(),
|
||||
...super.getNetworkSyncData(syncOptions),
|
||||
props: {
|
||||
lek: this._wasLeftKeyPressed,
|
||||
rik: this._wasRightKeyPressed,
|
||||
@@ -284,14 +319,16 @@ namespace gdjs {
|
||||
etm: this._engineTorqueMax,
|
||||
esm: this._engineSpeedMax,
|
||||
ei: this._engineInertia,
|
||||
es: this.getEngineSpeed(),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
override updateFromNetworkSyncData(
|
||||
networkSyncData: PhysicsCar3DNetworkSyncData
|
||||
networkSyncData: PhysicsCar3DNetworkSyncData,
|
||||
options: UpdateFromNetworkSyncDataOptions
|
||||
) {
|
||||
super.updateFromNetworkSyncData(networkSyncData);
|
||||
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||
|
||||
const behaviorSpecificProps = networkSyncData.props;
|
||||
this._hasPressedForwardKey = behaviorSpecificProps.upk;
|
||||
@@ -304,9 +341,15 @@ namespace gdjs {
|
||||
this._engineTorqueMax = behaviorSpecificProps.etm;
|
||||
this._engineSpeedMax = behaviorSpecificProps.esm;
|
||||
this._engineInertia = behaviorSpecificProps.ei;
|
||||
if (this._vehicleController) {
|
||||
this._vehicleController
|
||||
.GetEngine()
|
||||
.SetCurrentRPM(behaviorSpecificProps.es);
|
||||
}
|
||||
|
||||
// When the object is synchronized from the network, the inputs must not be cleared.
|
||||
this._dontClearInputsBetweenFrames = true;
|
||||
// When the object is synchronized from the network, the inputs must not be cleared,
|
||||
// except if asked specifically.
|
||||
this._clearInputsBetweenFrames = !!options.clearInputs;
|
||||
}
|
||||
|
||||
_getPhysicsPosition(result: Jolt.RVec3): Jolt.RVec3 {
|
||||
@@ -330,25 +373,33 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
override onDeActivate() {
|
||||
if (this._stepListener) {
|
||||
this._sharedData.physicsSystem.RemoveStepListener(this._stepListener);
|
||||
if (!this._physics3D) {
|
||||
return;
|
||||
}
|
||||
this._destroyBody();
|
||||
}
|
||||
|
||||
override onActivate() {
|
||||
if (this._stepListener) {
|
||||
this._sharedData.physicsSystem.AddStepListener(this._stepListener);
|
||||
const behavior = this.owner.getBehavior(
|
||||
this._physics3DBehaviorName
|
||||
) as gdjs.Physics3DRuntimeBehavior;
|
||||
if (!behavior) {
|
||||
return;
|
||||
}
|
||||
behavior._destroyBody();
|
||||
}
|
||||
|
||||
override onDestroy() {
|
||||
this._destroyedDuringFrameLogic = true;
|
||||
this._destroyBody();
|
||||
}
|
||||
|
||||
_destroyBody() {
|
||||
if (!this._vehicleController) {
|
||||
return;
|
||||
}
|
||||
this._destroyedDuringFrameLogic = true;
|
||||
this.onDeActivate();
|
||||
if (this._stepListener) {
|
||||
// stepListener is removed by onDeActivate
|
||||
this._sharedData.physicsSystem.RemoveStepListener(this._stepListener);
|
||||
Jolt.destroy(this._stepListener);
|
||||
this._stepListener = null;
|
||||
}
|
||||
@@ -360,6 +411,8 @@ namespace gdjs {
|
||||
// It is destroyed with the constraint.
|
||||
this._vehicleCollisionTester = null;
|
||||
if (this._physics3D) {
|
||||
const { behavior } = this._physics3D;
|
||||
behavior.resetToDefaultBodyUpdater();
|
||||
this._physics3D = null;
|
||||
}
|
||||
}
|
||||
@@ -473,7 +526,7 @@ namespace gdjs {
|
||||
this._previousAcceleratorStickForce = this._acceleratorStickForce;
|
||||
this._previousSteeringStickForce = this._steeringStickForce;
|
||||
|
||||
if (!this._dontClearInputsBetweenFrames) {
|
||||
if (this._clearInputsBetweenFrames) {
|
||||
this._hasPressedForwardKey = false;
|
||||
this._hasPressedBackwardKey = false;
|
||||
this._hasPressedRightKey = false;
|
||||
@@ -1110,7 +1163,7 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
destroyBody() {
|
||||
this.carBehavior.onDestroy();
|
||||
this.carBehavior._destroyBody();
|
||||
this.physicsBodyUpdater.destroyBody();
|
||||
}
|
||||
}
|
||||
|
@@ -2,11 +2,24 @@
|
||||
|
||||
namespace gdjs {
|
||||
interface PhysicsCharacter3DNetworkSyncDataType {
|
||||
sma: float;
|
||||
shm: float;
|
||||
grav: float;
|
||||
mfs: float;
|
||||
facc: float;
|
||||
fdec: float;
|
||||
fsm: float;
|
||||
sacc: float;
|
||||
sdec: float;
|
||||
ssm: float;
|
||||
jumpspeed: float;
|
||||
jumpsustime: float;
|
||||
sbpa: boolean;
|
||||
fwa: float;
|
||||
fws: float;
|
||||
sws: float;
|
||||
fs: float;
|
||||
js: float;
|
||||
cfs: float;
|
||||
cjs: float;
|
||||
cj: boolean;
|
||||
lek: boolean;
|
||||
rik: boolean;
|
||||
@@ -41,6 +54,7 @@ namespace gdjs {
|
||||
owner3D: gdjs.RuntimeObject3D;
|
||||
private _physics3DBehaviorName: string;
|
||||
private _physics3D: Physics3D | null = null;
|
||||
private _isHookedToPhysicsStep = false;
|
||||
character: Jolt.CharacterVirtual | null = null;
|
||||
/**
|
||||
* sharedData is a reference to the shared data of the scene, that registers
|
||||
@@ -101,7 +115,7 @@ namespace gdjs {
|
||||
// This is useful when the object is synchronized by an external source
|
||||
// like in a multiplayer game, and we want to be able to predict the
|
||||
// movement of the object, even if the inputs are not updated every frame.
|
||||
private _dontClearInputsBetweenFrames: boolean = false;
|
||||
private _clearInputsBetweenFrames: boolean = true;
|
||||
|
||||
/**
|
||||
* A very small value compare to 1 pixel, yet very huge compare to rounding errors.
|
||||
@@ -169,10 +183,15 @@ namespace gdjs {
|
||||
if (this._physics3D) {
|
||||
return this._physics3D;
|
||||
}
|
||||
if (!this.activated()) {
|
||||
return null;
|
||||
}
|
||||
const behavior = this.owner.getBehavior(
|
||||
this._physics3DBehaviorName
|
||||
) as gdjs.Physics3DRuntimeBehavior;
|
||||
|
||||
if (!behavior.activated()) {
|
||||
return null;
|
||||
}
|
||||
const sharedData = behavior._sharedData;
|
||||
const jolt = sharedData.jolt;
|
||||
const extendedUpdateSettings = new Jolt.ExtendedUpdateSettings();
|
||||
@@ -196,12 +215,40 @@ namespace gdjs {
|
||||
shapeFilter,
|
||||
};
|
||||
this.setStairHeightMax(this._stairHeightMax);
|
||||
sharedData.registerHook(this);
|
||||
if (!this._isHookedToPhysicsStep) {
|
||||
sharedData.registerHook(this);
|
||||
this._isHookedToPhysicsStep = true;
|
||||
}
|
||||
|
||||
// Destroy the body before switching the bodyUpdater,
|
||||
// to ensure the body of the previous bodyUpdater is not left alive.
|
||||
// (would be a memory leak and would create a phantom body in the physics world)
|
||||
// But transfer the linear and angular velocity to the new body,
|
||||
// so the body doesn't stop when it is recreated.
|
||||
let previousBodyData = {
|
||||
linearVelocityX: 0,
|
||||
linearVelocityY: 0,
|
||||
linearVelocityZ: 0,
|
||||
angularVelocityX: 0,
|
||||
angularVelocityY: 0,
|
||||
angularVelocityZ: 0,
|
||||
};
|
||||
if (behavior._body) {
|
||||
const linearVelocity = behavior._body.GetLinearVelocity();
|
||||
previousBodyData.linearVelocityX = linearVelocity.GetX();
|
||||
previousBodyData.linearVelocityY = linearVelocity.GetY();
|
||||
previousBodyData.linearVelocityZ = linearVelocity.GetZ();
|
||||
const angularVelocity = behavior._body.GetAngularVelocity();
|
||||
previousBodyData.angularVelocityX = angularVelocity.GetX();
|
||||
previousBodyData.angularVelocityY = angularVelocity.GetY();
|
||||
previousBodyData.angularVelocityZ = angularVelocity.GetZ();
|
||||
behavior.bodyUpdater.destroyBody();
|
||||
}
|
||||
|
||||
behavior.bodyUpdater =
|
||||
new gdjs.PhysicsCharacter3DRuntimeBehavior.CharacterBodyUpdater(this);
|
||||
behavior.collisionChecker = this.collisionChecker;
|
||||
behavior.recreateBody();
|
||||
behavior.recreateBody(previousBodyData);
|
||||
|
||||
// Always begin in the direction of the object.
|
||||
this._forwardAngle = this.owner.getAngle();
|
||||
@@ -268,19 +315,34 @@ namespace gdjs {
|
||||
return true;
|
||||
}
|
||||
|
||||
override getNetworkSyncData(): PhysicsCharacter3DNetworkSyncData {
|
||||
override getNetworkSyncData(
|
||||
options: GetNetworkSyncDataOptions
|
||||
): PhysicsCharacter3DNetworkSyncData {
|
||||
// This method is called, so we are synchronizing this object.
|
||||
// Let's clear the inputs between frames as we control it.
|
||||
this._dontClearInputsBetweenFrames = false;
|
||||
this._clearInputsBetweenFrames = true;
|
||||
|
||||
return {
|
||||
...super.getNetworkSyncData(),
|
||||
...super.getNetworkSyncData(options),
|
||||
props: {
|
||||
sma: this._slopeMaxAngle,
|
||||
shm: this._stairHeightMax,
|
||||
grav: this._gravity,
|
||||
mfs: this._maxFallingSpeed,
|
||||
facc: this._forwardAcceleration,
|
||||
fdec: this._forwardDeceleration,
|
||||
fsm: this._forwardSpeedMax,
|
||||
sacc: this._sidewaysAcceleration,
|
||||
sdec: this._sidewaysDeceleration,
|
||||
ssm: this._sidewaysSpeedMax,
|
||||
jumpspeed: this._jumpSpeed,
|
||||
jumpsustime: this._jumpSustainTime,
|
||||
fwa: this._forwardAngle,
|
||||
sbpa: this._shouldBindObjectAndForwardAngle,
|
||||
fws: this._currentForwardSpeed,
|
||||
sws: this._currentSidewaysSpeed,
|
||||
fs: this._currentFallSpeed,
|
||||
js: this._currentJumpSpeed,
|
||||
cfs: this._currentFallSpeed,
|
||||
cjs: this._currentJumpSpeed,
|
||||
cj: this._canJump,
|
||||
lek: this._wasLeftKeyPressed,
|
||||
rik: this._wasRightKeyPressed,
|
||||
@@ -297,16 +359,30 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
override updateFromNetworkSyncData(
|
||||
networkSyncData: PhysicsCharacter3DNetworkSyncData
|
||||
networkSyncData: PhysicsCharacter3DNetworkSyncData,
|
||||
options: UpdateFromNetworkSyncDataOptions
|
||||
) {
|
||||
super.updateFromNetworkSyncData(networkSyncData);
|
||||
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||
|
||||
const behaviorSpecificProps = networkSyncData.props;
|
||||
this._slopeMaxAngle = behaviorSpecificProps.sma;
|
||||
this._stairHeightMax = behaviorSpecificProps.shm;
|
||||
this._gravity = behaviorSpecificProps.grav;
|
||||
this._maxFallingSpeed = behaviorSpecificProps.mfs;
|
||||
this._forwardAcceleration = behaviorSpecificProps.facc;
|
||||
this._forwardDeceleration = behaviorSpecificProps.fdec;
|
||||
this._forwardSpeedMax = behaviorSpecificProps.fsm;
|
||||
this._sidewaysAcceleration = behaviorSpecificProps.sacc;
|
||||
this._sidewaysDeceleration = behaviorSpecificProps.sdec;
|
||||
this._sidewaysSpeedMax = behaviorSpecificProps.ssm;
|
||||
this._jumpSpeed = behaviorSpecificProps.jumpspeed;
|
||||
this._jumpSustainTime = behaviorSpecificProps.jumpsustime;
|
||||
this._forwardAngle = behaviorSpecificProps.fwa;
|
||||
this._shouldBindObjectAndForwardAngle = behaviorSpecificProps.sbpa;
|
||||
this._currentForwardSpeed = behaviorSpecificProps.fws;
|
||||
this._currentSidewaysSpeed = behaviorSpecificProps.sws;
|
||||
this._currentFallSpeed = behaviorSpecificProps.fs;
|
||||
this._currentJumpSpeed = behaviorSpecificProps.js;
|
||||
this._currentFallSpeed = behaviorSpecificProps.cfs;
|
||||
this._currentJumpSpeed = behaviorSpecificProps.cjs;
|
||||
this._canJump = behaviorSpecificProps.cj;
|
||||
this._hasPressedForwardKey = behaviorSpecificProps.upk;
|
||||
this._hasPressedBackwardKey = behaviorSpecificProps.dok;
|
||||
@@ -319,8 +395,8 @@ namespace gdjs {
|
||||
this._timeSinceCurrentJumpStart = behaviorSpecificProps.tscjs;
|
||||
this._jumpKeyHeldSinceJumpStart = behaviorSpecificProps.jkhsjs;
|
||||
|
||||
// When the object is synchronized from the network, the inputs must not be cleared.
|
||||
this._dontClearInputsBetweenFrames = true;
|
||||
// Clear user inputs between frames only if requested.
|
||||
this._clearInputsBetweenFrames = !!options.clearInputs;
|
||||
}
|
||||
|
||||
_getPhysicsPosition(result: Jolt.RVec3): Jolt.RVec3 {
|
||||
@@ -390,36 +466,48 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
override onDeActivate() {
|
||||
this.collisionChecker.clearContacts();
|
||||
if (!this._physics3D) {
|
||||
return;
|
||||
}
|
||||
this._destroyBody();
|
||||
}
|
||||
|
||||
override onActivate() {}
|
||||
override onActivate() {
|
||||
const behavior = this.owner.getBehavior(
|
||||
this._physics3DBehaviorName
|
||||
) as gdjs.Physics3DRuntimeBehavior;
|
||||
if (!behavior) {
|
||||
return;
|
||||
}
|
||||
behavior._destroyBody();
|
||||
}
|
||||
|
||||
override onDestroy() {
|
||||
this._destroyedDuringFrameLogic = true;
|
||||
this.onDeActivate();
|
||||
this._destroyCharacter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the character and its body from the physics engine.
|
||||
* This method is called when:
|
||||
* - The Physics3D behavior is deactivated
|
||||
* - This behavior is deactivated
|
||||
* - The object is destroyed
|
||||
*
|
||||
* Only deactivating the character behavior won't destroy the character.
|
||||
* Indeed, deactivated characters don't move as characters but still have collisions.
|
||||
*/
|
||||
_destroyCharacter() {
|
||||
_destroyBody() {
|
||||
if (this.character) {
|
||||
if (this._canBePushed) {
|
||||
this.charactersManager.removeCharacter(this.character);
|
||||
Jolt.destroy(this.character.GetListener());
|
||||
}
|
||||
this.collisionChecker.clearContacts();
|
||||
// The body is destroyed with the character.
|
||||
Jolt.destroy(this.character);
|
||||
this.character = null;
|
||||
if (this._physics3D) {
|
||||
const { behavior } = this._physics3D;
|
||||
behavior.resetToDefaultBodyUpdater();
|
||||
behavior.resetToDefaultCollisionChecker();
|
||||
this._physics3D.behavior._body = null;
|
||||
const {
|
||||
extendedUpdateSettings,
|
||||
@@ -629,7 +717,7 @@ namespace gdjs {
|
||||
this._wasJumpKeyPressed = this._hasPressedJumpKey;
|
||||
this._wasStickUsed = this._hasUsedStick;
|
||||
|
||||
if (!this._dontClearInputsBetweenFrames) {
|
||||
if (this._clearInputsBetweenFrames) {
|
||||
this._hasPressedForwardKey = false;
|
||||
this._hasPressedBackwardKey = false;
|
||||
this._hasPressedRightKey = false;
|
||||
@@ -1780,7 +1868,7 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
destroyBody() {
|
||||
this.characterBehavior._destroyCharacter();
|
||||
this.characterBehavior._destroyBody();
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -37,7 +37,8 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
"res/physics-deprecated32.png",
|
||||
"PhysicsBehavior",
|
||||
std::make_shared<PhysicsBehavior>(),
|
||||
std::make_shared<ScenePhysicsDatas>());
|
||||
std::make_shared<ScenePhysicsDatas>())
|
||||
.MarkAsIrrelevantForChildObjects();
|
||||
|
||||
aut.AddAction("SetStatic",
|
||||
("Make the object static"),
|
||||
|
@@ -147,7 +147,7 @@ namespace gdjs {
|
||||
// This is useful when the object is synchronized by an external source
|
||||
// like in a multiplayer game, and we want to be able to predict the
|
||||
// movement of the object, even if the inputs are not updated every frame.
|
||||
_dontClearInputsBetweenFrames: boolean = false;
|
||||
private _clearInputsBetweenFrames: boolean = true;
|
||||
// This is useful when the object is synchronized over the network,
|
||||
// object is controlled by the network and we want to ensure the current player
|
||||
// cannot control it.
|
||||
@@ -227,14 +227,16 @@ namespace gdjs {
|
||||
this._state = this._falling;
|
||||
}
|
||||
|
||||
getNetworkSyncData(): PlatformerObjectNetworkSyncData {
|
||||
getNetworkSyncData(
|
||||
syncOptions: GetNetworkSyncDataOptions
|
||||
): PlatformerObjectNetworkSyncData {
|
||||
// This method is called, so we are synchronizing this object.
|
||||
// Let's clear the inputs between frames as we control it.
|
||||
this._dontClearInputsBetweenFrames = false;
|
||||
this._clearInputsBetweenFrames = true;
|
||||
this._ignoreDefaultControlsAsSyncedByNetwork = false;
|
||||
|
||||
return {
|
||||
...super.getNetworkSyncData(),
|
||||
...super.getNetworkSyncData(syncOptions),
|
||||
props: {
|
||||
cs: this._currentSpeed,
|
||||
|
||||
@@ -263,11 +265,52 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
updateFromNetworkSyncData(
|
||||
networkSyncData: PlatformerObjectNetworkSyncData
|
||||
networkSyncData: PlatformerObjectNetworkSyncData,
|
||||
options: UpdateFromNetworkSyncDataOptions
|
||||
) {
|
||||
super.updateFromNetworkSyncData(networkSyncData);
|
||||
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||
|
||||
const behaviorSpecificProps = networkSyncData.props;
|
||||
|
||||
switch (behaviorSpecificProps.sn) {
|
||||
case 'Falling':
|
||||
if (behaviorSpecificProps.sn !== this._state.toString()) {
|
||||
this._setFalling();
|
||||
}
|
||||
this._falling.updateFromNetworkSyncData(behaviorSpecificProps.ssd);
|
||||
break;
|
||||
case 'OnFloor':
|
||||
// Let it handle automatically as we don't know which platform to land on.
|
||||
// @ts-ignore - we assume it's OnFloorStateNetworkSyncData
|
||||
this._onFloor.updateFromNetworkSyncData(behaviorSpecificProps.ssd);
|
||||
break;
|
||||
case 'Jumping':
|
||||
if (behaviorSpecificProps.sn !== this._state.toString()) {
|
||||
this._setJumping();
|
||||
}
|
||||
// @ts-ignore - we assume it's JumpingStateNetworkSyncData
|
||||
this._jumping.updateFromNetworkSyncData(behaviorSpecificProps.ssd);
|
||||
break;
|
||||
case 'GrabbingPlatform':
|
||||
// Let it handle automatically as we don't know which platform to grab.
|
||||
this._grabbingPlatform.updateFromNetworkSyncData(
|
||||
// @ts-ignore - we assume it's GrabbingPlatformStateNetworkSyncData
|
||||
behaviorSpecificProps.ssd
|
||||
);
|
||||
break;
|
||||
case 'OnLadder':
|
||||
if (behaviorSpecificProps.sn !== this._state.toString()) {
|
||||
this._setOnLadder();
|
||||
}
|
||||
this._onLadder.updateFromNetworkSyncData(behaviorSpecificProps.ssd);
|
||||
break;
|
||||
default:
|
||||
console.error(
|
||||
'Unknown state name: ' + behaviorSpecificProps.sn + '.'
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
if (behaviorSpecificProps.cs !== this._currentSpeed) {
|
||||
this._currentSpeed = behaviorSpecificProps.cs;
|
||||
}
|
||||
@@ -317,39 +360,10 @@ namespace gdjs {
|
||||
this._jumpKeyHeldSinceJumpStart = behaviorSpecificProps.jkhsjs;
|
||||
}
|
||||
|
||||
if (behaviorSpecificProps.sn !== this._state.toString()) {
|
||||
switch (behaviorSpecificProps.sn) {
|
||||
case 'Falling':
|
||||
this._setFalling();
|
||||
break;
|
||||
case 'OnFloor':
|
||||
// Let it handle automatically as we don't know which platform to land on.
|
||||
break;
|
||||
case 'Jumping':
|
||||
this._setJumping();
|
||||
break;
|
||||
case 'GrabbingPlatform':
|
||||
// Let it handle automatically as we don't know which platform to grab.
|
||||
break;
|
||||
case 'OnLadder':
|
||||
this._setOnLadder();
|
||||
break;
|
||||
default:
|
||||
console.error(
|
||||
'Unknown state name: ' + behaviorSpecificProps.sn + '.'
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (behaviorSpecificProps.sn === this._state.toString()) {
|
||||
this._state.updateFromNetworkSyncData(behaviorSpecificProps.ssd);
|
||||
}
|
||||
|
||||
// When the object is synchronized from the network, the inputs must not be cleared.
|
||||
this._dontClearInputsBetweenFrames = true;
|
||||
// Clear user inputs between frames only if requested.
|
||||
this._clearInputsBetweenFrames = !!options.clearInputs;
|
||||
// And we are not using the default controls.
|
||||
this._ignoreDefaultControlsAsSyncedByNetwork = true;
|
||||
this._ignoreDefaultControlsAsSyncedByNetwork = !options.keepControl;
|
||||
}
|
||||
|
||||
updateFromBehaviorData(oldBehaviorData, newBehaviorData): boolean {
|
||||
@@ -545,8 +559,9 @@ namespace gdjs {
|
||||
this._wasJumpKeyPressed = this._jumpKey;
|
||||
this._wasReleasePlatformKeyPressed = this._releasePlatformKey;
|
||||
this._wasReleaseLadderKeyPressed = this._releaseLadderKey;
|
||||
|
||||
//4) Do not forget to reset pressed keys
|
||||
if (!this._dontClearInputsBetweenFrames) {
|
||||
if (this._clearInputsBetweenFrames) {
|
||||
// Reset the keys only if the inputs are not supposed to survive between frames.
|
||||
// (Most of the time, except if this object is synchronized by an external source)
|
||||
this._leftKey = false;
|
||||
|
@@ -157,6 +157,57 @@ namespace gdjs {
|
||||
return true;
|
||||
}
|
||||
|
||||
trackChangesAndUpdateManagerIfNeeded() {
|
||||
if (!this.activated() && this._registeredInManager) {
|
||||
this._manager.removePlatform(this);
|
||||
this._registeredInManager = false;
|
||||
} else {
|
||||
if (this.activated() && !this._registeredInManager) {
|
||||
this._manager.addPlatform(this);
|
||||
this._registeredInManager = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
this._oldX !== this.owner.getX() ||
|
||||
this._oldY !== this.owner.getY() ||
|
||||
this._oldWidth !== this.owner.getWidth() ||
|
||||
this._oldHeight !== this.owner.getHeight() ||
|
||||
this._oldAngle !== this.owner.getAngle()
|
||||
) {
|
||||
if (this._registeredInManager) {
|
||||
this._manager.removePlatform(this);
|
||||
this._manager.addPlatform(this);
|
||||
}
|
||||
this._oldX = this.owner.getX();
|
||||
this._oldY = this.owner.getY();
|
||||
this._oldWidth = this.owner.getWidth();
|
||||
this._oldHeight = this.owner.getHeight();
|
||||
this._oldAngle = this.owner.getAngle();
|
||||
}
|
||||
}
|
||||
|
||||
getNetworkSyncData(
|
||||
syncOptions: GetNetworkSyncDataOptions
|
||||
): BehaviorNetworkSyncData {
|
||||
return super.getNetworkSyncData(syncOptions);
|
||||
}
|
||||
|
||||
updateFromNetworkSyncData(
|
||||
networkSyncData: BehaviorNetworkSyncData,
|
||||
options: UpdateFromNetworkSyncDataOptions
|
||||
): void {
|
||||
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||
|
||||
this.trackChangesAndUpdateManagerIfNeeded();
|
||||
}
|
||||
|
||||
onCreated(): void {
|
||||
// Register it right away if activated,
|
||||
// so it can be used by platformer objects in that same frame.
|
||||
this.trackChangesAndUpdateManagerIfNeeded();
|
||||
}
|
||||
|
||||
onDestroy() {
|
||||
if (this._manager && this._registeredInManager) {
|
||||
this._manager.removePlatform(this);
|
||||
@@ -175,34 +226,7 @@ namespace gdjs {
|
||||
}*/
|
||||
|
||||
//Make sure the platform is or is not in the platforms manager.
|
||||
if (!this.activated() && this._registeredInManager) {
|
||||
this._manager.removePlatform(this);
|
||||
this._registeredInManager = false;
|
||||
} else {
|
||||
if (this.activated() && !this._registeredInManager) {
|
||||
this._manager.addPlatform(this);
|
||||
this._registeredInManager = true;
|
||||
}
|
||||
}
|
||||
|
||||
//Track changes in size or position
|
||||
if (
|
||||
this._oldX !== this.owner.getX() ||
|
||||
this._oldY !== this.owner.getY() ||
|
||||
this._oldWidth !== this.owner.getWidth() ||
|
||||
this._oldHeight !== this.owner.getHeight() ||
|
||||
this._oldAngle !== this.owner.getAngle()
|
||||
) {
|
||||
if (this._registeredInManager) {
|
||||
this._manager.removePlatform(this);
|
||||
this._manager.addPlatform(this);
|
||||
}
|
||||
this._oldX = this.owner.getX();
|
||||
this._oldY = this.owner.getY();
|
||||
this._oldWidth = this.owner.getWidth();
|
||||
this._oldHeight = this.owner.getHeight();
|
||||
this._oldAngle = this.owner.getAngle();
|
||||
}
|
||||
this.trackChangesAndUpdateManagerIfNeeded();
|
||||
}
|
||||
|
||||
doStepPostEvents(instanceContainer: gdjs.RuntimeInstanceContainer) {}
|
||||
|
@@ -35,7 +35,7 @@ module.exports = {
|
||||
.addDependency()
|
||||
.setName('Safari View Controller Cordova plugin')
|
||||
.setDependencyType('cordova')
|
||||
.setExportName('cordova-plugin-safariviewcontroller');
|
||||
.setExportName('@gdevelop/cordova-plugin-safariviewcontroller');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
|
@@ -834,34 +834,51 @@ namespace gdjs {
|
||||
authWindowOptions,
|
||||
});
|
||||
|
||||
if (typeof SafariViewController === 'undefined') {
|
||||
logger.error(
|
||||
'Cordova plugin SafariViewController is not installed.'
|
||||
);
|
||||
resolve('errored');
|
||||
return;
|
||||
}
|
||||
|
||||
SafariViewController.isAvailable(function (available: boolean) {
|
||||
if (available) {
|
||||
SafariViewController.show(
|
||||
{
|
||||
url: targetUrl,
|
||||
hidden: false,
|
||||
animated: true,
|
||||
transition: 'slide',
|
||||
enterReaderModeIfAvailable: false,
|
||||
barColor: '#000000',
|
||||
tintColor: '#ffffff',
|
||||
controlTintColor: '#ffffff',
|
||||
},
|
||||
function (result: any) {
|
||||
// Other events are `opened` and `loaded`.
|
||||
if (result.event === 'closed') {
|
||||
resolve('dismissed');
|
||||
}
|
||||
},
|
||||
function (error: any) {
|
||||
logger.log('Error opening webview: ' + JSON.stringify(error));
|
||||
resolve('errored');
|
||||
}
|
||||
if (!available) {
|
||||
logger.error(
|
||||
'Cordova plugin SafariViewController is installed but not available'
|
||||
);
|
||||
} else {
|
||||
logger.error('Plugin SafariViewController is not available');
|
||||
resolve('errored');
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info(
|
||||
'Opening authentication window for Cordova with SafariViewController.'
|
||||
);
|
||||
SafariViewController.show(
|
||||
{
|
||||
url: targetUrl,
|
||||
hidden: false,
|
||||
animated: true,
|
||||
transition: 'slide',
|
||||
enterReaderModeIfAvailable: false,
|
||||
barColor: '#000000',
|
||||
tintColor: '#ffffff',
|
||||
controlTintColor: '#ffffff',
|
||||
},
|
||||
function (result: any) {
|
||||
// Other events are `opened` and `loaded`.
|
||||
if (result.event === 'closed') {
|
||||
resolve('dismissed');
|
||||
}
|
||||
},
|
||||
function (error: any) {
|
||||
logger.log(
|
||||
'Error opening authentication window: ' +
|
||||
JSON.stringify(error)
|
||||
);
|
||||
resolve('errored');
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
@@ -15,7 +15,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
|
||||
.SetExtensionInformation(
|
||||
"PrimitiveDrawing",
|
||||
_("Shape painter"),
|
||||
_("This provides an object that can be used to draw arbitrary shapes "
|
||||
_("An object that can be used to draw arbitrary 2D shapes "
|
||||
"on the screen using events."),
|
||||
"Florian Rival and Aurélien Vivet",
|
||||
"Open source (MIT License)")
|
||||
@@ -28,7 +28,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
|
||||
.AddObject<ShapePainterObject>(
|
||||
"Drawer", //"Drawer" is kept for compatibility with GD<=3.6.76
|
||||
_("Shape painter"),
|
||||
_("Allows you to draw simple shapes on the screen using the "
|
||||
_("Allows to draw simple 2D shapes on the screen using the "
|
||||
"events."),
|
||||
"CppPlatform/Extensions/primitivedrawingicon.png")
|
||||
.SetCategoryFullName(_("Advanced"))
|
||||
@@ -125,11 +125,11 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
|
||||
.SetFunctionName("DrawEllipse");
|
||||
|
||||
obj.AddAction("FilletRectangle",
|
||||
_("Fillet Rectangle"),
|
||||
_("Draw a fillet rectangle on screen"),
|
||||
_("Draw from _PARAM1_;_PARAM2_ to _PARAM3_;_PARAM4_ a fillet "
|
||||
"rectangle (fillet: _PARAM5_)"
|
||||
"with _PARAM0_"),
|
||||
_("Fillet Rectangle"),
|
||||
_("Draw a fillet rectangle on screen"),
|
||||
_("Draw from _PARAM1_;_PARAM2_ to _PARAM3_;_PARAM4_ a fillet "
|
||||
"rectangle (fillet: _PARAM5_)"
|
||||
"with _PARAM0_"),
|
||||
_("Drawing"),
|
||||
"res/actions/filletRectangle24.png",
|
||||
"res/actions/filletRectangle.png")
|
||||
@@ -142,7 +142,6 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
|
||||
.AddParameter("expression", _("Fillet (in pixels)"))
|
||||
.SetFunctionName("DrawFilletRectangle");
|
||||
|
||||
|
||||
obj.AddAction("RoundedRectangle",
|
||||
_("Rounded rectangle"),
|
||||
_("Draw a rounded rectangle on screen"),
|
||||
@@ -170,54 +169,53 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
|
||||
_("Drawing"),
|
||||
"res/actions/chamferRectangle24.png",
|
||||
"res/actions/chamferRectangle.png")
|
||||
.AddParameter("object", _("Shape Painter object"), "Drawer")
|
||||
.AddParameter("expression", _("Left X position"))
|
||||
.AddParameter("expression", _("Top Y position"))
|
||||
.AddParameter("expression", _("Right X position"))
|
||||
.AddParameter("expression", _("Bottom Y position"))
|
||||
.AddParameter("expression", _("Chamfer (in pixels)"))
|
||||
.SetFunctionName("DrawChamferRectangle");
|
||||
|
||||
.AddParameter("object", _("Shape Painter object"), "Drawer")
|
||||
.AddParameter("expression", _("Left X position"))
|
||||
.AddParameter("expression", _("Top Y position"))
|
||||
.AddParameter("expression", _("Right X position"))
|
||||
.AddParameter("expression", _("Bottom Y position"))
|
||||
.AddParameter("expression", _("Chamfer (in pixels)"))
|
||||
.SetFunctionName("DrawChamferRectangle");
|
||||
|
||||
obj.AddAction("Torus",
|
||||
_("Torus"),
|
||||
_("Draw a torus on screen"),
|
||||
_("Draw at _PARAM1_;_PARAM2_ a torus with "
|
||||
"inner radius: _PARAM3_, outer radius: _PARAM4_ and "
|
||||
"with start arc angle: _PARAM5_°, end angle: _PARAM6_° "
|
||||
"with _PARAM0_"),
|
||||
_("Drawing"),
|
||||
"res/actions/torus24.png",
|
||||
"res/actions/torus.png")
|
||||
|
||||
.AddParameter("object", _("Shape Painter object"), "Drawer")
|
||||
.AddParameter("expression", _("X position of center"))
|
||||
.AddParameter("expression", _("Y position of center"))
|
||||
.AddParameter("expression", _("Inner Radius (in pixels)"))
|
||||
.AddParameter("expression", _("Outer Radius (in pixels)"))
|
||||
.AddParameter("expression", _("Start Arc (in degrees)"))
|
||||
.AddParameter("expression", _("End Arc (in degrees)"))
|
||||
.SetFunctionName("DrawTorus");
|
||||
|
||||
|
||||
_("Torus"),
|
||||
_("Draw a torus on screen"),
|
||||
_("Draw at _PARAM1_;_PARAM2_ a torus with "
|
||||
"inner radius: _PARAM3_, outer radius: _PARAM4_ and "
|
||||
"with start arc angle: _PARAM5_°, end angle: _PARAM6_° "
|
||||
"with _PARAM0_"),
|
||||
_("Drawing"),
|
||||
"res/actions/torus24.png",
|
||||
"res/actions/torus.png")
|
||||
|
||||
.AddParameter("object", _("Shape Painter object"), "Drawer")
|
||||
.AddParameter("expression", _("X position of center"))
|
||||
.AddParameter("expression", _("Y position of center"))
|
||||
.AddParameter("expression", _("Inner Radius (in pixels)"))
|
||||
.AddParameter("expression", _("Outer Radius (in pixels)"))
|
||||
.AddParameter("expression", _("Start Arc (in degrees)"))
|
||||
.AddParameter("expression", _("End Arc (in degrees)"))
|
||||
.SetFunctionName("DrawTorus");
|
||||
|
||||
obj.AddAction("RegularPolygon",
|
||||
_("Regular Polygon"),
|
||||
_("Draw a regular polygon on screen"),
|
||||
_("Draw at _PARAM1_;_PARAM2_ a regular polygon with _PARAM3_ sides and radius: "
|
||||
_("Draw at _PARAM1_;_PARAM2_ a regular polygon with _PARAM3_ "
|
||||
"sides and radius: "
|
||||
"_PARAM4_ (rotation: _PARAM5_) "
|
||||
"with _PARAM0_"),
|
||||
_("Drawing"),
|
||||
"res/actions/regularPolygon24.png",
|
||||
"res/actions/regularPolygon.png")
|
||||
_("Drawing"),
|
||||
"res/actions/regularPolygon24.png",
|
||||
"res/actions/regularPolygon.png")
|
||||
|
||||
.AddParameter("object", _("Shape Painter object"), "Drawer")
|
||||
.AddParameter("expression", _("X position of center"))
|
||||
.AddParameter("expression", _("Y position of center"))
|
||||
.AddParameter("expression",
|
||||
_("Number of sides of the polygon (minimum: 3)"))
|
||||
.AddParameter("expression", _("Radius (in pixels)"))
|
||||
.AddParameter("expression", _("Rotation (in degrees)"))
|
||||
.SetFunctionName("DrawRegularPolygon");
|
||||
.AddParameter("object", _("Shape Painter object"), "Drawer")
|
||||
.AddParameter("expression", _("X position of center"))
|
||||
.AddParameter("expression", _("Y position of center"))
|
||||
.AddParameter("expression",
|
||||
_("Number of sides of the polygon (minimum: 3)"))
|
||||
.AddParameter("expression", _("Radius (in pixels)"))
|
||||
.AddParameter("expression", _("Rotation (in degrees)"))
|
||||
.SetFunctionName("DrawRegularPolygon");
|
||||
|
||||
obj.AddAction(
|
||||
"Star",
|
||||
|
@@ -37,6 +37,24 @@ namespace gdjs {
|
||||
|
||||
export type ShapePainterObjectData = ObjectData & ShapePainterObjectDataType;
|
||||
|
||||
type ShapePainterNetworkSyncDataType = {
|
||||
cbf: boolean; // clearBetweenFrames
|
||||
aa: Antialiasing; // antialiasing
|
||||
ac: boolean; // absoluteCoordinates
|
||||
fc: integer; // fillColor
|
||||
oc: integer; // outlineColor
|
||||
os: float; // outlineSize
|
||||
fo: float; // fillOpacity
|
||||
oo: float; // outlineOpacity
|
||||
scaleX: number;
|
||||
scaleY: number;
|
||||
ifx: boolean; // isFlippedX
|
||||
ify: boolean; // isFlippedY
|
||||
};
|
||||
|
||||
export type ShapePainterNetworkSyncData = ObjectNetworkSyncData &
|
||||
ShapePainterNetworkSyncDataType;
|
||||
|
||||
/**
|
||||
* The ShapePainterRuntimeObject allows to draw graphics shapes on screen.
|
||||
*/
|
||||
@@ -191,6 +209,70 @@ namespace gdjs {
|
||||
return true;
|
||||
}
|
||||
|
||||
getNetworkSyncData(
|
||||
syncOptions: GetNetworkSyncDataOptions
|
||||
): ShapePainterNetworkSyncData {
|
||||
return {
|
||||
...super.getNetworkSyncData(syncOptions),
|
||||
cbf: this._clearBetweenFrames,
|
||||
aa: this._antialiasing,
|
||||
ac: this._useAbsoluteCoordinates,
|
||||
fc: this._fillColor,
|
||||
oc: this._outlineColor,
|
||||
os: this._outlineSize,
|
||||
fo: this._fillOpacity,
|
||||
oo: this._outlineOpacity,
|
||||
scaleX: this.getScaleX(),
|
||||
scaleY: this.getScaleY(),
|
||||
ifx: this._flippedX,
|
||||
ify: this._flippedY,
|
||||
};
|
||||
}
|
||||
|
||||
updateFromNetworkSyncData(
|
||||
networkSyncData: ShapePainterNetworkSyncData,
|
||||
options: UpdateFromNetworkSyncDataOptions
|
||||
): void {
|
||||
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||
|
||||
if (networkSyncData.cbf !== undefined) {
|
||||
this._clearBetweenFrames = networkSyncData.cbf;
|
||||
}
|
||||
if (networkSyncData.aa !== undefined) {
|
||||
this.setAntialiasing(networkSyncData.aa);
|
||||
}
|
||||
if (networkSyncData.ac !== undefined) {
|
||||
this.setCoordinatesRelative(!networkSyncData.ac);
|
||||
}
|
||||
if (networkSyncData.fc !== undefined) {
|
||||
this._fillColor = networkSyncData.fc;
|
||||
}
|
||||
if (networkSyncData.oc !== undefined) {
|
||||
this._outlineColor = networkSyncData.oc;
|
||||
}
|
||||
if (networkSyncData.os !== undefined) {
|
||||
this.setOutlineSize(networkSyncData.os);
|
||||
}
|
||||
if (networkSyncData.fo !== undefined) {
|
||||
this.setFillOpacity(networkSyncData.fo);
|
||||
}
|
||||
if (networkSyncData.oo !== undefined) {
|
||||
this.setOutlineOpacity(networkSyncData.oo);
|
||||
}
|
||||
if (networkSyncData.scaleX !== undefined) {
|
||||
this.setScaleX(networkSyncData.scaleX);
|
||||
}
|
||||
if (networkSyncData.scaleY !== undefined) {
|
||||
this.setScaleY(networkSyncData.scaleY);
|
||||
}
|
||||
if (networkSyncData.ifx !== undefined) {
|
||||
this.flipX(networkSyncData.ifx);
|
||||
}
|
||||
if (networkSyncData.ify !== undefined) {
|
||||
this.flipY(networkSyncData.ify);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the extra parameters that could be set for an instance.
|
||||
* @param initialInstanceData The extra parameters
|
||||
|
190
Extensions/SaveState/JsExtension.js
Normal file
190
Extensions/SaveState/JsExtension.js
Normal file
@@ -0,0 +1,190 @@
|
||||
//@ts-check
|
||||
/// <reference path="../JsExtensionTypes.d.ts" />
|
||||
/**
|
||||
* This is a declaration of an extension for GDevelop 5.
|
||||
*
|
||||
* ℹ️ Changes in this file are watched and automatically imported if the editor
|
||||
* is running. You can also manually run `node import-GDJS-Runtime.js` (in newIDE/app/scripts).
|
||||
*
|
||||
* The file must be named "JsExtension.js", otherwise GDevelop won't load it.
|
||||
* ⚠️ If you make a change and the extension is not loaded, open the developer console
|
||||
* and search for any errors.
|
||||
*
|
||||
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
|
||||
*/
|
||||
|
||||
/** @type {ExtensionModule} */
|
||||
module.exports = {
|
||||
createExtension: function (_, gd) {
|
||||
const extension = new gd.PlatformExtension();
|
||||
extension
|
||||
.setExtensionInformation(
|
||||
'SaveState',
|
||||
_('Save State (experimental)'),
|
||||
_('Allows to save and load the full state of a game.'),
|
||||
'Neyl Mahfouf',
|
||||
'Open source (MIT License)'
|
||||
)
|
||||
.setExtensionHelpPath('/all-features/save-state')
|
||||
.setCategory('Game mechanic')
|
||||
.addInstructionOrExpressionGroupMetadata(_('Save State (experimental)'))
|
||||
.setIcon('res/actions/saveDown.svg');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'SaveGameSnapshotToVariable',
|
||||
_('Save game to a variable'),
|
||||
_('Takes a snapshot of the game and save it to a variable.'),
|
||||
_('Save the game in variable _PARAM1_'),
|
||||
_('Save'),
|
||||
'res/actions/saveDown.svg',
|
||||
'res/actions/saveDown.svg'
|
||||
)
|
||||
.addCodeOnlyParameter('currentScene', '')
|
||||
.addParameter('variable', _('Variable to store the save to'), '', false)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/SaveState/savestatetools.js')
|
||||
.setFunctionName('gdjs.saveState.saveVariableGameSnapshot');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'SaveGameSnapshotToStorage',
|
||||
_('Save game to device storage'),
|
||||
_('Takes a snapshot of the game and save it to device storage.'),
|
||||
_('Save the game to device storage under key _PARAM1_'),
|
||||
_('Save'),
|
||||
'res/actions/saveDown.svg',
|
||||
'res/actions/saveDown.svg'
|
||||
)
|
||||
.addCodeOnlyParameter('currentScene', '')
|
||||
.addParameter('string', _('Storage key to save to'), '', false)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/SaveState/savestatetools.js')
|
||||
.setFunctionName('gdjs.saveState.saveStorageGameSnapshot');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'LoadGameSnapshotFromVariable',
|
||||
_('Load game from variable'),
|
||||
_('Load game from a variable save snapshot.'),
|
||||
_('Load the game from variable _PARAM0_'),
|
||||
_('Load'),
|
||||
'res/actions/saveUp.svg',
|
||||
'res/actions/saveUp.svg'
|
||||
)
|
||||
.addParameter('variable', _('Variable to load the game from'), '', false)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/SaveState/savestatetools.js')
|
||||
.setFunctionName('gdjs.saveState.loadGameFromVariableSnapshot');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'LoadGameSnapshotFromStorage',
|
||||
_('Load game from device storage'),
|
||||
_('Load game from device storage save snapshot.'),
|
||||
_('Load the game from device storage under key _PARAM0_.'),
|
||||
_('Load'),
|
||||
'res/actions/saveUp.svg',
|
||||
'res/actions/saveUp.svg'
|
||||
)
|
||||
.addParameter('string', _('Storage key to load the game from'), '', false)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/SaveState/savestatetools.js')
|
||||
.setFunctionName('gdjs.saveState.loadGameFromStorageSnapshot');
|
||||
|
||||
extension
|
||||
.addExpressionAndCondition(
|
||||
'number',
|
||||
'TimeSinceLastSave',
|
||||
_('Time since last save'),
|
||||
_(
|
||||
'Time since the last save, in seconds. Returns -1 if no save happened, and a positive number otherwise.'
|
||||
),
|
||||
_('Time since the last save'),
|
||||
'',
|
||||
'res/actions/saveDown.svg'
|
||||
)
|
||||
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
|
||||
.setIncludeFile('Extensions/SaveState/savestatetools.js')
|
||||
.setFunctionName('gdjs.saveState.getSecondsSinceLastSave')
|
||||
.setGetter('gdjs.saveState.getSecondsSinceLastSave');
|
||||
|
||||
extension
|
||||
.addExpressionAndCondition(
|
||||
'number',
|
||||
'TimeSinceLastLoad',
|
||||
_('Time since last load'),
|
||||
_(
|
||||
'Time since the last load, in seconds. Returns -1 if no load happened, and a positive number otherwise.'
|
||||
),
|
||||
_('Time since the last load'),
|
||||
'',
|
||||
'res/actions/saveDown.svg'
|
||||
)
|
||||
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
|
||||
.setIncludeFile('Extensions/SaveState/savestatetools.js')
|
||||
.setFunctionName('gdjs.saveState.getSecondsSinceLastLoad')
|
||||
.setGetter('gdjs.saveState.getSecondsSinceLastLoad');
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'SaveJustSucceeded',
|
||||
_('Save just succeeded'),
|
||||
_('the save just succeeded'),
|
||||
_('the save just succeeded'),
|
||||
_('Save'),
|
||||
'res/actions/saveDown.svg',
|
||||
'res/actions/saveDown.svg'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/SaveState/savestatetools.js')
|
||||
.setFunctionName('gdjs.saveState.hasSaveJustSucceeded');
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'SaveJustFailed',
|
||||
_('Save just failed'),
|
||||
_('the save just failed'),
|
||||
_('the save just failed'),
|
||||
_('Save'),
|
||||
'res/actions/saveDown.svg',
|
||||
'res/actions/saveDown.svg'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/SaveState/savestatetools.js')
|
||||
.setFunctionName('gdjs.saveState.hasSaveJustFailed');
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'LoadJustSucceeded',
|
||||
_('Load just succeeded'),
|
||||
_('the load just succeeded'),
|
||||
_('the load just succeeded'),
|
||||
_('Load'),
|
||||
'res/actions/saveUp.svg',
|
||||
'res/actions/saveUp.svg'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/SaveState/savestatetools.js')
|
||||
.setFunctionName('gdjs.saveState.hasLoadJustSucceeded');
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'LoadJustFailed',
|
||||
_('Load just failed'),
|
||||
_('the load just failed'),
|
||||
_('the load just failed'),
|
||||
_('Load'),
|
||||
'res/actions/saveUp.svg',
|
||||
'res/actions/saveUp.svg'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/SaveState/savestatetools.js')
|
||||
.setFunctionName('gdjs.saveState.hasLoadJustFailed');
|
||||
|
||||
return extension;
|
||||
},
|
||||
runExtensionSanityTests: function (gd, extension) {
|
||||
return [];
|
||||
},
|
||||
};
|
360
Extensions/SaveState/savestatetools.ts
Normal file
360
Extensions/SaveState/savestatetools.ts
Normal file
@@ -0,0 +1,360 @@
|
||||
namespace gdjs {
|
||||
const logger = new gdjs.Logger('Save state');
|
||||
export type LoadRequestOptions = {
|
||||
loadStorageName?: string;
|
||||
loadVariable?: gdjs.Variable;
|
||||
};
|
||||
|
||||
export namespace saveState {
|
||||
export const getIndexedDbDatabaseName = () => {
|
||||
const gameId = gdjs.projectData.properties.projectUuid;
|
||||
return `gdevelop-game-${gameId}`;
|
||||
};
|
||||
export const getIndexedDbObjectStore = () => {
|
||||
return `game-saves`;
|
||||
};
|
||||
export const getIndexedDbStorageKey = (key: string) => {
|
||||
return `save-${key}`;
|
||||
};
|
||||
|
||||
const getNetworkSyncOptions: GetNetworkSyncDataOptions = {
|
||||
syncObjectIdentifiers: true,
|
||||
syncAllVariables: true,
|
||||
syncAllBehaviors: true,
|
||||
syncSceneTimers: true,
|
||||
syncOnceTriggers: true,
|
||||
syncSounds: true,
|
||||
syncTweens: true,
|
||||
syncLayers: true,
|
||||
syncAsyncTasks: true,
|
||||
syncSceneVisualProps: true,
|
||||
syncFullTileMaps: true,
|
||||
};
|
||||
const updateFromNetworkSyncDataOptions: UpdateFromNetworkSyncDataOptions = {
|
||||
clearSceneStack: true,
|
||||
preventInitialInstancesCreation: true,
|
||||
preventSoundsStoppingOnStartup: true,
|
||||
clearInputs: true,
|
||||
keepControl: true,
|
||||
ignoreVariableOwnership: true,
|
||||
};
|
||||
|
||||
let lastSaveTime: number | null = null;
|
||||
let lastLoadTime: number | null = null;
|
||||
let saveJustSucceeded: boolean = false;
|
||||
let saveJustFailed: boolean = false;
|
||||
let loadJustSucceeded: boolean = false;
|
||||
let loadJustFailed: boolean = false;
|
||||
|
||||
let loadRequestOptions: LoadRequestOptions | null = null;
|
||||
|
||||
export const getSecondsSinceLastSave = (): number => {
|
||||
if (!lastSaveTime) return -1;
|
||||
return Math.floor((Date.now() - lastSaveTime) / 1000);
|
||||
};
|
||||
export const getSecondsSinceLastLoad = (): number => {
|
||||
if (!lastLoadTime) return -1;
|
||||
return Math.floor((Date.now() - lastLoadTime) / 1000);
|
||||
};
|
||||
export const hasSaveJustSucceeded = () => {
|
||||
return saveJustSucceeded;
|
||||
};
|
||||
export const hasLoadJustSucceeded = () => {
|
||||
return loadJustSucceeded;
|
||||
};
|
||||
export const hasSaveJustFailed = () => {
|
||||
return saveJustFailed;
|
||||
};
|
||||
export const hasLoadJustFailed = () => {
|
||||
return loadJustFailed;
|
||||
};
|
||||
export const markSaveJustSucceeded = () => {
|
||||
saveJustSucceeded = true;
|
||||
lastSaveTime = Date.now();
|
||||
};
|
||||
export const markLoadJustSucceeded = () => {
|
||||
loadJustSucceeded = true;
|
||||
lastLoadTime = Date.now();
|
||||
};
|
||||
export const markSaveJustFailed = () => {
|
||||
saveJustFailed = true;
|
||||
};
|
||||
export const markLoadJustFailed = () => {
|
||||
loadJustFailed = true;
|
||||
};
|
||||
|
||||
// Ensure that the condition "save/load just succeeded/failed" are valid only for one frame.
|
||||
gdjs.registerRuntimeScenePostEventsCallback(() => {
|
||||
saveJustSucceeded = false;
|
||||
saveJustFailed = false;
|
||||
loadJustSucceeded = false;
|
||||
loadJustFailed = false;
|
||||
});
|
||||
|
||||
gdjs.registerRuntimeScenePostEventsCallback(
|
||||
(runtimeScene: gdjs.RuntimeScene) => {
|
||||
loadGameSnapshotAtTheEndOfFrameIfAny(runtimeScene);
|
||||
}
|
||||
);
|
||||
|
||||
const getGameSaveState = (runtimeScene: RuntimeScene) => {
|
||||
const gameSaveState: GameSaveState = {
|
||||
gameNetworkSyncData: {},
|
||||
layoutNetworkSyncDatas: [],
|
||||
};
|
||||
|
||||
const gameData = runtimeScene
|
||||
.getGame()
|
||||
.getNetworkSyncData(getNetworkSyncOptions);
|
||||
const scenes = runtimeScene.getGame().getSceneStack().getAllScenes();
|
||||
gameSaveState.gameNetworkSyncData = gameData || {};
|
||||
|
||||
scenes.forEach((scene, index) => {
|
||||
gameSaveState.layoutNetworkSyncDatas[index] = {
|
||||
sceneData: {} as LayoutNetworkSyncData,
|
||||
objectDatas: {},
|
||||
};
|
||||
|
||||
// First collect all object sync data, as they may generate unique
|
||||
// identifiers like their networkId.
|
||||
const sceneRuntimeObjects = scene.getAdhocListOfAllInstances();
|
||||
for (const key in sceneRuntimeObjects) {
|
||||
if (sceneRuntimeObjects.hasOwnProperty(key)) {
|
||||
const object = sceneRuntimeObjects[key];
|
||||
const objectSyncData = object.getNetworkSyncData(
|
||||
getNetworkSyncOptions
|
||||
);
|
||||
gameSaveState.layoutNetworkSyncDatas[index].objectDatas[object.id] =
|
||||
objectSyncData;
|
||||
}
|
||||
}
|
||||
|
||||
// Collect all scene data in the end.
|
||||
const sceneDatas = (scene.getNetworkSyncData(getNetworkSyncOptions) ||
|
||||
[]) as LayoutNetworkSyncData;
|
||||
|
||||
gameSaveState.layoutNetworkSyncDatas[index].sceneData = sceneDatas;
|
||||
});
|
||||
|
||||
return gameSaveState;
|
||||
};
|
||||
|
||||
export const saveVariableGameSnapshot = async function (
|
||||
currentScene: RuntimeScene,
|
||||
variable: gdjs.Variable
|
||||
) {
|
||||
try {
|
||||
const gameSaveState = getGameSaveState(currentScene);
|
||||
variable.fromJSObject(gameSaveState);
|
||||
markSaveJustSucceeded();
|
||||
} catch (error) {
|
||||
logger.error('Error saving to variable:', error);
|
||||
markSaveJustFailed();
|
||||
}
|
||||
};
|
||||
|
||||
export const saveStorageGameSnapshot = async function (
|
||||
currentScene: RuntimeScene,
|
||||
storageKey: string
|
||||
) {
|
||||
try {
|
||||
const gameSaveState = getGameSaveState(currentScene);
|
||||
await gdjs.indexedDb.saveToIndexedDB(
|
||||
getIndexedDbDatabaseName(),
|
||||
getIndexedDbObjectStore(),
|
||||
getIndexedDbStorageKey(storageKey),
|
||||
gameSaveState
|
||||
);
|
||||
markSaveJustSucceeded();
|
||||
} catch (error) {
|
||||
logger.error('Error saving to IndexedDB:', error);
|
||||
markSaveJustFailed();
|
||||
}
|
||||
};
|
||||
|
||||
export const loadGameFromVariableSnapshot = async function (
|
||||
variable: gdjs.Variable
|
||||
) {
|
||||
// The information is saved, so that the load can be done
|
||||
// at the end of the frame,
|
||||
// and avoid possible conflicts with running events.
|
||||
loadRequestOptions = {
|
||||
loadVariable: variable,
|
||||
};
|
||||
};
|
||||
|
||||
export const loadGameFromStorageSnapshot = async function (
|
||||
storageName: string
|
||||
) {
|
||||
// The information is saved, so that the load can be done
|
||||
// at the end of the frame,
|
||||
// and avoid possible conflicts with running events.
|
||||
loadRequestOptions = {
|
||||
loadStorageName: storageName,
|
||||
};
|
||||
};
|
||||
|
||||
const loadGameSnapshotAtTheEndOfFrameIfAny = function (
|
||||
runtimeScene: RuntimeScene
|
||||
) {
|
||||
if (!loadRequestOptions) return;
|
||||
|
||||
const optionsToApply = loadRequestOptions;
|
||||
// Reset it so we don't load it twice.
|
||||
loadRequestOptions = null;
|
||||
|
||||
if (optionsToApply.loadVariable) {
|
||||
const sceneVariables = runtimeScene.getVariables();
|
||||
const variablePathInScene =
|
||||
sceneVariables.getVariablePathInContainerByLoopingThroughAllVariables(
|
||||
optionsToApply.loadVariable
|
||||
);
|
||||
const gameVariables = runtimeScene.getGame().getVariables();
|
||||
const variablePathIngame =
|
||||
gameVariables.getVariablePathInContainerByLoopingThroughAllVariables(
|
||||
optionsToApply.loadVariable
|
||||
);
|
||||
const saveState =
|
||||
optionsToApply.loadVariable.toJSObject() as GameSaveState;
|
||||
|
||||
try {
|
||||
loadGameFromSave(runtimeScene, saveState, {
|
||||
variableToRehydrate: optionsToApply.loadVariable,
|
||||
variablePathInScene: variablePathInScene,
|
||||
variablePathInGame: variablePathIngame,
|
||||
});
|
||||
markLoadJustSucceeded();
|
||||
} catch (error) {
|
||||
logger.error('Error loading from variable:', error);
|
||||
markLoadJustFailed();
|
||||
}
|
||||
} else if (optionsToApply.loadStorageName) {
|
||||
gdjs.indexedDb
|
||||
.loadFromIndexedDB(
|
||||
getIndexedDbDatabaseName(),
|
||||
getIndexedDbObjectStore(),
|
||||
getIndexedDbStorageKey(optionsToApply.loadStorageName)
|
||||
)
|
||||
.then((jsonData) => {
|
||||
const saveState = jsonData as GameSaveState;
|
||||
loadGameFromSave(runtimeScene, saveState);
|
||||
markLoadJustSucceeded();
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.error('Error loading from IndexedDB:', error);
|
||||
markLoadJustFailed();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const loadGameFromSave = (
|
||||
runtimeScene: RuntimeScene,
|
||||
saveState: GameSaveState,
|
||||
saveOptions?: {
|
||||
variableToRehydrate: gdjs.Variable;
|
||||
variablePathInScene: string[] | null;
|
||||
variablePathInGame: string[] | null;
|
||||
}
|
||||
): void => {
|
||||
// Save the content of the save, as it will be erased after the load.
|
||||
const variableToRehydrateNetworkSyncData = saveOptions
|
||||
? saveOptions.variableToRehydrate.getNetworkSyncData(
|
||||
getNetworkSyncOptions
|
||||
)
|
||||
: null;
|
||||
|
||||
// First update the game, which will update the variables,
|
||||
// and set the scene stack to update when ready.
|
||||
const runtimeGame = runtimeScene.getGame();
|
||||
runtimeGame.updateFromNetworkSyncData(
|
||||
saveState.gameNetworkSyncData,
|
||||
updateFromNetworkSyncDataOptions
|
||||
);
|
||||
|
||||
// Apply the scene stack updates, as we are at the end of a frame,
|
||||
// we can safely do it.
|
||||
const sceneStack = runtimeGame.getSceneStack();
|
||||
sceneStack.applyUpdateFromNetworkSyncDataIfAny(
|
||||
updateFromNetworkSyncDataOptions
|
||||
);
|
||||
|
||||
// Then get all scenes, which we assume will be the expected ones
|
||||
// after the load has been done, so we can update them,
|
||||
// and create their objects.
|
||||
const runtimeScenes = sceneStack.getAllScenes();
|
||||
runtimeScenes.forEach((scene, index) => {
|
||||
const layoutSyncData = saveState.layoutNetworkSyncDatas[index];
|
||||
if (!layoutSyncData) return;
|
||||
|
||||
// Create objects first, so they are available for the scene update,
|
||||
// especially so that they have a networkId defined.
|
||||
const objectDatas = layoutSyncData.objectDatas;
|
||||
for (const id in objectDatas) {
|
||||
const objectNetworkSyncData = objectDatas[id];
|
||||
const objectName = objectNetworkSyncData.n;
|
||||
if (!objectName) {
|
||||
logger.warn('Tried to recreate an object without a name.');
|
||||
continue;
|
||||
}
|
||||
const object = scene.createObject(objectName);
|
||||
if (object) {
|
||||
object.updateFromNetworkSyncData(
|
||||
objectNetworkSyncData,
|
||||
updateFromNetworkSyncDataOptions
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Update the scene last.
|
||||
scene.updateFromNetworkSyncData(
|
||||
layoutSyncData.sceneData,
|
||||
updateFromNetworkSyncDataOptions
|
||||
);
|
||||
});
|
||||
|
||||
// Finally, if the save was done in a variable,
|
||||
// rehydrate the variable where the save was done,
|
||||
// as it has been erased by the load.
|
||||
if (saveOptions && variableToRehydrateNetworkSyncData) {
|
||||
const currentScene = sceneStack.getCurrentScene();
|
||||
if (!currentScene) return;
|
||||
const sceneVariables = currentScene.getVariables();
|
||||
const gameVariables = currentScene.getGame().getVariables();
|
||||
const { variablePathInScene, variablePathInGame } = saveOptions;
|
||||
|
||||
if (variablePathInScene && variablePathInScene.length > 0) {
|
||||
const variableName =
|
||||
variablePathInScene[variablePathInScene.length - 1];
|
||||
const variableInScene =
|
||||
sceneVariables.getVariableFromPath(variablePathInScene);
|
||||
if (variableInScene) {
|
||||
const variableNetworkSyncData: VariableNetworkSyncData = {
|
||||
name: variableName,
|
||||
...variableToRehydrateNetworkSyncData,
|
||||
};
|
||||
variableInScene.updateFromNetworkSyncData(
|
||||
variableNetworkSyncData,
|
||||
updateFromNetworkSyncDataOptions
|
||||
);
|
||||
}
|
||||
}
|
||||
if (variablePathInGame && variablePathInGame.length > 0) {
|
||||
const variableName =
|
||||
variablePathInGame[variablePathInGame.length - 1];
|
||||
const variableInGame =
|
||||
gameVariables.getVariableFromPath(variablePathInGame);
|
||||
if (variableInGame) {
|
||||
const variableNetworkSyncData: VariableNetworkSyncData = {
|
||||
name: variableName,
|
||||
...variableToRehydrateNetworkSyncData,
|
||||
};
|
||||
variableInGame.updateFromNetworkSyncData(
|
||||
variableNetworkSyncData,
|
||||
updateFromNetworkSyncDataOptions
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@@ -202,28 +202,18 @@ namespace gdjs {
|
||||
this._loadingSpineAtlases.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Unload the specified list of resources:
|
||||
* this clears the Spine atlases loaded in this manager.
|
||||
*
|
||||
* Usually called when scene resoures are unloaded.
|
||||
*
|
||||
* @param resourcesList The list of specific resources
|
||||
*/
|
||||
unloadResourcesList(resourcesList: ResourceData[]): void {
|
||||
resourcesList.forEach((resourceData) => {
|
||||
const loadedSpineAtlas = this._loadedSpineAtlases.get(resourceData);
|
||||
if (loadedSpineAtlas) {
|
||||
loadedSpineAtlas.dispose();
|
||||
this._loadedSpineAtlases.delete(resourceData);
|
||||
}
|
||||
unloadResource(resourceData: ResourceData): void {
|
||||
const loadedSpineAtlas = this._loadedSpineAtlases.get(resourceData);
|
||||
if (loadedSpineAtlas) {
|
||||
loadedSpineAtlas.dispose();
|
||||
this._loadedSpineAtlases.delete(resourceData);
|
||||
}
|
||||
|
||||
const loadingSpineAtlas = this._loadingSpineAtlases.get(resourceData);
|
||||
if (loadingSpineAtlas) {
|
||||
loadingSpineAtlas.then((atl) => atl.dispose());
|
||||
this._loadingSpineAtlases.delete(resourceData);
|
||||
}
|
||||
});
|
||||
const loadingSpineAtlas = this._loadingSpineAtlases.get(resourceData);
|
||||
if (loadingSpineAtlas) {
|
||||
loadingSpineAtlas.then((atl) => atl.dispose());
|
||||
this._loadingSpineAtlases.delete(resourceData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -127,21 +127,11 @@ namespace gdjs {
|
||||
this._loadedSpines.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Unload the specified list of resources:
|
||||
* this clears the Spine skeleton data loaded in this manager.
|
||||
*
|
||||
* Usually called when scene resoures are unloaded.
|
||||
*
|
||||
* @param resourcesList The list of specific resources
|
||||
*/
|
||||
unloadResourcesList(resourcesList: ResourceData[]): void {
|
||||
resourcesList.forEach((resourceData) => {
|
||||
const loadedSpine = this._loadedSpines.get(resourceData);
|
||||
if (loadedSpine) {
|
||||
this._loadedSpines.delete(resourceData);
|
||||
}
|
||||
});
|
||||
unloadResource(resourceData: ResourceData): void {
|
||||
const loadedSpine = this._loadedSpines.get(resourceData);
|
||||
if (loadedSpine) {
|
||||
this._loadedSpines.delete(resourceData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -14,8 +14,6 @@ namespace gdjs {
|
||||
|
||||
export type SpineNetworkSyncDataType = {
|
||||
opa: float;
|
||||
wid: float;
|
||||
hei: float;
|
||||
scaX: float;
|
||||
scaY: float;
|
||||
flipX: boolean;
|
||||
@@ -113,12 +111,12 @@ namespace gdjs {
|
||||
return true;
|
||||
}
|
||||
|
||||
getNetworkSyncData(): SpineNetworkSyncData {
|
||||
getNetworkSyncData(
|
||||
syncOptions: GetNetworkSyncDataOptions
|
||||
): SpineNetworkSyncData {
|
||||
return {
|
||||
...super.getNetworkSyncData(),
|
||||
...super.getNetworkSyncData(syncOptions),
|
||||
opa: this._opacity,
|
||||
wid: this.getWidth(),
|
||||
hei: this.getHeight(),
|
||||
scaX: this.getScaleX(),
|
||||
scaY: this.getScaleY(),
|
||||
flipX: this.isFlippedX(),
|
||||
@@ -131,18 +129,15 @@ namespace gdjs {
|
||||
};
|
||||
}
|
||||
|
||||
updateFromNetworkSyncData(syncData: SpineNetworkSyncData): void {
|
||||
super.updateFromNetworkSyncData(syncData);
|
||||
updateFromNetworkSyncData(
|
||||
syncData: SpineNetworkSyncData,
|
||||
options: UpdateFromNetworkSyncDataOptions
|
||||
): void {
|
||||
super.updateFromNetworkSyncData(syncData, options);
|
||||
|
||||
if (syncData.opa !== undefined && syncData.opa !== this._opacity) {
|
||||
this.setOpacity(syncData.opa);
|
||||
}
|
||||
if (syncData.wid !== undefined && syncData.wid !== this.getWidth()) {
|
||||
this.setWidth(syncData.wid);
|
||||
}
|
||||
if (syncData.hei !== undefined && syncData.hei !== this.getHeight()) {
|
||||
this.setHeight(syncData.hei);
|
||||
}
|
||||
if (syncData.scaX !== undefined && syncData.scaX !== this.getScaleX()) {
|
||||
this.setScaleX(syncData.scaX);
|
||||
}
|
||||
|
@@ -13,7 +13,8 @@ void DeclareSystemInfoExtension(gd::PlatformExtension& extension) {
|
||||
.SetExtensionInformation(
|
||||
"SystemInfo",
|
||||
_("System information"),
|
||||
_("Get information about the system and device running the game."),
|
||||
_("Conditions to check if the device has a touchscreen, is a mobile, "
|
||||
"or if the game runs as a preview."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetCategory("Advanced");
|
||||
|
@@ -78,6 +78,9 @@ module.exports = {
|
||||
} else if (propertyName === 'disabled') {
|
||||
objectContent.disabled = newValue === '1';
|
||||
return true;
|
||||
} else if (propertyName === 'spellCheck') {
|
||||
objectContent.spellCheck = newValue === '1';
|
||||
return true;
|
||||
} else if (propertyName === 'maxLength') {
|
||||
objectContent.maxLength = newValue;
|
||||
return true;
|
||||
@@ -160,6 +163,13 @@ module.exports = {
|
||||
.setLabel(_('Disabled'))
|
||||
.setGroup(_('Field'));
|
||||
|
||||
objectProperties
|
||||
.getOrCreate('spellCheck')
|
||||
.setValue(objectContent.spellCheck ? 'true' : 'false')
|
||||
.setType('boolean')
|
||||
.setLabel(_('Enable spell check'))
|
||||
.setGroup(_('Field'));
|
||||
|
||||
objectProperties
|
||||
.getOrCreate('textColor')
|
||||
.setValue(objectContent.textColor || '0;0;0')
|
||||
@@ -272,6 +282,7 @@ module.exports = {
|
||||
borderWidth: 1,
|
||||
readOnly: false,
|
||||
disabled: false,
|
||||
spellCheck: false,
|
||||
paddingX: 2,
|
||||
paddingY: 1,
|
||||
textAlign: 'left',
|
||||
@@ -592,6 +603,21 @@ module.exports = {
|
||||
.setFunctionName('setDisabled')
|
||||
.setGetter('isDisabled');
|
||||
|
||||
object
|
||||
.addExpressionAndConditionAndAction(
|
||||
'boolean',
|
||||
'SpellCheck',
|
||||
_('Spell check enabled'),
|
||||
_('spell check is enabled'),
|
||||
_('spell check enabled'),
|
||||
'',
|
||||
'res/conditions/text24_black.png'
|
||||
)
|
||||
.addParameter('object', _('Text input'), 'TextInputObject', false)
|
||||
.useStandardParameters('boolean', gd.ParameterOptions.makeNewOptions())
|
||||
.setFunctionName('setSpellCheck')
|
||||
.setGetter('isSpellCheckEnabled');
|
||||
|
||||
// Other expressions/conditions/actions:
|
||||
|
||||
// Deprecated
|
||||
|
@@ -106,6 +106,7 @@ namespace gdjs {
|
||||
this.updateBorderWidth();
|
||||
this.updateDisabled();
|
||||
this.updateReadOnly();
|
||||
this.updateSpellCheck();
|
||||
this.updateTextAlign();
|
||||
this.updateMaxLength();
|
||||
this.updatePadding();
|
||||
@@ -342,6 +343,12 @@ namespace gdjs {
|
||||
this._input.readOnly = this._object.isReadOnly();
|
||||
}
|
||||
|
||||
updateSpellCheck() {
|
||||
if (!this._input) return;
|
||||
|
||||
this._input.spellcheck = this._object.isSpellCheckEnabled();
|
||||
}
|
||||
|
||||
updateMaxLength() {
|
||||
const input = this._input;
|
||||
if (!input) return;
|
||||
|
@@ -54,6 +54,7 @@ namespace gdjs {
|
||||
disabled: boolean;
|
||||
readOnly: boolean;
|
||||
// ---- Values can be undefined because of support for these feature was added in v5.5.222.
|
||||
spellCheck?: boolean;
|
||||
paddingX?: float;
|
||||
paddingY?: float;
|
||||
textAlign?: SupportedTextAlign;
|
||||
@@ -64,8 +65,6 @@ namespace gdjs {
|
||||
|
||||
export type TextInputNetworkSyncDataType = {
|
||||
opa: float;
|
||||
wid: float;
|
||||
hei: float;
|
||||
txt: string;
|
||||
frn: string;
|
||||
fs: number;
|
||||
@@ -79,6 +78,7 @@ namespace gdjs {
|
||||
bw: float;
|
||||
dis: boolean;
|
||||
ro: boolean;
|
||||
sc: boolean;
|
||||
};
|
||||
|
||||
export type TextInputNetworkSyncData = ObjectNetworkSyncData &
|
||||
@@ -118,6 +118,7 @@ namespace gdjs {
|
||||
private _borderWidth: float;
|
||||
private _disabled: boolean;
|
||||
private _readOnly: boolean;
|
||||
private _spellCheck: boolean;
|
||||
private _isSubmitted: boolean;
|
||||
_renderer: TextInputRuntimeObjectRenderer;
|
||||
|
||||
@@ -142,6 +143,10 @@ namespace gdjs {
|
||||
this._borderWidth = objectData.content.borderWidth;
|
||||
this._disabled = objectData.content.disabled;
|
||||
this._readOnly = objectData.content.readOnly;
|
||||
this._spellCheck =
|
||||
objectData.content.spellCheck !== undefined
|
||||
? objectData.content.spellCheck
|
||||
: false;
|
||||
this._textAlign = parseTextAlign(objectData.content.textAlign);
|
||||
this._maxLength = objectData.content.maxLength || 0;
|
||||
this._paddingX =
|
||||
@@ -228,6 +233,12 @@ namespace gdjs {
|
||||
if (oldObjectData.content.readOnly !== newObjectData.content.readOnly) {
|
||||
this.setReadOnly(newObjectData.content.readOnly);
|
||||
}
|
||||
if (
|
||||
newObjectData.content.spellCheck !== undefined &&
|
||||
oldObjectData.content.spellCheck !== newObjectData.content.spellCheck
|
||||
) {
|
||||
this.setSpellCheck(newObjectData.content.spellCheck);
|
||||
}
|
||||
if (
|
||||
newObjectData.content.maxLength !== undefined &&
|
||||
oldObjectData.content.maxLength !== newObjectData.content.maxLength
|
||||
@@ -256,12 +267,12 @@ namespace gdjs {
|
||||
return true;
|
||||
}
|
||||
|
||||
getNetworkSyncData(): TextInputNetworkSyncData {
|
||||
getNetworkSyncData(
|
||||
syncOptions: GetNetworkSyncDataOptions
|
||||
): TextInputNetworkSyncData {
|
||||
return {
|
||||
...super.getNetworkSyncData(),
|
||||
...super.getNetworkSyncData(syncOptions),
|
||||
opa: this.getOpacity(),
|
||||
wid: this.getWidth(),
|
||||
hei: this.getHeight(),
|
||||
txt: this.getText(),
|
||||
frn: this.getFontResourceName(),
|
||||
fs: this.getFontSize(),
|
||||
@@ -275,15 +286,17 @@ namespace gdjs {
|
||||
bw: this.getBorderWidth(),
|
||||
dis: this.isDisabled(),
|
||||
ro: this.isReadOnly(),
|
||||
sc: this.isSpellCheckEnabled(),
|
||||
};
|
||||
}
|
||||
|
||||
updateFromNetworkSyncData(syncData: TextInputNetworkSyncData): void {
|
||||
super.updateFromNetworkSyncData(syncData);
|
||||
updateFromNetworkSyncData(
|
||||
syncData: TextInputNetworkSyncData,
|
||||
options: UpdateFromNetworkSyncDataOptions
|
||||
): void {
|
||||
super.updateFromNetworkSyncData(syncData, options);
|
||||
|
||||
if (syncData.opa !== undefined) this.setOpacity(syncData.opa);
|
||||
if (syncData.wid !== undefined) this.setWidth(syncData.wid);
|
||||
if (syncData.hei !== undefined) this.setHeight(syncData.hei);
|
||||
if (syncData.txt !== undefined) this.setText(syncData.txt);
|
||||
if (syncData.frn !== undefined) this.setFontResourceName(syncData.frn);
|
||||
if (syncData.fs !== undefined) this.setFontSize(syncData.fs);
|
||||
@@ -297,6 +310,7 @@ namespace gdjs {
|
||||
if (syncData.bw !== undefined) this.setBorderWidth(syncData.bw);
|
||||
if (syncData.dis !== undefined) this.setDisabled(syncData.dis);
|
||||
if (syncData.ro !== undefined) this.setReadOnly(syncData.ro);
|
||||
if (syncData.sc !== undefined) this.setSpellCheck(syncData.sc);
|
||||
}
|
||||
|
||||
updatePreRender(instanceContainer: RuntimeInstanceContainer): void {
|
||||
@@ -569,6 +583,15 @@ namespace gdjs {
|
||||
return this._readOnly;
|
||||
}
|
||||
|
||||
setSpellCheck(value: boolean) {
|
||||
this._spellCheck = value;
|
||||
this._renderer.updateSpellCheck();
|
||||
}
|
||||
|
||||
isSpellCheckEnabled(): boolean {
|
||||
return this._spellCheck;
|
||||
}
|
||||
|
||||
isFocused(): boolean {
|
||||
return this._renderer.isFocused();
|
||||
}
|
||||
|
@@ -449,6 +449,16 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
|
||||
.AddParameter("object", _("Object"), "Text")
|
||||
.UseStandardParameters("number", gd::ParameterOptions::MakeNewOptions());
|
||||
|
||||
obj.AddExpressionAndConditionAndAction("number",
|
||||
"LineHeight",
|
||||
_("Line height"),
|
||||
_("the line height of a text object"),
|
||||
_("the line height"),
|
||||
"",
|
||||
"res/actions/font24.png")
|
||||
.AddParameter("object", _("Object"), "Text")
|
||||
.UseStandardParameters("number", gd::ParameterOptions::MakeNewOptions());
|
||||
|
||||
// Support for deprecated "Size" actions/conditions:
|
||||
obj.AddDuplicatedAction("Size", "Text::SetFontSize").SetHidden();
|
||||
obj.AddDuplicatedCondition("Size", "Text::FontSize").SetHidden();
|
||||
|
@@ -96,6 +96,14 @@ class TextObjectJsExtension : public gd::PlatformExtension {
|
||||
.SetFunctionName("setOutlineThickness")
|
||||
.SetGetter("getOutlineThickness");
|
||||
|
||||
GetAllExpressionsForObject("TextObject::Text")["LineHeight"]
|
||||
.SetFunctionName("getLineHeight");
|
||||
GetAllConditionsForObject("TextObject::Text")["TextObject::Text::LineHeight"]
|
||||
.SetFunctionName("getLineHeight");
|
||||
GetAllActionsForObject("TextObject::Text")["TextObject::Text::SetLineHeight"]
|
||||
.SetFunctionName("setLineHeight")
|
||||
.SetGetter("getLineHeight");
|
||||
|
||||
GetAllActionsForObject("TextObject::Text")["TextObject::ShowShadow"]
|
||||
.SetFunctionName("showShadow");
|
||||
GetAllConditionsForObject("TextObject::Text")["TextObject::Text::IsShadowEnabled"]
|
||||
|
@@ -20,6 +20,7 @@ using namespace std;
|
||||
TextObject::TextObject()
|
||||
: text("Text"),
|
||||
characterSize(20),
|
||||
lineHeight(0),
|
||||
fontName(""),
|
||||
smoothed(true),
|
||||
bold(false),
|
||||
@@ -50,6 +51,10 @@ bool TextObject::UpdateProperty(const gd::String& propertyName,
|
||||
characterSize = newValue.To<double>();
|
||||
return true;
|
||||
}
|
||||
if (propertyName == "lineHeight") {
|
||||
lineHeight = newValue.To<double>();
|
||||
return true;
|
||||
}
|
||||
if (propertyName == "font") {
|
||||
fontName = newValue;
|
||||
return true;
|
||||
@@ -129,6 +134,13 @@ std::map<gd::String, gd::PropertyDescriptor> TextObject::GetProperties() const {
|
||||
.SetMeasurementUnit(gd::MeasurementUnit::GetPixel())
|
||||
.SetGroup(_("Font"));
|
||||
|
||||
objectProperties["lineHeight"]
|
||||
.SetValue(gd::String::From(lineHeight))
|
||||
.SetType("number")
|
||||
.SetLabel(_("Line height"))
|
||||
.SetMeasurementUnit(gd::MeasurementUnit::GetPixel())
|
||||
.SetGroup(_("Font"));
|
||||
|
||||
objectProperties["font"]
|
||||
.SetValue(fontName)
|
||||
.SetType("resource")
|
||||
@@ -271,6 +283,7 @@ void TextObject::DoUnserializeFrom(gd::Project& project,
|
||||
SetCharacterSize(content.GetChild("characterSize", 0, "CharacterSize")
|
||||
.GetValue()
|
||||
.GetInt());
|
||||
SetLineHeight(content.GetDoubleAttribute("lineHeight", 0));
|
||||
smoothed = content.GetBoolAttribute("smoothed");
|
||||
bold = content.GetBoolAttribute("bold");
|
||||
italic = content.GetBoolAttribute("italic");
|
||||
@@ -339,6 +352,7 @@ void TextObject::DoSerializeTo(gd::SerializerElement& element) const {
|
||||
content.AddChild("textAlignment").SetValue(GetTextAlignment());
|
||||
content.AddChild("verticalTextAlignment").SetValue(GetVerticalTextAlignment());
|
||||
content.AddChild("characterSize").SetValue(GetCharacterSize());
|
||||
content.AddChild("lineHeight").SetValue(GetLineHeight());
|
||||
content.AddChild("color").SetValue(GetColor());
|
||||
|
||||
content.SetAttribute("smoothed", smoothed);
|
||||
|
@@ -49,6 +49,12 @@ class GD_EXTENSION_API TextObject : public gd::ObjectConfiguration {
|
||||
*/
|
||||
inline double GetCharacterSize() const { return characterSize; };
|
||||
|
||||
/** \brief Change the line height. */
|
||||
inline void SetLineHeight(double value) { lineHeight = value; };
|
||||
|
||||
/** \brief Get the line height. */
|
||||
inline double GetLineHeight() const { return lineHeight; };
|
||||
|
||||
/** \brief Return the name of the font resource used for the text.
|
||||
*/
|
||||
inline const gd::String& GetFontName() const { return fontName; };
|
||||
@@ -120,6 +126,7 @@ class GD_EXTENSION_API TextObject : public gd::ObjectConfiguration {
|
||||
|
||||
gd::String text;
|
||||
double characterSize;
|
||||
double lineHeight;
|
||||
gd::String fontName;
|
||||
bool smoothed;
|
||||
bool bold, italic, underlined;
|
||||
|
@@ -86,6 +86,7 @@ namespace gdjs {
|
||||
? style.dropShadowDistance + style.dropShadowBlur
|
||||
: 0;
|
||||
style.padding = Math.ceil(this._object._padding + extraPaddingForShadow);
|
||||
style.lineHeight = this._object._lineHeight;
|
||||
|
||||
// Prevent spikey outlines by adding a miter limit
|
||||
style.miterLimit = 3;
|
||||
|
@@ -22,6 +22,8 @@ namespace gdjs {
|
||||
text: string;
|
||||
textAlignment: string;
|
||||
verticalTextAlignment: string;
|
||||
/** The line height */
|
||||
lineHeight: float;
|
||||
|
||||
isOutlineEnabled: boolean;
|
||||
outlineThickness: float;
|
||||
@@ -50,6 +52,7 @@ namespace gdjs {
|
||||
c: number[];
|
||||
scale: number;
|
||||
ta: string;
|
||||
vta: string;
|
||||
wrap: boolean;
|
||||
wrapw: float;
|
||||
oena: boolean;
|
||||
@@ -62,6 +65,7 @@ namespace gdjs {
|
||||
sha: float;
|
||||
shb: float;
|
||||
pad: integer;
|
||||
lh: float;
|
||||
};
|
||||
|
||||
export type TextObjectNetworkSyncData = ObjectNetworkSyncData &
|
||||
@@ -101,6 +105,8 @@ namespace gdjs {
|
||||
_shadowAngle: float;
|
||||
_shadowBlur: float;
|
||||
|
||||
_lineHeight: float;
|
||||
|
||||
_padding: integer = 5;
|
||||
_str: string;
|
||||
_renderer: gdjs.TextRuntimeObjectRenderer;
|
||||
@@ -139,6 +145,7 @@ namespace gdjs {
|
||||
this._shadowDistance = content.shadowDistance;
|
||||
this._shadowBlur = content.shadowBlurRadius;
|
||||
this._shadowAngle = content.shadowAngle;
|
||||
this._lineHeight = content.lineHeight || 0;
|
||||
|
||||
this._renderer = new gdjs.TextRuntimeObjectRenderer(
|
||||
this,
|
||||
@@ -149,7 +156,7 @@ namespace gdjs {
|
||||
this.onCreated();
|
||||
}
|
||||
|
||||
updateFromObjectData(
|
||||
override updateFromObjectData(
|
||||
oldObjectData: TextObjectData,
|
||||
newObjectData: TextObjectData
|
||||
): boolean {
|
||||
@@ -173,9 +180,6 @@ namespace gdjs {
|
||||
if (oldContent.text !== newContent.text) {
|
||||
this.setText(newContent.text);
|
||||
}
|
||||
if (oldContent.underlined !== newContent.underlined) {
|
||||
return false;
|
||||
}
|
||||
if (oldContent.textAlignment !== newContent.textAlignment) {
|
||||
this.setTextAlignment(newContent.textAlignment);
|
||||
}
|
||||
@@ -211,12 +215,21 @@ namespace gdjs {
|
||||
if (oldContent.shadowBlurRadius !== newContent.shadowBlurRadius) {
|
||||
this.setShadowBlurRadius(newContent.shadowBlurRadius);
|
||||
}
|
||||
if ((oldContent.lineHeight || 0) !== (newContent.lineHeight || 0)) {
|
||||
this.setLineHeight(newContent.lineHeight || 0);
|
||||
}
|
||||
if (oldContent.underlined !== newContent.underlined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
getNetworkSyncData(): TextObjectNetworkSyncData {
|
||||
override getNetworkSyncData(
|
||||
syncOptions: GetNetworkSyncDataOptions
|
||||
): TextObjectNetworkSyncData {
|
||||
return {
|
||||
...super.getNetworkSyncData(),
|
||||
...super.getNetworkSyncData(syncOptions),
|
||||
str: this._str,
|
||||
o: this.opacity,
|
||||
cs: this._characterSize,
|
||||
@@ -227,6 +240,7 @@ namespace gdjs {
|
||||
c: this._color,
|
||||
scale: this.getScale(),
|
||||
ta: this._textAlign,
|
||||
vta: this._verticalTextAlignment,
|
||||
wrap: this._wrapping,
|
||||
wrapw: this._wrappingWidth,
|
||||
oena: this._isOutlineEnabled,
|
||||
@@ -238,14 +252,16 @@ namespace gdjs {
|
||||
shd: this._shadowDistance,
|
||||
sha: this._shadowAngle,
|
||||
shb: this._shadowBlur,
|
||||
lh: this._lineHeight,
|
||||
pad: this._padding,
|
||||
};
|
||||
}
|
||||
|
||||
updateFromNetworkSyncData(
|
||||
networkSyncData: TextObjectNetworkSyncData
|
||||
override updateFromNetworkSyncData(
|
||||
networkSyncData: TextObjectNetworkSyncData,
|
||||
options: UpdateFromNetworkSyncDataOptions
|
||||
): void {
|
||||
super.updateFromNetworkSyncData(networkSyncData);
|
||||
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||
if (networkSyncData.str !== undefined) {
|
||||
this.setText(networkSyncData.str);
|
||||
}
|
||||
@@ -276,8 +292,8 @@ namespace gdjs {
|
||||
if (networkSyncData.ta !== undefined) {
|
||||
this.setTextAlignment(networkSyncData.ta);
|
||||
}
|
||||
if (networkSyncData.ta !== undefined) {
|
||||
this.setVerticalTextAlignment(networkSyncData.ta);
|
||||
if (networkSyncData.vta !== undefined) {
|
||||
this.setVerticalTextAlignment(networkSyncData.vta);
|
||||
}
|
||||
if (networkSyncData.wrap !== undefined) {
|
||||
this.setWrapping(networkSyncData.wrap);
|
||||
@@ -312,28 +328,30 @@ namespace gdjs {
|
||||
if (networkSyncData.shb !== undefined) {
|
||||
this.setShadowBlurRadius(networkSyncData.shb);
|
||||
}
|
||||
if (networkSyncData.lh !== undefined) {
|
||||
this.setLineHeight(networkSyncData.lh);
|
||||
}
|
||||
if (networkSyncData.pad !== undefined) {
|
||||
this.setPadding(networkSyncData.pad);
|
||||
}
|
||||
}
|
||||
|
||||
getRendererObject() {
|
||||
override getRendererObject() {
|
||||
return this._renderer.getRendererObject();
|
||||
}
|
||||
|
||||
update(instanceContainer: gdjs.RuntimeInstanceContainer): void {
|
||||
override update(instanceContainer: gdjs.RuntimeInstanceContainer): void {
|
||||
this._renderer.ensureUpToDate();
|
||||
}
|
||||
|
||||
onDestroyed(): void {
|
||||
override onDestroyed(): void {
|
||||
super.onDestroyed();
|
||||
this._renderer.destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the extra parameters that could be set for an instance.
|
||||
*/
|
||||
extraInitializationFromInitialInstance(initialInstanceData: InstanceData) {
|
||||
override extraInitializationFromInitialInstance(
|
||||
initialInstanceData: InstanceData
|
||||
) {
|
||||
if (initialInstanceData.customSize) {
|
||||
this.setWrappingWidth(initialInstanceData.width);
|
||||
this.setWrapping(true);
|
||||
@@ -353,27 +371,17 @@ namespace gdjs {
|
||||
this._renderer.updatePosition();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set object position on X axis.
|
||||
*/
|
||||
setX(x: float): void {
|
||||
override setX(x: float): void {
|
||||
super.setX(x);
|
||||
this._updateTextPosition();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set object position on Y axis.
|
||||
*/
|
||||
setY(y: float): void {
|
||||
override setY(y: float): void {
|
||||
super.setY(y);
|
||||
this._updateTextPosition();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the angle of the object.
|
||||
* @param angle The new angle of the object
|
||||
*/
|
||||
setAngle(angle: float): void {
|
||||
override setAngle(angle: float): void {
|
||||
super.setAngle(angle);
|
||||
this._renderer.updateAngle();
|
||||
}
|
||||
@@ -455,6 +463,22 @@ namespace gdjs {
|
||||
this._renderer.updateStyle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the line height of the text.
|
||||
*/
|
||||
getLineHeight(): float {
|
||||
return this._lineHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the line height of the text.
|
||||
* @param value The new line height for the text.
|
||||
*/
|
||||
setLineHeight(value: float): void {
|
||||
this._lineHeight = value;
|
||||
this._renderer.updateStyle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the resource to use for the font.
|
||||
* @param fontResourceName The name of the font resource.
|
||||
@@ -499,14 +523,14 @@ namespace gdjs {
|
||||
/**
|
||||
* Get width of the text.
|
||||
*/
|
||||
getWidth(): float {
|
||||
override getWidth(): float {
|
||||
return this._wrapping ? this._wrappingWidth : this._renderer.getWidth();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get height of the text.
|
||||
*/
|
||||
getHeight(): float {
|
||||
override getHeight(): float {
|
||||
return this._renderer.getHeight();
|
||||
}
|
||||
|
||||
@@ -584,16 +608,10 @@ namespace gdjs {
|
||||
|
||||
/**
|
||||
* Change the text color.
|
||||
* @param colorString color as a "R;G;B" string, for example: "255;0;0"
|
||||
* @param rgbOrHexColor color as a "R;G;B" string, for example: "255;0;0"
|
||||
*/
|
||||
setColor(colorString: string): void {
|
||||
const color = colorString.split(';');
|
||||
if (color.length < 3) {
|
||||
return;
|
||||
}
|
||||
this._color[0] = parseInt(color[0], 10);
|
||||
this._color[1] = parseInt(color[1], 10);
|
||||
this._color[2] = parseInt(color[2], 10);
|
||||
setColor(rgbOrHexColor: string): void {
|
||||
this._color = gdjs.rgbOrHexToRGBColor(rgbOrHexColor);
|
||||
this._useGradient = false;
|
||||
this._renderer.updateStyle();
|
||||
}
|
||||
@@ -685,11 +703,11 @@ namespace gdjs {
|
||||
}
|
||||
}
|
||||
|
||||
setWidth(width: float): void {
|
||||
override setWidth(width: float): void {
|
||||
this.setWrappingWidth(width);
|
||||
}
|
||||
|
||||
getDrawableY(): float {
|
||||
override getDrawableY(): float {
|
||||
return (
|
||||
this.getY() -
|
||||
(this._verticalTextAlignment === 'center'
|
||||
@@ -702,18 +720,12 @@ namespace gdjs {
|
||||
|
||||
/**
|
||||
* Set the outline for the text object.
|
||||
* @param str color as a "R;G;B" string, for example: "255;0;0"
|
||||
* @param rgbOrHexColor color as a "R;G;B" string, for example: "255;0;0"
|
||||
* @param thickness thickness of the outline (0 = disabled)
|
||||
* @deprecated Prefer independent setters.
|
||||
*/
|
||||
setOutline(str: string, thickness: number): void {
|
||||
const color = str.split(';');
|
||||
if (color.length < 3) {
|
||||
return;
|
||||
}
|
||||
this._outlineColor[0] = parseInt(color[0], 10);
|
||||
this._outlineColor[1] = parseInt(color[1], 10);
|
||||
this._outlineColor[2] = parseInt(color[2], 10);
|
||||
setOutline(rgbOrHexColor: string, thickness: number): void {
|
||||
this._outlineColor = gdjs.rgbOrHexToRGBColor(rgbOrHexColor);
|
||||
this._outlineThickness = thickness;
|
||||
this._renderer.updateStyle();
|
||||
}
|
||||
@@ -755,25 +767,19 @@ namespace gdjs {
|
||||
|
||||
/**
|
||||
* Set the shadow for the text object.
|
||||
* @param str color as a "R;G;B" string, for example: "255;0;0"
|
||||
* @param rgbOrHexColor color as a "R;G;B" string, for example: "255;0;0"
|
||||
* @param distance distance between the shadow and the text, in pixels.
|
||||
* @param blur amount of shadow blur, in pixels.
|
||||
* @param angle shadow offset direction, in degrees.
|
||||
* @deprecated Prefer independent setters.
|
||||
*/
|
||||
setShadow(
|
||||
str: string,
|
||||
rgbOrHexColor: string,
|
||||
distance: number,
|
||||
blur: integer,
|
||||
angle: float
|
||||
): void {
|
||||
const color = str.split(';');
|
||||
if (color.length < 3) {
|
||||
return;
|
||||
}
|
||||
this._shadowColor[0] = parseInt(color[0], 10);
|
||||
this._shadowColor[1] = parseInt(color[1], 10);
|
||||
this._shadowColor[2] = parseInt(color[2], 10);
|
||||
this._shadowColor = gdjs.rgbOrHexToRGBColor(rgbOrHexColor);
|
||||
this._shadowDistance = distance;
|
||||
this._shadowBlur = blur;
|
||||
this._shadowAngle = angle;
|
||||
@@ -886,38 +892,18 @@ namespace gdjs {
|
||||
strThirdColor: string,
|
||||
strFourthColor: string
|
||||
): void {
|
||||
const colorFirst = strFirstColor.split(';');
|
||||
const colorSecond = strSecondColor.split(';');
|
||||
const colorThird = strThirdColor.split(';');
|
||||
const colorFourth = strFourthColor.split(';');
|
||||
this._gradient = [];
|
||||
if (colorFirst.length == 3) {
|
||||
this._gradient.push([
|
||||
parseInt(colorFirst[0], 10),
|
||||
parseInt(colorFirst[1], 10),
|
||||
parseInt(colorFirst[2], 10),
|
||||
]);
|
||||
if (strFirstColor) {
|
||||
this._gradient.push(gdjs.rgbOrHexToRGBColor(strFirstColor));
|
||||
}
|
||||
if (colorSecond.length == 3) {
|
||||
this._gradient.push([
|
||||
parseInt(colorSecond[0], 10),
|
||||
parseInt(colorSecond[1], 10),
|
||||
parseInt(colorSecond[2], 10),
|
||||
]);
|
||||
if (strSecondColor) {
|
||||
this._gradient.push(gdjs.rgbOrHexToRGBColor(strSecondColor));
|
||||
}
|
||||
if (colorThird.length == 3) {
|
||||
this._gradient.push([
|
||||
parseInt(colorThird[0], 10),
|
||||
parseInt(colorThird[1], 10),
|
||||
parseInt(colorThird[2], 10),
|
||||
]);
|
||||
if (strThirdColor) {
|
||||
this._gradient.push(gdjs.rgbOrHexToRGBColor(strThirdColor));
|
||||
}
|
||||
if (colorFourth.length == 3) {
|
||||
this._gradient.push([
|
||||
parseInt(colorFourth[0], 10),
|
||||
parseInt(colorFourth[1], 10),
|
||||
parseInt(colorFourth[2], 10),
|
||||
]);
|
||||
if (strFourthColor) {
|
||||
this._gradient.push(gdjs.rgbOrHexToRGBColor(strFourthColor));
|
||||
}
|
||||
this._gradientType = strGradientType;
|
||||
this._useGradient = this._gradient.length > 1 ? true : false;
|
||||
|
@@ -9,7 +9,7 @@ import {
|
||||
* A tile map model.
|
||||
*
|
||||
* Tile map files are parsed into this model by {@link TiledTileMapLoader} or {@link LDtkTileMapLoader}.
|
||||
* This model is used for rending ({@link TileMapRuntimeObjectPixiRenderer})
|
||||
* This model is used for rendering ({@link TileMapRuntimeObjectPixiRenderer})
|
||||
* and hitboxes handling ({@link TransformedCollisionTileMap}).
|
||||
* This allows to support new file format with only a new parser.
|
||||
*/
|
||||
@@ -71,7 +71,7 @@ export declare class EditableTileMap {
|
||||
tileSetRowCount: number;
|
||||
}
|
||||
): EditableTileMap;
|
||||
toJSObject(): Object;
|
||||
toJSObject(): EditableTileMapAsJsObject;
|
||||
/**
|
||||
* @returns The tile map width in pixels.
|
||||
*/
|
||||
@@ -205,7 +205,7 @@ declare abstract class AbstractEditableLayer {
|
||||
*/
|
||||
constructor(tileMap: EditableTileMap, id: integer);
|
||||
setVisible(visible: boolean): void;
|
||||
toJSObject(): Object;
|
||||
toJSObject(): EditableTileMapLayerAsJsObject;
|
||||
/**
|
||||
* @returns true if the layer is visible.
|
||||
*/
|
||||
@@ -284,7 +284,7 @@ export declare class EditableTileMapLayer extends AbstractEditableLayer {
|
||||
tileMap: EditableTileMap,
|
||||
isTileIdValid: (tileId: number) => boolean
|
||||
): EditableTileMapLayer;
|
||||
toJSObject(): Object;
|
||||
toJSObject(): EditableTileMapLayerAsJsObject;
|
||||
/**
|
||||
* The opacity (between 0-1) of the layer
|
||||
*/
|
||||
|
@@ -2,7 +2,6 @@
|
||||
namespace gdjs {
|
||||
export type SimpleTileMapObjectDataType = {
|
||||
content: {
|
||||
opacity: number;
|
||||
atlasImage: string;
|
||||
rowCount: number;
|
||||
columnCount: number;
|
||||
@@ -16,10 +15,7 @@ namespace gdjs {
|
||||
|
||||
export type SimpleTileMapNetworkSyncDataType = {
|
||||
op: number;
|
||||
ai: string;
|
||||
wid: number;
|
||||
hei: number;
|
||||
// TODO: Support tilemap synchronization. Find an efficient way to send tiles changes.
|
||||
tm?: TileMapHelper.EditableTileMapAsJsObject;
|
||||
};
|
||||
|
||||
export type SimpleTileMapNetworkSyncData = ObjectNetworkSyncData &
|
||||
@@ -40,6 +36,7 @@ namespace gdjs {
|
||||
_opacity: float = 255;
|
||||
_atlasImage: string;
|
||||
_tileMapManager: gdjs.TileMap.TileMapRuntimeManager;
|
||||
_tileMap: TileMapHelper.EditableTileMap | null = null;
|
||||
_renderer: gdjs.TileMapRuntimeObjectPixiRenderer;
|
||||
readonly _rowCount: number;
|
||||
readonly _columnCount: number;
|
||||
@@ -64,7 +61,6 @@ namespace gdjs {
|
||||
objectData: SimpleTileMapObjectDataType
|
||||
) {
|
||||
super(instanceContainer, objectData);
|
||||
this._opacity = objectData.content.opacity;
|
||||
this._atlasImage = objectData.content.atlasImage;
|
||||
this._rowCount = objectData.content.rowCount;
|
||||
this._columnCount = objectData.content.columnCount;
|
||||
@@ -89,16 +85,19 @@ namespace gdjs {
|
||||
instanceContainer
|
||||
);
|
||||
|
||||
this._loadInitialTileMap((tileMap: TileMapHelper.EditableTileMap) => {
|
||||
this._renderer.updatePosition();
|
||||
this._loadTileMap(
|
||||
this._initialTileMapAsJsObject,
|
||||
(tileMap: TileMapHelper.EditableTileMap) => {
|
||||
this._renderer.updatePosition();
|
||||
|
||||
this._collisionTileMap = new gdjs.TileMap.TransformedCollisionTileMap(
|
||||
tileMap,
|
||||
this._hitBoxTag
|
||||
);
|
||||
this._collisionTileMap = new gdjs.TileMap.TransformedCollisionTileMap(
|
||||
tileMap,
|
||||
this._hitBoxTag
|
||||
);
|
||||
|
||||
this.updateTransformation();
|
||||
});
|
||||
this.updateTransformation();
|
||||
}
|
||||
);
|
||||
|
||||
// *ALWAYS* call `this.onCreated()` at the very end of your object constructor.
|
||||
this.onCreated();
|
||||
@@ -141,8 +140,8 @@ namespace gdjs {
|
||||
);
|
||||
if (!shouldContinue) return;
|
||||
if (this._collisionTileMap) {
|
||||
const tileMap = this._renderer.getTileMap();
|
||||
if (tileMap) this._collisionTileMap.updateFromTileMap(tileMap);
|
||||
if (this._tileMap)
|
||||
this._collisionTileMap.updateFromTileMap(this._tileMap);
|
||||
}
|
||||
this._isTileMapDirty = false;
|
||||
}
|
||||
@@ -152,9 +151,6 @@ namespace gdjs {
|
||||
oldObjectData: SimpleTileMapObjectData,
|
||||
newObjectData: SimpleTileMapObjectData
|
||||
): boolean {
|
||||
if (oldObjectData.content.opacity !== newObjectData.content.opacity) {
|
||||
this.setOpacity(newObjectData.content.opacity);
|
||||
}
|
||||
if (
|
||||
oldObjectData.content.atlasImage !== newObjectData.content.atlasImage
|
||||
) {
|
||||
@@ -165,41 +161,58 @@ namespace gdjs {
|
||||
return true;
|
||||
}
|
||||
|
||||
getNetworkSyncData(): SimpleTileMapNetworkSyncData {
|
||||
return {
|
||||
...super.getNetworkSyncData(),
|
||||
getNetworkSyncData(
|
||||
syncOptions: GetNetworkSyncDataOptions
|
||||
): SimpleTileMapNetworkSyncData {
|
||||
const syncData: SimpleTileMapNetworkSyncData = {
|
||||
...super.getNetworkSyncData(syncOptions),
|
||||
op: this._opacity,
|
||||
ai: this._atlasImage,
|
||||
wid: this.getWidth(),
|
||||
hei: this.getHeight(),
|
||||
};
|
||||
if (this._tileMap && syncOptions.syncFullTileMaps) {
|
||||
const currentTileMapAsJsObject = this._tileMap.toJSObject();
|
||||
syncData.tm = currentTileMapAsJsObject;
|
||||
}
|
||||
|
||||
return syncData;
|
||||
}
|
||||
|
||||
updateFromNetworkSyncData(
|
||||
networkSyncData: SimpleTileMapNetworkSyncData
|
||||
networkSyncData: SimpleTileMapNetworkSyncData,
|
||||
options: UpdateFromNetworkSyncDataOptions
|
||||
): void {
|
||||
super.updateFromNetworkSyncData(networkSyncData);
|
||||
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||
|
||||
if (
|
||||
networkSyncData.op !== undefined &&
|
||||
networkSyncData.op !== this._opacity
|
||||
) {
|
||||
this.setOpacity(networkSyncData.op);
|
||||
}
|
||||
if (
|
||||
networkSyncData.wid !== undefined &&
|
||||
networkSyncData.wid !== this.getWidth()
|
||||
) {
|
||||
this.setWidth(networkSyncData.wid);
|
||||
}
|
||||
if (
|
||||
networkSyncData.hei !== undefined &&
|
||||
networkSyncData.hei !== this.getHeight()
|
||||
) {
|
||||
this.setHeight(networkSyncData.hei);
|
||||
}
|
||||
if (networkSyncData.ai !== undefined) {
|
||||
// TODO: support changing the atlas texture
|
||||
if (networkSyncData.tm !== undefined) {
|
||||
this._loadTileMap(
|
||||
networkSyncData.tm,
|
||||
(tileMap: TileMapHelper.EditableTileMap) => {
|
||||
if (networkSyncData.w !== undefined) {
|
||||
this.setWidth(networkSyncData.w);
|
||||
}
|
||||
if (networkSyncData.h !== undefined) {
|
||||
this.setHeight(networkSyncData.h);
|
||||
}
|
||||
if (networkSyncData.op !== undefined) {
|
||||
this.setOpacity(networkSyncData.op);
|
||||
}
|
||||
|
||||
// 4. Update position (calculations based on renderer's dimensions).
|
||||
this._renderer.updatePosition();
|
||||
|
||||
if (this._collisionTileMap) {
|
||||
// If collision tile map is already defined, only update it.
|
||||
this._collisionTileMap.updateFromTileMap(tileMap);
|
||||
} else {
|
||||
this._collisionTileMap =
|
||||
new gdjs.TileMap.TransformedCollisionTileMap(
|
||||
tileMap,
|
||||
this._hitBoxTag
|
||||
);
|
||||
}
|
||||
|
||||
this.updateTransformation();
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,39 +228,43 @@ namespace gdjs {
|
||||
|
||||
// 2. Update the renderer so that it updates the tilemap object
|
||||
// (used for width and position calculations).
|
||||
this._loadInitialTileMap((tileMap: TileMapHelper.EditableTileMap) => {
|
||||
// 3. Set custom dimensions & opacity if applicable.
|
||||
if (initialInstanceData.customSize) {
|
||||
this.setWidth(initialInstanceData.width);
|
||||
this.setHeight(initialInstanceData.height);
|
||||
}
|
||||
if (initialInstanceData.opacity !== undefined) {
|
||||
this.setOpacity(initialInstanceData.opacity);
|
||||
}
|
||||
this._loadTileMap(
|
||||
this._initialTileMapAsJsObject,
|
||||
(tileMap: TileMapHelper.EditableTileMap) => {
|
||||
// 3. Set custom dimensions & opacity if applicable.
|
||||
if (initialInstanceData.customSize) {
|
||||
this.setWidth(initialInstanceData.width);
|
||||
this.setHeight(initialInstanceData.height);
|
||||
}
|
||||
if (initialInstanceData.opacity !== undefined) {
|
||||
this.setOpacity(initialInstanceData.opacity);
|
||||
}
|
||||
|
||||
// 4. Update position (calculations based on renderer's dimensions).
|
||||
this._renderer.updatePosition();
|
||||
// 4. Update position (calculations based on renderer's dimensions).
|
||||
this._renderer.updatePosition();
|
||||
|
||||
if (this._collisionTileMap) {
|
||||
// If collision tile map is already defined, there's a good chance it means
|
||||
// extraInitializationFromInitialInstance is called when hot reloading the
|
||||
// scene so the collision is tile map is updated instead of being re-created.
|
||||
this._collisionTileMap.updateFromTileMap(tileMap);
|
||||
} else {
|
||||
this._collisionTileMap = new gdjs.TileMap.TransformedCollisionTileMap(
|
||||
tileMap,
|
||||
this._hitBoxTag
|
||||
);
|
||||
if (this._collisionTileMap) {
|
||||
// If collision tile map is already defined, there's a good chance it means
|
||||
// extraInitializationFromInitialInstance is called when hot reloading the
|
||||
// scene so the collision is tile map is updated instead of being re-created.
|
||||
this._collisionTileMap.updateFromTileMap(tileMap);
|
||||
} else {
|
||||
this._collisionTileMap =
|
||||
new gdjs.TileMap.TransformedCollisionTileMap(
|
||||
tileMap,
|
||||
this._hitBoxTag
|
||||
);
|
||||
}
|
||||
|
||||
this.updateTransformation();
|
||||
}
|
||||
|
||||
this.updateTransformation();
|
||||
});
|
||||
);
|
||||
}
|
||||
|
||||
private _loadInitialTileMap(
|
||||
private _loadTileMap(
|
||||
tileMapAsJsObject: TileMapHelper.EditableTileMapAsJsObject,
|
||||
tileMapLoadingCallback: (tileMap: TileMapHelper.EditableTileMap) => void
|
||||
): void {
|
||||
if (!this._initialTileMapAsJsObject) return;
|
||||
if (this._columnCount <= 0 || this._rowCount <= 0) {
|
||||
console.error(
|
||||
`Tilemap object ${this.name} is not configured properly.`
|
||||
@@ -256,7 +273,7 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
this._tileMapManager.getOrLoadSimpleTileMap(
|
||||
this._initialTileMapAsJsObject,
|
||||
tileMapAsJsObject,
|
||||
this.name,
|
||||
this._tileSize,
|
||||
this._columnCount,
|
||||
@@ -300,7 +317,8 @@ namespace gdjs {
|
||||
// getOrLoadTextureCache already log warns and errors.
|
||||
return;
|
||||
}
|
||||
this._renderer.updatePixiTileMap(tileMap, textureCache);
|
||||
this._tileMap = tileMap;
|
||||
this._renderer.refreshPixiTileMap(textureCache);
|
||||
tileMapLoadingCallback(tileMap);
|
||||
},
|
||||
(error) => {
|
||||
@@ -656,7 +674,7 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
getTileAtGridCoordinates(columnIndex: integer, rowIndex: integer): integer {
|
||||
return this._renderer.getTileId(columnIndex, rowIndex, 0);
|
||||
return this.getTileId(columnIndex, rowIndex, 0);
|
||||
}
|
||||
|
||||
setTileAtPosition(tileId: number, x: float, y: float) {
|
||||
@@ -670,11 +688,10 @@ namespace gdjs {
|
||||
columnIndex: integer,
|
||||
rowIndex: integer
|
||||
) {
|
||||
const tileMap = this._renderer._tileMap;
|
||||
if (!tileMap) {
|
||||
if (!this._tileMap) {
|
||||
return;
|
||||
}
|
||||
const layer = tileMap.getTileLayer(this._layerIndex);
|
||||
const layer = this._tileMap.getTileLayer(this._layerIndex);
|
||||
if (!layer) {
|
||||
return;
|
||||
}
|
||||
@@ -686,8 +703,8 @@ namespace gdjs {
|
||||
|
||||
if (this._collisionTileMap) {
|
||||
const oldTileDefinition =
|
||||
oldTileId !== undefined && tileMap.getTileDefinition(oldTileId);
|
||||
const newTileDefinition = tileMap.getTileDefinition(tileId);
|
||||
oldTileId !== undefined && this._tileMap.getTileDefinition(oldTileId);
|
||||
const newTileDefinition = this._tileMap.getTileDefinition(tileId);
|
||||
const hadFullHitBox =
|
||||
!!oldTileDefinition &&
|
||||
oldTileDefinition.hasFullHitBox(this._hitBoxTag);
|
||||
@@ -723,7 +740,7 @@ namespace gdjs {
|
||||
rowIndex: integer,
|
||||
flip: boolean
|
||||
) {
|
||||
this._renderer.flipTileOnY(columnIndex, rowIndex, 0, flip);
|
||||
this.flipTileOnY(columnIndex, rowIndex, 0, flip);
|
||||
this._isTileMapDirty = true;
|
||||
// No need to invalidate hit boxes since at the moment, collision mask
|
||||
// cannot be configured on each tile.
|
||||
@@ -734,7 +751,7 @@ namespace gdjs {
|
||||
rowIndex: integer,
|
||||
flip: boolean
|
||||
) {
|
||||
this._renderer.flipTileOnX(columnIndex, rowIndex, 0, flip);
|
||||
this.flipTileOnX(columnIndex, rowIndex, 0, flip);
|
||||
this._isTileMapDirty = true;
|
||||
// No need to invalidate hit boxes since at the moment, collision mask
|
||||
// cannot be configured on each tile.
|
||||
@@ -744,22 +761,22 @@ namespace gdjs {
|
||||
const [columnIndex, rowIndex] =
|
||||
this.getGridCoordinatesFromSceneCoordinates(x, y);
|
||||
|
||||
return this._renderer.isTileFlippedOnX(columnIndex, rowIndex, 0);
|
||||
return this.isTileFlippedOnX(columnIndex, rowIndex, 0);
|
||||
}
|
||||
|
||||
isTileFlippedOnXAtGridCoordinates(columnIndex: integer, rowIndex: integer) {
|
||||
return this._renderer.isTileFlippedOnX(columnIndex, rowIndex, 0);
|
||||
return this.isTileFlippedOnX(columnIndex, rowIndex, 0);
|
||||
}
|
||||
|
||||
isTileFlippedOnYAtPosition(x: float, y: float) {
|
||||
const [columnIndex, rowIndex] =
|
||||
this.getGridCoordinatesFromSceneCoordinates(x, y);
|
||||
|
||||
return this._renderer.isTileFlippedOnY(columnIndex, rowIndex, 0);
|
||||
return this.isTileFlippedOnY(columnIndex, rowIndex, 0);
|
||||
}
|
||||
|
||||
isTileFlippedOnYAtGridCoordinates(columnIndex: integer, rowIndex: integer) {
|
||||
return this._renderer.isTileFlippedOnY(columnIndex, rowIndex, 0);
|
||||
return this.isTileFlippedOnY(columnIndex, rowIndex, 0);
|
||||
}
|
||||
|
||||
removeTileAtPosition(x: float, y: float) {
|
||||
@@ -769,11 +786,10 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
removeTileAtGridCoordinates(columnIndex: integer, rowIndex: integer) {
|
||||
const tileMap = this._renderer._tileMap;
|
||||
if (!tileMap) {
|
||||
if (!this._tileMap) {
|
||||
return;
|
||||
}
|
||||
const layer = tileMap.getTileLayer(this._layerIndex);
|
||||
const layer = this._tileMap.getTileLayer(this._layerIndex);
|
||||
if (!layer) {
|
||||
return;
|
||||
}
|
||||
@@ -795,24 +811,28 @@ namespace gdjs {
|
||||
|
||||
setGridRowCount(targetRowCount: integer) {
|
||||
if (targetRowCount <= 0) return;
|
||||
this._renderer.setGridRowCount(targetRowCount);
|
||||
if (!this._tileMap) return;
|
||||
this._tileMap.setDimensionY(targetRowCount);
|
||||
this._isTileMapDirty = true;
|
||||
this.invalidateHitboxes();
|
||||
}
|
||||
|
||||
setGridColumnCount(targetColumnCount: integer) {
|
||||
if (targetColumnCount <= 0) return;
|
||||
this._renderer.setGridColumnCount(targetColumnCount);
|
||||
if (!this._tileMap) return;
|
||||
this._tileMap.setDimensionX(targetColumnCount);
|
||||
this._isTileMapDirty = true;
|
||||
this.invalidateHitboxes();
|
||||
}
|
||||
|
||||
getGridRowCount(): integer {
|
||||
return this._renderer.getGridRowCount();
|
||||
if (!this._tileMap) return 0;
|
||||
return this._tileMap.getDimensionY();
|
||||
}
|
||||
|
||||
getGridColumnCount(): integer {
|
||||
return this._renderer.getGridColumnCount();
|
||||
if (!this._tileMap) return 0;
|
||||
return this._tileMap.getDimensionX();
|
||||
}
|
||||
|
||||
getTilesetColumnCount(): integer {
|
||||
@@ -822,6 +842,73 @@ namespace gdjs {
|
||||
getTilesetRowCount(): integer {
|
||||
return this._rowCount;
|
||||
}
|
||||
|
||||
getTileMap(): TileMapHelper.EditableTileMap | null {
|
||||
return this._tileMap;
|
||||
}
|
||||
|
||||
getTileMapWidth() {
|
||||
const tileMap = this._tileMap;
|
||||
return tileMap ? tileMap.getWidth() : 20;
|
||||
}
|
||||
|
||||
getTileMapHeight() {
|
||||
const tileMap = this._tileMap;
|
||||
return tileMap ? tileMap.getHeight() : 20;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param x The layer column.
|
||||
* @param y The layer row.
|
||||
* @param layerIndex The layer index.
|
||||
* @returns The tile's id.
|
||||
*/
|
||||
getTileId(x: integer, y: integer, layerIndex: integer): integer {
|
||||
if (!this._tileMap) return -1;
|
||||
return this._tileMap.getTileId(x, y, layerIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param x The layer column.
|
||||
* @param y The layer row.
|
||||
* @param layerIndex The layer index.
|
||||
* @param flip true if the tile should be flipped.
|
||||
*/
|
||||
flipTileOnY(x: integer, y: integer, layerIndex: integer, flip: boolean) {
|
||||
if (!this._tileMap) return;
|
||||
this._tileMap.flipTileOnY(x, y, layerIndex, flip);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param x The layer column.
|
||||
* @param y The layer row.
|
||||
* @param layerIndex The layer index.
|
||||
* @param flip true if the tile should be flipped.
|
||||
*/
|
||||
flipTileOnX(x: integer, y: integer, layerIndex: integer, flip: boolean) {
|
||||
if (!this._tileMap) return;
|
||||
this._tileMap.flipTileOnX(x, y, layerIndex, flip);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param x The layer column.
|
||||
* @param y The layer row.
|
||||
* @param layerIndex The layer index.
|
||||
*/
|
||||
isTileFlippedOnX(x: integer, y: integer, layerIndex: integer): boolean {
|
||||
if (!this._tileMap) return false;
|
||||
return this._tileMap.isTileFlippedOnX(x, y, layerIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param x The layer column.
|
||||
* @param y The layer row.
|
||||
* @param layerIndex The layer index.
|
||||
*/
|
||||
isTileFlippedOnY(x: integer, y: integer, layerIndex: integer): boolean {
|
||||
if (!this._tileMap) return false;
|
||||
return this._tileMap.isTileFlippedOnY(x, y, layerIndex);
|
||||
}
|
||||
}
|
||||
gdjs.registerObject(
|
||||
'TileMap::SimpleTileMap',
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user