mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
Compare commits
428 Commits
event-func
...
feat/touch
Author | SHA1 | Date | |
---|---|---|---|
![]() |
d924cb4269 | ||
![]() |
c2f17f9348 | ||
![]() |
0ba59d5d7f | ||
![]() |
6878a4cd75 | ||
![]() |
33eed58c62 | ||
![]() |
f516eff739 | ||
![]() |
baefc272f6 | ||
![]() |
2badc72dfb | ||
![]() |
5e54f02061 | ||
![]() |
827bb2d08d | ||
![]() |
0fda0baa48 | ||
![]() |
e655cc0661 | ||
![]() |
225d1b37ab | ||
![]() |
5a0ab9dffa | ||
![]() |
9300604d56 | ||
![]() |
02fcf1dbc2 | ||
![]() |
a70b2e2c2c | ||
![]() |
3e04b5a82f | ||
![]() |
7c4617da99 | ||
![]() |
cbcf0b7b70 | ||
![]() |
5d1fe83655 | ||
![]() |
d6d7c5c1fb | ||
![]() |
7cf70a6fd7 | ||
![]() |
24c2dbc340 | ||
![]() |
290901849c | ||
![]() |
b7f7a39aa7 | ||
![]() |
8c04771a87 | ||
![]() |
febf15b279 | ||
![]() |
59721cba7e | ||
![]() |
53cd78bb45 | ||
![]() |
49f8ce9385 | ||
![]() |
a05e4b7ecc | ||
![]() |
de2a9725f9 | ||
![]() |
b2ec3b5387 | ||
![]() |
1873b5e592 | ||
![]() |
e47e35c090 | ||
![]() |
ae572683f1 | ||
![]() |
f8e387230f | ||
![]() |
9451e5969f | ||
![]() |
ad7ddf09a3 | ||
![]() |
8e70930d8d | ||
![]() |
3568a999f9 | ||
![]() |
ab3bda24dc | ||
![]() |
d33a7331b3 | ||
![]() |
4d3793815f | ||
![]() |
207097bf03 | ||
![]() |
0e3d2b9570 | ||
![]() |
87fac429e8 | ||
![]() |
e1835d1144 | ||
![]() |
72068460e1 | ||
![]() |
5da76ae655 | ||
![]() |
3b1097931b | ||
![]() |
b5b25ad710 | ||
![]() |
2279f069af | ||
![]() |
ec7e408cd1 | ||
![]() |
f8a99b9cfa | ||
![]() |
4c7231e6ae | ||
![]() |
883d32515c | ||
![]() |
f0f3c257fa | ||
![]() |
42fce7d9ce | ||
![]() |
facac37fff | ||
![]() |
1272b601c6 | ||
![]() |
58e35cfaf5 | ||
![]() |
f0a68db0d4 | ||
![]() |
15ed28ad8d | ||
![]() |
2befc9781b | ||
![]() |
8fb2872c36 | ||
![]() |
98033515c8 | ||
![]() |
25c02cea2e | ||
![]() |
6d5be78fec | ||
![]() |
f6e60085db | ||
![]() |
a61648af70 | ||
![]() |
4291d5597a | ||
![]() |
2b496c6fd3 | ||
![]() |
7af3fa5f2f | ||
![]() |
5ce9591f68 | ||
![]() |
d5929010a7 | ||
![]() |
30f2f5256b | ||
![]() |
806d59fb88 | ||
![]() |
e5f18ae2d8 | ||
![]() |
15a7fd1f85 | ||
![]() |
3678a0dd45 | ||
![]() |
1e984f0965 | ||
![]() |
197bd913b8 | ||
![]() |
baef911d61 | ||
![]() |
771e16e779 | ||
![]() |
a2cf8e694b | ||
![]() |
cb151ea30e | ||
![]() |
7d1ebc8963 | ||
![]() |
510a699c03 | ||
![]() |
37bed36315 | ||
![]() |
08892b68d4 | ||
![]() |
5394cc5201 | ||
![]() |
e079fa4108 | ||
![]() |
5a26e883b8 | ||
![]() |
6bf5b389b5 | ||
![]() |
5e3dfb0e9c | ||
![]() |
f6a6c981f8 | ||
![]() |
2837a2906a | ||
![]() |
8c63fae2f2 | ||
![]() |
f837c22290 | ||
![]() |
465bfa4deb | ||
![]() |
a047ecdf9c | ||
![]() |
415c1bfd2f | ||
![]() |
04c28de00b | ||
![]() |
e4a911db25 | ||
![]() |
5fcd67d77b | ||
![]() |
a7df6de044 | ||
![]() |
81c3199d00 | ||
![]() |
dbe93a4bfd | ||
![]() |
e0edbe8d57 | ||
![]() |
c25d51805f | ||
![]() |
ab695370bb | ||
![]() |
15ee216e33 | ||
![]() |
3fac0522c9 | ||
![]() |
86db08ac3f | ||
![]() |
8d502d7c5c | ||
![]() |
8d735fc726 | ||
![]() |
1e1f4bb2a3 | ||
![]() |
12d18c45bc | ||
![]() |
d255ab458b | ||
![]() |
fbfa0315de | ||
![]() |
d8000aca10 | ||
![]() |
a2660ff0dc | ||
![]() |
000d5785cf | ||
![]() |
9fe04712a9 | ||
![]() |
846afd9e0a | ||
![]() |
9575705d29 | ||
![]() |
6125ff0f90 | ||
![]() |
a5428a8843 | ||
![]() |
19be45cda6 | ||
![]() |
889c97cb27 | ||
![]() |
1223eaa348 | ||
![]() |
21ea077768 | ||
![]() |
11895decd9 | ||
![]() |
c0e1e9fac6 | ||
![]() |
bd9c631e1b | ||
![]() |
4a6e8ef664 | ||
![]() |
e25345000d | ||
![]() |
111d37fc15 | ||
![]() |
1d83da41a9 | ||
![]() |
4a83c9eb59 | ||
![]() |
177cb2c519 | ||
![]() |
b5d69dee4c | ||
![]() |
fbdaebe575 | ||
![]() |
2a2fd75ca3 | ||
![]() |
a65f2174eb | ||
![]() |
af7563b4b7 | ||
![]() |
9db493e87e | ||
![]() |
49a3a18b51 | ||
![]() |
1861c3be41 | ||
![]() |
0489e7036b | ||
![]() |
895dc625eb | ||
![]() |
794d5a781c | ||
![]() |
52aea76677 | ||
![]() |
4f87191176 | ||
![]() |
43d4e2e8cc | ||
![]() |
c21dfbcc1f | ||
![]() |
cbfaa13978 | ||
![]() |
0aba5cf551 | ||
![]() |
cc75db6d09 | ||
![]() |
a3ef07c163 | ||
![]() |
a8ede5eee7 | ||
![]() |
ce965ca31c | ||
![]() |
039fbb8b1b | ||
![]() |
80c1e67146 | ||
![]() |
2591cabdd0 | ||
![]() |
45620d6bf4 | ||
![]() |
89a59cdd35 | ||
![]() |
17b819a423 | ||
![]() |
afb4e3c1c6 | ||
![]() |
e5f8fe1bf8 | ||
![]() |
30de5c6fa9 | ||
![]() |
79a29cf7d5 | ||
![]() |
98a1323bf5 | ||
![]() |
0fe6585897 | ||
![]() |
48d35a50b5 | ||
![]() |
33dd605c57 | ||
![]() |
3a0888046f | ||
![]() |
7917994835 | ||
![]() |
9e2bab43f7 | ||
![]() |
bab0c227f8 | ||
![]() |
5ad2be4a8a | ||
![]() |
7e03f47f08 | ||
![]() |
39e18678f5 | ||
![]() |
f54b13a9f5 | ||
![]() |
7c6137a4fc | ||
![]() |
703adc090a | ||
![]() |
81f0047dab | ||
![]() |
ef70add27b | ||
![]() |
d45932cc04 | ||
![]() |
081a97a5fc | ||
![]() |
2b3cedb441 | ||
![]() |
76426117ff | ||
![]() |
e8720780eb | ||
![]() |
e5ed642121 | ||
![]() |
a6602292b7 | ||
![]() |
859d8e08a0 | ||
![]() |
8128adfd7b | ||
![]() |
f9317dd17f | ||
![]() |
0cbd6e2fe9 | ||
![]() |
32a169014a | ||
![]() |
37d3fd99eb | ||
![]() |
5acc1f5560 | ||
![]() |
887693a90d | ||
![]() |
e0973a8231 | ||
![]() |
876ce0d0a5 | ||
![]() |
9f614ce7e0 | ||
![]() |
63de997e60 | ||
![]() |
d2aa49fd1c | ||
![]() |
d355c16bdf | ||
![]() |
8c7f5d1ea8 | ||
![]() |
08635de08e | ||
![]() |
4dc8676848 | ||
![]() |
164a230cbc | ||
![]() |
f3dc551284 | ||
![]() |
ec674e85d6 | ||
![]() |
32f47900cd | ||
![]() |
320774c8d8 | ||
![]() |
1594f44a72 | ||
![]() |
726d3a8816 | ||
![]() |
097f13db42 | ||
![]() |
15585c2007 | ||
![]() |
ca0a2c3215 | ||
![]() |
d2820bdf2a | ||
![]() |
947e5eb9a3 | ||
![]() |
5901e34f6d | ||
![]() |
1c942d2f9d | ||
![]() |
b419bf8f35 | ||
![]() |
7edfa1284a | ||
![]() |
32e4006b2c | ||
![]() |
e773a1dbfc | ||
![]() |
3942214ba1 | ||
![]() |
461fc36401 | ||
![]() |
fa9198174e | ||
![]() |
96460d92d3 | ||
![]() |
e0d376c15b | ||
![]() |
46e0301dd0 | ||
![]() |
14aa26c651 | ||
![]() |
ab59dfaa86 | ||
![]() |
4841f46d95 | ||
![]() |
faccc6a6f2 | ||
![]() |
f336e76a86 | ||
![]() |
811604d15a | ||
![]() |
dd005941e9 | ||
![]() |
311381332f | ||
![]() |
50173c4127 | ||
![]() |
e030adbb72 | ||
![]() |
3762ccf639 | ||
![]() |
d2758400e0 | ||
![]() |
1551ec440e | ||
![]() |
bd493e39e4 | ||
![]() |
2fb663b63f | ||
![]() |
3878f35107 | ||
![]() |
e055c0edbf | ||
![]() |
a02afc4c68 | ||
![]() |
30130df6fb | ||
![]() |
400cd2e22c | ||
![]() |
dc25a0e87f | ||
![]() |
d21abd97a7 | ||
![]() |
bd38eaf924 | ||
![]() |
360e98b3ee | ||
![]() |
e03908da83 | ||
![]() |
64e7afea7c | ||
![]() |
13077f7ce8 | ||
![]() |
ef68f4e7f6 | ||
![]() |
eaeef23bc6 | ||
![]() |
5a3b12a257 | ||
![]() |
794b74a90c | ||
![]() |
ac4a500fff | ||
![]() |
13a5939550 | ||
![]() |
e560f0f08b | ||
![]() |
3894720a93 | ||
![]() |
2a89b95510 | ||
![]() |
e8e4bf07df | ||
![]() |
4d3b98f78b | ||
![]() |
42ee73b046 | ||
![]() |
0a5df21c1b | ||
![]() |
b0ea0c262b | ||
![]() |
ffa9c45692 | ||
![]() |
15f6f16d22 | ||
![]() |
c3240b6767 | ||
![]() |
2f7fe905b8 | ||
![]() |
0de1d42d73 | ||
![]() |
c4c8931c73 | ||
![]() |
1c9eb654ce | ||
![]() |
ee5b55cfbb | ||
![]() |
7e121526ca | ||
![]() |
79dfec1feb | ||
![]() |
d30a278b97 | ||
![]() |
5ae9e80987 | ||
![]() |
245fb1b3ab | ||
![]() |
bedf8a48a2 | ||
![]() |
3c77c9e2d8 | ||
![]() |
92ba2c3111 | ||
![]() |
3ea754bba2 | ||
![]() |
a3429fb687 | ||
![]() |
476a107c46 | ||
![]() |
f98d28bee2 | ||
![]() |
df5ee8732a | ||
![]() |
bd500f5a90 | ||
![]() |
536518f4bb | ||
![]() |
7e11936fee | ||
![]() |
b0da0508e1 | ||
![]() |
48cc5a27f6 | ||
![]() |
d470513f84 | ||
![]() |
baaeb4317f | ||
![]() |
40156c5e74 | ||
![]() |
da0a47b487 | ||
![]() |
ee12b0b372 | ||
![]() |
31f7ec3a17 | ||
![]() |
3ec7617b14 | ||
![]() |
69e9eebd8e | ||
![]() |
268af00ce8 | ||
![]() |
ad748e4ed5 | ||
![]() |
f933736f1f | ||
![]() |
89861de544 | ||
![]() |
d503fff76b | ||
![]() |
e8dc9e3b20 | ||
![]() |
e40cdfca95 | ||
![]() |
e357fca9c0 | ||
![]() |
6b4f022157 | ||
![]() |
29c276d061 | ||
![]() |
b28eec1071 | ||
![]() |
c3c774579d | ||
![]() |
193977d6d2 | ||
![]() |
931bb10edb | ||
![]() |
408b9c151c | ||
![]() |
bd96a63087 | ||
![]() |
dd5eb3b96f | ||
![]() |
cd46428b81 | ||
![]() |
29926f057e | ||
![]() |
5444896b76 | ||
![]() |
4adb7295bd | ||
![]() |
2edd127d68 | ||
![]() |
eb45ed8b9a | ||
![]() |
3a81c76741 | ||
![]() |
14e2a6236a | ||
![]() |
1ea065dec6 | ||
![]() |
db42bbef62 | ||
![]() |
bd0b610a90 | ||
![]() |
f46213ce6e | ||
![]() |
0909e1d5fe | ||
![]() |
95c15b2edb | ||
![]() |
e48115336d | ||
![]() |
daa957fa7b | ||
![]() |
3d5a997633 | ||
![]() |
ca15d0316d | ||
![]() |
96a9c28e44 | ||
![]() |
006f8b1900 | ||
![]() |
d576b3ede8 | ||
![]() |
195664ee09 | ||
![]() |
bf85daf354 | ||
![]() |
d3192734a1 | ||
![]() |
16f5ecdd0b | ||
![]() |
b17fa8e9ae | ||
![]() |
88bcd797e9 | ||
![]() |
577d605b87 | ||
![]() |
a5e29a7f14 | ||
![]() |
7f27526c7c | ||
![]() |
f3adb4e876 | ||
![]() |
c571a65a32 | ||
![]() |
55f38231a9 | ||
![]() |
6a2e37d147 | ||
![]() |
f6ec9634ce | ||
![]() |
eedb030619 | ||
![]() |
c828c2cace | ||
![]() |
b4188893a0 | ||
![]() |
8ea26ec148 | ||
![]() |
bf73e430f8 | ||
![]() |
343f6a572d | ||
![]() |
66134e95ce | ||
![]() |
1ffb03ccd5 | ||
![]() |
998bf76e72 | ||
![]() |
47553bc020 | ||
![]() |
c84b2410b9 | ||
![]() |
fbbac73e94 | ||
![]() |
c2f0f9b9a3 | ||
![]() |
4d02ae092b | ||
![]() |
7db2849404 | ||
![]() |
d5a3b55e98 | ||
![]() |
79206fbfee | ||
![]() |
2089e29664 | ||
![]() |
38c5425d0c | ||
![]() |
b0d45e80de | ||
![]() |
2ac5beee0c | ||
![]() |
c99c73fa96 | ||
![]() |
f796228d06 | ||
![]() |
ca3836b3c7 | ||
![]() |
918f8ec989 | ||
![]() |
f44d9682ee | ||
![]() |
ce6800164d | ||
![]() |
4afb2de178 | ||
![]() |
9865fd3663 | ||
![]() |
5501527974 | ||
![]() |
3e4826bafc | ||
![]() |
d914f9f5be | ||
![]() |
ab8c90dd41 | ||
![]() |
8882841a8d | ||
![]() |
3d357950f9 | ||
![]() |
ef5d2651c0 | ||
![]() |
1dd4bb9b7a | ||
![]() |
682e6f6b03 | ||
![]() |
b468c28ae8 | ||
![]() |
9fc9452a08 | ||
![]() |
3b176b7152 | ||
![]() |
f1d1a9b66b | ||
![]() |
c0d2f491a8 | ||
![]() |
aeecce2ea8 | ||
![]() |
647ee149ff | ||
![]() |
22313e148a | ||
![]() |
31f2d7ce2e | ||
![]() |
4a0efec6c2 | ||
![]() |
5f555df5c1 | ||
![]() |
53a611a1b9 | ||
![]() |
14511b23af | ||
![]() |
fa2371274d | ||
![]() |
0aea8dfa0f | ||
![]() |
81ca18098d | ||
![]() |
b6e44a022f | ||
![]() |
1a8eee2477 | ||
![]() |
d0ef92da03 | ||
![]() |
9c98cb3b3b | ||
![]() |
3681542056 | ||
![]() |
7c0bf135d7 | ||
![]() |
9a31dd046c | ||
![]() |
74401a1f9c | ||
![]() |
cedc6ea3e9 |
@@ -329,7 +329,6 @@ gd::String EventsCodeGenerator::GenerateConditionCode(
|
||||
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())) {
|
||||
@@ -357,6 +356,11 @@ gd::String EventsCodeGenerator::GenerateConditionCode(
|
||||
}
|
||||
}
|
||||
}
|
||||
bool isAnyBehaviorMissing =
|
||||
gd::EventsCodeGenerator::CheckBehaviorParameters(condition, instrInfos);
|
||||
if (isAnyBehaviorMissing) {
|
||||
return "/* Missing behavior - skipped. */";
|
||||
}
|
||||
|
||||
if (instrInfos.IsObjectInstruction()) {
|
||||
gd::String objectName = condition.GetParameter(0).GetPlainString();
|
||||
@@ -488,14 +492,16 @@ gd::String EventsCodeGenerator::GenerateConditionsListCode(
|
||||
return outputCode;
|
||||
}
|
||||
|
||||
void EventsCodeGenerator::CheckBehaviorParameters(
|
||||
bool EventsCodeGenerator::CheckBehaviorParameters(
|
||||
const gd::Instruction &instruction,
|
||||
const gd::InstructionMetadata &instrInfos) {
|
||||
gd::ParameterMetadataTools::IterateOverParameters(
|
||||
bool isAnyBehaviorMissing = false;
|
||||
gd::ParameterMetadataTools::IterateOverParametersWithIndex(
|
||||
instruction.GetParameters(), instrInfos.parameters,
|
||||
[this](const gd::ParameterMetadata ¶meterMetadata,
|
||||
const gd::Expression ¶meterValue,
|
||||
const gd::String &lastObjectName) {
|
||||
[this, &isAnyBehaviorMissing,
|
||||
&instrInfos](const gd::ParameterMetadata ¶meterMetadata,
|
||||
const gd::Expression ¶meterValue, 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 =
|
||||
@@ -506,13 +512,25 @@ void EventsCodeGenerator::CheckBehaviorParameters(
|
||||
|
||||
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);
|
||||
if (diagnosticReport) diagnosticReport->Add(projectDiagnostic);
|
||||
if (diagnosticReport)
|
||||
diagnosticReport->Add(projectDiagnostic);
|
||||
}
|
||||
}
|
||||
});
|
||||
return isAnyBehaviorMissing;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -552,7 +570,6 @@ gd::String EventsCodeGenerator::GenerateActionCode(
|
||||
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())) {
|
||||
@@ -579,6 +596,11 @@ gd::String EventsCodeGenerator::GenerateActionCode(
|
||||
}
|
||||
}
|
||||
}
|
||||
bool isAnyBehaviorMissing =
|
||||
gd::EventsCodeGenerator::CheckBehaviorParameters(action, instrInfos);
|
||||
if (isAnyBehaviorMissing) {
|
||||
return "/* Missing behavior - skipped. */";
|
||||
}
|
||||
|
||||
// Call free function first if available
|
||||
if (instrInfos.IsObjectInstruction()) {
|
||||
@@ -769,7 +791,7 @@ gd::String EventsCodeGenerator::GenerateActionsListCode(
|
||||
} else {
|
||||
outputCode += actionCode;
|
||||
}
|
||||
outputCode += "}";
|
||||
outputCode += "}\n";
|
||||
}
|
||||
|
||||
return outputCode;
|
||||
|
@@ -837,7 +837,7 @@ protected:
|
||||
virtual gd::String GenerateGetBehaviorNameCode(
|
||||
const gd::String& behaviorName);
|
||||
|
||||
void CheckBehaviorParameters(
|
||||
bool CheckBehaviorParameters(
|
||||
const gd::Instruction &instruction,
|
||||
const gd::InstructionMetadata &instrInfos);
|
||||
|
||||
|
@@ -52,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")
|
||||
@@ -66,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",
|
||||
|
@@ -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.
|
||||
|
@@ -813,6 +813,13 @@ gd::String PlatformExtension::GetObjectFullType(const gd::String& extensionName,
|
||||
return extensionName + separator + objectName;
|
||||
}
|
||||
|
||||
gd::String PlatformExtension::GetVariantFullType(const gd::String& extensionName,
|
||||
const gd::String& objectName,
|
||||
const gd::String& variantName) {
|
||||
const auto& separator = GetNamespaceSeparator();
|
||||
return extensionName + separator + objectName + separator + variantName;
|
||||
}
|
||||
|
||||
gd::String PlatformExtension::GetExtensionFromFullObjectType(
|
||||
const gd::String& type) {
|
||||
const auto separatorIndex =
|
||||
|
@@ -663,6 +663,10 @@ class GD_CORE_API PlatformExtension {
|
||||
static gd::String GetObjectFullType(const gd::String& extensionName,
|
||||
const gd::String& objectName);
|
||||
|
||||
static gd::String GetVariantFullType(const gd::String& extensionName,
|
||||
const gd::String& objectName,
|
||||
const gd::String& variantName);
|
||||
|
||||
static gd::String GetExtensionFromFullObjectType(const gd::String& type);
|
||||
|
||||
static gd::String GetObjectNameFromFullObjectType(const gd::String& type);
|
||||
|
@@ -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.
|
||||
|
23
Core/GDCore/IDE/Events/UsedObjectTypeFinder.cpp
Normal file
23
Core/GDCore/IDE/Events/UsedObjectTypeFinder.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
#include "UsedObjectTypeFinder.h"
|
||||
|
||||
#include "GDCore/Events/Instruction.h"
|
||||
#include "GDCore/IDE/ProjectBrowserHelper.h"
|
||||
#include "GDCore/Project/Object.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
bool UsedObjectTypeFinder::ScanProject(gd::Project &project,
|
||||
const gd::String &objectType) {
|
||||
UsedObjectTypeFinder worker(project, objectType);
|
||||
gd::ProjectBrowserHelper::ExposeProjectObjects(project, worker);
|
||||
return worker.hasFoundObjectType;
|
||||
};
|
||||
|
||||
void UsedObjectTypeFinder::DoVisitObject(gd::Object &object) {
|
||||
if (!hasFoundObjectType && object.GetType() == objectType) {
|
||||
hasFoundObjectType = true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace gd
|
39
Core/GDCore/IDE/Events/UsedObjectTypeFinder.h
Normal file
39
Core/GDCore/IDE/Events/UsedObjectTypeFinder.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <set>
|
||||
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
|
||||
#include "GDCore/Extensions/Metadata/SourceFileMetadata.h"
|
||||
#include "GDCore/Extensions/PlatformExtension.h"
|
||||
#include "GDCore/IDE/Events/ArbitraryEventsWorker.h"
|
||||
#include "GDCore/IDE/Project/ArbitraryObjectsWorker.h"
|
||||
#include "GDCore/String.h"
|
||||
|
||||
namespace gd {
|
||||
class Project;
|
||||
class Object;
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
|
||||
class GD_CORE_API UsedObjectTypeFinder : public ArbitraryObjectsWorker {
|
||||
public:
|
||||
static bool ScanProject(gd::Project &project, const gd::String &objectType);
|
||||
|
||||
private:
|
||||
UsedObjectTypeFinder(gd::Project &project_, const gd::String &objectType_)
|
||||
: project(project_), objectType(objectType_){};
|
||||
gd::Project &project;
|
||||
const gd::String &objectType;
|
||||
bool hasFoundObjectType = false;
|
||||
|
||||
// Object Visitor
|
||||
void DoVisitObject(gd::Object &object) override;
|
||||
};
|
||||
|
||||
}; // namespace gd
|
@@ -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;
|
||||
|
@@ -7,7 +7,6 @@
|
||||
#include <map>
|
||||
#include "GDCore/CommonTools.h"
|
||||
#include "GDCore/IDE/AbstractFileSystem.h"
|
||||
#include "GDCore/IDE/Project/ResourcesAbsolutePathChecker.h"
|
||||
#include "GDCore/IDE/Project/ResourcesMergingHelper.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Tools/Localization.h"
|
||||
@@ -26,42 +25,37 @@ bool ProjectResourcesCopier::CopyAllResourcesTo(
|
||||
bool preserveAbsoluteFilenames,
|
||||
bool preserveDirectoryStructure) {
|
||||
if (updateOriginalProject) {
|
||||
gd::ProjectResourcesCopier::CopyAllResourcesTo(
|
||||
originalProject, originalProject, fs, destinationDirectory,
|
||||
preserveAbsoluteFilenames, preserveDirectoryStructure);
|
||||
gd::ProjectResourcesCopier::AdaptFilePathsAndCopyAllResourcesTo(
|
||||
originalProject, fs, destinationDirectory, preserveAbsoluteFilenames,
|
||||
preserveDirectoryStructure);
|
||||
} else {
|
||||
gd::Project clonedProject = originalProject;
|
||||
gd::ProjectResourcesCopier::CopyAllResourcesTo(
|
||||
originalProject, clonedProject, fs, destinationDirectory,
|
||||
preserveAbsoluteFilenames, preserveDirectoryStructure);
|
||||
gd::ProjectResourcesCopier::AdaptFilePathsAndCopyAllResourcesTo(
|
||||
clonedProject, fs, destinationDirectory, preserveAbsoluteFilenames,
|
||||
preserveDirectoryStructure);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ProjectResourcesCopier::CopyAllResourcesTo(
|
||||
gd::Project& originalProject,
|
||||
gd::Project& clonedProject,
|
||||
bool ProjectResourcesCopier::AdaptFilePathsAndCopyAllResourcesTo(
|
||||
gd::Project& project,
|
||||
AbstractFileSystem& fs,
|
||||
gd::String destinationDirectory,
|
||||
bool preserveAbsoluteFilenames,
|
||||
bool preserveDirectoryStructure) {
|
||||
|
||||
// Check if there are some resources with absolute filenames
|
||||
gd::ResourcesAbsolutePathChecker absolutePathChecker(originalProject.GetResourcesManager(), fs);
|
||||
gd::ResourceExposer::ExposeWholeProjectResources(originalProject, absolutePathChecker);
|
||||
|
||||
auto projectDirectory = fs.DirNameFrom(originalProject.GetProjectFile());
|
||||
auto projectDirectory = fs.DirNameFrom(project.GetProjectFile());
|
||||
std::cout << "Copying all resources from " << projectDirectory << " to "
|
||||
<< destinationDirectory << "..." << std::endl;
|
||||
|
||||
// Get the resources to be copied
|
||||
gd::ResourcesMergingHelper resourcesMergingHelper(
|
||||
clonedProject.GetResourcesManager(), fs);
|
||||
project.GetResourcesManager(), fs);
|
||||
resourcesMergingHelper.SetBaseDirectory(projectDirectory);
|
||||
resourcesMergingHelper.PreserveDirectoriesStructure(
|
||||
preserveDirectoryStructure);
|
||||
resourcesMergingHelper.PreserveAbsoluteFilenames(preserveAbsoluteFilenames);
|
||||
gd::ResourceExposer::ExposeWholeProjectResources(clonedProject,
|
||||
gd::ResourceExposer::ExposeWholeProjectResources(project,
|
||||
resourcesMergingHelper);
|
||||
|
||||
// Copy resources
|
||||
|
@@ -50,12 +50,10 @@ class GD_CORE_API ProjectResourcesCopier {
|
||||
bool preserveDirectoryStructure = true);
|
||||
|
||||
private:
|
||||
static bool CopyAllResourcesTo(gd::Project& originalProject,
|
||||
gd::Project& clonedProject,
|
||||
gd::AbstractFileSystem& fs,
|
||||
gd::String destinationDirectory,
|
||||
bool preserveAbsoluteFilenames = true,
|
||||
bool preserveDirectoryStructure = true);
|
||||
static bool AdaptFilePathsAndCopyAllResourcesTo(
|
||||
gd::Project &project, gd::AbstractFileSystem &fs,
|
||||
gd::String destinationDirectory, bool preserveAbsoluteFilenames = true,
|
||||
bool preserveDirectoryStructure = true);
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -1,17 +0,0 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
#include "ResourcesAbsolutePathChecker.h"
|
||||
#include "GDCore/IDE/AbstractFileSystem.h"
|
||||
#include "GDCore/String.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
void ResourcesAbsolutePathChecker::ExposeFile(gd::String& resourceFilename) {
|
||||
if (fs.IsAbsolute(resourceFilename)) hasAbsoluteFilenames = true;
|
||||
}
|
||||
|
||||
} // namespace gd
|
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "GDCore/IDE/AbstractFileSystem.h"
|
||||
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
|
||||
#include "GDCore/String.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
/**
|
||||
* \brief Helper used to check if a project has at least a resource with an
|
||||
* absolute filename.
|
||||
*
|
||||
* \see ArbitraryResourceWorker
|
||||
*
|
||||
* \ingroup IDE
|
||||
*/
|
||||
class GD_CORE_API ResourcesAbsolutePathChecker
|
||||
: public ArbitraryResourceWorker {
|
||||
public:
|
||||
ResourcesAbsolutePathChecker(gd::ResourcesManager &resourcesManager,
|
||||
AbstractFileSystem &fileSystem)
|
||||
: ArbitraryResourceWorker(resourcesManager), hasAbsoluteFilenames(false),
|
||||
fs(fileSystem){};
|
||||
virtual ~ResourcesAbsolutePathChecker(){};
|
||||
|
||||
/**
|
||||
* Return true if there is at least a resource with an absolute filename.
|
||||
*/
|
||||
bool HasResourceWithAbsoluteFilenames() const {
|
||||
return hasAbsoluteFilenames;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if there is a resource with an absolute path
|
||||
*/
|
||||
virtual void ExposeFile(gd::String& resource);
|
||||
|
||||
private:
|
||||
bool hasAbsoluteFilenames;
|
||||
AbstractFileSystem& fs;
|
||||
};
|
||||
|
||||
} // namespace gd
|
@@ -22,6 +22,14 @@ void ResourcesMergingHelper::ExposeFile(gd::String& resourceFilename) {
|
||||
resourceFullFilename = gd::AbstractFileSystem::NormalizeSeparator(
|
||||
resourceFullFilename); // Protect against \ on Linux.
|
||||
|
||||
if (shouldUseOriginalAbsoluteFilenames) {
|
||||
// There is no need to fill `newFilenames` and `oldFilenames` since the file
|
||||
// location stays the same.
|
||||
fs.MakeAbsolute(resourceFullFilename, baseDirectory);
|
||||
resourceFilename = resourceFullFilename;
|
||||
return;
|
||||
}
|
||||
|
||||
// In the case of absolute filenames that we don't want to preserve, or
|
||||
// in the case of copying files without preserving relative folders, the new
|
||||
// names will be generated from the filename alone (with collision protection).
|
||||
|
@@ -3,8 +3,7 @@
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#ifndef RESOURCESMERGINGHELPER_H
|
||||
#define RESOURCESMERGINGHELPER_H
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
@@ -58,6 +57,15 @@ public:
|
||||
preserveAbsoluteFilenames = preserveAbsoluteFilenames_;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Set if the absolute filenames of original files must be used for
|
||||
* any resource.
|
||||
*/
|
||||
void SetShouldUseOriginalAbsoluteFilenames(
|
||||
bool shouldUseOriginalAbsoluteFilenames_ = true) {
|
||||
shouldUseOriginalAbsoluteFilenames = shouldUseOriginalAbsoluteFilenames_;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Return a map containing the resources old absolute filename as key,
|
||||
* and the resources new filenames as value. The new filenames are relative to
|
||||
@@ -93,10 +101,13 @@ public:
|
||||
///< absolute (C:\MyFile.png will not be
|
||||
///< transformed into a relative filename
|
||||
///< (MyFile.png).
|
||||
/**
|
||||
* Set to true if the absolute filenames of original files must be used for
|
||||
* any resource.
|
||||
*/
|
||||
bool shouldUseOriginalAbsoluteFilenames = false;
|
||||
gd::AbstractFileSystem&
|
||||
fs; ///< The gd::AbstractFileSystem used to manipulate files.
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
||||
#endif // RESOURCESMERGINGHELPER_H
|
||||
|
@@ -6,6 +6,7 @@
|
||||
#include "SceneResourcesFinder.h"
|
||||
|
||||
#include "GDCore/IDE/ResourceExposer.h"
|
||||
#include "GDCore/Project/EventsBasedObjectVariant.h"
|
||||
#include "GDCore/Project/Layout.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
@@ -27,6 +28,14 @@ std::set<gd::String> SceneResourcesFinder::FindSceneResources(gd::Project &proje
|
||||
return resourceWorker.resourceNames;
|
||||
}
|
||||
|
||||
std::set<gd::String> SceneResourcesFinder::FindEventsBasedObjectVariantResources(gd::Project &project,
|
||||
gd::EventsBasedObjectVariant &variant) {
|
||||
gd::SceneResourcesFinder resourceWorker(project.GetResourcesManager());
|
||||
|
||||
gd::ResourceExposer::ExposeEventsBasedObjectVariantResources(project, variant, resourceWorker);
|
||||
return resourceWorker.resourceNames;
|
||||
}
|
||||
|
||||
void SceneResourcesFinder::AddUsedResource(gd::String &resourceName) {
|
||||
if (resourceName.empty()) {
|
||||
return;
|
||||
|
@@ -15,6 +15,7 @@ namespace gd {
|
||||
class Project;
|
||||
class Layout;
|
||||
class SerializerElement;
|
||||
class EventsBasedObjectVariant;
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
@@ -27,7 +28,7 @@ namespace gd {
|
||||
class SceneResourcesFinder : private gd::ArbitraryResourceWorker {
|
||||
public:
|
||||
/**
|
||||
* @brief Find resource usages in a given scenes.
|
||||
* @brief Find resource usages in a given scene.
|
||||
*
|
||||
* It doesn't include resources used globally.
|
||||
*/
|
||||
@@ -41,6 +42,13 @@ public:
|
||||
*/
|
||||
static std::set<gd::String> FindProjectResources(gd::Project &project);
|
||||
|
||||
/**
|
||||
* @brief Find resource usages in a given events-based object variant.
|
||||
*/
|
||||
static std::set<gd::String>
|
||||
FindEventsBasedObjectVariantResources(gd::Project &project,
|
||||
gd::EventsBasedObjectVariant &variant);
|
||||
|
||||
virtual ~SceneResourcesFinder(){};
|
||||
|
||||
private:
|
||||
|
@@ -332,6 +332,12 @@ void ProjectBrowserHelper::ExposeLayoutObjects(gd::Layout &layout,
|
||||
worker.Launch(layout.GetObjects());
|
||||
}
|
||||
|
||||
void ProjectBrowserHelper::ExposeEventsBasedObjectVariantObjects(
|
||||
gd::EventsBasedObjectVariant &eventsBasedObjectVariant,
|
||||
gd::ArbitraryObjectsWorker &worker) {
|
||||
worker.Launch(eventsBasedObjectVariant.GetObjects());
|
||||
}
|
||||
|
||||
void ProjectBrowserHelper::ExposeProjectFunctions(
|
||||
gd::Project &project, gd::ArbitraryEventsFunctionsWorker &worker) {
|
||||
|
||||
|
@@ -13,6 +13,7 @@ class EventsFunctionsExtension;
|
||||
class EventsFunction;
|
||||
class EventsBasedBehavior;
|
||||
class EventsBasedObject;
|
||||
class EventsBasedObjectVariant;
|
||||
class ArbitraryEventsWorker;
|
||||
class ArbitraryEventsWorkerWithContext;
|
||||
class ArbitraryEventsFunctionsWorker;
|
||||
@@ -207,6 +208,17 @@ public:
|
||||
static void ExposeLayoutObjects(gd::Layout &layout,
|
||||
gd::ArbitraryObjectsWorker &worker);
|
||||
|
||||
/**
|
||||
* \brief Call the specified worker on all ObjectContainers of the
|
||||
* events-based object variant.
|
||||
*
|
||||
* This should be the preferred way to traverse all the objects of an
|
||||
* events-based object variant.
|
||||
*/
|
||||
static void ExposeEventsBasedObjectVariantObjects(
|
||||
gd::EventsBasedObjectVariant &eventsBasedObjectVariant,
|
||||
gd::ArbitraryObjectsWorker &worker);
|
||||
|
||||
/**
|
||||
* \brief Call the specified worker on all FunctionsContainers of the project
|
||||
* (global, layouts...)
|
||||
|
@@ -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,34 @@ 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::ExposeEventsBasedObjectVariantResources(
|
||||
gd::Project &project,
|
||||
gd::EventsBasedObjectVariant &eventsBasedObjectVariant,
|
||||
gd::ArbitraryResourceWorker &worker) {
|
||||
// Expose object configuration resources
|
||||
auto objectWorker = gd::GetResourceWorkerOnObjects(project, worker);
|
||||
gd::ProjectBrowserHelper::ExposeEventsBasedObjectVariantObjects(
|
||||
eventsBasedObjectVariant, objectWorker);
|
||||
|
||||
// Expose layer effect resources
|
||||
auto &layers = eventsBasedObjectVariant.GetLayers();
|
||||
for (std::size_t layerIndex = 0; layerIndex < layers.GetLayersCount();
|
||||
layerIndex++) {
|
||||
auto &layer = layers.GetLayer(layerIndex);
|
||||
|
||||
auto &effects = layer.GetEffects();
|
||||
for (size_t effectIndex = 0; effectIndex < effects.GetEffectsCount();
|
||||
effectIndex++) {
|
||||
auto &effect = effects.GetEffect(effectIndex);
|
||||
gd::ResourceExposer::ExposeEffectResources(project.GetCurrentPlatform(),
|
||||
effect, worker);
|
||||
}
|
||||
}
|
||||
// We don't check the events because it would cost too much to do it for every
|
||||
// variant. Resource usage in events-based object events and their
|
||||
// dependencies should be rare.
|
||||
}
|
||||
|
||||
void ResourceExposer::ExposeEffectResources(
|
||||
|
@@ -9,10 +9,11 @@ namespace gd {
|
||||
class Platform;
|
||||
class Project;
|
||||
class ArbitraryResourceWorker;
|
||||
class EventsBasedObjectVariant;
|
||||
class EventsFunctionsExtension;
|
||||
class Effect;
|
||||
class Layout;
|
||||
} // namespace gd
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
|
||||
@@ -20,7 +21,7 @@ namespace gd {
|
||||
* \brief
|
||||
*/
|
||||
class GD_CORE_API ResourceExposer {
|
||||
public:
|
||||
public:
|
||||
/**
|
||||
* \brief Called ( e.g. during compilation ) so as to inventory internal
|
||||
* resources, sometimes update their filename or any other work or resources.
|
||||
@@ -50,6 +51,14 @@ class GD_CORE_API ResourceExposer {
|
||||
gd::Layout &layout,
|
||||
gd::ArbitraryResourceWorker &worker);
|
||||
|
||||
/**
|
||||
* @brief Expose the resources used in a given events-based object variant.
|
||||
*/
|
||||
static void ExposeEventsBasedObjectVariantResources(
|
||||
gd::Project &project,
|
||||
gd::EventsBasedObjectVariant &eventsBasedObjectVariant,
|
||||
gd::ArbitraryResourceWorker &worker);
|
||||
|
||||
/**
|
||||
* @brief Expose the resources used in a given effect.
|
||||
*/
|
||||
|
@@ -37,6 +37,7 @@ void EventsBasedObjectVariant::SerializeTo(SerializerElement &element) const {
|
||||
|
||||
layers.SerializeLayersTo(element.AddChild("layers"));
|
||||
initialInstances.SerializeTo(element.AddChild("instances"));
|
||||
editorSettings.SerializeTo(element.AddChild("editionSettings"));
|
||||
}
|
||||
|
||||
void EventsBasedObjectVariant::UnserializeFrom(
|
||||
@@ -66,6 +67,7 @@ void EventsBasedObjectVariant::UnserializeFrom(
|
||||
layers.Reset();
|
||||
}
|
||||
initialInstances.UnserializeFrom(element.GetChild("instances"));
|
||||
editorSettings.UnserializeFrom(element.GetChild("editionSettings"));
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "GDCore/IDE/Dialogs/LayoutEditorCanvas/EditorSettings.h"
|
||||
#include "GDCore/Project/InitialInstancesContainer.h"
|
||||
#include "GDCore/Project/LayersContainer.h"
|
||||
#include "GDCore/Project/ObjectsContainer.h"
|
||||
@@ -199,6 +200,19 @@ public:
|
||||
const gd::String &GetAssetStoreOriginalName() const {
|
||||
return assetStoreOriginalName;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* \brief Get the user settings for the IDE.
|
||||
*/
|
||||
const gd::EditorSettings& GetAssociatedEditorSettings() const {
|
||||
return editorSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the user settings for the IDE.
|
||||
*/
|
||||
gd::EditorSettings& GetAssociatedEditorSettings() { return editorSettings; }
|
||||
|
||||
void SerializeTo(SerializerElement &element) const;
|
||||
|
||||
@@ -224,6 +238,7 @@ private:
|
||||
* store.
|
||||
*/
|
||||
gd::String assetStoreOriginalName;
|
||||
gd::EditorSettings editorSettings;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -235,9 +235,6 @@ class GD_CORE_API EventsFunction {
|
||||
|
||||
/**
|
||||
* \brief Return the parameters of the function that are used in the events.
|
||||
*
|
||||
* \warning `ActionWithOperator` function are muted by this function. Make sure
|
||||
* to use the right functions container to avoid strange side effects.
|
||||
*
|
||||
* \note During code/extension generation, new parameters are added
|
||||
* to the generated function, like "runtimeScene" and "eventsFunctionContext".
|
||||
|
@@ -60,6 +60,18 @@ void InitialInstance::UnserializeFrom(const SerializerElement& element) {
|
||||
} else {
|
||||
SetHasCustomDepth(false);
|
||||
}
|
||||
if (element.HasChild("defaultWidth") ||
|
||||
element.HasAttribute("defaultWidth")) {
|
||||
defaultWidth = element.GetDoubleAttribute("defaultWidth");
|
||||
}
|
||||
if (element.HasChild("defaultHeight") ||
|
||||
element.HasAttribute("defaultHeight")) {
|
||||
defaultHeight = element.GetDoubleAttribute("defaultHeight");
|
||||
}
|
||||
if (element.HasChild("defaultDepth") ||
|
||||
element.HasAttribute("defaultDepth")) {
|
||||
defaultDepth = element.GetDoubleAttribute("defaultDepth");
|
||||
}
|
||||
SetZOrder(element.GetIntAttribute("zOrder", 0, "plan"));
|
||||
SetOpacity(element.GetIntAttribute("opacity", 255));
|
||||
SetLayer(element.GetStringAttribute("layer"));
|
||||
@@ -74,45 +86,51 @@ void InitialInstance::UnserializeFrom(const SerializerElement& element) {
|
||||
if (persistentUuid.empty()) ResetPersistentUuid();
|
||||
|
||||
numberProperties.clear();
|
||||
const SerializerElement& numberPropertiesElement =
|
||||
element.GetChild("numberProperties", 0, "floatInfos");
|
||||
numberPropertiesElement.ConsiderAsArrayOf("property", "Info");
|
||||
for (std::size_t j = 0; j < numberPropertiesElement.GetChildrenCount(); ++j) {
|
||||
gd::String name =
|
||||
numberPropertiesElement.GetChild(j).GetStringAttribute("name");
|
||||
double value =
|
||||
numberPropertiesElement.GetChild(j).GetDoubleAttribute("value");
|
||||
if (element.HasChild("numberProperties", "floatInfos")) {
|
||||
const SerializerElement& numberPropertiesElement =
|
||||
element.GetChild("numberProperties", 0, "floatInfos");
|
||||
numberPropertiesElement.ConsiderAsArrayOf("property", "Info");
|
||||
for (std::size_t j = 0; j < numberPropertiesElement.GetChildrenCount(); ++j) {
|
||||
gd::String name =
|
||||
numberPropertiesElement.GetChild(j).GetStringAttribute("name");
|
||||
double value =
|
||||
numberPropertiesElement.GetChild(j).GetDoubleAttribute("value");
|
||||
|
||||
// Compatibility with GD <= 5.1.164
|
||||
if (name == "z") {
|
||||
SetZ(value);
|
||||
} else if (name == "rotationX") {
|
||||
SetRotationX(value);
|
||||
} else if (name == "rotationY") {
|
||||
SetRotationY(value);
|
||||
} else if (name == "depth") {
|
||||
SetHasCustomDepth(true);
|
||||
SetCustomDepth(value);
|
||||
}
|
||||
// end of compatibility code
|
||||
else {
|
||||
numberProperties[name] = value;
|
||||
// Compatibility with GD <= 5.1.164
|
||||
if (name == "z") {
|
||||
SetZ(value);
|
||||
} else if (name == "rotationX") {
|
||||
SetRotationX(value);
|
||||
} else if (name == "rotationY") {
|
||||
SetRotationY(value);
|
||||
} else if (name == "depth") {
|
||||
SetHasCustomDepth(true);
|
||||
SetCustomDepth(value);
|
||||
}
|
||||
// end of compatibility code
|
||||
else {
|
||||
numberProperties[name] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stringProperties.clear();
|
||||
const SerializerElement& stringPropElement =
|
||||
element.GetChild("stringProperties", 0, "stringInfos");
|
||||
stringPropElement.ConsiderAsArrayOf("property", "Info");
|
||||
for (std::size_t j = 0; j < stringPropElement.GetChildrenCount(); ++j) {
|
||||
gd::String name = stringPropElement.GetChild(j).GetStringAttribute("name");
|
||||
gd::String value =
|
||||
stringPropElement.GetChild(j).GetStringAttribute("value");
|
||||
stringProperties[name] = value;
|
||||
if (element.HasChild("stringProperties", "stringInfos")) {
|
||||
const SerializerElement& stringPropElement =
|
||||
element.GetChild("stringProperties", 0, "stringInfos");
|
||||
stringPropElement.ConsiderAsArrayOf("property", "Info");
|
||||
for (std::size_t j = 0; j < stringPropElement.GetChildrenCount(); ++j) {
|
||||
gd::String name = stringPropElement.GetChild(j).GetStringAttribute("name");
|
||||
gd::String value =
|
||||
stringPropElement.GetChild(j).GetStringAttribute("value");
|
||||
stringProperties[name] = value;
|
||||
}
|
||||
}
|
||||
|
||||
GetVariables().UnserializeFrom(
|
||||
element.GetChild("initialVariables", 0, "InitialVariables"));
|
||||
if (element.HasChild("initialVariables", "InitialVariables")) {
|
||||
GetVariables().UnserializeFrom(
|
||||
element.GetChild("initialVariables", 0, "InitialVariables"));
|
||||
}
|
||||
}
|
||||
|
||||
void InitialInstance::SerializeTo(SerializerElement& element) const {
|
||||
@@ -133,6 +151,8 @@ void InitialInstance::SerializeTo(SerializerElement& element) const {
|
||||
element.SetAttribute("width", GetCustomWidth());
|
||||
element.SetAttribute("height", GetCustomHeight());
|
||||
if (HasCustomDepth()) element.SetAttribute("depth", GetCustomDepth());
|
||||
// defaultWidth, defaultHeight and defaultDepth are not serialized
|
||||
// because they are evaluated by InGameEditor.
|
||||
if (IsLocked()) element.SetAttribute("locked", IsLocked());
|
||||
if (IsSealed()) element.SetAttribute("sealed", IsSealed());
|
||||
if (ShouldKeepRatio()) element.SetAttribute("keepRatio", ShouldKeepRatio());
|
||||
|
@@ -219,6 +219,13 @@ class GD_CORE_API InitialInstance {
|
||||
double GetCustomDepth() const { return depth; }
|
||||
void SetCustomDepth(double depth_) { depth = depth_; }
|
||||
|
||||
double GetDefaultWidth() const { return defaultWidth; }
|
||||
double GetDefaultHeight() const { return defaultHeight; }
|
||||
double GetDefaultDepth() const { return defaultDepth; }
|
||||
void SetDefaultWidth(double width_) { defaultWidth = width_; }
|
||||
void SetDefaultHeight(double height_) { defaultHeight = height_; }
|
||||
void SetDefaultDepth(double depth_) { defaultDepth = depth_; }
|
||||
|
||||
/**
|
||||
* \brief Return true if the instance is locked and cannot be moved in the
|
||||
* IDE.
|
||||
@@ -366,7 +373,11 @@ class GD_CORE_API InitialInstance {
|
||||
*/
|
||||
InitialInstance& ResetPersistentUuid();
|
||||
|
||||
const gd::String& GetPersistentUuid() const { return persistentUuid; }
|
||||
/**
|
||||
* \brief Reset the persistent UUID used to recognize
|
||||
* the same initial instance between serialization.
|
||||
*/
|
||||
const gd::String& GetPersistentUuid() const { return persistentUuid; }
|
||||
///@}
|
||||
|
||||
private:
|
||||
@@ -395,6 +406,9 @@ class GD_CORE_API InitialInstance {
|
||||
double width; ///< Instance custom width
|
||||
double height; ///< Instance custom height
|
||||
double depth; ///< Instance custom depth
|
||||
double defaultWidth = 0; ///< Instance default width as reported by InGameEditor
|
||||
double defaultHeight = 0; ///< Instance default height as reported by InGameEditor
|
||||
double defaultDepth = 0; ///< Instance default depth as reported by InGameEditor
|
||||
gd::VariablesContainer initialVariables; ///< Instance specific variables
|
||||
bool locked; ///< True if the instance is locked
|
||||
bool sealed; ///< True if the instance is sealed
|
||||
|
@@ -23,6 +23,7 @@ Layer::Layer()
|
||||
camera3DNearPlaneDistance(3),
|
||||
camera3DFarPlaneDistance(10000),
|
||||
camera3DFieldOfView(45),
|
||||
camera2DPlaneMaxDrawingDistance(5000),
|
||||
ambientLightColorR(200),
|
||||
ambientLightColorG(200),
|
||||
ambientLightColorB(200) {}
|
||||
@@ -56,6 +57,8 @@ void Layer::SerializeTo(SerializerElement& element) const {
|
||||
element.SetAttribute("camera3DFarPlaneDistance",
|
||||
GetCamera3DFarPlaneDistance());
|
||||
element.SetAttribute("camera3DFieldOfView", GetCamera3DFieldOfView());
|
||||
element.SetAttribute("camera2DPlaneMaxDrawingDistance",
|
||||
GetCamera2DPlaneMaxDrawingDistance());
|
||||
|
||||
SerializerElement& camerasElement = element.AddChild("cameras");
|
||||
camerasElement.ConsiderAsArrayOf("camera");
|
||||
@@ -99,6 +102,8 @@ void Layer::UnserializeFrom(const SerializerElement& element) {
|
||||
"camera3DFarPlaneDistance", 10000, "threeDFarPlaneDistance"));
|
||||
SetCamera3DFieldOfView(element.GetDoubleAttribute(
|
||||
"camera3DFieldOfView", 45, "threeDFieldOfView"));
|
||||
SetCamera2DPlaneMaxDrawingDistance(element.GetDoubleAttribute(
|
||||
"camera2DPlaneMaxDrawingDistance", 5000));
|
||||
|
||||
cameras.clear();
|
||||
SerializerElement& camerasElement = element.GetChild("cameras");
|
||||
|
@@ -182,6 +182,8 @@ class GD_CORE_API Layer {
|
||||
}
|
||||
double GetCamera3DFieldOfView() const { return camera3DFieldOfView; }
|
||||
void SetCamera3DFieldOfView(double angle) { camera3DFieldOfView = angle; }
|
||||
double GetCamera2DPlaneMaxDrawingDistance() const { return camera2DPlaneMaxDrawingDistance; }
|
||||
void SetCamera2DPlaneMaxDrawingDistance(double distance) { camera2DPlaneMaxDrawingDistance = distance; }
|
||||
///@}
|
||||
|
||||
/** \name Cameras
|
||||
@@ -292,6 +294,7 @@ class GD_CORE_API Layer {
|
||||
double camera3DNearPlaneDistance; ///< 3D camera frustum near plan distance
|
||||
double camera3DFarPlaneDistance; ///< 3D camera frustum far plan distance
|
||||
double camera3DFieldOfView; ///< 3D camera field of view (fov) in degrees
|
||||
double camera2DPlaneMaxDrawingDistance; ///< Max drawing distance of the 2D plane when in the 3D world
|
||||
unsigned int ambientLightColorR; ///< Ambient light color Red component
|
||||
unsigned int ambientLightColorG; ///< Ambient light color Green component
|
||||
unsigned int ambientLightColorB; ///< Ambient light color Blue component
|
||||
|
@@ -730,6 +730,8 @@ void Project::UnserializeFrom(const SerializerElement& element) {
|
||||
SetPackageName(propElement.GetStringAttribute("packageName"));
|
||||
SetTemplateSlug(propElement.GetStringAttribute("templateSlug"));
|
||||
SetOrientation(propElement.GetStringAttribute("orientation", "default"));
|
||||
SetEffectsHiddenInEditor(
|
||||
propElement.GetBoolAttribute("areEffectsHiddenInEditor", false));
|
||||
SetFolderProject(propElement.GetBoolAttribute("folderProject"));
|
||||
SetLastCompilationDirectory(propElement
|
||||
.GetChild("latestCompilationDirectory",
|
||||
@@ -1109,6 +1111,10 @@ void Project::SerializeTo(SerializerElement& element) const {
|
||||
propElement.SetAttribute("packageName", packageName);
|
||||
propElement.SetAttribute("templateSlug", templateSlug);
|
||||
propElement.SetAttribute("orientation", orientation);
|
||||
if (areEffectsHiddenInEditor) {
|
||||
propElement.SetBoolAttribute("areEffectsHiddenInEditor",
|
||||
areEffectsHiddenInEditor);
|
||||
}
|
||||
platformSpecificAssets.SerializeTo(
|
||||
propElement.AddChild("platformSpecificAssets"));
|
||||
loadingScreen.SerializeTo(propElement.AddChild("loadingScreen"));
|
||||
@@ -1150,6 +1156,8 @@ void Project::SerializeTo(SerializerElement& element) const {
|
||||
// end of compatibility code
|
||||
|
||||
extensionProperties.SerializeTo(propElement.AddChild("extensionProperties"));
|
||||
|
||||
playableDevicesElement.AddChild("").SetStringValue("mobile");
|
||||
|
||||
SerializerElement& platformsElement = propElement.AddChild("platforms");
|
||||
platformsElement.ConsiderAsArrayOf("platform");
|
||||
@@ -1319,6 +1327,8 @@ void Project::Init(const gd::Project& game) {
|
||||
|
||||
sceneResourcesPreloading = game.sceneResourcesPreloading;
|
||||
sceneResourcesUnloading = game.sceneResourcesUnloading;
|
||||
|
||||
areEffectsHiddenInEditor = game.areEffectsHiddenInEditor;
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -506,6 +506,20 @@ class GD_CORE_API Project {
|
||||
*/
|
||||
void SetCurrentPlatform(const gd::String& platformName);
|
||||
|
||||
/**
|
||||
* Check if the effects are shown.
|
||||
*/
|
||||
bool AreEffectsHiddenInEditor() const { return areEffectsHiddenInEditor; }
|
||||
|
||||
/**
|
||||
* Define the project as playable on a mobile.
|
||||
* \param enable True When false effects are not shown and a default light is
|
||||
* used for 3D layers.
|
||||
*/
|
||||
void SetEffectsHiddenInEditor(bool enable = true) {
|
||||
areEffectsHiddenInEditor = enable;
|
||||
}
|
||||
|
||||
///@}
|
||||
|
||||
/** \name Factory method
|
||||
@@ -1165,6 +1179,9 @@ class GD_CORE_API Project {
|
||||
mutable unsigned int gdBuildVersion =
|
||||
0; ///< The GD build version used the last
|
||||
///< time the project was saved.
|
||||
bool areEffectsHiddenInEditor =
|
||||
false; ///< When false effects are not shown and a default light is used
|
||||
///< for 3D layers.
|
||||
};
|
||||
|
||||
} // 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);
|
||||
}
|
||||
@@ -56,7 +70,9 @@ void PropertyDescriptor::UnserializeFrom(const SerializerElement& element) {
|
||||
currentValue = element.GetChild("value").GetStringValue();
|
||||
type = element.GetChild("type").GetStringValue();
|
||||
if (type == "Number") {
|
||||
gd::String unitName = element.GetChild("unit").GetStringValue();
|
||||
gd::String unitName = element.HasChild("unit")
|
||||
? element.GetChild("unit").GetStringValue()
|
||||
: "";
|
||||
measurementUnit =
|
||||
gd::MeasurementUnit::HasDefaultMeasurementUnitNamed(unitName)
|
||||
? measurementUnit =
|
||||
@@ -80,6 +96,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") {
|
||||
|
@@ -147,15 +147,9 @@ namespace gdjs {
|
||||
if (initialInstanceData.depth !== undefined) {
|
||||
this.setDepth(initialInstanceData.depth);
|
||||
}
|
||||
if (initialInstanceData.flippedX) {
|
||||
this.flipX(initialInstanceData.flippedX);
|
||||
}
|
||||
if (initialInstanceData.flippedY) {
|
||||
this.flipY(initialInstanceData.flippedY);
|
||||
}
|
||||
if (initialInstanceData.flippedZ) {
|
||||
this.flipZ(initialInstanceData.flippedZ);
|
||||
}
|
||||
this.flipX(!!initialInstanceData.flippedX);
|
||||
this.flipY(!!initialInstanceData.flippedY);
|
||||
this.flipZ(!!initialInstanceData.flippedZ);
|
||||
}
|
||||
|
||||
setX(x: float): void {
|
||||
@@ -322,6 +316,18 @@ namespace gdjs {
|
||||
this.setAngle(gdjs.toDegrees(mesh.rotation.z));
|
||||
}
|
||||
|
||||
override getOriginalWidth(): float {
|
||||
return this._originalWidth;
|
||||
}
|
||||
|
||||
override getOriginalHeight(): float {
|
||||
return this._originalHeight;
|
||||
}
|
||||
|
||||
getOriginalDepth(): float {
|
||||
return this._originalDepth;
|
||||
}
|
||||
|
||||
getWidth(): float {
|
||||
return this._width;
|
||||
}
|
||||
@@ -368,31 +374,6 @@ namespace gdjs {
|
||||
this.getRenderer().updateSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the width of the object for a scale of 1.
|
||||
*
|
||||
* It can't be 0.
|
||||
*/
|
||||
_getOriginalWidth(): float {
|
||||
return this._originalWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the height of the object for a scale of 1.
|
||||
*
|
||||
* It can't be 0.
|
||||
*/
|
||||
_getOriginalHeight(): float {
|
||||
return this._originalHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the object size on the Z axis (called "depth") when the scale equals 1.
|
||||
*/
|
||||
_getOriginalDepth(): float {
|
||||
return this._originalDepth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the width of the object for a scale of 1.
|
||||
*/
|
||||
|
@@ -11,6 +11,8 @@ namespace gdjs {
|
||||
this._object = runtimeObject;
|
||||
this._threeObject3D = threeObject3D;
|
||||
this._threeObject3D.rotation.order = 'ZYX';
|
||||
//@ts-ignore
|
||||
this._threeObject3D.gdjsRuntimeObject = runtimeObject;
|
||||
|
||||
instanceContainer
|
||||
.getLayer('')
|
||||
|
@@ -115,6 +115,12 @@ namespace gdjs {
|
||||
* Rotations around X and Y are not taken into account.
|
||||
*/
|
||||
getUnrotatedAABBMaxZ(): number;
|
||||
|
||||
/**
|
||||
* Return the depth of the object before any custom size is applied.
|
||||
* @return The depth of the object
|
||||
*/
|
||||
getOriginalDepth(): float;
|
||||
}
|
||||
|
||||
export interface Object3DDataContent {
|
||||
@@ -131,7 +137,11 @@ namespace gdjs {
|
||||
export namespace Base3DHandler {
|
||||
export const is3D = (
|
||||
object: gdjs.RuntimeObject
|
||||
): object is gdjs.RuntimeObject & gdjs.Base3DHandler => {
|
||||
): object is gdjs.RuntimeObject &
|
||||
gdjs.Base3DHandler &
|
||||
gdjs.Resizable &
|
||||
gdjs.Scalable &
|
||||
gdjs.Flippable => {
|
||||
//@ts-ignore We are checking if the methods are present.
|
||||
return object.getZ && object.setZ;
|
||||
};
|
||||
@@ -243,6 +253,10 @@ namespace gdjs {
|
||||
getUnrotatedAABBMaxZ(): number {
|
||||
return this.object.getUnrotatedAABBMaxZ();
|
||||
}
|
||||
|
||||
getOriginalDepth(): float {
|
||||
return this.object.getOriginalDepth();
|
||||
}
|
||||
}
|
||||
|
||||
gdjs.registerBehavior('Scene3D::Base3DBehavior', gdjs.Base3DBehavior);
|
||||
|
@@ -74,15 +74,9 @@ namespace gdjs {
|
||||
if (initialInstanceData.depth !== undefined) {
|
||||
this.setDepth(initialInstanceData.depth);
|
||||
}
|
||||
if (initialInstanceData.flippedX) {
|
||||
this.flipX(initialInstanceData.flippedX);
|
||||
}
|
||||
if (initialInstanceData.flippedY) {
|
||||
this.flipY(initialInstanceData.flippedY);
|
||||
}
|
||||
if (initialInstanceData.flippedZ) {
|
||||
this.flipZ(initialInstanceData.flippedZ);
|
||||
}
|
||||
this.flipX(!!initialInstanceData.flippedX);
|
||||
this.flipY(!!initialInstanceData.flippedY);
|
||||
this.flipZ(!!initialInstanceData.flippedZ);
|
||||
}
|
||||
|
||||
getNetworkSyncData(): CustomObject3DNetworkSyncDataType {
|
||||
@@ -315,6 +309,10 @@ namespace gdjs {
|
||||
return this._maxZ - this._minZ;
|
||||
}
|
||||
|
||||
getOriginalDepth(): float {
|
||||
return this._instanceContainer._getInitialInnerAreaDepth();
|
||||
}
|
||||
|
||||
override _updateUntransformedHitBoxes(): void {
|
||||
super._updateUntransformedHitBoxes();
|
||||
|
||||
|
@@ -1908,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()
|
||||
@@ -1929,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');
|
||||
@@ -2013,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()
|
||||
@@ -2057,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')
|
||||
|
@@ -278,9 +278,9 @@ namespace gdjs {
|
||||
rotationX,
|
||||
rotationY,
|
||||
rotationZ,
|
||||
this._getOriginalWidth(),
|
||||
this._getOriginalHeight(),
|
||||
this._getOriginalDepth(),
|
||||
this.getOriginalWidth(),
|
||||
this.getOriginalHeight(),
|
||||
this.getOriginalDepth(),
|
||||
keepAspectRatio
|
||||
);
|
||||
}
|
||||
|
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,
|
||||
|
@@ -218,9 +218,11 @@ namespace gdjs {
|
||||
this.setWrappingWidth(initialInstanceData.width);
|
||||
this.setWrapping(true);
|
||||
}
|
||||
if (initialInstanceData.opacity !== undefined) {
|
||||
this.setOpacity(initialInstanceData.opacity);
|
||||
}
|
||||
this.setOpacity(
|
||||
initialInstanceData.opacity === undefined
|
||||
? 255
|
||||
: initialInstanceData.opacity
|
||||
);
|
||||
}
|
||||
|
||||
override onDestroyed(): void {
|
||||
|
@@ -388,6 +388,9 @@ namespace gdjs {
|
||||
|
||||
return new gdjs.PromiseTask(
|
||||
(async () => {
|
||||
if (runtimeScene.getGame().isInGameEdition()) {
|
||||
return;
|
||||
}
|
||||
const scoreSavingState = (_scoreSavingStateByLeaderboard[
|
||||
leaderboardId
|
||||
] =
|
||||
@@ -423,6 +426,9 @@ namespace gdjs {
|
||||
) =>
|
||||
new gdjs.PromiseTask(
|
||||
(async () => {
|
||||
if (runtimeScene.getGame().isInGameEdition()) {
|
||||
return;
|
||||
}
|
||||
const playerId = gdjs.playerAuthentication.getUserId();
|
||||
const playerToken = gdjs.playerAuthentication.getUserToken();
|
||||
if (!playerId || !playerToken) {
|
||||
@@ -747,6 +753,9 @@ namespace gdjs {
|
||||
leaderboardId: string,
|
||||
displayLoader: boolean
|
||||
) {
|
||||
if (runtimeScene.getGame().isInGameEdition()) {
|
||||
return;
|
||||
}
|
||||
// First ensure we're not trying to display multiple times the same leaderboard (in which case
|
||||
// we "de-duplicate" the request to display it).
|
||||
if (leaderboardId === _requestedLeaderboardId) {
|
||||
|
@@ -1841,6 +1841,9 @@ namespace gdjs {
|
||||
displayLoader: boolean,
|
||||
openLobbiesPageIfFailure: boolean
|
||||
) => {
|
||||
if (runtimeScene.getGame().isInGameEdition()) {
|
||||
return;
|
||||
}
|
||||
if (isQuickJoiningTooFast()) {
|
||||
return;
|
||||
}
|
||||
@@ -1860,6 +1863,9 @@ namespace gdjs {
|
||||
displayLoader: boolean,
|
||||
openLobbiesPageIfFailure: boolean
|
||||
) => {
|
||||
if (runtimeScene.getGame().isInGameEdition()) {
|
||||
return;
|
||||
}
|
||||
if (isQuickJoiningTooFast()) {
|
||||
return;
|
||||
}
|
||||
@@ -1893,6 +1899,9 @@ namespace gdjs {
|
||||
export const openLobbiesWindow = async (
|
||||
runtimeScene: gdjs.RuntimeScene
|
||||
) => {
|
||||
if (runtimeScene.getGame().isInGameEdition()) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
isLobbiesWindowOpen(runtimeScene) ||
|
||||
gdjs.playerAuthentication.isAuthenticationWindowOpen()
|
||||
|
@@ -53,6 +53,8 @@ namespace gdjs {
|
||||
|
||||
_renderer: gdjs.PanelSpriteRuntimeObjectRenderer;
|
||||
|
||||
_objectData: PanelSpriteObjectData;
|
||||
|
||||
/**
|
||||
* @param instanceContainer The container the object belongs to.
|
||||
* @param panelSpriteObjectData The initial properties of the object
|
||||
@@ -62,6 +64,7 @@ namespace gdjs {
|
||||
panelSpriteObjectData: PanelSpriteObjectData
|
||||
) {
|
||||
super(instanceContainer, panelSpriteObjectData);
|
||||
this._objectData = panelSpriteObjectData;
|
||||
this._rBorder = panelSpriteObjectData.rightMargin;
|
||||
this._lBorder = panelSpriteObjectData.leftMargin;
|
||||
this._tBorder = panelSpriteObjectData.topMargin;
|
||||
@@ -84,6 +87,7 @@ namespace gdjs {
|
||||
oldObjectData: PanelSpriteObjectData,
|
||||
newObjectData: PanelSpriteObjectData
|
||||
): boolean {
|
||||
this._objectData = newObjectData;
|
||||
if (oldObjectData.width !== newObjectData.width) {
|
||||
this.setWidth(newObjectData.width);
|
||||
}
|
||||
@@ -163,9 +167,11 @@ namespace gdjs {
|
||||
this.setWidth(initialInstanceData.width);
|
||||
this.setHeight(initialInstanceData.height);
|
||||
}
|
||||
if (initialInstanceData.opacity !== undefined) {
|
||||
this.setOpacity(initialInstanceData.opacity);
|
||||
}
|
||||
this.setOpacity(
|
||||
initialInstanceData.opacity === undefined
|
||||
? 255
|
||||
: initialInstanceData.opacity
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -244,6 +250,14 @@ namespace gdjs {
|
||||
this.setHeight(newHeight);
|
||||
}
|
||||
|
||||
override getOriginalWidth(): float {
|
||||
return this._objectData.width;
|
||||
}
|
||||
|
||||
override getOriginalHeight(): float {
|
||||
return this._objectData.height;
|
||||
}
|
||||
|
||||
setOpacity(opacity: float): void {
|
||||
if (opacity < 0) {
|
||||
opacity = 0;
|
||||
|
@@ -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')
|
||||
|
@@ -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'
|
||||
)
|
||||
@@ -2043,7 +2048,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',
|
||||
@@ -2612,7 +2622,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'),
|
||||
@@ -3300,7 +3310,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',
|
||||
|
@@ -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"),
|
||||
|
@@ -647,6 +647,9 @@ namespace gdjs {
|
||||
export const displayAuthenticationBanner = function (
|
||||
runtimeScene: gdjs.RuntimeScene
|
||||
) {
|
||||
if (runtimeScene.getGame().isInGameEdition()) {
|
||||
return;
|
||||
}
|
||||
if (_authenticationBanner) {
|
||||
// Banner already displayed, ensure it's visible.
|
||||
_authenticationBanner.style.opacity = '1';
|
||||
@@ -1042,6 +1045,10 @@ namespace gdjs {
|
||||
): gdjs.PromiseTask<{ status: 'logged' | 'errored' | 'dismissed' }> =>
|
||||
new gdjs.PromiseTask(
|
||||
new Promise((resolve) => {
|
||||
if (runtimeScene.getGame().isInGameEdition()) {
|
||||
resolve({ status: 'dismissed' });
|
||||
}
|
||||
|
||||
// Create the authentication container for the player to wait.
|
||||
const domElementContainer = runtimeScene
|
||||
.getGame()
|
||||
|
@@ -196,12 +196,8 @@ namespace gdjs {
|
||||
* @param initialInstanceData The extra parameters
|
||||
*/
|
||||
extraInitializationFromInitialInstance(initialInstanceData: InstanceData) {
|
||||
if (initialInstanceData.flippedX) {
|
||||
this.flipX(initialInstanceData.flippedX);
|
||||
}
|
||||
if (initialInstanceData.flippedY) {
|
||||
this.flipY(initialInstanceData.flippedY);
|
||||
}
|
||||
this.flipX(!!initialInstanceData.flippedX);
|
||||
this.flipY(!!initialInstanceData.flippedY);
|
||||
}
|
||||
|
||||
stepBehaviorsPreEvents(instanceContainer: gdjs.RuntimeInstanceContainer) {
|
||||
|
@@ -202,28 +202,22 @@ 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.getFromName(
|
||||
resourceData.name
|
||||
);
|
||||
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.getFromName(
|
||||
resourceData.name
|
||||
);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -199,15 +199,13 @@ namespace gdjs {
|
||||
this.setSize(initialInstanceData.width, initialInstanceData.height);
|
||||
this.invalidateHitboxes();
|
||||
}
|
||||
if (initialInstanceData.opacity !== undefined) {
|
||||
this.setOpacity(initialInstanceData.opacity);
|
||||
}
|
||||
if (initialInstanceData.flippedX) {
|
||||
this.flipX(initialInstanceData.flippedX);
|
||||
}
|
||||
if (initialInstanceData.flippedY) {
|
||||
this.flipY(initialInstanceData.flippedY);
|
||||
}
|
||||
this.setOpacity(
|
||||
initialInstanceData.opacity === undefined
|
||||
? 255
|
||||
: initialInstanceData.opacity
|
||||
);
|
||||
this.flipX(!!initialInstanceData.flippedX);
|
||||
this.flipY(!!initialInstanceData.flippedY);
|
||||
}
|
||||
|
||||
getDrawableX(): number {
|
||||
|
@@ -141,7 +141,9 @@ namespace gdjs {
|
||||
);
|
||||
this._borderOpacity = objectData.content.borderOpacity;
|
||||
this._borderWidth = objectData.content.borderWidth;
|
||||
this._disabled = objectData.content.disabled;
|
||||
this._disabled = instanceContainer.getGame().isInGameEdition()
|
||||
? true
|
||||
: objectData.content.disabled;
|
||||
this._readOnly = objectData.content.readOnly;
|
||||
this._spellCheck =
|
||||
objectData.content.spellCheck !== undefined
|
||||
@@ -329,9 +331,11 @@ namespace gdjs {
|
||||
this.setHeight(initialInstanceData.height);
|
||||
this._renderer.updatePadding();
|
||||
}
|
||||
if (initialInstanceData.opacity !== undefined) {
|
||||
this.setOpacity(initialInstanceData.opacity);
|
||||
}
|
||||
this.setOpacity(
|
||||
initialInstanceData.opacity === undefined
|
||||
? 255
|
||||
: initialInstanceData.opacity
|
||||
);
|
||||
}
|
||||
|
||||
onScenePaused(runtimeScene: gdjs.RuntimeScene): void {
|
||||
@@ -561,6 +565,9 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
setDisabled(value: boolean) {
|
||||
if (this.getInstanceContainer().getGame().isInGameEdition()) {
|
||||
return;
|
||||
}
|
||||
this._disabled = value;
|
||||
this._renderer.updateDisabled();
|
||||
}
|
||||
|
@@ -334,7 +334,9 @@ namespace gdjs {
|
||||
return this._renderer.getRendererObject();
|
||||
}
|
||||
|
||||
override update(instanceContainer: gdjs.RuntimeInstanceContainer): void {
|
||||
override updatePreRender(
|
||||
instanceContainer: gdjs.RuntimeInstanceContainer
|
||||
): void {
|
||||
this._renderer.ensureUpToDate();
|
||||
}
|
||||
|
||||
@@ -352,9 +354,11 @@ namespace gdjs {
|
||||
} else {
|
||||
this.setWrapping(false);
|
||||
}
|
||||
if (initialInstanceData.opacity !== undefined) {
|
||||
this.setOpacity(initialInstanceData.opacity);
|
||||
}
|
||||
this.setOpacity(
|
||||
initialInstanceData.opacity === undefined
|
||||
? 255
|
||||
: initialInstanceData.opacity
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -205,9 +205,11 @@ namespace gdjs {
|
||||
this.setWidth(initialInstanceData.width);
|
||||
this.setHeight(initialInstanceData.height);
|
||||
}
|
||||
if (initialInstanceData.opacity !== undefined) {
|
||||
this.setOpacity(initialInstanceData.opacity);
|
||||
}
|
||||
this.setOpacity(
|
||||
initialInstanceData.opacity === undefined
|
||||
? 255
|
||||
: initialInstanceData.opacity
|
||||
);
|
||||
|
||||
// 4. Update position (calculations based on renderer's dimensions).
|
||||
this._renderer.updatePosition();
|
||||
@@ -415,6 +417,14 @@ namespace gdjs {
|
||||
return this._renderer.getHeight();
|
||||
}
|
||||
|
||||
override getOriginalWidth(): float {
|
||||
return this._renderer.getTileMapWidth();
|
||||
}
|
||||
|
||||
override getOriginalHeight(): float {
|
||||
return this._renderer.getTileMapHeight();
|
||||
}
|
||||
|
||||
getScaleX(): float {
|
||||
return this._renderer.getScaleX();
|
||||
}
|
||||
|
@@ -193,9 +193,11 @@ namespace gdjs {
|
||||
this.setWidth(initialInstanceData.width);
|
||||
this.setHeight(initialInstanceData.height);
|
||||
}
|
||||
if (initialInstanceData.opacity !== undefined) {
|
||||
this.setOpacity(initialInstanceData.opacity);
|
||||
}
|
||||
this.setOpacity(
|
||||
initialInstanceData.opacity === undefined
|
||||
? 255
|
||||
: initialInstanceData.opacity
|
||||
);
|
||||
}
|
||||
|
||||
private _updateTileMap(): void {
|
||||
@@ -337,6 +339,14 @@ namespace gdjs {
|
||||
this.setHeight(newHeight);
|
||||
}
|
||||
|
||||
override getOriginalWidth(): float {
|
||||
return this._renderer.getTileMapWidth();
|
||||
}
|
||||
|
||||
override getOriginalHeight(): float {
|
||||
return this._renderer.getTileMapHeight();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the scale of the object (or the geometric mean of the X and Y scale in case they are different).
|
||||
*
|
||||
|
@@ -42,6 +42,8 @@ namespace gdjs {
|
||||
|
||||
_renderer: gdjs.TiledSpriteRuntimeObjectRenderer;
|
||||
|
||||
_objectData: TiledSpriteObjectData;
|
||||
|
||||
/**
|
||||
* @param instanceContainer The container the object belongs to.
|
||||
* @param tiledSpriteObjectData The initial properties of the object
|
||||
@@ -51,6 +53,7 @@ namespace gdjs {
|
||||
tiledSpriteObjectData: TiledSpriteObjectData
|
||||
) {
|
||||
super(instanceContainer, tiledSpriteObjectData);
|
||||
this._objectData = tiledSpriteObjectData;
|
||||
this._renderer = new gdjs.TiledSpriteRuntimeObjectRenderer(
|
||||
this,
|
||||
instanceContainer,
|
||||
@@ -66,6 +69,7 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
updateFromObjectData(oldObjectData, newObjectData): boolean {
|
||||
this._objectData = newObjectData;
|
||||
if (oldObjectData.texture !== newObjectData.texture) {
|
||||
this.setTexture(newObjectData.texture, this.getRuntimeScene());
|
||||
}
|
||||
@@ -126,9 +130,11 @@ namespace gdjs {
|
||||
this.setWidth(initialInstanceData.width);
|
||||
this.setHeight(initialInstanceData.height);
|
||||
}
|
||||
if (initialInstanceData.opacity !== undefined) {
|
||||
this.setOpacity(initialInstanceData.opacity);
|
||||
}
|
||||
this.setOpacity(
|
||||
initialInstanceData.opacity === undefined
|
||||
? 255
|
||||
: initialInstanceData.opacity
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -220,6 +226,14 @@ namespace gdjs {
|
||||
this.setHeight(height);
|
||||
}
|
||||
|
||||
override getOriginalWidth(): float {
|
||||
return this._objectData.width;
|
||||
}
|
||||
|
||||
override getOriginalHeight(): float {
|
||||
return this._objectData.height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the offset on the X-axis when displaying the image of the Tiled Sprite object.
|
||||
* @param xOffset The new offset on the X-axis.
|
||||
|
@@ -347,17 +347,21 @@ describe('gdjs.TweenRuntimeBehavior', () => {
|
||||
// Check that there is no NaN.
|
||||
for (let i = 0; i < 11; i++) {
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
expect(camera.getCameraZoom(runtimeScene, '', 0)).to.be(0);
|
||||
// The tween tries to set the camera zoom to 0, but it has no effect
|
||||
// because it doesn't make sense.
|
||||
expect(camera.getCameraZoom(runtimeScene, '', 0)).to.be(1);
|
||||
}
|
||||
});
|
||||
|
||||
it('can tween a layer camera zoom from 0', () => {
|
||||
// The zoom stays at 1 because 0 doesn't make sense.
|
||||
camera.setCameraZoom(runtimeScene, 0, '', 0);
|
||||
// Here, it actually tweens from 1 to 1.
|
||||
tween.tweenCameraZoom2(runtimeScene, 'MyTween', 1, '', 'linear', 0.25);
|
||||
// A camera zoom of 0 doesn't make sense.
|
||||
// Check that there is no NaN.
|
||||
for (let i = 0; i < 11; i++) {
|
||||
expect(camera.getCameraZoom(runtimeScene, '', 0)).to.be(0);
|
||||
expect(camera.getCameraZoom(runtimeScene, '', 0)).to.be(1);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
}
|
||||
expect(camera.getCameraZoom(runtimeScene, '', 0)).to.be(1);
|
||||
|
@@ -146,9 +146,11 @@ namespace gdjs {
|
||||
this.setWidth(initialInstanceData.width);
|
||||
this.setHeight(initialInstanceData.height);
|
||||
}
|
||||
if (initialInstanceData.opacity !== undefined) {
|
||||
this.setOpacity(initialInstanceData.opacity);
|
||||
}
|
||||
this.setOpacity(
|
||||
initialInstanceData.opacity === undefined
|
||||
? 255
|
||||
: initialInstanceData.opacity
|
||||
);
|
||||
}
|
||||
|
||||
onDestroyed(): void {
|
||||
|
@@ -40,7 +40,6 @@ gd::String EventsCodeGenerator::GenerateEventsListCompleteFunctionCode(
|
||||
gdjs::EventsCodeGenerator& codeGenerator,
|
||||
gd::String fullyQualifiedFunctionName,
|
||||
gd::String functionArgumentsCode,
|
||||
gd::String contextClassCode,
|
||||
gd::String functionPreEventsCode,
|
||||
const gd::EventsList& events,
|
||||
gd::String functionPostEventsCode,
|
||||
@@ -82,7 +81,6 @@ gd::String EventsCodeGenerator::GenerateEventsListCompleteFunctionCode(
|
||||
globalDeclarations +
|
||||
globalObjectLists + "\n\n" +
|
||||
codeGenerator.GetCustomCodeOutsideMain() + "\n\n" +
|
||||
contextClassCode + "\n\n" +
|
||||
fullyQualifiedFunctionName + " = function(" +
|
||||
functionArgumentsCode +
|
||||
") {\n" +
|
||||
@@ -114,7 +112,6 @@ gd::String EventsCodeGenerator::GenerateLayoutCode(
|
||||
codeGenerator,
|
||||
codeGenerator.GetCodeNamespaceAccessor() + "func",
|
||||
"runtimeScene",
|
||||
"",
|
||||
"runtimeScene.getOnceTriggers().startNewFrame();\n",
|
||||
scene.GetEvents(),
|
||||
"",
|
||||
@@ -148,40 +145,19 @@ gd::String EventsCodeGenerator::GenerateEventsFunctionCode(
|
||||
gd::DiagnosticReport diagnosticReport;
|
||||
codeGenerator.SetDiagnosticReport(&diagnosticReport);
|
||||
|
||||
auto parameterList = codeGenerator.GenerateEventsFunctionParameterDeclarationsList(
|
||||
eventsFunction.GetParametersForEvents(
|
||||
eventsFunctionsExtension.GetEventsFunctions()),
|
||||
0, true);
|
||||
// Generate the code setting up the context of the function.
|
||||
gd::String fullPreludeCode = "let scopeInstanceContainer = null;\n" +
|
||||
codeGenerator.GenerateFreeEventsFunctionContext(
|
||||
eventsFunctionsExtension, eventsFunction,
|
||||
"runtimeScene.getOnceTriggers()");
|
||||
|
||||
gd::String output = GenerateEventsListCompleteFunctionCode(
|
||||
codeGenerator, codeGenerator.GetCodeNamespaceAccessor() + "func",
|
||||
parameterList,
|
||||
codeGenerator.GenerateFreeEventsFunctionContext(
|
||||
eventsFunctionsExtension, eventsFunction,
|
||||
"runtimeScene.getOnceTriggers()") +
|
||||
codeGenerator.GetCodeNamespaceAccessor() + "context = null;\n" + //
|
||||
codeGenerator.GetCodeNamespaceAccessor() + "isExecuted = false;\n",
|
||||
|
||||
"if (!" + codeGenerator.GetCodeNamespaceAccessor() + "context) " +
|
||||
codeGenerator.GetCodeNamespaceAccessor() + "context = new " +
|
||||
codeGenerator.GetCodeNamespaceAccessor() + "Context(" +
|
||||
parameterList + ");\n"
|
||||
"const eventsFunctionContext = " +
|
||||
codeGenerator.GetCodeNamespaceAccessor() + "isExecuted ? new " +
|
||||
codeGenerator.GetCodeNamespaceAccessor() + "Context(" +
|
||||
parameterList + ") : " + codeGenerator.GetCodeNamespaceAccessor() +
|
||||
"context;\n" + //
|
||||
"if (!" + codeGenerator.GetCodeNamespaceAccessor() +
|
||||
"isExecuted) {\n" + codeGenerator.GetCodeNamespaceAccessor() +
|
||||
"context.reinitialize(" + parameterList + ");\n" + //
|
||||
codeGenerator.GetCodeNamespaceAccessor() +
|
||||
"isExecuted = true;\n"
|
||||
"}\n",
|
||||
|
||||
eventsFunction.GetEvents(),
|
||||
"if (eventsFunctionContext === " +
|
||||
codeGenerator.GetCodeNamespaceAccessor() + "context) " +
|
||||
codeGenerator.GetCodeNamespaceAccessor() + "isExecuted = false;\n",
|
||||
codeGenerator.GenerateEventsFunctionParameterDeclarationsList(
|
||||
eventsFunction.GetParametersForEvents(
|
||||
eventsFunctionsExtension.GetEventsFunctions()),
|
||||
0, true),
|
||||
fullPreludeCode, eventsFunction.GetEvents(), "",
|
||||
codeGenerator.GenerateEventsFunctionReturn(eventsFunction));
|
||||
|
||||
// TODO: the editor should pass the diagnostic report and display it to the
|
||||
@@ -228,59 +204,45 @@ gd::String EventsCodeGenerator::GenerateBehaviorEventsFunctionCode(
|
||||
gd::DiagnosticReport diagnosticReport;
|
||||
codeGenerator.SetDiagnosticReport(&diagnosticReport);
|
||||
|
||||
auto contextParameterList = codeGenerator.GenerateEventsFunctionParameterDeclarationsList(
|
||||
eventsFunction.GetParametersForEvents(
|
||||
eventsBasedBehavior.GetEventsFunctions()),
|
||||
2, true) + ", that";
|
||||
|
||||
// Generate the code setting up the context of the function.
|
||||
gd::String fullPreludeCode =
|
||||
preludeCode + "\n" + "const that = this;\n" +
|
||||
preludeCode + "\n" + "var that = this;\n" +
|
||||
// runtimeScene is supposed to be always accessible, read
|
||||
// it from the behavior.
|
||||
// TODO: this should be renamed to "instanceContainer" and have the code
|
||||
// generation adapted for this (rely less on `gdjs.RuntimeScene`, and more
|
||||
// on `RuntimeInstanceContainer`).
|
||||
"const runtimeScene = this._runtimeScene;\n" +
|
||||
"if (!" + codeGenerator.GetCodeNamespaceAccessor() + "context) " +
|
||||
codeGenerator.GetCodeNamespaceAccessor() + "context = new " +
|
||||
codeGenerator.GetCodeNamespaceAccessor() + "Context(" +
|
||||
contextParameterList + ");\n"
|
||||
"const eventsFunctionContext = " +
|
||||
codeGenerator.GetCodeNamespaceAccessor() + "isExecuted ? new " +
|
||||
codeGenerator.GetCodeNamespaceAccessor() + "Context(" +
|
||||
contextParameterList + ") : " + codeGenerator.GetCodeNamespaceAccessor() +
|
||||
"context;\n" + //
|
||||
"if (!" + codeGenerator.GetCodeNamespaceAccessor() +
|
||||
"isExecuted) {\n" + codeGenerator.GetCodeNamespaceAccessor() +
|
||||
"context.reinitialize(" + contextParameterList + ");\n" + //
|
||||
codeGenerator.GetCodeNamespaceAccessor() +
|
||||
"isExecuted = true;\n"
|
||||
"}\n";
|
||||
|
||||
auto parameterList =
|
||||
codeGenerator.GenerateEventsFunctionParameterDeclarationsList(
|
||||
eventsFunction.GetParametersForEvents(
|
||||
eventsBasedBehavior.GetEventsFunctions()),
|
||||
2, false);
|
||||
|
||||
gd::String output = GenerateEventsListCompleteFunctionCode(
|
||||
codeGenerator, fullyQualifiedFunctionName, parameterList,
|
||||
// TODO: this should be renamed to "instanceContainer" and have the code generation
|
||||
// adapted for this (rely less on `gdjs.RuntimeScene`, and more on `RuntimeInstanceContainer`).
|
||||
"var runtimeScene = this._runtimeScene;\n" +
|
||||
"let scopeInstanceContainer = null;\n" +
|
||||
// By convention of Behavior Events Function, the object is accessible
|
||||
// as a parameter called "Object", and thisObjectList is an array
|
||||
// containing it (for faster access, without having to go through the
|
||||
// hashmap).
|
||||
"var thisObjectList = [this.owner];\n" +
|
||||
"var Object = Hashtable.newFrom({Object: thisObjectList});\n" +
|
||||
// By convention of Behavior Events Function, the behavior is accessible
|
||||
// as a parameter called "Behavior".
|
||||
"var Behavior = this.name;\n" +
|
||||
codeGenerator.GenerateBehaviorEventsFunctionContext(
|
||||
eventsFunctionsExtension, eventsBasedBehavior, eventsFunction,
|
||||
eventsFunctionsExtension,
|
||||
eventsBasedBehavior,
|
||||
eventsFunction,
|
||||
onceTriggersVariable,
|
||||
// Pass the names of the parameters considered as the current
|
||||
// object and behavior parameters:
|
||||
"Object", "Behavior") +
|
||||
codeGenerator.GetCodeNamespaceAccessor() + "context = null;\n" + //
|
||||
codeGenerator.GetCodeNamespaceAccessor() + "isExecuted = false;\n",
|
||||
"Object",
|
||||
"Behavior");
|
||||
|
||||
gd::String output = GenerateEventsListCompleteFunctionCode(
|
||||
codeGenerator,
|
||||
fullyQualifiedFunctionName,
|
||||
codeGenerator.GenerateEventsFunctionParameterDeclarationsList(
|
||||
eventsFunction.GetParametersForEvents(
|
||||
eventsBasedBehavior.GetEventsFunctions()),
|
||||
2,
|
||||
false),
|
||||
fullPreludeCode,
|
||||
eventsFunction.GetEvents(),
|
||||
"if (eventsFunctionContext === " +
|
||||
codeGenerator.GetCodeNamespaceAccessor() + "context) {" +
|
||||
codeGenerator.GetCodeNamespaceAccessor() + "context.clear();\n" +
|
||||
codeGenerator.GetCodeNamespaceAccessor() + "isExecuted = false;\n"
|
||||
"};\n",
|
||||
"",
|
||||
codeGenerator.GenerateEventsFunctionReturn(eventsFunction));
|
||||
|
||||
// TODO: the editor should pass the diagnostic report and display it to the
|
||||
@@ -328,56 +290,54 @@ gd::String EventsCodeGenerator::GenerateObjectEventsFunctionCode(
|
||||
gd::DiagnosticReport diagnosticReport;
|
||||
codeGenerator.SetDiagnosticReport(&diagnosticReport);
|
||||
|
||||
auto contextParameterList = codeGenerator.GenerateEventsFunctionParameterDeclarationsList(
|
||||
eventsFunction.GetParametersForEvents(
|
||||
eventsBasedObject.GetEventsFunctions()),
|
||||
1, true) + ", that";
|
||||
|
||||
// Generate the code setting up the context of the function.
|
||||
gd::String fullPreludeCode =
|
||||
preludeCode + "\n" + "const that = this;\n" +
|
||||
preludeCode + "\n" + "var that = this;\n" +
|
||||
// runtimeScene is supposed to be always accessible, read
|
||||
// it from the object.
|
||||
// TODO: this should be renamed to "instanceContainer" and have the code generation
|
||||
// adapted for this (rely less on `gdjs.RuntimeScene`, and more on `RuntimeInstanceContainer`).
|
||||
"const runtimeScene = this._instanceContainer;\n"
|
||||
"if (!" + codeGenerator.GetCodeNamespaceAccessor() + "context) " +
|
||||
codeGenerator.GetCodeNamespaceAccessor() + "context = new " +
|
||||
codeGenerator.GetCodeNamespaceAccessor() + "Context(" +
|
||||
contextParameterList + ");\n"
|
||||
"const eventsFunctionContext = " +
|
||||
codeGenerator.GetCodeNamespaceAccessor() + "isExecuted ? new " +
|
||||
codeGenerator.GetCodeNamespaceAccessor() + "Context(" +
|
||||
contextParameterList + ") : " + codeGenerator.GetCodeNamespaceAccessor() +
|
||||
"context;\n" + //
|
||||
"if (!" + codeGenerator.GetCodeNamespaceAccessor() +
|
||||
"isExecuted) {\n" + codeGenerator.GetCodeNamespaceAccessor() +
|
||||
"context.reinitialize(" + contextParameterList + ");\n" + //
|
||||
codeGenerator.GetCodeNamespaceAccessor() +
|
||||
"isExecuted = true;\n"
|
||||
"}\n";
|
||||
"var runtimeScene = this._instanceContainer;\n" +
|
||||
"let scopeInstanceContainer = this._instanceContainer;\n" +
|
||||
// By convention of Object Events Function, the object is accessible
|
||||
// as a parameter called "Object", and thisObjectList is an array
|
||||
// containing it (for faster access, without having to go through the
|
||||
// hashmap).
|
||||
"var thisObjectList = [this];\n" +
|
||||
"var Object = Hashtable.newFrom({Object: thisObjectList});\n";
|
||||
|
||||
auto parameterList =
|
||||
// Add child-objects
|
||||
for (auto& childObject : eventsBasedObject.GetObjects().GetObjects()) {
|
||||
// child-object are never picked because they are not parameters.
|
||||
const auto& childName = ManObjListName(childObject->GetName());
|
||||
fullPreludeCode += "var this" + childName +
|
||||
"List = [...runtimeScene.getObjects(" +
|
||||
ConvertToStringExplicit(childObject->GetName()) +
|
||||
")];\n" + "var " + childName + " = Hashtable.newFrom({" +
|
||||
ConvertToStringExplicit(childObject->GetName()) +
|
||||
": this" + childName + "List});\n";
|
||||
}
|
||||
|
||||
fullPreludeCode += codeGenerator.GenerateObjectEventsFunctionContext(
|
||||
eventsFunctionsExtension,
|
||||
eventsBasedObject,
|
||||
eventsFunction,
|
||||
onceTriggersVariable,
|
||||
// Pass the names of the parameters considered as the current
|
||||
// object and behavior parameters:
|
||||
"Object");
|
||||
|
||||
gd::String output = GenerateEventsListCompleteFunctionCode(
|
||||
codeGenerator,
|
||||
fullyQualifiedFunctionName,
|
||||
codeGenerator.GenerateEventsFunctionParameterDeclarationsList(
|
||||
// TODO EBO use constants for firstParameterIndex
|
||||
eventsFunction.GetParametersForEvents(
|
||||
eventsBasedObject.GetEventsFunctions()),
|
||||
1, false);
|
||||
|
||||
gd::String output = GenerateEventsListCompleteFunctionCode(
|
||||
codeGenerator, fullyQualifiedFunctionName, parameterList,
|
||||
codeGenerator.GenerateObjectEventsFunctionContext(
|
||||
eventsFunctionsExtension, eventsBasedObject, eventsFunction,
|
||||
onceTriggersVariable,
|
||||
// Pass the names of the parameters considered as the current
|
||||
// object and behavior parameters:
|
||||
"Object") +
|
||||
codeGenerator.GetCodeNamespaceAccessor() + "context = null;\n" + //
|
||||
codeGenerator.GetCodeNamespaceAccessor() + "isExecuted = false;\n",
|
||||
fullPreludeCode, eventsFunction.GetEvents(),
|
||||
"if (eventsFunctionContext === " +
|
||||
codeGenerator.GetCodeNamespaceAccessor() + "context) " +
|
||||
codeGenerator.GetCodeNamespaceAccessor() + "isExecuted = false;\n" +
|
||||
1,
|
||||
false),
|
||||
fullPreludeCode,
|
||||
eventsFunction.GetEvents(),
|
||||
endingCode,
|
||||
codeGenerator.GenerateEventsFunctionReturn(eventsFunction));
|
||||
|
||||
@@ -421,33 +381,6 @@ gd::String EventsCodeGenerator::GenerateEventsFunctionParameterDeclarationsList(
|
||||
return declaration;
|
||||
}
|
||||
|
||||
gd::String EventsCodeGenerator::GenerateEventsFunctionParametersToAttribues(
|
||||
const gd::ParameterMetadataContainer ¶meters, int firstParameterIndex,
|
||||
bool addsSceneParameter) {
|
||||
gd::String declaration =
|
||||
addsSceneParameter ? " this.runtimeScene = runtimeScene;\n" : "";
|
||||
// By convention, the first two arguments of a behavior events function
|
||||
// are the object and the behavior, which are not passed to the called
|
||||
// function in the generated JS code.
|
||||
for (size_t i = firstParameterIndex; i < parameters.GetParametersCount(); ++i) {
|
||||
const auto ¶meter = parameters.GetParameter(i);
|
||||
if (parameter.GetValueTypeMetadata().IsObject() ||
|
||||
parameter.GetValueTypeMetadata().IsBehavior()) {
|
||||
continue;
|
||||
}
|
||||
gd::String parameterMangledName =
|
||||
parameter.GetName().empty()
|
||||
? "_"
|
||||
: EventsCodeNameMangler::GetMangledName(parameter.GetName());
|
||||
declaration +=
|
||||
" this." + parameterMangledName + " = " + parameterMangledName + ";\n";
|
||||
}
|
||||
declaration +=
|
||||
" this.parentEventsFunctionContext = parentEventsFunctionContext;\n";
|
||||
|
||||
return declaration;
|
||||
}
|
||||
|
||||
gd::String EventsCodeGenerator::GenerateFreeEventsFunctionContext(
|
||||
const gd::EventsFunctionsExtension& eventsFunctionsExtension,
|
||||
const gd::EventsFunction& eventsFunction,
|
||||
@@ -482,17 +415,17 @@ gd::String EventsCodeGenerator::GenerateBehaviorEventsFunctionContext(
|
||||
// optimized getter for it (bypassing "Object" hashmap, and directly return
|
||||
// the array containing it).
|
||||
if (!thisObjectName.empty()) {
|
||||
objectsGettersMap += " " +
|
||||
ConvertToStringExplicit(thisObjectName) + ": " + thisObjectName;
|
||||
objectArraysMap += " " +
|
||||
ConvertToStringExplicit(thisObjectName) + ": thisObjectList";
|
||||
objectsGettersMap +=
|
||||
ConvertToStringExplicit(thisObjectName) + ": " + thisObjectName + "\n";
|
||||
objectArraysMap +=
|
||||
ConvertToStringExplicit(thisObjectName) + ": thisObjectList\n";
|
||||
}
|
||||
|
||||
if (!thisBehaviorName.empty()) {
|
||||
// If we have a behavior considered as the current behavior ("this")
|
||||
// (usually called Behavior in behavior events function), generate a
|
||||
// slightly more optimized getter for it.
|
||||
behaviorNamesMap += " " + ConvertToStringExplicit(thisBehaviorName) + ": " +
|
||||
behaviorNamesMap += ConvertToStringExplicit(thisBehaviorName) + ": " +
|
||||
thisBehaviorName + "\n";
|
||||
|
||||
// Add required behaviors from properties
|
||||
@@ -509,40 +442,11 @@ gd::String EventsCodeGenerator::GenerateBehaviorEventsFunctionContext(
|
||||
gd::String comma = behaviorNamesMap.empty() ? "" : ", ";
|
||||
behaviorNamesMap +=
|
||||
comma + ConvertToStringExplicit(propertyDescriptor.GetName()) +
|
||||
": that._get" + propertyDescriptor.GetName() + "()\n";
|
||||
": this._get" + propertyDescriptor.GetName() + "()\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gd::String constructorAdditionalCode =
|
||||
"this.that = that;\n"
|
||||
" const thisObjectList = [that.owner];\n";
|
||||
if (!thisObjectName.empty()) {
|
||||
constructorAdditionalCode += " const " + thisObjectName +
|
||||
" = Hashtable.newFrom({ " + thisObjectName +
|
||||
": thisObjectList });\n";
|
||||
}
|
||||
if (!thisBehaviorName.empty()) {
|
||||
constructorAdditionalCode +=
|
||||
" const " + thisBehaviorName + " = that.name;\n";
|
||||
}
|
||||
|
||||
gd::String reinitializeAdditionalCode = "";
|
||||
gd::String clearAdditionalCode = "";
|
||||
if (!thisObjectName.empty()) {
|
||||
reinitializeAdditionalCode += " this._objectArraysMap[" +
|
||||
ConvertToStringExplicit(thisObjectName) +
|
||||
"][0] = that.owner;\n";
|
||||
clearAdditionalCode += " this._objectArraysMap[" +
|
||||
ConvertToStringExplicit(thisObjectName) +
|
||||
"][0] = null;\n";
|
||||
}
|
||||
if (!thisBehaviorName.empty()) {
|
||||
reinitializeAdditionalCode += " this._behaviorNamesMap[" +
|
||||
ConvertToStringExplicit(thisBehaviorName) +
|
||||
"] = that.name;\n";
|
||||
}
|
||||
|
||||
return GenerateEventsFunctionContext(eventsFunctionsExtension,
|
||||
eventsBasedBehavior.GetEventsFunctions(),
|
||||
eventsFunction,
|
||||
@@ -550,9 +454,6 @@ gd::String EventsCodeGenerator::GenerateBehaviorEventsFunctionContext(
|
||||
objectsGettersMap,
|
||||
objectArraysMap,
|
||||
behaviorNamesMap,
|
||||
constructorAdditionalCode,
|
||||
reinitializeAdditionalCode,
|
||||
clearAdditionalCode,
|
||||
thisObjectName,
|
||||
thisBehaviorName);
|
||||
}
|
||||
@@ -574,69 +475,24 @@ gd::String EventsCodeGenerator::GenerateObjectEventsFunctionContext(
|
||||
// optimized getter for it (bypassing "Object" hashmap, and directly return
|
||||
// the array containing it).
|
||||
if (!thisObjectName.empty()) {
|
||||
objectsGettersMap += " " +
|
||||
ConvertToStringExplicit(thisObjectName) + ": " + thisObjectName;
|
||||
objectArraysMap += " " +
|
||||
ConvertToStringExplicit(thisObjectName) + ": thisObjectList";
|
||||
objectsGettersMap +=
|
||||
ConvertToStringExplicit(thisObjectName) + ": " + thisObjectName + "\n";
|
||||
objectArraysMap +=
|
||||
ConvertToStringExplicit(thisObjectName) + ": thisObjectList\n";
|
||||
|
||||
// Add child-objects
|
||||
for (auto& childObject : eventsBasedObject.GetObjects().GetObjects()) {
|
||||
const auto& childName = ManObjListName(childObject->GetName());
|
||||
// child-object are never picked because they are not parameters.
|
||||
objectsGettersMap += ",\n " +
|
||||
objectsGettersMap += ", " +
|
||||
ConvertToStringExplicit(childObject->GetName()) +
|
||||
": " + childName;
|
||||
objectArraysMap += ",\n " +
|
||||
": " + childName + "\n";
|
||||
objectArraysMap += ", " +
|
||||
ConvertToStringExplicit(childObject->GetName()) +
|
||||
": this" + childName + "List";
|
||||
": this" + childName + "List\n";
|
||||
}
|
||||
}
|
||||
|
||||
// By convention of Object Events Function, the object is accessible
|
||||
// as a parameter called "Object", and thisObjectList is an array
|
||||
// containing it (for faster access, without having to go through the
|
||||
// hashmap).
|
||||
gd::String constructorAdditionalCode = "this.that = that;\n"
|
||||
" const thisObjectList = [that];\n";
|
||||
if (!thisObjectName.empty()) {
|
||||
constructorAdditionalCode += " const " + thisObjectName +
|
||||
" = Hashtable.newFrom({" + thisObjectName +
|
||||
": thisObjectList});\n";
|
||||
}
|
||||
// Add child-objects
|
||||
for (auto& childObject : eventsBasedObject.GetObjects().GetObjects()) {
|
||||
// child-object are never picked because they are not parameters.
|
||||
const auto& childName = ManObjListName(childObject->GetName());
|
||||
constructorAdditionalCode +=
|
||||
" const this" + childName + "List = [...runtimeScene.getObjects(" +
|
||||
ConvertToStringExplicit(childObject->GetName()) + ")];\n" + //
|
||||
" const " + childName + " = Hashtable.newFrom({" +
|
||||
ConvertToStringExplicit(childObject->GetName()) + ": this" + childName +
|
||||
"List});\n";
|
||||
}
|
||||
|
||||
gd::String reinitializeAdditionalCode = "";
|
||||
gd::String clearAdditionalCode = "";
|
||||
if (!thisObjectName.empty()) {
|
||||
reinitializeAdditionalCode += " this._objectArraysMap[" +
|
||||
ConvertToStringExplicit(thisObjectName) +
|
||||
"][0] = that;\n";
|
||||
clearAdditionalCode += " this._objectArraysMap[" +
|
||||
ConvertToStringExplicit(thisObjectName) +
|
||||
"][0] = null;\n";
|
||||
}
|
||||
// Add child-objects
|
||||
for (auto& childObject : eventsBasedObject.GetObjects().GetObjects()) {
|
||||
// child-object are never picked because they are not parameters.
|
||||
const auto& childName = ManObjListName(childObject->GetName());
|
||||
reinitializeAdditionalCode +=
|
||||
" gdjs.copyArray(runtimeScene.getObjects(" +
|
||||
ConvertToStringExplicit(childObject->GetName()) +
|
||||
"), "
|
||||
"this._objectArraysMap[" +
|
||||
ConvertToStringExplicit(childObject->GetName()) + "]);\n";
|
||||
}
|
||||
|
||||
return GenerateEventsFunctionContext(eventsFunctionsExtension,
|
||||
eventsBasedObject.GetEventsFunctions(),
|
||||
eventsFunction,
|
||||
@@ -644,9 +500,6 @@ gd::String EventsCodeGenerator::GenerateObjectEventsFunctionContext(
|
||||
objectsGettersMap,
|
||||
objectArraysMap,
|
||||
behaviorNamesMap,
|
||||
constructorAdditionalCode,
|
||||
reinitializeAdditionalCode,
|
||||
clearAdditionalCode,
|
||||
thisObjectName);
|
||||
}
|
||||
|
||||
@@ -658,9 +511,6 @@ gd::String EventsCodeGenerator::GenerateEventsFunctionContext(
|
||||
gd::String& objectsGettersMap,
|
||||
gd::String& objectArraysMap,
|
||||
gd::String& behaviorNamesMap,
|
||||
const gd::String& constructorAdditionalCode,
|
||||
const gd::String& reinitializeAdditionalCode,
|
||||
const gd::String& clearAdditionalCode,
|
||||
const gd::String& thisObjectName,
|
||||
const gd::String& thisBehaviorName) {
|
||||
const auto& extensionName = eventsFunctionsExtension.GetName();
|
||||
@@ -681,11 +531,6 @@ gd::String EventsCodeGenerator::GenerateEventsFunctionContext(
|
||||
// Conditions/expressions are available to deal with them in events.
|
||||
|
||||
gd::String argumentsGetters;
|
||||
gd::String reinitializeObjectsMap;
|
||||
gd::String reinitializeArraysMap;
|
||||
gd::String reinitializeBehaviorNamesMap;
|
||||
gd::String clearObjectsMap;
|
||||
gd::String clearArraysMap;
|
||||
|
||||
for (const auto& parameterPtr : parameters.GetInternalVector()) {
|
||||
const auto& parameter = *parameterPtr;
|
||||
@@ -701,26 +546,13 @@ gd::String EventsCodeGenerator::GenerateEventsFunctionContext(
|
||||
|
||||
// Generate map that will be used to get the lists of objects passed
|
||||
// as parameters (either as objects lists or array).
|
||||
gd::String comma = objectsGettersMap.empty() ? "" : ",\n ";
|
||||
gd::String comma = objectsGettersMap.empty() ? "" : ", ";
|
||||
objectsGettersMap += comma +
|
||||
ConvertToStringExplicit(parameter.GetName()) + ": " +
|
||||
parameterMangledName;
|
||||
parameterMangledName + "\n";
|
||||
objectArraysMap += comma + ConvertToStringExplicit(parameter.GetName()) +
|
||||
": gdjs.objectsListsToArray(" + parameterMangledName +
|
||||
")";
|
||||
reinitializeObjectsMap += " this._objectsMap[" +
|
||||
ConvertToStringExplicit(parameter.GetName()) +
|
||||
"] = " + parameterMangledName + ";\n";
|
||||
reinitializeArraysMap +=
|
||||
" gdjs.objectsListsToArray(" + parameterMangledName +
|
||||
", this._objectArraysMap[" +
|
||||
ConvertToStringExplicit(parameter.GetName()) + "]);\n";
|
||||
clearObjectsMap += " this._objectsMap[" +
|
||||
ConvertToStringExplicit(parameter.GetName()) +
|
||||
"] = null;\n";
|
||||
clearArraysMap += " this._objectArraysMap[" +
|
||||
ConvertToStringExplicit(parameter.GetName()) +
|
||||
"].length = 0;\n";
|
||||
")\n";
|
||||
} else if (gd::ParameterMetadata::IsBehavior(parameter.GetType())) {
|
||||
if (parameter.GetName() == thisBehaviorName) {
|
||||
continue;
|
||||
@@ -728,171 +560,122 @@ gd::String EventsCodeGenerator::GenerateEventsFunctionContext(
|
||||
|
||||
// Generate map that will be used to transform from behavior name used in
|
||||
// function to the "real" behavior name from the caller.
|
||||
gd::String comma = behaviorNamesMap.empty() ? "" : ",\n";
|
||||
gd::String comma = behaviorNamesMap.empty() ? "" : ", ";
|
||||
behaviorNamesMap += comma + ConvertToStringExplicit(parameter.GetName()) +
|
||||
": " + parameterMangledName;
|
||||
reinitializeBehaviorNamesMap +=
|
||||
"this._behaviorNamesMap[" +
|
||||
ConvertToStringExplicit(parameter.GetName()) +
|
||||
"] = " + parameterMangledName + ";\n";
|
||||
": " + parameterMangledName + "\n";
|
||||
} else {
|
||||
argumentsGetters +=
|
||||
" if (argName === " + ConvertToStringExplicit(parameter.GetName()) +
|
||||
") return this." + parameterMangledName + ";\n";
|
||||
"if (argName === " + ConvertToStringExplicit(parameter.GetName()) +
|
||||
") return " + parameterMangledName + ";\n";
|
||||
}
|
||||
}
|
||||
|
||||
const gd::String async = eventsFunction.IsAsync()
|
||||
? " this.task = new gdjs.ManuallyResolvableTask(),\n"
|
||||
? " task: new gdjs.ManuallyResolvableTask(),\n"
|
||||
: "";
|
||||
const gd::String clearAsync = eventsFunction.IsAsync()
|
||||
? " this.task = null,\n"
|
||||
: "";
|
||||
const int firstParameterIndex =
|
||||
(thisObjectName.empty() ? 0 : 1) + (thisBehaviorName.empty() ? 0 : 1);
|
||||
auto parameterList =
|
||||
GenerateEventsFunctionParameterDeclarationsList(
|
||||
eventsFunction.GetParametersForEvents(eventsFunctionsContainer),
|
||||
firstParameterIndex, true) +
|
||||
(thisObjectName.empty() && thisBehaviorName.empty() ? "" : ", that");
|
||||
|
||||
return GetCodeNamespaceAccessor() + "Context = class {\n"
|
||||
"constructor(" + parameterList + ") {\n" +
|
||||
GenerateEventsFunctionParametersToAttribues(
|
||||
eventsFunction.GetParametersForEvents(
|
||||
eventsFunctionsContainer),
|
||||
firstParameterIndex, true) +
|
||||
constructorAdditionalCode +
|
||||
// The async task, if there is one
|
||||
async +
|
||||
return gd::String("var eventsFunctionContext = {\n") +
|
||||
// The async task, if there is one
|
||||
async +
|
||||
// The object name to parameter map:
|
||||
" this._objectsMap = {\n" +
|
||||
objectsGettersMap + "\n"
|
||||
" };\n"
|
||||
" _objectsMap: {\n" + objectsGettersMap +
|
||||
"},\n"
|
||||
// The object name to arrays map:
|
||||
" this._objectArraysMap = {\n" +
|
||||
objectArraysMap + "\n"
|
||||
" };\n"
|
||||
" _objectArraysMap: {\n" +
|
||||
objectArraysMap +
|
||||
"},\n"
|
||||
// The behavior name to parameter map:
|
||||
" this._behaviorNamesMap = {\n" +
|
||||
behaviorNamesMap + "\n"
|
||||
" };\n"
|
||||
" this.globalVariablesForExtension = "
|
||||
" _behaviorNamesMap: {\n" +
|
||||
behaviorNamesMap +
|
||||
"},\n"
|
||||
" globalVariablesForExtension: "
|
||||
"runtimeScene.getGame().getVariablesForExtension(" +
|
||||
ConvertToStringExplicit(extensionName) + ");\n" +
|
||||
" this.sceneVariablesForExtension = "
|
||||
ConvertToStringExplicit(extensionName) + "),\n" +
|
||||
" sceneVariablesForExtension: "
|
||||
"runtimeScene.getScene().getVariablesForExtension(" +
|
||||
ConvertToStringExplicit(extensionName) + ");\n"
|
||||
ConvertToStringExplicit(extensionName) + "),\n" +
|
||||
// The local variables stack:
|
||||
" this.localVariables = [];\n"
|
||||
"}\n"
|
||||
|
||||
"reinitialize(" + parameterList + ") {\n" +
|
||||
GenerateEventsFunctionParametersToAttribues(
|
||||
eventsFunction.GetParametersForEvents(
|
||||
eventsFunctionsContainer),
|
||||
firstParameterIndex, true) +
|
||||
// The async task, if there is one
|
||||
async +
|
||||
// The object name to parameter map:
|
||||
reinitializeObjectsMap +
|
||||
// The object name to arrays map:
|
||||
reinitializeArraysMap +
|
||||
// The behavior name to parameter map:
|
||||
reinitializeBehaviorNamesMap +
|
||||
// globalVariablesForExtension stays the same.
|
||||
" this.sceneVariablesForExtension = "
|
||||
"runtimeScene.getScene().getVariablesForExtension(" +
|
||||
ConvertToStringExplicit(extensionName) + ");\n" +
|
||||
reinitializeAdditionalCode +
|
||||
"}\n"
|
||||
|
||||
"clear() {\n" +
|
||||
" this.runtimeScene = null;\n"
|
||||
" this.parentEventsFunctionContext = null;\n"
|
||||
// globalVariablesForExtension stays the same.
|
||||
" this.sceneVariablesForExtension = null;\n" +
|
||||
// The async task, if there is one
|
||||
clearAsync +
|
||||
// The object name to parameter map:
|
||||
clearObjectsMap +
|
||||
// The object name to arrays map:
|
||||
clearArraysMap +
|
||||
clearAdditionalCode +
|
||||
"}\n"
|
||||
|
||||
" localVariables: [],\n"
|
||||
// Function that will be used to query objects, when a new object list
|
||||
// is needed by events. We assume it's used a lot by the events
|
||||
// generated code, so we cache the arrays in a map.
|
||||
" getObjects(objectName) {\n"
|
||||
" return this._objectArraysMap[objectName] || [];\n"
|
||||
" }\n"
|
||||
" getObjects: function(objectName) {\n" +
|
||||
" return eventsFunctionContext._objectArraysMap[objectName] || "
|
||||
"[];\n" +
|
||||
" },\n" +
|
||||
// Function that can be used in JS code to get the lists of objects
|
||||
// and filter/alter them (not actually used in events).
|
||||
" getObjectsLists(objectName) {\n"
|
||||
" return this._objectsMap[objectName] || null;\n"
|
||||
" }\n"
|
||||
" getObjectsLists: function(objectName) {\n" +
|
||||
" return eventsFunctionContext._objectsMap[objectName] || null;\n"
|
||||
" },\n" +
|
||||
// Function that will be used to query behavior name (as behavior name
|
||||
// can be different between the parameter name vs the actual behavior
|
||||
// name passed as argument).
|
||||
" getBehaviorName(behaviorName) {\n"
|
||||
" getBehaviorName: function(behaviorName) {\n" +
|
||||
// TODO EBO Handle behavior name collision between parameters and
|
||||
// children
|
||||
" return this._behaviorNamesMap[behaviorName] || "
|
||||
" return eventsFunctionContext._behaviorNamesMap[behaviorName] || "
|
||||
"behaviorName;\n"
|
||||
" }\n"
|
||||
" },\n" +
|
||||
// Creator function that will be used to create new objects. We
|
||||
// need to check if the function was given the context of the calling
|
||||
// function (parentEventsFunctionContext). If this is the case, use it
|
||||
// to create the new object as the object names used in the function
|
||||
// are not the same as the objects available in the scene.
|
||||
" createObject(objectName) {\n"
|
||||
" const objectsList = this._objectsMap[objectName];\n"
|
||||
" createObject: function(objectName) {\n"
|
||||
" const objectsList = "
|
||||
"eventsFunctionContext._objectsMap[objectName];\n" +
|
||||
// TODO: we could speed this up by storing a map of object names, but
|
||||
// the cost of creating/storing it for each events function might not
|
||||
// be worth it.
|
||||
" if (objectsList) {\n"
|
||||
" const object = this.parentEventsFunctionContext ?\n"
|
||||
" if (objectsList) {\n" +
|
||||
" const object = parentEventsFunctionContext && "
|
||||
// Check if `objectName` is not a child object and is passed by
|
||||
// parameter from a parent instance container.
|
||||
"!(scopeInstanceContainer && "
|
||||
"scopeInstanceContainer.isObjectRegistered(objectName)) ?\n" +
|
||||
" "
|
||||
"this.parentEventsFunctionContext.createObject(objectsList.firstKey()) "
|
||||
":\n"
|
||||
" this.runtimeScene.createObject(objectsList.firstKey());\n"
|
||||
"parentEventsFunctionContext.createObject(objectsList.firstKey()) "
|
||||
":\n" +
|
||||
" runtimeScene.createObject(objectsList.firstKey());\n" +
|
||||
// Add the new instance to object lists
|
||||
" if (object) {\n"
|
||||
" objectsList.get(objectsList.firstKey()).push(object);\n"
|
||||
" this._objectArraysMap[objectName].push(object);\n"
|
||||
" }\n"
|
||||
" return object;\n"
|
||||
" }\n"
|
||||
" if (object) {\n" +
|
||||
" objectsList.get(objectsList.firstKey()).push(object);\n" +
|
||||
" "
|
||||
"eventsFunctionContext._objectArraysMap[objectName].push(object);\n" +
|
||||
" }\n" + " return object;\n" + " }\n" +
|
||||
// Unknown object, don't create anything:
|
||||
" return null;\n"
|
||||
" }\n"
|
||||
" return null;\n" +
|
||||
" },\n"
|
||||
// Function to count instances on the scene. We need it here because
|
||||
// it needs the objects map to get the object names of the parent
|
||||
// context.
|
||||
" getInstancesCountOnScene(objectName) {\n"
|
||||
" const objectsList = this._objectsMap[objectName];\n"
|
||||
" let count = 0;\n"
|
||||
" if (objectsList) {\n"
|
||||
" for(const objectName in objectsList.items)\n"
|
||||
" count += this.parentEventsFunctionContext ?\n"
|
||||
" this.parentEventsFunctionContext.getInstancesCountOnScene(objectName) :\n"
|
||||
" this.runtimeScene.getInstancesCountOnScene(objectName);\n"
|
||||
" }\n"
|
||||
" return count;\n"
|
||||
" }\n"
|
||||
" getInstancesCountOnScene: function(objectName) {\n"
|
||||
" const objectsList = "
|
||||
"eventsFunctionContext._objectsMap[objectName];\n" +
|
||||
" let count = 0;\n" + " if (objectsList) {\n" +
|
||||
" for(const objectName in objectsList.items)\n" +
|
||||
" count += parentEventsFunctionContext && "
|
||||
// Check if `objectName` is not a child object and is passed by
|
||||
// parameter from a parent instance container.
|
||||
"!(scopeInstanceContainer && "
|
||||
"scopeInstanceContainer.isObjectRegistered(objectName)) ?\n" +
|
||||
"parentEventsFunctionContext.getInstancesCountOnScene(objectName) "
|
||||
":\n" +
|
||||
" runtimeScene.getInstancesCountOnScene(objectName);\n" +
|
||||
" }\n" + " return count;\n" +
|
||||
" },\n"
|
||||
// Allow to get a layer directly from the context for convenience:
|
||||
" getLayer(layerName) {\n"
|
||||
" return this.runtimeScene.getLayer(layerName);\n"
|
||||
" }\n"
|
||||
" getLayer: function(layerName) {\n"
|
||||
" return runtimeScene.getLayer(layerName);\n"
|
||||
" },\n"
|
||||
// Getter for arguments that are not objects
|
||||
" getArgument(argName) {\n" + argumentsGetters + //
|
||||
" return \"\";\n"
|
||||
" }\n"
|
||||
" getArgument: function(argName) {\n" +
|
||||
argumentsGetters + " return \"\";\n" + " },\n" +
|
||||
// Expose OnceTriggers (will be pointing either to the runtime scene
|
||||
// ones, or the ones from the behavior):
|
||||
" getOnceTriggers() { return this." + onceTriggersVariable + "; }\n"
|
||||
"}\n";
|
||||
" getOnceTriggers: function() { return " + onceTriggersVariable +
|
||||
"; }\n" + "};\n";
|
||||
}
|
||||
|
||||
gd::String EventsCodeGenerator::GenerateEventsFunctionReturn(
|
||||
|
@@ -375,7 +375,6 @@ class EventsCodeGenerator : public gd::EventsCodeGenerator {
|
||||
gdjs::EventsCodeGenerator& codeGenerator,
|
||||
gd::String fullyQualifiedFunctionName,
|
||||
gd::String functionArgumentsCode,
|
||||
gd::String contextClassCode,
|
||||
gd::String functionPreEventsCode,
|
||||
const gd::EventsList& events,
|
||||
gd::String functionPostEventsCode,
|
||||
@@ -410,16 +409,6 @@ class EventsCodeGenerator : public gd::EventsCodeGenerator {
|
||||
int firstParameterIndex,
|
||||
bool addsSceneParameter);
|
||||
|
||||
/**
|
||||
* \brief Generate the affectation from parameters to class attributes.
|
||||
*
|
||||
* \note runtimeScene is always added as the first parameter, and
|
||||
* parentEventsFunctionContext as the last parameter.
|
||||
*/
|
||||
gd::String GenerateEventsFunctionParametersToAttribues(
|
||||
const gd::ParameterMetadataContainer ¶meters, int firstParameterIndex,
|
||||
bool addsSceneParameter);
|
||||
|
||||
/**
|
||||
* \brief Generate the "eventsFunctionContext" object that allow a free
|
||||
* function to provides access objects, object creation and access to
|
||||
@@ -486,9 +475,6 @@ class EventsCodeGenerator : public gd::EventsCodeGenerator {
|
||||
gd::String &objectsGettersMap,
|
||||
gd::String &objectArraysMap,
|
||||
gd::String &behaviorNamesMap,
|
||||
const gd::String &constructorAdditionalCode = "",
|
||||
const gd::String &reinitializeAdditionalCode = "",
|
||||
const gd::String& clearAdditionalCode = "",
|
||||
const gd::String &thisObjectName = "",
|
||||
const gd::String &thisBehaviorName = "");
|
||||
};
|
||||
|
@@ -18,6 +18,8 @@ KeyboardExtension::KeyboardExtension() {
|
||||
"gdjs.evtTools.input.wasKeyReleased");
|
||||
GetAllConditions()["KeyFromTextPressed"].SetFunctionName(
|
||||
"gdjs.evtTools.input.isKeyPressed");
|
||||
GetAllConditions()["KeyFromTextJustPressed"].SetFunctionName(
|
||||
"gdjs.evtTools.input.wasKeyJustPressed");
|
||||
GetAllConditions()["KeyFromTextReleased"].SetFunctionName(
|
||||
"gdjs.evtTools.input.wasKeyReleased");
|
||||
GetAllConditions()["AnyKeyPressed"].SetFunctionName(
|
||||
|
@@ -18,6 +18,9 @@
|
||||
#include "GDCore/IDE/Project/ProjectResourcesCopier.h"
|
||||
#include "GDCore/IDE/Project/SceneResourcesFinder.h"
|
||||
#include "GDCore/IDE/ProjectStripper.h"
|
||||
#include "GDCore/Project/EventsBasedObject.h"
|
||||
#include "GDCore/Project/EventsBasedObjectVariant.h"
|
||||
#include "GDCore/Project/EventsFunctionsExtension.h"
|
||||
#include "GDCore/Project/ExternalEvents.h"
|
||||
#include "GDCore/Project/ExternalLayout.h"
|
||||
#include "GDCore/Project/Layout.h"
|
||||
@@ -47,7 +50,7 @@ Exporter::~Exporter() {}
|
||||
bool Exporter::ExportProjectForPixiPreview(
|
||||
const PreviewExportOptions &options) {
|
||||
ExporterHelper helper(fs, gdjsRoot, codeOutputDir);
|
||||
return helper.ExportProjectForPixiPreview(options);
|
||||
return helper.ExportProjectForPixiPreview(options, includesFiles);
|
||||
}
|
||||
|
||||
bool Exporter::ExportWholePixiProject(const ExportOptions &options) {
|
||||
@@ -80,7 +83,7 @@ bool Exporter::ExportWholePixiProject(const ExportOptions &options) {
|
||||
|
||||
// Prepare the export directory
|
||||
fs.MkDir(exportDir);
|
||||
std::vector<gd::String> includesFiles;
|
||||
includesFiles.clear();
|
||||
std::vector<gd::String> resourcesFiles;
|
||||
|
||||
// Export the resources (before generating events as some resources
|
||||
@@ -98,6 +101,7 @@ bool Exporter::ExportWholePixiProject(const ExportOptions &options) {
|
||||
helper.AddLibsInclude(
|
||||
/*pixiRenderers=*/true,
|
||||
usedExtensionsResult.Has3DObjects(),
|
||||
/*isInGameEditor=*/false,
|
||||
/*includeWebsocketDebuggerClient=*/false,
|
||||
/*includeWindowMessageDebuggerClient=*/false,
|
||||
/*includeMinimalDebuggerClient=*/false,
|
||||
@@ -120,7 +124,7 @@ bool Exporter::ExportWholePixiProject(const ExportOptions &options) {
|
||||
helper.ExportEffectIncludes(exportedProject, includesFiles);
|
||||
|
||||
// Export events
|
||||
if (!helper.ExportEventsCode(exportedProject,
|
||||
if (!helper.ExportScenesEventsCode(exportedProject,
|
||||
codeOutputDir,
|
||||
includesFiles,
|
||||
wholeProjectDiagnosticReport,
|
||||
@@ -130,29 +134,10 @@ bool Exporter::ExportWholePixiProject(const ExportOptions &options) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto projectUsedResources =
|
||||
gd::SceneResourcesFinder::FindProjectResources(exportedProject);
|
||||
std::unordered_map<gd::String, std::set<gd::String>> scenesUsedResources;
|
||||
for (std::size_t layoutIndex = 0;
|
||||
layoutIndex < exportedProject.GetLayoutsCount();
|
||||
layoutIndex++) {
|
||||
auto &layout = exportedProject.GetLayout(layoutIndex);
|
||||
scenesUsedResources[layout.GetName()] =
|
||||
gd::SceneResourcesFinder::FindSceneResources(exportedProject, layout);
|
||||
}
|
||||
|
||||
// Strip the project (*after* generating events as the events may use
|
||||
// stripped things like objects groups...)...
|
||||
gd::ProjectStripper::StripProjectForExport(exportedProject);
|
||||
|
||||
//...and export it
|
||||
gd::SerializerElement noRuntimeGameOptions;
|
||||
helper.ExportProjectData(fs,
|
||||
exportedProject,
|
||||
codeOutputDir + "/data.js",
|
||||
noRuntimeGameOptions,
|
||||
projectUsedResources,
|
||||
scenesUsedResources);
|
||||
helper.ExportProjectData(fs, exportedProject, codeOutputDir + "/data.js",
|
||||
noRuntimeGameOptions);
|
||||
includesFiles.push_back(codeOutputDir + "/data.js");
|
||||
|
||||
helper.ExportIncludesAndLibs(includesFiles, exportDir, false);
|
||||
@@ -215,4 +200,18 @@ bool Exporter::ExportWholePixiProject(const ExportOptions &options) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void Exporter::SerializeProjectData(const gd::Project &project,
|
||||
const PreviewExportOptions &options,
|
||||
gd::SerializerElement &projectDataElement) {
|
||||
ExporterHelper::SerializeProjectData(fs, project, options,
|
||||
projectDataElement);
|
||||
}
|
||||
|
||||
void Exporter::SerializeRuntimeGameOptions(
|
||||
const PreviewExportOptions &options,
|
||||
gd::SerializerElement &runtimeGameOptionsElement) {
|
||||
ExporterHelper::SerializeRuntimeGameOptions(
|
||||
fs, gdjsRoot, options, includesFiles, runtimeGameOptionsElement);
|
||||
}
|
||||
|
||||
} // namespace gdjs
|
||||
|
@@ -16,6 +16,7 @@ class Project;
|
||||
class Layout;
|
||||
class ExternalLayout;
|
||||
class AbstractFileSystem;
|
||||
class SerializerElement;
|
||||
} // namespace gd
|
||||
namespace gdjs {
|
||||
struct PreviewExportOptions;
|
||||
@@ -64,7 +65,33 @@ class Exporter {
|
||||
codeOutputDir = codeOutputDir_;
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* \brief Serialize a project without its events to JSON
|
||||
*
|
||||
* \param project The project to be exported
|
||||
* \param options The content of the extra configuration
|
||||
* \param projectDataElement The element where the project data is serialized
|
||||
*/
|
||||
void SerializeProjectData(const gd::Project &project,
|
||||
const PreviewExportOptions &options,
|
||||
gd::SerializerElement &projectDataElement);
|
||||
|
||||
/**
|
||||
* \brief Serialize the content of the extra configuration to store
|
||||
* in gdjs.runtimeGameOptions to JSON
|
||||
*
|
||||
* \warning `ExportProjectForPixiPreview` must be called first to serialize
|
||||
* the list of scripts files.
|
||||
*
|
||||
* \param options The content of the extra configuration
|
||||
* \param runtimeGameOptionsElement The element where the game options are
|
||||
* serialized
|
||||
*/
|
||||
void
|
||||
SerializeRuntimeGameOptions(const PreviewExportOptions &options,
|
||||
gd::SerializerElement &runtimeGameOptionsElement);
|
||||
|
||||
private:
|
||||
gd::AbstractFileSystem&
|
||||
fs; ///< The abstract file system to be used for exportation.
|
||||
gd::String lastError; ///< The last error that occurred.
|
||||
@@ -72,6 +99,8 @@ class Exporter {
|
||||
gdjsRoot; ///< The root directory of GDJS, used to copy runtime files.
|
||||
gd::String codeOutputDir; ///< The directory where JS code is outputted. Will
|
||||
///< be then copied to the final output directory.
|
||||
std::vector<gd::String>
|
||||
includesFiles; ///< The list of scripts files - useful for hot-reloading
|
||||
};
|
||||
|
||||
} // namespace gdjs
|
||||
|
@@ -28,10 +28,13 @@
|
||||
#include "GDCore/IDE/Events/UsedExtensionsFinder.h"
|
||||
#include "GDCore/IDE/ExportedDependencyResolver.h"
|
||||
#include "GDCore/IDE/Project/ProjectResourcesCopier.h"
|
||||
#include "GDCore/IDE/Project/ResourcesMergingHelper.h"
|
||||
#include "GDCore/IDE/Project/SceneResourcesFinder.h"
|
||||
#include "GDCore/IDE/ProjectStripper.h"
|
||||
#include "GDCore/IDE/ResourceExposer.h"
|
||||
#include "GDCore/IDE/SceneNameMangler.h"
|
||||
#include "GDCore/Project/EventsBasedObject.h"
|
||||
#include "GDCore/Project/EventsBasedObjectVariant.h"
|
||||
#include "GDCore/Project/EventsFunctionsExtension.h"
|
||||
#include "GDCore/Project/ExternalEvents.h"
|
||||
#include "GDCore/Project/ExternalLayout.h"
|
||||
@@ -58,7 +61,6 @@ double GetTimeSpent(double previousTime) { return GetTimeNow() - previousTime; }
|
||||
double LogTimeSpent(const gd::String &name, double previousTime) {
|
||||
gd::LogStatus(name + " took " + gd::String::From(GetTimeSpent(previousTime)) +
|
||||
"ms");
|
||||
std::cout << std::endl;
|
||||
return GetTimeNow();
|
||||
}
|
||||
} // namespace
|
||||
@@ -104,128 +106,297 @@ ExporterHelper::ExporterHelper(gd::AbstractFileSystem &fileSystem,
|
||||
: fs(fileSystem), gdjsRoot(gdjsRoot_), codeOutputDir(codeOutputDir_) {};
|
||||
|
||||
bool ExporterHelper::ExportProjectForPixiPreview(
|
||||
const PreviewExportOptions &options) {
|
||||
const PreviewExportOptions &options,
|
||||
std::vector<gd::String> &includesFiles) {
|
||||
|
||||
if (options.isInGameEdition && !options.shouldReloadProjectData &&
|
||||
!options.shouldReloadLibraries && !options.shouldGenerateScenesEventsCode) {
|
||||
gd::LogStatus("Skip project export entirely");
|
||||
return "";
|
||||
}
|
||||
|
||||
double previousTime = GetTimeNow();
|
||||
fs.MkDir(options.exportPath);
|
||||
fs.ClearDir(options.exportPath);
|
||||
std::vector<gd::String> includesFiles;
|
||||
if (options.shouldClearExportFolder) {
|
||||
fs.ClearDir(options.exportPath);
|
||||
}
|
||||
includesFiles.clear();
|
||||
std::vector<gd::String> resourcesFiles;
|
||||
|
||||
// TODO Try to remove side effects to avoid the copy
|
||||
// that destroys the AST in cache.
|
||||
gd::Project exportedProject = options.project;
|
||||
const gd::Project &immutableProject = exportedProject;
|
||||
const gd::Project &immutableProject = options.project;
|
||||
previousTime = LogTimeSpent("Project cloning", previousTime);
|
||||
|
||||
if (options.fullLoadingScreen) {
|
||||
// Use project properties fallback to set empty properties
|
||||
if (exportedProject.GetAuthorIds().empty() &&
|
||||
!options.fallbackAuthorId.empty()) {
|
||||
exportedProject.GetAuthorIds().push_back(options.fallbackAuthorId);
|
||||
}
|
||||
if (exportedProject.GetAuthorUsernames().empty() &&
|
||||
!options.fallbackAuthorUsername.empty()) {
|
||||
exportedProject.GetAuthorUsernames().push_back(
|
||||
options.fallbackAuthorUsername);
|
||||
if (options.isInGameEdition) {
|
||||
if (options.shouldReloadProjectData || options.shouldGenerateScenesEventsCode) {
|
||||
auto projectDirectory = fs.DirNameFrom(exportedProject.GetProjectFile());
|
||||
gd::ResourcesMergingHelper resourcesMergingHelper(
|
||||
exportedProject.GetResourcesManager(), fs);
|
||||
resourcesMergingHelper.SetBaseDirectory(projectDirectory);
|
||||
resourcesMergingHelper.SetShouldUseOriginalAbsoluteFilenames();
|
||||
gd::ResourceExposer::ExposeWholeProjectResources(exportedProject,
|
||||
resourcesMergingHelper);
|
||||
|
||||
previousTime = LogTimeSpent("Resource path resolving", previousTime);
|
||||
}
|
||||
gd::LogStatus("Resource export is skipped");
|
||||
} else {
|
||||
// Most of the time, we skip the logo and minimum duration so that
|
||||
// the preview start as soon as possible.
|
||||
exportedProject.GetLoadingScreen()
|
||||
.ShowGDevelopLogoDuringLoadingScreen(false)
|
||||
.SetMinDuration(0);
|
||||
exportedProject.GetWatermark().ShowGDevelopWatermark(false);
|
||||
// Export resources (*before* generating events as some resources filenames
|
||||
// may be updated)
|
||||
ExportResources(fs, exportedProject, options.exportPath);
|
||||
|
||||
previousTime = LogTimeSpent("Resource export", previousTime);
|
||||
}
|
||||
|
||||
// Export resources (*before* generating events as some resources filenames
|
||||
// may be updated)
|
||||
ExportResources(fs, exportedProject, options.exportPath);
|
||||
|
||||
previousTime = LogTimeSpent("Resource export", previousTime);
|
||||
|
||||
// Compatibility with GD <= 5.0-beta56
|
||||
// Stay compatible with text objects declaring their font as just a filename
|
||||
// without a font resource - by manually adding these resources.
|
||||
AddDeprecatedFontFilesToFontResources(
|
||||
fs, exportedProject.GetResourcesManager(), options.exportPath);
|
||||
// end of compatibility code
|
||||
|
||||
auto usedExtensionsResult =
|
||||
gd::UsedExtensionsFinder::ScanProject(exportedProject);
|
||||
|
||||
// Export engine libraries
|
||||
AddLibsInclude(/*pixiRenderers=*/true,
|
||||
usedExtensionsResult.Has3DObjects(),
|
||||
/*includeWebsocketDebuggerClient=*/
|
||||
!options.websocketDebuggerServerAddress.empty(),
|
||||
/*includeWindowMessageDebuggerClient=*/
|
||||
options.useWindowMessageDebuggerClient,
|
||||
/*includeMinimalDebuggerClient=*/
|
||||
options.useMinimalDebuggerClient,
|
||||
/*includeCaptureManager=*/
|
||||
!options.captureOptions.IsEmpty(),
|
||||
/*includeInAppTutorialMessage*/
|
||||
!options.inAppTutorialMessageInPreview.empty(),
|
||||
immutableProject.GetLoadingScreen().GetGDevelopLogoStyle(),
|
||||
includesFiles);
|
||||
|
||||
// Export files for free function, object and behaviors
|
||||
for (const auto &includeFile : usedExtensionsResult.GetUsedIncludeFiles()) {
|
||||
InsertUnique(includesFiles, includeFile);
|
||||
}
|
||||
for (const auto &requiredFile : usedExtensionsResult.GetUsedRequiredFiles()) {
|
||||
InsertUnique(resourcesFiles, requiredFile);
|
||||
if (options.shouldReloadProjectData || options.shouldGenerateScenesEventsCode) {
|
||||
// Compatibility with GD <= 5.0-beta56
|
||||
// Stay compatible with text objects declaring their font as just a filename
|
||||
// without a font resource - by manually adding these resources.
|
||||
AddDeprecatedFontFilesToFontResources(
|
||||
fs, exportedProject.GetResourcesManager(), options.exportPath);
|
||||
// end of compatibility code
|
||||
}
|
||||
|
||||
// Export effects (after engine libraries as they auto-register themselves to
|
||||
// the engine)
|
||||
ExportEffectIncludes(exportedProject, includesFiles);
|
||||
std::vector<gd::SourceFileMetadata> noUsedSourceFiles;
|
||||
std::vector<gd::SourceFileMetadata> &usedSourceFiles = noUsedSourceFiles;
|
||||
if (options.shouldReloadLibraries) {
|
||||
auto usedExtensionsResult =
|
||||
gd::UsedExtensionsFinder::ScanProject(exportedProject);
|
||||
usedSourceFiles = usedExtensionsResult.GetUsedSourceFiles();
|
||||
|
||||
previousTime = LogTimeSpent("Include files export", previousTime);
|
||||
// Export engine libraries
|
||||
AddLibsInclude(/*pixiRenderers=*/true,
|
||||
/*pixiInThreeRenderers=*/
|
||||
usedExtensionsResult.Has3DObjects(),
|
||||
/*isInGameEdition=*/
|
||||
options.isInGameEdition,
|
||||
/*includeWebsocketDebuggerClient=*/
|
||||
!options.websocketDebuggerServerAddress.empty(),
|
||||
/*includeWindowMessageDebuggerClient=*/
|
||||
options.useWindowMessageDebuggerClient,
|
||||
/*includeMinimalDebuggerClient=*/
|
||||
options.useMinimalDebuggerClient,
|
||||
/*includeCaptureManager=*/
|
||||
!options.captureOptions.IsEmpty(),
|
||||
/*includeInAppTutorialMessage*/
|
||||
!options.inAppTutorialMessageInPreview.empty(),
|
||||
immutableProject.GetLoadingScreen().GetGDevelopLogoStyle(),
|
||||
includesFiles);
|
||||
|
||||
if (!options.projectDataOnlyExport) {
|
||||
// Export files for free function, object and behaviors
|
||||
for (const auto &includeFile : usedExtensionsResult.GetUsedIncludeFiles()) {
|
||||
InsertUnique(includesFiles, includeFile);
|
||||
}
|
||||
for (const auto &requiredFile : usedExtensionsResult.GetUsedRequiredFiles()) {
|
||||
InsertUnique(resourcesFiles, requiredFile);
|
||||
}
|
||||
if (options.isInGameEdition) {
|
||||
// TODO Scan the objects and events of event-based objects
|
||||
// (it could be an alternative method ScanProjectAndEventsBasedObjects in
|
||||
// UsedExtensionsFinder).
|
||||
// This is already done by UsedExtensionsFinder, but maybe it shouldn't.
|
||||
|
||||
// Export all event-based objects because they can be edited even if they
|
||||
// are not used yet.
|
||||
for (std::size_t e = 0;
|
||||
e < exportedProject.GetEventsFunctionsExtensionsCount(); e++) {
|
||||
auto &eventsFunctionsExtension =
|
||||
exportedProject.GetEventsFunctionsExtension(e);
|
||||
|
||||
for (auto &&eventsBasedObjectUniquePtr :
|
||||
eventsFunctionsExtension.GetEventsBasedObjects()
|
||||
.GetInternalVector()) {
|
||||
auto eventsBasedObject = eventsBasedObjectUniquePtr.get();
|
||||
|
||||
auto metadata = gd::MetadataProvider::GetExtensionAndObjectMetadata(
|
||||
exportedProject.GetCurrentPlatform(),
|
||||
gd::PlatformExtension::GetObjectFullType(
|
||||
eventsFunctionsExtension.GetName(),
|
||||
eventsBasedObject->GetName()));
|
||||
for (auto &&includeFile : metadata.GetMetadata().includeFiles) {
|
||||
InsertUnique(includesFiles, includeFile);
|
||||
}
|
||||
for (auto &behaviorType :
|
||||
metadata.GetMetadata().GetDefaultBehaviors()) {
|
||||
auto behaviorMetadata =
|
||||
gd::MetadataProvider::GetExtensionAndBehaviorMetadata(
|
||||
exportedProject.GetCurrentPlatform(), behaviorType);
|
||||
for (auto &&includeFile :
|
||||
behaviorMetadata.GetMetadata().includeFiles) {
|
||||
InsertUnique(includesFiles, includeFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Export effects (after engine libraries as they auto-register themselves to
|
||||
// the engine)
|
||||
ExportEffectIncludes(exportedProject, includesFiles);
|
||||
|
||||
previousTime = LogTimeSpent("Include files export", previousTime);
|
||||
}
|
||||
else {
|
||||
gd::LogStatus("Include files export is skipped");
|
||||
}
|
||||
|
||||
if (options.shouldGenerateScenesEventsCode) {
|
||||
gd::WholeProjectDiagnosticReport &wholeProjectDiagnosticReport =
|
||||
options.project.GetWholeProjectDiagnosticReport();
|
||||
wholeProjectDiagnosticReport.Clear();
|
||||
|
||||
// Generate events code
|
||||
if (!ExportEventsCode(immutableProject,
|
||||
if (!ExportScenesEventsCode(immutableProject,
|
||||
codeOutputDir,
|
||||
includesFiles,
|
||||
wholeProjectDiagnosticReport,
|
||||
true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
previousTime = LogTimeSpent("Events code export", previousTime);
|
||||
}
|
||||
|
||||
auto projectUsedResources =
|
||||
gd::SceneResourcesFinder::FindProjectResources(exportedProject);
|
||||
std::unordered_map<gd::String, std::set<gd::String>> scenesUsedResources;
|
||||
for (std::size_t layoutIndex = 0;
|
||||
layoutIndex < exportedProject.GetLayoutsCount();
|
||||
layoutIndex++) {
|
||||
auto &layout = exportedProject.GetLayout(layoutIndex);
|
||||
scenesUsedResources[layout.GetName()] =
|
||||
gd::SceneResourcesFinder::FindSceneResources(exportedProject, layout);
|
||||
else {
|
||||
gd::LogStatus("Events code export is skipped");
|
||||
}
|
||||
|
||||
// Strip the project (*after* generating events as the events may use stripped
|
||||
// things (objects groups...))
|
||||
gd::ProjectStripper::StripProjectForExport(exportedProject);
|
||||
exportedProject.SetFirstLayout(options.layoutName);
|
||||
if (options.shouldReloadProjectData) {
|
||||
|
||||
if (options.fullLoadingScreen) {
|
||||
// Use project properties fallback to set empty properties
|
||||
if (exportedProject.GetAuthorIds().empty() &&
|
||||
!options.fallbackAuthorId.empty()) {
|
||||
exportedProject.GetAuthorIds().push_back(options.fallbackAuthorId);
|
||||
}
|
||||
if (exportedProject.GetAuthorUsernames().empty() &&
|
||||
!options.fallbackAuthorUsername.empty()) {
|
||||
exportedProject.GetAuthorUsernames().push_back(
|
||||
options.fallbackAuthorUsername);
|
||||
}
|
||||
} else {
|
||||
// Most of the time, we skip the logo and minimum duration so that
|
||||
// the preview start as soon as possible.
|
||||
exportedProject.GetLoadingScreen()
|
||||
.ShowGDevelopLogoDuringLoadingScreen(false)
|
||||
.SetMinDuration(0);
|
||||
exportedProject.GetWatermark().ShowGDevelopWatermark(false);
|
||||
}
|
||||
|
||||
previousTime = LogTimeSpent("Data stripping", previousTime);
|
||||
gd::SerializerElement runtimeGameOptions;
|
||||
ExporterHelper::SerializeRuntimeGameOptions(fs, gdjsRoot, options,
|
||||
includesFiles, runtimeGameOptions);
|
||||
ExportProjectData(fs,
|
||||
exportedProject,
|
||||
codeOutputDir + "/data.js",
|
||||
runtimeGameOptions);
|
||||
includesFiles.push_back(codeOutputDir + "/data.js");
|
||||
|
||||
previousTime = LogTimeSpent("Project data export", previousTime);
|
||||
}
|
||||
else {
|
||||
gd::LogStatus("Project data export is skipped");
|
||||
}
|
||||
|
||||
if (options.shouldReloadLibraries) {
|
||||
// Copy all the dependencies and their source maps
|
||||
ExportIncludesAndLibs(includesFiles, options.exportPath, true);
|
||||
ExportIncludesAndLibs(resourcesFiles, options.exportPath, true);
|
||||
|
||||
// TODO Build a full includesFiles list without actually doing export or
|
||||
// generation.
|
||||
if (options.shouldGenerateScenesEventsCode) {
|
||||
// Create the index file
|
||||
if (!ExportIndexFile(exportedProject, gdjsRoot + "/Runtime/index.html",
|
||||
options.exportPath, includesFiles, usedSourceFiles,
|
||||
options.nonRuntimeScriptsCacheBurst,
|
||||
"gdjs.runtimeGameOptions")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
previousTime = LogTimeSpent("Include and libs export", previousTime);
|
||||
} else {
|
||||
gd::LogStatus("Include and libs export is skipped");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
gd::String ExporterHelper::ExportProjectData(
|
||||
gd::AbstractFileSystem &fs, gd::Project &project, gd::String filename,
|
||||
const gd::SerializerElement &runtimeGameOptions) {
|
||||
fs.MkDir(fs.DirNameFrom(filename));
|
||||
|
||||
gd::SerializerElement projectDataElement;
|
||||
ExporterHelper::StriptAndSerializeProjectData(project, projectDataElement);
|
||||
|
||||
// Save the project to JSON
|
||||
gd::String output =
|
||||
"gdjs.projectData = " + gd::Serializer::ToJSON(projectDataElement) +
|
||||
";\ngdjs.runtimeGameOptions = " + gd::Serializer::ToJSON(runtimeGameOptions) +
|
||||
";\n";
|
||||
|
||||
if (!fs.WriteToFile(filename, output))
|
||||
return "Unable to write " + filename;
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
void ExporterHelper::SerializeRuntimeGameOptions(
|
||||
gd::AbstractFileSystem &fs, const gd::String &gdjsRoot,
|
||||
const PreviewExportOptions &options,
|
||||
std::vector<gd::String> &includesFiles,
|
||||
gd::SerializerElement &runtimeGameOptions) {
|
||||
// Create the setup options passed to the gdjs.RuntimeGame
|
||||
gd::SerializerElement runtimeGameOptions;
|
||||
runtimeGameOptions.AddChild("isPreview").SetBoolValue(true);
|
||||
if (!options.externalLayoutName.empty()) {
|
||||
runtimeGameOptions.AddChild("injectExternalLayout")
|
||||
.SetValue(options.externalLayoutName);
|
||||
|
||||
auto &initialRuntimeGameStatus =
|
||||
runtimeGameOptions.AddChild("initialRuntimeGameStatus");
|
||||
initialRuntimeGameStatus.AddChild("sceneName")
|
||||
.SetStringValue(options.layoutName);
|
||||
if (options.isInGameEdition) {
|
||||
initialRuntimeGameStatus.AddChild("isInGameEdition").SetBoolValue(true);
|
||||
initialRuntimeGameStatus.AddChild("editorId").SetValue(options.editorId);
|
||||
if (!options.editorCamera3DCameraMode.empty()) {
|
||||
auto &editorCamera3D =
|
||||
initialRuntimeGameStatus.AddChild("editorCamera3D");
|
||||
editorCamera3D.AddChild("cameraMode").SetStringValue(
|
||||
options.editorCamera3DCameraMode);
|
||||
editorCamera3D.AddChild("positionX")
|
||||
.SetDoubleValue(options.editorCamera3DPositionX);
|
||||
editorCamera3D.AddChild("positionY")
|
||||
.SetDoubleValue(options.editorCamera3DPositionY);
|
||||
editorCamera3D.AddChild("positionZ")
|
||||
.SetDoubleValue(options.editorCamera3DPositionZ);
|
||||
editorCamera3D.AddChild("rotationAngle")
|
||||
.SetDoubleValue(options.editorCamera3DRotationAngle);
|
||||
editorCamera3D.AddChild("elevationAngle")
|
||||
.SetDoubleValue(options.editorCamera3DElevationAngle);
|
||||
editorCamera3D.AddChild("distance")
|
||||
.SetDoubleValue(options.editorCamera3DDistance);
|
||||
}
|
||||
}
|
||||
runtimeGameOptions.AddChild("projectDataOnlyExport")
|
||||
.SetBoolValue(options.projectDataOnlyExport);
|
||||
if (!options.externalLayoutName.empty()) {
|
||||
initialRuntimeGameStatus.AddChild("injectedExternalLayoutName")
|
||||
.SetValue(options.externalLayoutName);
|
||||
|
||||
if (options.isInGameEdition) {
|
||||
initialRuntimeGameStatus.AddChild("skipCreatingInstancesFromScene")
|
||||
.SetBoolValue(true);
|
||||
}
|
||||
}
|
||||
if (!options.eventsBasedObjectType.empty()) {
|
||||
initialRuntimeGameStatus.AddChild("eventsBasedObjectType")
|
||||
.SetValue(options.eventsBasedObjectType);
|
||||
initialRuntimeGameStatus.AddChild("eventsBasedObjectVariantName")
|
||||
.SetValue(options.eventsBasedObjectVariantName);
|
||||
}
|
||||
|
||||
runtimeGameOptions.AddChild("shouldReloadLibraries")
|
||||
.SetBoolValue(options.shouldReloadLibraries);
|
||||
runtimeGameOptions.AddChild("shouldGenerateScenesEventsCode")
|
||||
.SetBoolValue(options.shouldGenerateScenesEventsCode);
|
||||
|
||||
runtimeGameOptions.AddChild("nativeMobileApp")
|
||||
.SetBoolValue(options.nativeMobileApp);
|
||||
runtimeGameOptions.AddChild("websocketDebuggerServerAddress")
|
||||
@@ -297,71 +468,93 @@ bool ExporterHelper::ExportProjectForPixiPreview(
|
||||
|
||||
for (const auto &includeFile : includesFiles) {
|
||||
auto hashIt = options.includeFileHashes.find(includeFile);
|
||||
gd::String scriptSrc = GetExportedIncludeFilename(includeFile);
|
||||
gd::String scriptSrc = GetExportedIncludeFilename(fs, gdjsRoot, includeFile);
|
||||
scriptFilesElement.AddChild("scriptFile")
|
||||
.SetStringAttribute("path", scriptSrc)
|
||||
.SetIntAttribute(
|
||||
"hash",
|
||||
hashIt != options.includeFileHashes.end() ? hashIt->second : 0);
|
||||
}
|
||||
|
||||
// Export the project
|
||||
ExportProjectData(fs,
|
||||
exportedProject,
|
||||
codeOutputDir + "/data.js",
|
||||
runtimeGameOptions,
|
||||
projectUsedResources,
|
||||
scenesUsedResources);
|
||||
includesFiles.push_back(codeOutputDir + "/data.js");
|
||||
|
||||
previousTime = LogTimeSpent("Project data export", previousTime);
|
||||
|
||||
// Copy all the dependencies and their source maps
|
||||
ExportIncludesAndLibs(includesFiles, options.exportPath, true);
|
||||
ExportIncludesAndLibs(resourcesFiles, options.exportPath, true);
|
||||
|
||||
// Create the index file
|
||||
if (!ExportIndexFile(exportedProject,
|
||||
gdjsRoot + "/Runtime/index.html",
|
||||
options.exportPath,
|
||||
includesFiles,
|
||||
usedExtensionsResult.GetUsedSourceFiles(),
|
||||
options.nonRuntimeScriptsCacheBurst,
|
||||
"gdjs.runtimeGameOptions"))
|
||||
return false;
|
||||
|
||||
previousTime = LogTimeSpent("Include and libs export", previousTime);
|
||||
return true;
|
||||
}
|
||||
|
||||
gd::String ExporterHelper::ExportProjectData(
|
||||
gd::AbstractFileSystem &fs,
|
||||
gd::Project &project,
|
||||
gd::String filename,
|
||||
const gd::SerializerElement &runtimeGameOptions,
|
||||
std::set<gd::String> &projectUsedResources,
|
||||
std::unordered_map<gd::String, std::set<gd::String>> &scenesUsedResources) {
|
||||
fs.MkDir(fs.DirNameFrom(filename));
|
||||
void ExporterHelper::SerializeProjectData(gd::AbstractFileSystem &fs,
|
||||
const gd::Project &project,
|
||||
const PreviewExportOptions &options,
|
||||
gd::SerializerElement &rootElement) {
|
||||
gd::Project clonedProject = project;
|
||||
|
||||
// Replace all resource file paths with the one used in exported projects.
|
||||
auto projectDirectory = fs.DirNameFrom(project.GetProjectFile());
|
||||
gd::ResourcesMergingHelper resourcesMergingHelper(
|
||||
clonedProject.GetResourcesManager(), fs);
|
||||
resourcesMergingHelper.SetBaseDirectory(projectDirectory);
|
||||
if (options.isInGameEdition) {
|
||||
resourcesMergingHelper.SetShouldUseOriginalAbsoluteFilenames();
|
||||
} else {
|
||||
resourcesMergingHelper.PreserveDirectoriesStructure(false);
|
||||
resourcesMergingHelper.PreserveAbsoluteFilenames(false);
|
||||
}
|
||||
gd::ResourceExposer::ExposeWholeProjectResources(clonedProject,
|
||||
resourcesMergingHelper);
|
||||
|
||||
ExporterHelper::StriptAndSerializeProjectData(clonedProject, rootElement);
|
||||
}
|
||||
|
||||
void ExporterHelper::StriptAndSerializeProjectData(
|
||||
gd::Project &project, gd::SerializerElement &rootElement) {
|
||||
auto projectUsedResources =
|
||||
gd::SceneResourcesFinder::FindProjectResources(project);
|
||||
std::unordered_map<gd::String, std::set<gd::String>> scenesUsedResources;
|
||||
for (std::size_t layoutIndex = 0;
|
||||
layoutIndex < project.GetLayoutsCount(); layoutIndex++) {
|
||||
auto &layout = project.GetLayout(layoutIndex);
|
||||
scenesUsedResources[layout.GetName()] =
|
||||
gd::SceneResourcesFinder::FindSceneResources(project, layout);
|
||||
}
|
||||
std::unordered_map<gd::String, std::set<gd::String>>
|
||||
eventsBasedObjectVariantsUsedResources;
|
||||
for (std::size_t extensionIndex = 0;
|
||||
extensionIndex < project.GetEventsFunctionsExtensionsCount();
|
||||
extensionIndex++) {
|
||||
auto &eventsFunctionsExtension =
|
||||
project.GetEventsFunctionsExtension(extensionIndex);
|
||||
for (auto &&eventsBasedObject :
|
||||
eventsFunctionsExtension.GetEventsBasedObjects().GetInternalVector()) {
|
||||
|
||||
auto eventsBasedObjectType = gd::PlatformExtension::GetObjectFullType(
|
||||
eventsFunctionsExtension.GetName(), eventsBasedObject->GetName());
|
||||
eventsBasedObjectVariantsUsedResources[eventsBasedObjectType] =
|
||||
gd::SceneResourcesFinder::FindEventsBasedObjectVariantResources(
|
||||
project, eventsBasedObject->GetDefaultVariant());
|
||||
|
||||
for (auto &&eventsBasedObjectVariant :
|
||||
eventsBasedObject->GetVariants().GetInternalVector()) {
|
||||
|
||||
auto variantType = gd::PlatformExtension::GetVariantFullType(
|
||||
eventsFunctionsExtension.GetName(), eventsBasedObject->GetName(),
|
||||
eventsBasedObjectVariant->GetName());
|
||||
eventsBasedObjectVariantsUsedResources[variantType] =
|
||||
gd::SceneResourcesFinder::FindEventsBasedObjectVariantResources(
|
||||
project, *eventsBasedObjectVariant);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Strip the project (*after* generating events as the events may use stripped
|
||||
// things (objects groups...))
|
||||
gd::ProjectStripper::StripProjectForExport(project);
|
||||
|
||||
// Save the project to JSON
|
||||
gd::SerializerElement rootElement;
|
||||
project.SerializeTo(rootElement);
|
||||
SerializeUsedResources(
|
||||
rootElement, projectUsedResources, scenesUsedResources);
|
||||
gd::String output =
|
||||
"gdjs.projectData = " + gd::Serializer::ToJSON(rootElement) + ";\n" +
|
||||
"gdjs.runtimeGameOptions = " +
|
||||
gd::Serializer::ToJSON(runtimeGameOptions) + ";\n";
|
||||
|
||||
if (!fs.WriteToFile(filename, output)) return "Unable to write " + filename;
|
||||
|
||||
return "";
|
||||
SerializeUsedResources(rootElement, projectUsedResources, scenesUsedResources,
|
||||
eventsBasedObjectVariantsUsedResources);
|
||||
}
|
||||
|
||||
void ExporterHelper::SerializeUsedResources(
|
||||
gd::SerializerElement &rootElement,
|
||||
std::set<gd::String> &projectUsedResources,
|
||||
std::unordered_map<gd::String, std::set<gd::String>> &scenesUsedResources) {
|
||||
std::unordered_map<gd::String, std::set<gd::String>> &scenesUsedResources,
|
||||
std::unordered_map<gd::String, std::set<gd::String>>
|
||||
&eventsBasedObjectVariantsUsedResources) {
|
||||
auto serializeUsedResources =
|
||||
[](gd::SerializerElement &element,
|
||||
std::set<gd::String> &usedResources) -> void {
|
||||
@@ -385,6 +578,41 @@ void ExporterHelper::SerializeUsedResources(
|
||||
auto &layoutUsedResources = scenesUsedResources[layoutName];
|
||||
serializeUsedResources(layoutElement, layoutUsedResources);
|
||||
}
|
||||
|
||||
auto &extensionsElement = rootElement.GetChild("eventsFunctionsExtensions");
|
||||
for (std::size_t extensionIndex = 0;
|
||||
extensionIndex < extensionsElement.GetChildrenCount();
|
||||
extensionIndex++) {
|
||||
auto &extensionElement = extensionsElement.GetChild(extensionIndex);
|
||||
const auto extensionName = extensionElement.GetStringAttribute("name");
|
||||
|
||||
auto &objectsElement = extensionElement.GetChild("eventsBasedObjects");
|
||||
|
||||
for (std::size_t objectIndex = 0;
|
||||
objectIndex < objectsElement.GetChildrenCount(); objectIndex++) {
|
||||
auto &objectElement = objectsElement.GetChild(objectIndex);
|
||||
const auto objectName = objectElement.GetStringAttribute("name");
|
||||
|
||||
auto eventsBasedObjectType =
|
||||
gd::PlatformExtension::GetObjectFullType(extensionName, objectName);
|
||||
auto &objectUsedResources =
|
||||
eventsBasedObjectVariantsUsedResources[eventsBasedObjectType];
|
||||
serializeUsedResources(objectElement, objectUsedResources);
|
||||
|
||||
auto &variantsElement = objectElement.GetChild("variants");
|
||||
for (std::size_t variantIndex = 0;
|
||||
variantIndex < variantsElement.GetChildrenCount(); variantIndex++) {
|
||||
auto &variantElement = variantsElement.GetChild(variantIndex);
|
||||
const auto variantName = variantElement.GetStringAttribute("name");
|
||||
|
||||
auto variantType = gd::PlatformExtension::GetVariantFullType(
|
||||
extensionName, objectName, variantName);
|
||||
auto &variantUsedResources =
|
||||
eventsBasedObjectVariantsUsedResources[variantType];
|
||||
serializeUsedResources(variantElement, variantUsedResources);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ExporterHelper::ExportIndexFile(
|
||||
@@ -775,7 +1003,7 @@ bool ExporterHelper::CompleteIndexFile(
|
||||
gd::String codeFilesIncludes;
|
||||
for (auto &include : includesFiles) {
|
||||
gd::String scriptSrc =
|
||||
GetExportedIncludeFilename(include, nonRuntimeScriptsCacheBurst);
|
||||
GetExportedIncludeFilename(fs, gdjsRoot, include, nonRuntimeScriptsCacheBurst);
|
||||
|
||||
// Sanity check if the file exists - if not skip it to avoid
|
||||
// including it in the list of scripts.
|
||||
@@ -801,6 +1029,7 @@ bool ExporterHelper::CompleteIndexFile(
|
||||
|
||||
void ExporterHelper::AddLibsInclude(bool pixiRenderers,
|
||||
bool pixiInThreeRenderers,
|
||||
bool isInGameEdition,
|
||||
bool includeWebsocketDebuggerClient,
|
||||
bool includeWindowMessageDebuggerClient,
|
||||
bool includeMinimalDebuggerClient,
|
||||
@@ -877,6 +1106,7 @@ void ExporterHelper::AddLibsInclude(bool pixiRenderers,
|
||||
InsertUnique(includesFiles, "debugger-client/hot-reloader.js");
|
||||
InsertUnique(includesFiles, "debugger-client/abstract-debugger-client.js");
|
||||
InsertUnique(includesFiles, "debugger-client/InGameDebugger.js");
|
||||
InsertUnique(includesFiles, "InGameEditor/InGameEditor.js");
|
||||
}
|
||||
if (includeWebsocketDebuggerClient) {
|
||||
InsertUnique(includesFiles, "debugger-client/websocket-debugger-client.js");
|
||||
@@ -889,14 +1119,16 @@ void ExporterHelper::AddLibsInclude(bool pixiRenderers,
|
||||
InsertUnique(includesFiles, "debugger-client/minimal-debugger-client.js");
|
||||
}
|
||||
|
||||
if (pixiInThreeRenderers) {
|
||||
if (pixiInThreeRenderers || isInGameEdition) {
|
||||
InsertUnique(includesFiles, "pixi-renderers/three.js");
|
||||
InsertUnique(includesFiles, "pixi-renderers/ThreeAddons.js");
|
||||
InsertUnique(includesFiles, "pixi-renderers/draco/gltf/draco_decoder.wasm");
|
||||
InsertUnique(includesFiles,
|
||||
"pixi-renderers/draco/gltf/draco_wasm_wrapper.js");
|
||||
// Extensions in JS may use it.
|
||||
InsertUnique(includesFiles, "Extensions/3D/Scene3DTools.js");
|
||||
}
|
||||
if (pixiRenderers) {
|
||||
if (pixiRenderers || isInGameEdition) {
|
||||
InsertUnique(includesFiles, "pixi-renderers/pixi.js");
|
||||
InsertUnique(includesFiles, "pixi-renderers/pixi-filters-tools.js");
|
||||
InsertUnique(includesFiles, "pixi-renderers/runtimegame-pixi-renderer.js");
|
||||
@@ -920,7 +1152,12 @@ void ExporterHelper::AddLibsInclude(bool pixiRenderers,
|
||||
includesFiles,
|
||||
"fontfaceobserver-font-manager/fontfaceobserver-font-manager.js");
|
||||
}
|
||||
if (pixiInThreeRenderers) {
|
||||
if (isInGameEdition) {
|
||||
// `InGameEditor` uses the `is3D` function.
|
||||
InsertUnique(includesFiles, "Extensions/3D/Base3DBehavior.js");
|
||||
InsertUnique(includesFiles, "Extensions/3D/HemisphereLight.js");
|
||||
}
|
||||
if (pixiInThreeRenderers || isInGameEdition) {
|
||||
InsertUnique(includesFiles, "Extensions/3D/A_RuntimeObject3D.js");
|
||||
InsertUnique(includesFiles, "Extensions/3D/A_RuntimeObject3DRenderer.js");
|
||||
InsertUnique(includesFiles, "Extensions/3D/CustomRuntimeObject3D.js");
|
||||
@@ -958,7 +1195,7 @@ bool ExporterHelper::ExportEffectIncludes(
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ExporterHelper::ExportEventsCode(
|
||||
bool ExporterHelper::ExportScenesEventsCode(
|
||||
const gd::Project &project,
|
||||
gd::String outputDir,
|
||||
std::vector<gd::String> &includesFiles,
|
||||
@@ -994,6 +1231,7 @@ bool ExporterHelper::ExportEventsCode(
|
||||
}
|
||||
|
||||
gd::String ExporterHelper::GetExportedIncludeFilename(
|
||||
gd::AbstractFileSystem &fs, const gd::String &gdjsRoot,
|
||||
const gd::String &include, unsigned int nonRuntimeScriptsCacheBurst) {
|
||||
auto addSearchParameterToUrl = [](const gd::String &url,
|
||||
const gd::String &urlEncodedParameterName,
|
||||
|
@@ -42,9 +42,9 @@ struct PreviewExportOptions {
|
||||
useWindowMessageDebuggerClient(false),
|
||||
useMinimalDebuggerClient(false),
|
||||
nativeMobileApp(false),
|
||||
projectDataOnlyExport(false),
|
||||
fullLoadingScreen(false),
|
||||
isDevelopmentEnvironment(false),
|
||||
isInGameEdition(false),
|
||||
nonRuntimeScriptsCacheBurst(0),
|
||||
inAppTutorialMessageInPreview(""),
|
||||
inAppTutorialMessagePositionInPreview(""),
|
||||
@@ -145,6 +145,26 @@ struct PreviewExportOptions {
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set the (optional) events-based object to be instantiated in the scene
|
||||
* at the beginning of the previewed game.
|
||||
*/
|
||||
PreviewExportOptions &SetEventsBasedObjectType(
|
||||
const gd::String &eventsBasedObjectType_) {
|
||||
eventsBasedObjectType = eventsBasedObjectType_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set the (optional) events-based object variant to be instantiated in the scene
|
||||
* at the beginning of the previewed game.
|
||||
*/
|
||||
PreviewExportOptions &SetEventsBasedObjectVariantName(
|
||||
const gd::String &eventsBasedObjectVariantName_) {
|
||||
eventsBasedObjectVariantName = eventsBasedObjectVariantName_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set the hash associated to an include file. Useful for the preview
|
||||
* hot-reload, to know if a file changed.
|
||||
@@ -156,11 +176,34 @@ struct PreviewExportOptions {
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set if the export should only export the project data, not
|
||||
* exporting events code.
|
||||
* \brief Set if the exported folder should be cleared befor the export.
|
||||
*/
|
||||
PreviewExportOptions &SetProjectDataOnlyExport(bool enable) {
|
||||
projectDataOnlyExport = enable;
|
||||
PreviewExportOptions &SetShouldClearExportFolder(bool enable) {
|
||||
shouldClearExportFolder = enable;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set if the `ProjectData` must be reloaded.
|
||||
*/
|
||||
PreviewExportOptions &SetShouldReloadProjectData(bool enable) {
|
||||
shouldReloadProjectData = enable;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set if GDJS libraries must be reloaded.
|
||||
*/
|
||||
PreviewExportOptions &SetShouldReloadLibraries(bool enable) {
|
||||
shouldReloadLibraries = enable;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set if the export should export events code.
|
||||
*/
|
||||
PreviewExportOptions &SetShouldGenerateScenesEventsCode(bool enable) {
|
||||
shouldGenerateScenesEventsCode = enable;
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -182,6 +225,40 @@ struct PreviewExportOptions {
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set if the export is made for being edited in the editor.
|
||||
*/
|
||||
PreviewExportOptions &SetIsInGameEdition(bool enable) {
|
||||
isInGameEdition = enable;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set the in-game editor identifier.
|
||||
*/
|
||||
PreviewExportOptions &SetEditorId(const gd::String &editorId_) {
|
||||
editorId = editorId_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set the camera state to use in the in-game editor.
|
||||
*/
|
||||
PreviewExportOptions &
|
||||
SetEditorCameraState3D(const gd::String &cameraMode, double positionX,
|
||||
double positionY, double positionZ,
|
||||
double rotationAngle, double elevationAngle,
|
||||
double distance) {
|
||||
editorCamera3DCameraMode = cameraMode;
|
||||
editorCamera3DPositionX = positionX;
|
||||
editorCamera3DPositionY = positionY;
|
||||
editorCamera3DPositionZ = positionZ;
|
||||
editorCamera3DRotationAngle = rotationAngle;
|
||||
editorCamera3DElevationAngle = elevationAngle;
|
||||
editorCamera3DDistance = distance;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief If set to a non zero value, the exported script URLs will have an
|
||||
* extra search parameter added (with the given value) to ensure browser cache
|
||||
@@ -294,6 +371,8 @@ struct PreviewExportOptions {
|
||||
bool useMinimalDebuggerClient;
|
||||
gd::String layoutName;
|
||||
gd::String externalLayoutName;
|
||||
gd::String eventsBasedObjectType;
|
||||
gd::String eventsBasedObjectVariantName;
|
||||
gd::String fallbackAuthorUsername;
|
||||
gd::String fallbackAuthorId;
|
||||
gd::String playerId;
|
||||
@@ -303,9 +382,21 @@ struct PreviewExportOptions {
|
||||
gd::String inAppTutorialMessagePositionInPreview;
|
||||
bool nativeMobileApp;
|
||||
std::map<gd::String, int> includeFileHashes;
|
||||
bool projectDataOnlyExport;
|
||||
bool shouldClearExportFolder = true;
|
||||
bool shouldReloadProjectData = true;
|
||||
bool shouldReloadLibraries = true;
|
||||
bool shouldGenerateScenesEventsCode = true;
|
||||
bool fullLoadingScreen;
|
||||
bool isDevelopmentEnvironment;
|
||||
bool isInGameEdition;
|
||||
gd::String editorId;
|
||||
gd::String editorCamera3DCameraMode;
|
||||
double editorCamera3DPositionX = 0;
|
||||
double editorCamera3DPositionY = 0;
|
||||
double editorCamera3DPositionZ = 0;
|
||||
double editorCamera3DRotationAngle = 0;
|
||||
double editorCamera3DElevationAngle = 0;
|
||||
double editorCamera3DDistance = 0;
|
||||
unsigned int nonRuntimeScriptsCacheBurst;
|
||||
gd::String electronRemoteRequirePath;
|
||||
gd::String gdevelopResourceToken;
|
||||
@@ -379,23 +470,50 @@ class ExporterHelper {
|
||||
const gd::String &GetLastError() const { return lastError; };
|
||||
|
||||
/**
|
||||
* \brief Export a project to JSON
|
||||
* \brief Export a project without its events and options to 2 JS variables
|
||||
*
|
||||
* \param fs The abstract file system to use to write the file
|
||||
* \param project The project to be exported.
|
||||
* \param filename The filename where export the project
|
||||
* \param runtimeGameOptions The content of the extra configuration to store
|
||||
* in gdjs.runtimeGameOptions \return Empty string if everything is ok,
|
||||
* in gdjs.runtimeGameOptions
|
||||
*
|
||||
* \return Empty string if everything is ok,
|
||||
* description of the error otherwise.
|
||||
*/
|
||||
static gd::String ExportProjectData(
|
||||
gd::AbstractFileSystem &fs,
|
||||
gd::Project &project,
|
||||
gd::String filename,
|
||||
const gd::SerializerElement &runtimeGameOptions,
|
||||
std::set<gd::String> &projectUsedResources,
|
||||
std::unordered_map<gd::String, std::set<gd::String>>
|
||||
&layersUsedResources);
|
||||
static gd::String
|
||||
ExportProjectData(gd::AbstractFileSystem &fs, gd::Project &project,
|
||||
gd::String filename,
|
||||
const gd::SerializerElement &runtimeGameOptions);
|
||||
|
||||
/**
|
||||
* \brief Serialize a project without its events to JSON
|
||||
*
|
||||
* \param fs The abstract file system to use to write the file
|
||||
* \param project The project to be exported.
|
||||
* \param projectDataElement The element where the project data is serialized
|
||||
*/
|
||||
static void SerializeProjectData(gd::AbstractFileSystem &fs,
|
||||
const gd::Project &project,
|
||||
const PreviewExportOptions &options,
|
||||
gd::SerializerElement &projectDataElement);
|
||||
|
||||
/**
|
||||
* \brief Serialize the content of the extra configuration to store
|
||||
* in gdjs.runtimeGameOptions to JSON
|
||||
*
|
||||
* \param fs The abstract file system to use to write the file
|
||||
* \param options The content of the extra configuration
|
||||
* \param includesFiles The list of scripts files - useful for hot-reloading
|
||||
* \param runtimeGameOptionsElement The element where the game options are
|
||||
* serialized
|
||||
*/
|
||||
static void
|
||||
SerializeRuntimeGameOptions(gd::AbstractFileSystem &fs,
|
||||
const gd::String &gdjsRoot,
|
||||
const PreviewExportOptions &options,
|
||||
std::vector<gd::String> &includesFiles,
|
||||
gd::SerializerElement &runtimeGameOptionsElement);
|
||||
|
||||
/**
|
||||
* \brief Copy all the resources of the project to to the export directory,
|
||||
@@ -416,6 +534,7 @@ class ExporterHelper {
|
||||
*/
|
||||
void AddLibsInclude(bool pixiRenderers,
|
||||
bool pixiInThreeRenderers,
|
||||
bool isInGameEdition,
|
||||
bool includeWebsocketDebuggerClient,
|
||||
bool includeWindowMessageDebuggerClient,
|
||||
bool includeMinimalDebuggerClient,
|
||||
@@ -453,7 +572,7 @@ class ExporterHelper {
|
||||
* includesFiles A reference to a vector that will be filled with JS files to
|
||||
* be exported along with the project. ( including "codeX.js" files ).
|
||||
*/
|
||||
bool ExportEventsCode(
|
||||
bool ExportScenesEventsCode(
|
||||
const gd::Project &project,
|
||||
gd::String outputDir,
|
||||
std::vector<gd::String> &includesFiles,
|
||||
@@ -578,14 +697,17 @@ class ExporterHelper {
|
||||
* a browser pointing to the preview.
|
||||
*
|
||||
* \param options The options to generate the preview.
|
||||
* \param includesFiles The list of scripts files - useful for hot-reloading
|
||||
*/
|
||||
bool ExportProjectForPixiPreview(const PreviewExportOptions &options);
|
||||
bool ExportProjectForPixiPreview(const PreviewExportOptions &options,
|
||||
std::vector<gd::String> &includesFiles);
|
||||
|
||||
/**
|
||||
* \brief Given an include file, returns the name of the file to reference
|
||||
* in the exported game.
|
||||
*/
|
||||
gd::String GetExportedIncludeFilename(
|
||||
static gd::String GetExportedIncludeFilename(
|
||||
gd::AbstractFileSystem &fs, const gd::String &gdjsRoot,
|
||||
const gd::String &include, unsigned int nonRuntimeScriptsCacheBurst = 0);
|
||||
|
||||
/**
|
||||
@@ -612,11 +734,22 @@ class ExporterHelper {
|
||||
///< be then copied to the final output directory.
|
||||
|
||||
private:
|
||||
static void SerializeUsedResources(
|
||||
gd::SerializerElement &rootElement,
|
||||
std::set<gd::String> &projectUsedResources,
|
||||
std::unordered_map<gd::String, std::set<gd::String>>
|
||||
&layersUsedResources);
|
||||
static void
|
||||
SerializeUsedResources(gd::SerializerElement &rootElement,
|
||||
std::set<gd::String> &projectUsedResources,
|
||||
std::unordered_map<gd::String, std::set<gd::String>>
|
||||
&layersUsedResources,
|
||||
std::unordered_map<gd::String, std::set<gd::String>>
|
||||
&eventsBasedObjectVariantsUsedResources);
|
||||
|
||||
/**
|
||||
* \brief Stript a project and serialize it to JSON
|
||||
*
|
||||
* \param project The project to be exported.
|
||||
*/
|
||||
static void
|
||||
StriptAndSerializeProjectData(gd::Project &project,
|
||||
gd::SerializerElement &rootElement);
|
||||
};
|
||||
|
||||
} // namespace gdjs
|
||||
|
@@ -5,7 +5,7 @@
|
||||
<name>GDJS_PROJECTNAME</name>
|
||||
<content src="index.html" />
|
||||
<plugin name="cordova-plugin-whitelist" version="1" />
|
||||
<plugin name="cordova-plugin-screen-orientation" version="3.0.2" />
|
||||
<plugin name="cordova-plugin-screen-orientation" version="3.0.4" />
|
||||
<access origin="*" />
|
||||
<allow-intent href="http://*/*" />
|
||||
<allow-intent href="https://*/*" />
|
||||
@@ -67,4 +67,4 @@
|
||||
<!-- Keep cordova-plugin-ionic-webview plugin last as it has a deployment-target to 11, which
|
||||
affects the installation of other plugins.-->
|
||||
<plugin name="cordova-plugin-ionic-webview" version="5.0.1" />
|
||||
</widget>
|
||||
</widget>
|
||||
|
@@ -13,7 +13,7 @@ namespace gdjs {
|
||||
export type CustomObjectConfiguration = ObjectConfiguration & {
|
||||
animatable?: SpriteAnimationData[];
|
||||
variant: string;
|
||||
childrenContent: { [objectName: string]: ObjectConfiguration & any };
|
||||
childrenContent?: { [objectName: string]: ObjectConfiguration & any };
|
||||
isInnerAreaFollowingParentSize: boolean;
|
||||
};
|
||||
|
||||
@@ -110,37 +110,19 @@ namespace gdjs {
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!eventsBasedObjectData.defaultVariant) {
|
||||
eventsBasedObjectData.defaultVariant = {
|
||||
...eventsBasedObjectData,
|
||||
name: '',
|
||||
};
|
||||
}
|
||||
// Legacy events-based objects don't have any instance in their default
|
||||
// variant since there wasn't a graphical editor at the time.
|
||||
// In this case, the editor doesn't allow to choose a variant, but a
|
||||
// variant may have stayed after a user rolled back the extension.
|
||||
// This variant must be ignored to match what the editor shows.
|
||||
const isForcedToOverrideEventsBasedObjectChildrenConfiguration =
|
||||
eventsBasedObjectData.defaultVariant.instances.length == 0;
|
||||
let usedVariantData: EventsBasedObjectVariantData =
|
||||
eventsBasedObjectData.defaultVariant;
|
||||
if (
|
||||
customObjectData.variant &&
|
||||
!isForcedToOverrideEventsBasedObjectChildrenConfiguration
|
||||
) {
|
||||
for (
|
||||
let variantIndex = 0;
|
||||
variantIndex < eventsBasedObjectData.variants.length;
|
||||
variantIndex++
|
||||
) {
|
||||
const variantData = eventsBasedObjectData.variants[variantIndex];
|
||||
if (variantData.name === customObjectData.variant) {
|
||||
usedVariantData = variantData;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const usedVariantData: EventsBasedObjectVariantData | null =
|
||||
this.getRuntimeScene()
|
||||
.getGame()
|
||||
.getEventsBasedObjectVariantData(
|
||||
customObjectData.type,
|
||||
customObjectData.variant
|
||||
);
|
||||
if (!usedVariantData) {
|
||||
// This can't actually happen.
|
||||
logger.error(
|
||||
`Unknown variant "${customObjectData.variant}" for object "${customObjectData.type}".`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
this._isInnerAreaFollowingParentSize =
|
||||
@@ -170,8 +152,7 @@ namespace gdjs {
|
||||
override reinitialize(objectData: ObjectData & CustomObjectConfiguration) {
|
||||
super.reinitialize(objectData);
|
||||
|
||||
this._reinitializeRenderer();
|
||||
this._initializeFromObjectData(objectData);
|
||||
this._reinitializeContentFromObjectData(objectData);
|
||||
|
||||
// When changing the variant, the instance is like a new instance.
|
||||
// We call again `onCreated` at the end, like done by the constructor
|
||||
@@ -179,6 +160,14 @@ namespace gdjs {
|
||||
this.onCreated();
|
||||
}
|
||||
|
||||
private _reinitializeContentFromObjectData(
|
||||
objectData: ObjectData & CustomObjectConfiguration
|
||||
) {
|
||||
this._reinitializeRenderer();
|
||||
this._instanceContainer._unloadContent();
|
||||
this._initializeFromObjectData(objectData);
|
||||
}
|
||||
|
||||
override updateFromObjectData(
|
||||
oldObjectData: ObjectData & CustomObjectConfiguration,
|
||||
newObjectData: ObjectData & CustomObjectConfiguration
|
||||
@@ -206,8 +195,7 @@ namespace gdjs {
|
||||
this._instanceContainer._initialInnerArea.max[1] !==
|
||||
this._innerArea.max[1]);
|
||||
|
||||
this._reinitializeRenderer();
|
||||
this._initializeFromObjectData(newObjectData);
|
||||
this._reinitializeContentFromObjectData(newObjectData);
|
||||
|
||||
// The generated code calls the onCreated super implementation at the end.
|
||||
this.onCreated();
|
||||
@@ -261,15 +249,13 @@ namespace gdjs {
|
||||
this.setWidth(initialInstanceData.width);
|
||||
this.setHeight(initialInstanceData.height);
|
||||
}
|
||||
if (initialInstanceData.opacity !== undefined) {
|
||||
this.setOpacity(initialInstanceData.opacity);
|
||||
}
|
||||
if (initialInstanceData.flippedX) {
|
||||
this.flipX(initialInstanceData.flippedX);
|
||||
}
|
||||
if (initialInstanceData.flippedY) {
|
||||
this.flipY(initialInstanceData.flippedY);
|
||||
}
|
||||
this.setOpacity(
|
||||
initialInstanceData.opacity === undefined
|
||||
? 255
|
||||
: initialInstanceData.opacity
|
||||
);
|
||||
this.flipX(!!initialInstanceData.flippedX);
|
||||
this.flipY(!!initialInstanceData.flippedY);
|
||||
}
|
||||
|
||||
override onDeletedFromScene(): void {
|
||||
@@ -300,8 +286,13 @@ namespace gdjs {
|
||||
if (profiler) {
|
||||
profiler.end(this.type);
|
||||
}
|
||||
}
|
||||
|
||||
this._instanceContainer._updateObjectsPostEvents();
|
||||
override stepBehaviorsPostEvents(
|
||||
instanceContainer: gdjs.RuntimeInstanceContainer
|
||||
): void {
|
||||
super.stepBehaviorsPostEvents(instanceContainer);
|
||||
this._instanceContainer._stepBehaviorsPostEvents();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -603,6 +594,20 @@ namespace gdjs {
|
||||
return this._unrotatedAABB.max[1];
|
||||
}
|
||||
|
||||
getOriginalWidth(): float {
|
||||
return (
|
||||
this._instanceContainer.getInitialUnrotatedViewportMaxX() -
|
||||
this._instanceContainer.getInitialUnrotatedViewportMinX()
|
||||
);
|
||||
}
|
||||
|
||||
getOriginalHeight(): float {
|
||||
return (
|
||||
this._instanceContainer.getInitialUnrotatedViewportMaxY() -
|
||||
this._instanceContainer.getInitialUnrotatedViewportMinY()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the internal width of the object according to its children.
|
||||
*/
|
||||
|
@@ -16,6 +16,7 @@ namespace gdjs {
|
||||
_parent: gdjs.RuntimeInstanceContainer;
|
||||
/** The object that is built with the instances of this container. */
|
||||
_customObject: gdjs.CustomRuntimeObject;
|
||||
// TODO Remove this attribute
|
||||
_isLoaded: boolean = false;
|
||||
/**
|
||||
* The default size defined by users in the custom object initial instances editor.
|
||||
@@ -46,15 +47,28 @@ namespace gdjs {
|
||||
this._debuggerRenderer = new gdjs.DebuggerRenderer(this);
|
||||
}
|
||||
|
||||
// TODO `_layers` and `_orderedLayers` should not be used directly.
|
||||
|
||||
addLayer(layerData: LayerData) {
|
||||
if (this._layers.containsKey(layerData.name)) {
|
||||
return;
|
||||
}
|
||||
// This code is duplicated with `RuntimeScene.addLayer` because it avoids
|
||||
// to expose a method to build a layer.
|
||||
const layer = new gdjs.RuntimeCustomObjectLayer(layerData, this);
|
||||
this._layers.put(layerData.name, layer);
|
||||
this._orderedLayers.push(layer);
|
||||
}
|
||||
|
||||
_unloadContent() {
|
||||
this.onDeletedFromScene(this._parent);
|
||||
// At this point, layer renderers are already removed by
|
||||
// `CustomRuntimeObject._reinitializeRenderer`.
|
||||
// It's not great to do this here, but it allows to keep it private.
|
||||
this._layers.clear();
|
||||
this._orderedLayers.length = 0;
|
||||
}
|
||||
|
||||
createObject(objectName: string): gdjs.RuntimeObject | null {
|
||||
const result = super.createObject(objectName);
|
||||
this._customObject.onChildrenLocationChanged();
|
||||
@@ -63,21 +77,14 @@ namespace gdjs {
|
||||
|
||||
/**
|
||||
* Load the container from the given initial configuration.
|
||||
* @param customObjectData An object containing the container data.
|
||||
* @param customObjectData An object containing the parent object data.
|
||||
* @param eventsBasedObjectVariantData An object containing the container data.
|
||||
* @see gdjs.RuntimeGame#getSceneAndExtensionsData
|
||||
*/
|
||||
loadFrom(
|
||||
customObjectData: ObjectData & CustomObjectConfiguration,
|
||||
eventsBasedObjectVariantData: EventsBasedObjectVariantData
|
||||
) {
|
||||
if (this._isLoaded) {
|
||||
this.onDeletedFromScene(this._parent);
|
||||
}
|
||||
|
||||
const isForcedToOverrideEventsBasedObjectChildrenConfiguration =
|
||||
!eventsBasedObjectVariantData.name &&
|
||||
eventsBasedObjectVariantData.instances.length == 0;
|
||||
|
||||
this._setOriginalInnerArea(eventsBasedObjectVariantData);
|
||||
|
||||
// Registering objects
|
||||
@@ -87,19 +94,21 @@ namespace gdjs {
|
||||
++i
|
||||
) {
|
||||
const childObjectData = eventsBasedObjectVariantData.objects[i];
|
||||
// The children configuration override only applies to the default variant.
|
||||
if (
|
||||
customObjectData.childrenContent &&
|
||||
(!eventsBasedObjectVariantData.name ||
|
||||
isForcedToOverrideEventsBasedObjectChildrenConfiguration)
|
||||
gdjs.CustomRuntimeObjectInstanceContainer.hasChildrenConfigurationOverriding(
|
||||
customObjectData,
|
||||
eventsBasedObjectVariantData
|
||||
)
|
||||
) {
|
||||
this.registerObject({
|
||||
...childObjectData,
|
||||
// The custom object overrides its events-based object configuration.
|
||||
// The custom object overrides its variant configuration with
|
||||
// a legacy children configuration.
|
||||
...customObjectData.childrenContent[childObjectData.name],
|
||||
});
|
||||
} else {
|
||||
// The custom object follows its events-based object configuration.
|
||||
// The custom object follows its variant configuration.
|
||||
this.registerObject(childObjectData);
|
||||
}
|
||||
}
|
||||
@@ -154,6 +163,28 @@ namespace gdjs {
|
||||
this._isLoaded = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the custom object has a children configuration overriding that
|
||||
* should be used instead of the variant's objects configurations.
|
||||
* @param customObjectData An object containing the parent object data.
|
||||
* @param eventsBasedObjectVariantData An object containing the container data.
|
||||
* @returns
|
||||
*/
|
||||
static hasChildrenConfigurationOverriding(
|
||||
customObjectData: CustomObjectConfiguration,
|
||||
eventsBasedObjectVariantData: EventsBasedObjectVariantData
|
||||
): boolean {
|
||||
const isForcedToOverrideEventsBasedObjectChildrenConfiguration =
|
||||
!eventsBasedObjectVariantData.name &&
|
||||
eventsBasedObjectVariantData.instances.length == 0;
|
||||
|
||||
// The children configuration override only applies to the default variant.
|
||||
return customObjectData.childrenContent
|
||||
? !eventsBasedObjectVariantData.name ||
|
||||
isForcedToOverrideEventsBasedObjectChildrenConfiguration
|
||||
: false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize `_initialInnerArea` if it doesn't exist.
|
||||
* `_initialInnerArea` is shared by every instance to save memory.
|
||||
@@ -161,7 +192,10 @@ namespace gdjs {
|
||||
private _setOriginalInnerArea(
|
||||
eventsBasedObjectData: EventsBasedObjectVariantData
|
||||
) {
|
||||
if (eventsBasedObjectData.instances.length > 0) {
|
||||
if (
|
||||
eventsBasedObjectData.instances.length > 0 ||
|
||||
this.getGame().isInGameEdition()
|
||||
) {
|
||||
if (!eventsBasedObjectData._initialInnerArea) {
|
||||
eventsBasedObjectData._initialInnerArea = {
|
||||
min: [
|
||||
@@ -341,6 +375,12 @@ namespace gdjs {
|
||||
return this._initialInnerArea ? this._initialInnerArea.max[1] : 0;
|
||||
}
|
||||
|
||||
_getInitialInnerAreaDepth(): float {
|
||||
return this._initialInnerArea
|
||||
? this._initialInnerArea.max[2] - this._initialInnerArea.min[2]
|
||||
: 0;
|
||||
}
|
||||
|
||||
getViewportWidth(): float {
|
||||
return this._customObject.getUnscaledWidth();
|
||||
}
|
||||
|
3816
GDJS/Runtime/InGameEditor/InGameEditor.ts
Normal file
3816
GDJS/Runtime/InGameEditor/InGameEditor.ts
Normal file
File diff suppressed because it is too large
Load Diff
@@ -163,28 +163,21 @@ namespace gdjs {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unload the specified list of resources:
|
||||
* this clears the models, resources loaded and destroy 3D models loaders 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 loadedThreeModel = this._loadedThreeModels.get(resourceData);
|
||||
if (loadedThreeModel) {
|
||||
loadedThreeModel.scene.clear();
|
||||
this._loadedThreeModels.delete(resourceData);
|
||||
}
|
||||
unloadResource(resourceData: ResourceData): void {
|
||||
const loadedThreeModel = this._loadedThreeModels.getFromName(
|
||||
resourceData.name
|
||||
);
|
||||
if (loadedThreeModel) {
|
||||
loadedThreeModel.scene.clear();
|
||||
this._loadedThreeModels.delete(resourceData);
|
||||
}
|
||||
|
||||
const downloadedArrayBuffer =
|
||||
this._downloadedArrayBuffers.get(resourceData);
|
||||
if (downloadedArrayBuffer) {
|
||||
this._downloadedArrayBuffers.delete(resourceData);
|
||||
}
|
||||
});
|
||||
const downloadedArrayBuffer = this._downloadedArrayBuffers.getFromName(
|
||||
resourceData.name
|
||||
);
|
||||
if (downloadedArrayBuffer) {
|
||||
this._downloadedArrayBuffers.delete(resourceData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -282,6 +282,27 @@ namespace gdjs {
|
||||
}
|
||||
}
|
||||
|
||||
async loadResources(
|
||||
resourceNames: Array<string>,
|
||||
onProgress: (loadingCount: integer, totalCount: integer) => void
|
||||
): Promise<void> {
|
||||
let loadedCount = 0;
|
||||
await processAndRetryIfNeededWithPromisePool(
|
||||
resourceNames,
|
||||
maxForegroundConcurrency,
|
||||
maxAttempt,
|
||||
async (resourceName) => {
|
||||
const resource = this._resources.get(resourceName);
|
||||
if (resource) {
|
||||
await this._loadResource(resource);
|
||||
await this._processResource(resource);
|
||||
}
|
||||
loadedCount++;
|
||||
onProgress(loadedCount, this._resources.size);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the resources that are needed to launch the first scene.
|
||||
*/
|
||||
@@ -534,7 +555,9 @@ namespace gdjs {
|
||||
`Unloading of resources of kind ${kindResourceManager} for scene ${unloadedSceneName}: `,
|
||||
resources.map((resource) => resource.name).join(', ')
|
||||
);
|
||||
resourceManager.unloadResourcesList(resources);
|
||||
for (const resource of resources) {
|
||||
resourceManager.unloadResource(resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -549,6 +572,23 @@ namespace gdjs {
|
||||
// TODO: mark the scene as unloaded so it's not automatically loaded again eagerly.
|
||||
}
|
||||
|
||||
/**
|
||||
* To be called when hot-reloading resources.
|
||||
*/
|
||||
unloadAllResources(): void {
|
||||
debugLogger.log(`Unloading of all resources was requested.`);
|
||||
for (const resource of this._resources.values()) {
|
||||
const resourceManager = this._resourceManagersMap.get(resource.kind);
|
||||
if (resourceManager) {
|
||||
resourceManager.unloadResource(resource);
|
||||
}
|
||||
}
|
||||
for (const sceneLoadingState of this._sceneLoadingStates.values()) {
|
||||
sceneLoadingState.status = 'not-loaded';
|
||||
}
|
||||
debugLogger.log(`Unloading of all resources finished.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Put a given scene at the end of the queue.
|
||||
*
|
||||
@@ -650,6 +690,9 @@ namespace gdjs {
|
||||
* the resource (this can be for example a token needed to access the resource).
|
||||
*/
|
||||
getFullUrl(url: string) {
|
||||
if (this._runtimeGame.isInGameEdition()) {
|
||||
url = addSearchParameterToUrl(url, 'cache', '' + Date.now());
|
||||
}
|
||||
const { gdevelopResourceToken } = this._runtimeGame._options;
|
||||
if (!gdevelopResourceToken) return url;
|
||||
|
||||
|
@@ -31,19 +31,19 @@ namespace gdjs {
|
||||
getResourceKinds(): Array<ResourceKind>;
|
||||
|
||||
/**
|
||||
* Should clear all resources, data, loaders stored by this manager.
|
||||
* Clear all resources, data, loaders stored by this manager.
|
||||
* Using the manager after calling this method is undefined behavior.
|
||||
*/
|
||||
dispose(): void;
|
||||
|
||||
/**
|
||||
* Should clear all specified resources data and anything stored by this manager
|
||||
* for these resources.
|
||||
* Clear any data in cache for a resource. Embedded resources are also
|
||||
* cleared.
|
||||
*
|
||||
* Usually called when scene resoures are unloaded.
|
||||
* Usually called when scene resources are unloaded.
|
||||
*
|
||||
* @param resourcesList The list of specific resources that need to be clear
|
||||
* @param resourceData The resource to clear
|
||||
*/
|
||||
unloadResourcesList(resourcesList: ResourceData[]): void;
|
||||
unloadResource(resourceData: ResourceData): void;
|
||||
}
|
||||
}
|
||||
|
@@ -575,10 +575,18 @@ namespace gdjs {
|
||||
this._cacheOrClearRemovedInstances();
|
||||
}
|
||||
|
||||
_updateObjectsForInGameEditor() {
|
||||
const allInstancesList = this.getAdhocListOfAllInstances();
|
||||
for (let i = 0, len = allInstancesList.length; i < len; ++i) {
|
||||
const obj = allInstancesList[i];
|
||||
obj.update(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the objects (update positions, time management...)
|
||||
* Call each behavior stepPostEvents method.
|
||||
*/
|
||||
_updateObjectsPostEvents() {
|
||||
_stepBehaviorsPostEvents() {
|
||||
this._cacheOrClearRemovedInstances();
|
||||
|
||||
// It is *mandatory* to create and iterate on a external list of all objects, as the behaviors
|
||||
@@ -612,7 +620,7 @@ namespace gdjs {
|
||||
getObjects(name: string): gdjs.RuntimeObject[] | undefined {
|
||||
if (!this._instances.containsKey(name)) {
|
||||
logger.info(
|
||||
'RuntimeScene.getObjects: No instances called "' +
|
||||
'RuntimeInstanceContainer.getObjects: No instances called "' +
|
||||
name +
|
||||
'"! Adding it.'
|
||||
);
|
||||
|
@@ -55,13 +55,14 @@ namespace gdjs {
|
||||
_timeScale: float = 1;
|
||||
_defaultZOrder: integer = 0;
|
||||
_hidden: boolean;
|
||||
_initialEffectsData: Array<EffectData>;
|
||||
_initialLayerData: LayerData;
|
||||
|
||||
// TODO EBO Don't store scene layer related data in layers used by custom objects.
|
||||
// (both these 3D settings and the lighting layer properties below).
|
||||
_initialCamera3DFieldOfView: float;
|
||||
_initialCamera3DFarPlaneDistance: float;
|
||||
_initialCamera3DNearPlaneDistance: float;
|
||||
_initialCamera2DPlaneMaxDrawingDistance: float;
|
||||
|
||||
_runtimeScene: gdjs.RuntimeInstanceContainer;
|
||||
_effectsManager: gdjs.EffectsManager;
|
||||
@@ -94,7 +95,9 @@ namespace gdjs {
|
||||
layerData.camera3DNearPlaneDistance || 0.1;
|
||||
this._initialCamera3DFarPlaneDistance =
|
||||
layerData.camera3DFarPlaneDistance || 2000;
|
||||
this._initialEffectsData = layerData.effects || [];
|
||||
this._initialCamera2DPlaneMaxDrawingDistance =
|
||||
layerData.camera2DPlaneMaxDrawingDistance || 5000;
|
||||
this._initialLayerData = layerData;
|
||||
this._runtimeScene = instanceContainer;
|
||||
this._effectsManager = instanceContainer.getGame().getEffectsManager();
|
||||
this._isLightingLayer = layerData.isLightingLayer;
|
||||
@@ -396,6 +399,9 @@ namespace gdjs {
|
||||
getInitialCamera3DFarPlaneDistance(): float {
|
||||
return this._initialCamera3DFarPlaneDistance;
|
||||
}
|
||||
getInitialCamera2DPlaneMaxDrawingDistance(): float {
|
||||
return this._initialCamera2DPlaneMaxDrawingDistance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the initial effects data for the layer. Only to
|
||||
@@ -403,7 +409,7 @@ namespace gdjs {
|
||||
* @deprecated
|
||||
*/
|
||||
getInitialEffectsData(): EffectData[] {
|
||||
return this._initialEffectsData;
|
||||
return this._initialLayerData.effects || [];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -107,8 +107,12 @@ namespace gdjs {
|
||||
exception: Error,
|
||||
runtimeGame: gdjs.RuntimeGame
|
||||
) => {
|
||||
const sceneNames = runtimeGame.getSceneStack().getAllSceneNames();
|
||||
const currentScene = runtimeGame.getSceneStack().getCurrentScene();
|
||||
const currentScene = runtimeGame.isInGameEdition()
|
||||
? runtimeGame.getInGameEditor()?.getCurrentScene()
|
||||
: runtimeGame.getSceneStack().getCurrentScene();
|
||||
const sceneNames = runtimeGame.isInGameEdition()
|
||||
? [currentScene?.getName()]
|
||||
: runtimeGame.getSceneStack().getAllSceneNames();
|
||||
return {
|
||||
type: 'javascript-uncaught-exception',
|
||||
exception,
|
||||
@@ -116,6 +120,7 @@ namespace gdjs {
|
||||
playerId: runtimeGame.getPlayerId(),
|
||||
sessionId: runtimeGame.getSessionId(),
|
||||
isPreview: runtimeGame.isPreview(),
|
||||
isInGameEdition: runtimeGame.isInGameEdition(),
|
||||
gdevelop: {
|
||||
previewContext: runtimeGame.getAdditionalOptions().previewContext,
|
||||
isNativeMobileApp: runtimeGame.getAdditionalOptions().nativeMobileApp,
|
||||
@@ -233,42 +238,242 @@ namespace gdjs {
|
||||
protected handleCommand(data: any) {
|
||||
const that = this;
|
||||
const runtimeGame = this._runtimegame;
|
||||
const inGameEditor = runtimeGame.getInGameEditor();
|
||||
if (!data || !data.command) {
|
||||
// Not a command that's meant to be handled by the debugger, return silently to
|
||||
// avoid polluting the console.
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.command === 'play') {
|
||||
runtimeGame.pause(false);
|
||||
} else if (data.command === 'pause') {
|
||||
runtimeGame.pause(true);
|
||||
that.sendRuntimeGameDump();
|
||||
} else if (data.command === 'refresh') {
|
||||
that.sendRuntimeGameDump();
|
||||
} else if (data.command === 'set') {
|
||||
that.set(data.path, data.newValue);
|
||||
} else if (data.command === 'call') {
|
||||
that.call(data.path, data.args);
|
||||
} else if (data.command === 'profiler.start') {
|
||||
runtimeGame.startCurrentSceneProfiler(function (stoppedProfiler) {
|
||||
that.sendProfilerOutput(
|
||||
stoppedProfiler.getFramesAverageMeasures(),
|
||||
stoppedProfiler.getStats()
|
||||
try {
|
||||
if (data.command === 'play') {
|
||||
runtimeGame.pause(false);
|
||||
} else if (data.command === 'pause') {
|
||||
runtimeGame.pause(true);
|
||||
that.sendRuntimeGameDump();
|
||||
} else if (data.command === 'refresh') {
|
||||
that.sendRuntimeGameDump();
|
||||
} else if (data.command === 'getStatus') {
|
||||
that.sendRuntimeGameStatus();
|
||||
} else if (data.command === 'set') {
|
||||
that.set(data.path, data.newValue);
|
||||
} else if (data.command === 'call') {
|
||||
that.call(data.path, data.args);
|
||||
} else if (data.command === 'profiler.start') {
|
||||
runtimeGame.startCurrentSceneProfiler(function (stoppedProfiler) {
|
||||
that.sendProfilerOutput(
|
||||
stoppedProfiler.getFramesAverageMeasures(),
|
||||
stoppedProfiler.getStats()
|
||||
);
|
||||
that.sendProfilerStopped();
|
||||
});
|
||||
that.sendProfilerStarted();
|
||||
} else if (data.command === 'profiler.stop') {
|
||||
runtimeGame.stopCurrentSceneProfiler();
|
||||
} else if (data.command === 'hotReload') {
|
||||
this._hasLoggedUncaughtException = false;
|
||||
that._hotReloader
|
||||
.hotReload({
|
||||
projectData: data.payload.projectData,
|
||||
runtimeGameOptions: data.payload.runtimeGameOptions,
|
||||
shouldReloadResources:
|
||||
data.payload.shouldReloadResources || false,
|
||||
})
|
||||
.then((logs) => {
|
||||
that.sendHotReloaderLogs(logs);
|
||||
});
|
||||
} else if (data.command === 'hotReloadObjects') {
|
||||
if (inGameEditor) {
|
||||
const editedInstanceContainer =
|
||||
inGameEditor.getEditedInstanceContainer();
|
||||
if (editedInstanceContainer) {
|
||||
that._hotReloader.hotReloadRuntimeSceneObjects(
|
||||
data.payload.updatedObjects,
|
||||
editedInstanceContainer
|
||||
);
|
||||
}
|
||||
}
|
||||
} else if (data.command === 'hotReloadLayers') {
|
||||
if (inGameEditor) {
|
||||
const editedInstanceContainer =
|
||||
inGameEditor.getEditedInstanceContainer();
|
||||
if (editedInstanceContainer) {
|
||||
inGameEditor.onLayersDataChange(
|
||||
data.payload.layers,
|
||||
data.payload.areEffectsHidden
|
||||
);
|
||||
runtimeGame._data.areEffectsHiddenInEditor =
|
||||
data.payload.areEffectsHidden;
|
||||
that._hotReloader.hotReloadRuntimeSceneLayers(
|
||||
data.payload.layers,
|
||||
editedInstanceContainer
|
||||
);
|
||||
}
|
||||
}
|
||||
} else if (data.command === 'setBackgroundColor') {
|
||||
if (inGameEditor) {
|
||||
const editedInstanceContainer =
|
||||
inGameEditor.getEditedInstanceContainer();
|
||||
if (editedInstanceContainer) {
|
||||
const backgroundColor = data.payload.backgroundColor;
|
||||
if (
|
||||
backgroundColor &&
|
||||
editedInstanceContainer instanceof gdjs.RuntimeScene
|
||||
) {
|
||||
const sceneData = runtimeGame.getSceneData(
|
||||
editedInstanceContainer.getScene().getName()
|
||||
);
|
||||
if (sceneData) {
|
||||
editedInstanceContainer._backgroundColor =
|
||||
gdjs.rgbToHexNumber(
|
||||
backgroundColor[0],
|
||||
backgroundColor[1],
|
||||
backgroundColor[2]
|
||||
);
|
||||
sceneData.r = backgroundColor[0];
|
||||
sceneData.v = backgroundColor[1];
|
||||
sceneData.b = backgroundColor[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (data.command === 'hotReloadAllInstances') {
|
||||
if (inGameEditor) {
|
||||
const editedInstanceContainer =
|
||||
inGameEditor.getEditedInstanceContainer();
|
||||
if (editedInstanceContainer) {
|
||||
that._hotReloader.hotReloadRuntimeInstances(
|
||||
inGameEditor.getEditedInstanceDataList(),
|
||||
data.payload.instances,
|
||||
editedInstanceContainer
|
||||
);
|
||||
}
|
||||
}
|
||||
} else if (data.command === 'switchForInGameEdition') {
|
||||
if (!this._runtimegame.isInGameEdition()) return;
|
||||
|
||||
const sceneName = data.sceneName || null;
|
||||
const eventsBasedObjectType = data.eventsBasedObjectType || null;
|
||||
if (!sceneName && !eventsBasedObjectType) {
|
||||
logger.warn(
|
||||
'No scene name specified, switchForInGameEdition aborted'
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (inGameEditor) {
|
||||
const wasPaused = this._runtimegame.isPaused();
|
||||
this._runtimegame.pause(true);
|
||||
inGameEditor.switchToSceneOrVariant(
|
||||
data.editorId || null,
|
||||
sceneName,
|
||||
data.externalLayoutName || null,
|
||||
eventsBasedObjectType,
|
||||
data.eventsBasedObjectVariantName || null,
|
||||
data.editorCamera3D || null
|
||||
);
|
||||
this._runtimegame.pause(wasPaused);
|
||||
}
|
||||
} else if (data.command === 'setVisibleStatus') {
|
||||
if (inGameEditor) {
|
||||
inGameEditor.setVisibleStatus(data.visible);
|
||||
}
|
||||
} else if (data.command === 'updateInstances') {
|
||||
if (inGameEditor) {
|
||||
inGameEditor.reloadInstances(data.payload.instances);
|
||||
}
|
||||
} else if (data.command === 'addInstances') {
|
||||
if (inGameEditor) {
|
||||
inGameEditor.addInstances(data.payload.instances);
|
||||
inGameEditor.setSelectedObjects(
|
||||
data.payload.instances.map((instance) => instance.persistentUuid)
|
||||
);
|
||||
if (data.payload.moveUnderCursor) {
|
||||
inGameEditor.moveSelectionUnderCursor();
|
||||
}
|
||||
}
|
||||
} else if (data.command === 'deleteSelection') {
|
||||
if (inGameEditor) {
|
||||
inGameEditor.deleteSelection();
|
||||
}
|
||||
} else if (data.command === 'dragNewInstance') {
|
||||
const gameCoords = runtimeGame
|
||||
.getRenderer()
|
||||
.convertPageToGameCoords(data.x, data.y);
|
||||
runtimeGame
|
||||
.getInputManager()
|
||||
.onMouseMove(gameCoords[0], gameCoords[1]);
|
||||
|
||||
if (inGameEditor)
|
||||
inGameEditor.dragNewInstance({
|
||||
name: data.name,
|
||||
dropped: data.dropped,
|
||||
});
|
||||
} else if (data.command === 'cancelDragNewInstance') {
|
||||
if (inGameEditor) inGameEditor.cancelDragNewInstance();
|
||||
} else if (data.command === 'setInstancesEditorSettings') {
|
||||
if (inGameEditor)
|
||||
inGameEditor.updateInstancesEditorSettings(
|
||||
data.payload.instancesEditorSettings
|
||||
);
|
||||
} else if (data.command === 'zoomToInitialPosition') {
|
||||
if (inGameEditor) {
|
||||
inGameEditor.zoomToInitialPosition(data.payload.visibleScreenArea);
|
||||
}
|
||||
} else if (data.command === 'zoomToFitContent') {
|
||||
if (inGameEditor) {
|
||||
inGameEditor.zoomToFitContent(data.payload.visibleScreenArea);
|
||||
}
|
||||
} else if (data.command === 'setSelectedLayer') {
|
||||
if (inGameEditor) {
|
||||
inGameEditor.setSelectedLayerName(data.payload.layerName);
|
||||
}
|
||||
} else if (data.command === 'zoomToFitSelection') {
|
||||
if (inGameEditor) {
|
||||
inGameEditor.zoomToFitSelection(data.payload.visibleScreenArea);
|
||||
}
|
||||
} else if (data.command === 'zoomBy') {
|
||||
if (inGameEditor) {
|
||||
inGameEditor.zoomBy(data.payload.zoomFactor);
|
||||
}
|
||||
} else if (data.command === 'setZoom') {
|
||||
if (inGameEditor) {
|
||||
inGameEditor.setZoom(data.payload.zoom);
|
||||
}
|
||||
} else if (data.command === 'setSelectedInstances') {
|
||||
if (inGameEditor) {
|
||||
inGameEditor.setSelectedObjects(data.payload.instanceUuids);
|
||||
}
|
||||
} else if (data.command === 'centerViewOnLastSelectedInstance') {
|
||||
if (inGameEditor) {
|
||||
// TODO: use data.payload.visibleScreenArea
|
||||
inGameEditor.centerViewOnLastSelectedInstance();
|
||||
}
|
||||
} else if (data.command === 'updateInnerArea') {
|
||||
if (inGameEditor) {
|
||||
inGameEditor.updateInnerArea(
|
||||
data.payload.areaMinX,
|
||||
data.payload.areaMinY,
|
||||
data.payload.areaMinZ,
|
||||
data.payload.areaMaxX,
|
||||
data.payload.areaMaxY,
|
||||
data.payload.areaMaxZ
|
||||
);
|
||||
}
|
||||
} else if (data.command === 'getSelectionAABB') {
|
||||
if (inGameEditor) {
|
||||
this.sendSelectionAABB(data.messageId);
|
||||
}
|
||||
} else if (data.command === 'hardReload') {
|
||||
// This usually means that the preview was modified so much that an entire reload
|
||||
// is needed, or that the runtime itself could have been modified.
|
||||
this.launchHardReload();
|
||||
} else {
|
||||
logger.info(
|
||||
'Unknown command "' + data.command + '" received by the debugger.'
|
||||
);
|
||||
that.sendProfilerStopped();
|
||||
});
|
||||
that.sendProfilerStarted();
|
||||
} else if (data.command === 'profiler.stop') {
|
||||
runtimeGame.stopCurrentSceneProfiler();
|
||||
} else if (data.command === 'hotReload') {
|
||||
that._hotReloader.hotReload().then((logs) => {
|
||||
that.sendHotReloaderLogs(logs);
|
||||
});
|
||||
} else {
|
||||
logger.info(
|
||||
'Unknown command "' + data.command + '" received by the debugger.'
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
this.onUncaughtException(error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -330,9 +535,12 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
onUncaughtException(exception: Error): void {
|
||||
logger.error('Uncaught exception: ' + exception);
|
||||
logger.error('Uncaught exception: ', exception, exception.stack);
|
||||
|
||||
this._inGameDebugger.setUncaughtException(exception);
|
||||
const runtimeGame = this._runtimegame;
|
||||
if (!runtimeGame.isInGameEdition()) {
|
||||
this._inGameDebugger.setUncaughtException(exception);
|
||||
}
|
||||
|
||||
if (!this._hasLoggedUncaughtException) {
|
||||
// Only log an uncaught exception once, to avoid spamming the debugger server
|
||||
@@ -435,6 +643,20 @@ namespace gdjs {
|
||||
return true;
|
||||
}
|
||||
|
||||
sendRuntimeGameStatus(): void {
|
||||
const currentScene = this._runtimegame.getSceneStack().getCurrentScene();
|
||||
this._sendMessage(
|
||||
circularSafeStringify({
|
||||
command: 'status',
|
||||
payload: {
|
||||
isPaused: this._runtimegame.isPaused(),
|
||||
isInGameEdition: this._runtimegame.isInGameEdition(),
|
||||
sceneName: currentScene ? currentScene.getName() : null,
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump all the relevant data from the {@link RuntimeGame} instance and send it to the server.
|
||||
*/
|
||||
@@ -515,7 +737,10 @@ namespace gdjs {
|
||||
this._sendMessage(
|
||||
circularSafeStringify({
|
||||
command: 'hotReloader.logs',
|
||||
payload: logs,
|
||||
payload: {
|
||||
isInGameEdition: this._runtimegame.isInGameEdition(),
|
||||
logs,
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -544,26 +769,152 @@ namespace gdjs {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback called when the game is paused.
|
||||
*/
|
||||
sendGamePaused(): void {
|
||||
sendInstanceChanges(changes: {
|
||||
isSendingBackSelectionForDefaultSize: boolean;
|
||||
updatedInstances: Array<InstanceData>;
|
||||
addedInstances: Array<InstanceData>;
|
||||
selectedInstances: Array<InstancePersistentUuidData>;
|
||||
removedInstances: Array<InstancePersistentUuidData>;
|
||||
}): void {
|
||||
const inGameEditor = this._runtimegame.getInGameEditor();
|
||||
if (!inGameEditor) {
|
||||
return;
|
||||
}
|
||||
this._sendMessage(
|
||||
circularSafeStringify({
|
||||
command: 'game.paused',
|
||||
payload: null,
|
||||
command: 'updateInstances',
|
||||
editorId: inGameEditor.getEditorId(),
|
||||
payload: changes,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback called when the game is resumed.
|
||||
*/
|
||||
sendGameResumed(): void {
|
||||
sendOpenContextMenu(cursorX: float, cursorY: float): void {
|
||||
const inGameEditor = this._runtimegame.getInGameEditor();
|
||||
if (!inGameEditor) {
|
||||
return;
|
||||
}
|
||||
this._sendMessage(
|
||||
circularSafeStringify({
|
||||
command: 'game.resumed',
|
||||
payload: null,
|
||||
command: 'openContextMenu',
|
||||
editorId: inGameEditor.getEditorId(),
|
||||
payload: { cursorX, cursorY },
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
sendCameraState(cameraState: EditorCameraState): void {
|
||||
const inGameEditor = this._runtimegame.getInGameEditor();
|
||||
if (!inGameEditor) {
|
||||
return;
|
||||
}
|
||||
this._sendMessage(
|
||||
circularSafeStringify({
|
||||
command: 'setCameraState',
|
||||
editorId: inGameEditor.getEditorId(),
|
||||
payload: cameraState,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
sendUndo(): void {
|
||||
const inGameEditor = this._runtimegame.getInGameEditor();
|
||||
if (!inGameEditor) {
|
||||
return;
|
||||
}
|
||||
this._sendMessage(
|
||||
circularSafeStringify({
|
||||
command: 'undo',
|
||||
editorId: inGameEditor.getEditorId(),
|
||||
payload: {},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
sendRedo(): void {
|
||||
const inGameEditor = this._runtimegame.getInGameEditor();
|
||||
if (!inGameEditor) {
|
||||
return;
|
||||
}
|
||||
this._sendMessage(
|
||||
circularSafeStringify({
|
||||
command: 'redo',
|
||||
editorId: inGameEditor.getEditorId(),
|
||||
payload: {},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
sendCopy(): void {
|
||||
const inGameEditor = this._runtimegame.getInGameEditor();
|
||||
if (!inGameEditor) {
|
||||
return;
|
||||
}
|
||||
this._sendMessage(
|
||||
circularSafeStringify({
|
||||
command: 'copy',
|
||||
editorId: inGameEditor.getEditorId(),
|
||||
payload: {},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
sendPaste(): void {
|
||||
const inGameEditor = this._runtimegame.getInGameEditor();
|
||||
if (!inGameEditor) {
|
||||
return;
|
||||
}
|
||||
this._sendMessage(
|
||||
circularSafeStringify({
|
||||
command: 'paste',
|
||||
editorId: inGameEditor.getEditorId(),
|
||||
payload: {},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
sendCut(): void {
|
||||
const inGameEditor = this._runtimegame.getInGameEditor();
|
||||
if (!inGameEditor) {
|
||||
return;
|
||||
}
|
||||
this._sendMessage(
|
||||
circularSafeStringify({
|
||||
command: 'cut',
|
||||
editorId: inGameEditor.getEditorId(),
|
||||
payload: {},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
sendSelectionAABB(messageId: number): void {
|
||||
const inGameEditor = this._runtimegame.getInGameEditor();
|
||||
if (!inGameEditor) {
|
||||
return;
|
||||
}
|
||||
const selectionAABB = inGameEditor.getSelectionAABB();
|
||||
this._sendMessage(
|
||||
circularSafeStringify({
|
||||
command: 'selectionAABB',
|
||||
editorId: inGameEditor.getEditorId(),
|
||||
messageId,
|
||||
payload: selectionAABB
|
||||
? {
|
||||
minX: selectionAABB.min[0],
|
||||
minY: selectionAABB.min[1],
|
||||
minZ: selectionAABB.min[2],
|
||||
maxX: selectionAABB.max[0],
|
||||
maxY: selectionAABB.max[1],
|
||||
maxZ: selectionAABB.max[2],
|
||||
}
|
||||
: {
|
||||
minX: 0,
|
||||
minY: 0,
|
||||
minZ: 0,
|
||||
maxX: 0,
|
||||
maxY: 0,
|
||||
maxZ: 0,
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -587,5 +938,43 @@ namespace gdjs {
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
launchHardReload(): void {
|
||||
try {
|
||||
const reloadUrl = new URL(location.href);
|
||||
|
||||
// Construct the initial status to be restored.
|
||||
const initialRuntimeGameStatus =
|
||||
this._runtimegame.getAdditionalOptions().initialRuntimeGameStatus;
|
||||
// We use empty strings to avoid `null` to become `"null"`.
|
||||
const runtimeGameStatus: RuntimeGameStatus = {
|
||||
editorId: initialRuntimeGameStatus?.editorId || '',
|
||||
isPaused: this._runtimegame.isPaused(),
|
||||
isInGameEdition: this._runtimegame.isInGameEdition(),
|
||||
sceneName: initialRuntimeGameStatus?.sceneName || '',
|
||||
injectedExternalLayoutName:
|
||||
initialRuntimeGameStatus?.injectedExternalLayoutName || '',
|
||||
skipCreatingInstancesFromScene:
|
||||
initialRuntimeGameStatus?.skipCreatingInstancesFromScene || false,
|
||||
eventsBasedObjectType:
|
||||
initialRuntimeGameStatus?.eventsBasedObjectType || '',
|
||||
eventsBasedObjectVariantName:
|
||||
initialRuntimeGameStatus?.eventsBasedObjectVariantName || '',
|
||||
editorCamera3D: this._runtimegame.getInGameEditor()?.getCameraState(),
|
||||
};
|
||||
|
||||
reloadUrl.searchParams.set(
|
||||
'runtimeGameStatus',
|
||||
JSON.stringify(runtimeGameStatus)
|
||||
);
|
||||
location.replace(reloadUrl);
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
'Could not reload the game with the new initial status',
|
||||
error
|
||||
);
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -144,18 +144,30 @@ namespace gdjs {
|
||||
});
|
||||
}
|
||||
|
||||
hotReload(): Promise<HotReloaderLog[]> {
|
||||
async hotReload({
|
||||
shouldReloadResources,
|
||||
projectData: newProjectData,
|
||||
runtimeGameOptions: newRuntimeGameOptions,
|
||||
}: {
|
||||
shouldReloadResources: boolean;
|
||||
projectData: ProjectData;
|
||||
runtimeGameOptions: RuntimeGameOptions;
|
||||
}): Promise<HotReloaderLog[]> {
|
||||
logger.info('Hot reload started');
|
||||
const wasPaused = this._runtimeGame.isPaused();
|
||||
this._runtimeGame.pause(true);
|
||||
this._logs = [];
|
||||
|
||||
// Save old data of the project, to be used to compute
|
||||
// the difference between the old and new project data:
|
||||
|
||||
const oldProjectData: ProjectData = gdjs.projectData;
|
||||
gdjs.projectData = newProjectData;
|
||||
|
||||
const oldScriptFiles = gdjs.runtimeGameOptions
|
||||
.scriptFiles as RuntimeGameOptionsScriptFile[];
|
||||
const oldRuntimeGameOptions = gdjs.runtimeGameOptions;
|
||||
gdjs.runtimeGameOptions = newRuntimeGameOptions;
|
||||
|
||||
const oldScriptFiles =
|
||||
oldRuntimeGameOptions.scriptFiles as RuntimeGameOptionsScriptFile[];
|
||||
|
||||
oldScriptFiles.forEach((scriptFile) => {
|
||||
this._alreadyLoadedScriptFiles[scriptFile.path] = true;
|
||||
@@ -167,76 +179,102 @@ namespace gdjs {
|
||||
gdjs.behaviorsTypes.items[behaviorTypeName];
|
||||
}
|
||||
|
||||
// Reload projectData and runtimeGameOptions stored by convention in data.js:
|
||||
return this._reloadScript('data.js').then(() => {
|
||||
const newProjectData: ProjectData = gdjs.projectData;
|
||||
if (gdjs.inAppTutorialMessage) {
|
||||
gdjs.inAppTutorialMessage.displayInAppTutorialMessage(
|
||||
this._runtimeGame,
|
||||
newRuntimeGameOptions.inAppTutorialMessageInPreview,
|
||||
newRuntimeGameOptions.inAppTutorialMessagePositionInPreview || ''
|
||||
);
|
||||
}
|
||||
|
||||
const newRuntimeGameOptions: RuntimeGameOptions =
|
||||
gdjs.runtimeGameOptions;
|
||||
const newScriptFiles =
|
||||
newRuntimeGameOptions.scriptFiles as RuntimeGameOptionsScriptFile[];
|
||||
const shouldGenerateScenesEventsCode =
|
||||
!!newRuntimeGameOptions.shouldGenerateScenesEventsCode;
|
||||
const shouldReloadLibraries =
|
||||
!!newRuntimeGameOptions.shouldReloadLibraries;
|
||||
|
||||
if (gdjs.inAppTutorialMessage) {
|
||||
gdjs.inAppTutorialMessage.displayInAppTutorialMessage(
|
||||
this._runtimeGame,
|
||||
newRuntimeGameOptions.inAppTutorialMessageInPreview,
|
||||
newRuntimeGameOptions.inAppTutorialMessagePositionInPreview || ''
|
||||
// Reload the changed scripts, which will have the side effects of re-running
|
||||
// the new scripts, potentially replacing the code of the free functions from
|
||||
// extensions (which is fine) and registering updated behaviors (which will
|
||||
// need to be re-instantiated in runtime objects).
|
||||
try {
|
||||
if (shouldReloadLibraries) {
|
||||
await this.reloadScriptFiles(
|
||||
newProjectData,
|
||||
oldScriptFiles,
|
||||
newScriptFiles,
|
||||
shouldGenerateScenesEventsCode
|
||||
);
|
||||
}
|
||||
|
||||
const newScriptFiles =
|
||||
newRuntimeGameOptions.scriptFiles as RuntimeGameOptionsScriptFile[];
|
||||
const projectDataOnlyExport =
|
||||
!!newRuntimeGameOptions.projectDataOnlyExport;
|
||||
|
||||
// Reload the changed scripts, which will have the side effects of re-running
|
||||
// the new scripts, potentially replacing the code of the free functions from
|
||||
// extensions (which is fine) and registering updated behaviors (which will
|
||||
// need to be re-instantiated in runtime objects).
|
||||
return this.reloadScriptFiles(
|
||||
newProjectData,
|
||||
oldScriptFiles,
|
||||
newScriptFiles,
|
||||
projectDataOnlyExport
|
||||
)
|
||||
.then(() => {
|
||||
const changedRuntimeBehaviors =
|
||||
this._computeChangedRuntimeBehaviors(
|
||||
oldBehaviorConstructors,
|
||||
gdjs.behaviorsTypes.items
|
||||
);
|
||||
return this._hotReloadRuntimeGame(
|
||||
oldProjectData,
|
||||
newProjectData,
|
||||
changedRuntimeBehaviors,
|
||||
this._runtimeGame
|
||||
const newRuntimeGameStatus =
|
||||
newRuntimeGameOptions.initialRuntimeGameStatus;
|
||||
if (
|
||||
newRuntimeGameStatus &&
|
||||
newRuntimeGameStatus.editorId &&
|
||||
newRuntimeGameStatus.isInGameEdition
|
||||
) {
|
||||
if (shouldReloadResources) {
|
||||
// Unloading all resources will force them to be loaded again,
|
||||
// which is sufficient for ensuring they are up-to-date as
|
||||
// resources will be loaded with a 'cache bursting' parameter.
|
||||
this._runtimeGame._resourcesLoader.unloadAllResources();
|
||||
}
|
||||
// The editor don't need to hot-reload the current scene because the
|
||||
// editor always stays in the initial state.
|
||||
this._runtimeGame.setProjectData(newProjectData);
|
||||
await this._runtimeGame.loadFirstAssetsAndStartBackgroundLoading(
|
||||
newRuntimeGameStatus.sceneName || newProjectData.firstLayout,
|
||||
() => {}
|
||||
);
|
||||
const inGameEditor = this._runtimeGame.getInGameEditor();
|
||||
if (inGameEditor) {
|
||||
await inGameEditor.switchToSceneOrVariant(
|
||||
newRuntimeGameStatus.editorId || null,
|
||||
newRuntimeGameStatus.sceneName,
|
||||
newRuntimeGameStatus.injectedExternalLayoutName,
|
||||
newRuntimeGameStatus.eventsBasedObjectType,
|
||||
newRuntimeGameStatus.eventsBasedObjectVariantName,
|
||||
newRuntimeGameStatus.editorCamera3D || null
|
||||
);
|
||||
})
|
||||
.catch((error) => {
|
||||
const errorTarget = error.target;
|
||||
if (errorTarget instanceof HTMLScriptElement) {
|
||||
this._logs.push({
|
||||
kind: 'fatal',
|
||||
message: 'Unable to reload script: ' + errorTarget.src,
|
||||
});
|
||||
} else {
|
||||
this._logs.push({
|
||||
kind: 'fatal',
|
||||
message:
|
||||
'Unexpected error happened while hot-reloading: ' +
|
||||
error.message +
|
||||
'\n' +
|
||||
error.stack,
|
||||
});
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
logger.info(
|
||||
'Hot reload finished with logs:',
|
||||
this._logs.map((log) => '\n' + log.kind + ': ' + log.message)
|
||||
);
|
||||
this._runtimeGame.pause(false);
|
||||
return this._logs;
|
||||
}
|
||||
} else {
|
||||
const changedRuntimeBehaviors = this._computeChangedRuntimeBehaviors(
|
||||
oldBehaviorConstructors,
|
||||
gdjs.behaviorsTypes.items
|
||||
);
|
||||
await this._hotReloadRuntimeGame(
|
||||
oldProjectData,
|
||||
newProjectData,
|
||||
changedRuntimeBehaviors,
|
||||
this._runtimeGame
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
const errorTarget = error.target;
|
||||
if (errorTarget instanceof HTMLScriptElement) {
|
||||
this._logs.push({
|
||||
kind: 'fatal',
|
||||
message: 'Unable to reload script: ' + errorTarget.src,
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this._logs.push({
|
||||
kind: 'fatal',
|
||||
message:
|
||||
'Unexpected error happened while hot-reloading: ' +
|
||||
error.message +
|
||||
'\n' +
|
||||
error.stack,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
logger.info(
|
||||
'Hot reload finished with logs:',
|
||||
this._logs.map((log) => '\n' + log.kind + ': ' + log.message)
|
||||
);
|
||||
this._runtimeGame.pause(wasPaused);
|
||||
return this._logs;
|
||||
}
|
||||
|
||||
_computeChangedRuntimeBehaviors(
|
||||
@@ -281,12 +319,12 @@ namespace gdjs {
|
||||
newProjectData: ProjectData,
|
||||
oldScriptFiles: RuntimeGameOptionsScriptFile[],
|
||||
newScriptFiles: RuntimeGameOptionsScriptFile[],
|
||||
projectDataOnlyExport: boolean
|
||||
shouldGenerateScenesEventsCode: boolean
|
||||
): Promise<void[]> {
|
||||
const reloadPromises: Array<Promise<void>> = [];
|
||||
|
||||
// Reload events, only if they were exported.
|
||||
if (!projectDataOnlyExport) {
|
||||
if (shouldGenerateScenesEventsCode) {
|
||||
newProjectData.layouts.forEach((_layoutData, index) => {
|
||||
reloadPromises.push(this._reloadScript('code' + index + '.js'));
|
||||
});
|
||||
@@ -326,7 +364,7 @@ namespace gdjs {
|
||||
)[0];
|
||||
|
||||
// A file may be removed because of a partial preview.
|
||||
if (!newScriptFile && !projectDataOnlyExport) {
|
||||
if (!newScriptFile && !shouldGenerateScenesEventsCode) {
|
||||
this._logs.push({
|
||||
kind: 'warning',
|
||||
message: 'Script file ' + oldScriptFile.path + ' was removed.',
|
||||
@@ -694,6 +732,16 @@ namespace gdjs {
|
||||
runtimeScene.setEventsGeneratedCodeFunction(newLayoutData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the children object data into every custom object data.
|
||||
*
|
||||
* At the runtime, this is done at the object instantiation.
|
||||
* For hot-reloading, it's done before hands to optimize.
|
||||
*
|
||||
* @param projectData The project data
|
||||
* @param objectDatas The object datas to modify
|
||||
* @returns
|
||||
*/
|
||||
static resolveCustomObjectConfigurations(
|
||||
projectData: ProjectData,
|
||||
objectDatas: ObjectData[]
|
||||
@@ -717,27 +765,43 @@ namespace gdjs {
|
||||
if (!eventsBasedObjectData) {
|
||||
return objectData;
|
||||
}
|
||||
|
||||
const customObjectConfiguration = objectData as ObjectData &
|
||||
CustomObjectConfiguration;
|
||||
const eventsBasedObjectVariantData =
|
||||
gdjs.RuntimeGame._getEventsBasedObjectVariantData(
|
||||
eventsBasedObjectData,
|
||||
customObjectConfiguration.variant
|
||||
);
|
||||
|
||||
// Apply the legacy children configuration overriding if any.
|
||||
const mergedChildObjectDataList =
|
||||
customObjectConfiguration.childrenContent
|
||||
? eventsBasedObjectData.objects.map((objectData) => ({
|
||||
...objectData,
|
||||
...customObjectConfiguration.childrenContent[objectData.name],
|
||||
}))
|
||||
gdjs.CustomRuntimeObjectInstanceContainer.hasChildrenConfigurationOverriding(
|
||||
customObjectConfiguration,
|
||||
eventsBasedObjectVariantData
|
||||
)
|
||||
? eventsBasedObjectData.objects.map((objectData) =>
|
||||
customObjectConfiguration.childrenContent
|
||||
? {
|
||||
...objectData,
|
||||
...customObjectConfiguration.childrenContent[
|
||||
objectData.name
|
||||
],
|
||||
}
|
||||
: objectData
|
||||
)
|
||||
: eventsBasedObjectData.objects;
|
||||
|
||||
const mergedObjectConfiguration = {
|
||||
...eventsBasedObjectData,
|
||||
...objectData,
|
||||
// ObjectData doesn't have an `objects` attribute.
|
||||
// ObjectData doesn't have an `objects` nor `instances` attribute.
|
||||
// This is a small optimization to avoid to create an
|
||||
// InstanceContainerData for each instance to hot-reload their inner
|
||||
// scene (see `_hotReloadRuntimeInstanceContainer` call from
|
||||
// `_hotReloadRuntimeSceneInstances`).
|
||||
...eventsBasedObjectData,
|
||||
...eventsBasedObjectVariantData,
|
||||
objects: mergedChildObjectDataList,
|
||||
// It must be the last one to ensure the object name won't be overridden.
|
||||
...objectData,
|
||||
};
|
||||
return mergedObjectConfiguration;
|
||||
});
|
||||
@@ -751,6 +815,12 @@ namespace gdjs {
|
||||
changedRuntimeBehaviors: ChangedRuntimeBehavior[],
|
||||
runtimeInstanceContainer: gdjs.RuntimeInstanceContainer
|
||||
): void {
|
||||
if (!oldLayoutData.objects || newLayoutData.objects) {
|
||||
// It can happen when `hotReloadRuntimeInstances` is executed.
|
||||
// `hotReloadRuntimeInstances` doesn't resolve the custom objects
|
||||
// because it can only modify the 1st level of instances.
|
||||
return;
|
||||
}
|
||||
const oldObjectDataList = HotReloader.resolveCustomObjectConfigurations(
|
||||
oldProjectData,
|
||||
oldLayoutData.objects
|
||||
@@ -921,16 +991,62 @@ namespace gdjs {
|
||||
return;
|
||||
}
|
||||
|
||||
hotReloadRuntimeSceneObjects(
|
||||
updatedObjects: Array<ObjectData>,
|
||||
// runtimeInstanceContainer gives an access as a map.
|
||||
runtimeInstanceContainer: gdjs.RuntimeInstanceContainer
|
||||
): void {
|
||||
const oldObjects: Array<ObjectData | null> = updatedObjects.map(
|
||||
(objectData) =>
|
||||
runtimeInstanceContainer._objects.get(objectData.name) || null
|
||||
);
|
||||
|
||||
const projectData: ProjectData = this._runtimeGame._data;
|
||||
const newObjectDataList = HotReloader.resolveCustomObjectConfigurations(
|
||||
projectData,
|
||||
updatedObjects
|
||||
);
|
||||
|
||||
this._hotReloadRuntimeSceneObjects(
|
||||
oldObjects,
|
||||
newObjectDataList,
|
||||
runtimeInstanceContainer
|
||||
);
|
||||
// Update the GameData
|
||||
for (let index = 0; index < updatedObjects.length; index++) {
|
||||
const oldObjectData = oldObjects[index];
|
||||
// When the object is new, the hot-reload call `registerObject`
|
||||
// so `_objects` is already updated.
|
||||
if (oldObjectData) {
|
||||
// In gdjs.CustomRuntimeObjectInstanceContainer.loadFrom, object can
|
||||
// be registered with a different instance from the ProjectData. This
|
||||
// is only done for children of a custom object with a children overriding.
|
||||
// In the case of the editor, the fake custom object used for editing
|
||||
// variants has no children overriding (see
|
||||
// gdjs.RuntimeGame._createSceneWithCustomObject).
|
||||
// Thus, the oldObjectData is always the one from the ProjectData.
|
||||
HotReloader.assignOrDelete(oldObjectData, updatedObjects[index]);
|
||||
} else {
|
||||
console.warn(
|
||||
`Can't update object data for "${updatedObjects[index].name}" because it doesn't exist.`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_hotReloadRuntimeSceneObjects(
|
||||
oldObjects: ObjectData[],
|
||||
oldObjects: Array<ObjectData | null>,
|
||||
newObjects: ObjectData[],
|
||||
runtimeInstanceContainer: gdjs.RuntimeInstanceContainer
|
||||
): void {
|
||||
oldObjects.forEach((oldObjectData) => {
|
||||
if (!oldObjectData) {
|
||||
return;
|
||||
}
|
||||
const name = oldObjectData.name;
|
||||
const newObjectData = newObjects.filter(
|
||||
const newObjectData = newObjects.find(
|
||||
(objectData) => objectData.name === name
|
||||
)[0];
|
||||
);
|
||||
|
||||
// Note: if an object is renamed in the editor, it will be considered as removed,
|
||||
// and the new object name as a new object to register.
|
||||
@@ -952,9 +1068,9 @@ namespace gdjs {
|
||||
});
|
||||
newObjects.forEach((newObjectData) => {
|
||||
const name = newObjectData.name;
|
||||
const oldObjectData = oldObjects.filter(
|
||||
(layerData) => layerData.name === name
|
||||
)[0];
|
||||
const oldObjectData = oldObjects.find(
|
||||
(layerData) => layerData && layerData.name === name
|
||||
);
|
||||
if (
|
||||
(!oldObjectData || oldObjectData.type !== newObjectData.type) &&
|
||||
!runtimeInstanceContainer.isObjectRegistered(name)
|
||||
@@ -1192,6 +1308,31 @@ namespace gdjs {
|
||||
);
|
||||
}
|
||||
|
||||
hotReloadRuntimeSceneLayers(
|
||||
newLayers: LayerData[],
|
||||
runtimeInstanceContainer: gdjs.RuntimeInstanceContainer
|
||||
): void {
|
||||
const layerNames = [];
|
||||
runtimeInstanceContainer.getAllLayerNames(layerNames);
|
||||
const oldLayers = layerNames.map((layerName) =>
|
||||
runtimeInstanceContainer.hasLayer(layerName)
|
||||
? runtimeInstanceContainer.getLayer(layerName)._initialLayerData
|
||||
: null
|
||||
);
|
||||
this._hotReloadRuntimeSceneLayers(
|
||||
oldLayers.filter(Boolean) as LayerData[],
|
||||
newLayers,
|
||||
runtimeInstanceContainer
|
||||
);
|
||||
// Update the GameData
|
||||
for (let index = 0; index < newLayers.length; index++) {
|
||||
const oldLayer = oldLayers[index];
|
||||
if (oldLayer) {
|
||||
HotReloader.assignOrDelete(oldLayer, newLayers[index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_hotReloadRuntimeSceneLayers(
|
||||
oldLayers: LayerData[],
|
||||
newLayers: LayerData[],
|
||||
@@ -1273,6 +1414,8 @@ namespace gdjs {
|
||||
newLayer.effects,
|
||||
runtimeLayer
|
||||
);
|
||||
|
||||
runtimeLayer._initialLayerData = newLayer;
|
||||
}
|
||||
|
||||
_hotReloadRuntimeLayerEffects(
|
||||
@@ -1357,6 +1500,28 @@ namespace gdjs {
|
||||
}
|
||||
}
|
||||
|
||||
hotReloadRuntimeInstances(
|
||||
oldInstances: InstanceData[],
|
||||
newInstances: InstanceData[],
|
||||
runtimeInstanceContainer: RuntimeInstanceContainer
|
||||
): void {
|
||||
const projectData: ProjectData = gdjs.projectData;
|
||||
const objects: Array<ObjectData> = [];
|
||||
runtimeInstanceContainer._objects.values(objects);
|
||||
projectData.layouts;
|
||||
this._hotReloadRuntimeSceneInstances(
|
||||
projectData,
|
||||
projectData,
|
||||
[],
|
||||
objects,
|
||||
objects,
|
||||
oldInstances,
|
||||
newInstances,
|
||||
runtimeInstanceContainer
|
||||
);
|
||||
gdjs.copyArray(newInstances, oldInstances);
|
||||
}
|
||||
|
||||
_hotReloadRuntimeSceneInstances(
|
||||
oldProjectData: ProjectData,
|
||||
newProjectData: ProjectData,
|
||||
@@ -1423,6 +1588,9 @@ namespace gdjs {
|
||||
);
|
||||
} else {
|
||||
// Reload objects that were created at runtime.
|
||||
// This is a subset of what is done by `_hotReloadRuntimeInstance`.
|
||||
// Since the instance doesn't exist in the editor, it's properties
|
||||
// can't be updated, only the object changes are applied.
|
||||
|
||||
// Update variables
|
||||
this._hotReloadVariablesContainer(
|
||||
@@ -1431,6 +1599,7 @@ namespace gdjs {
|
||||
runtimeObject.getVariables()
|
||||
);
|
||||
|
||||
// Update the content of custom object
|
||||
if (runtimeObject instanceof gdjs.CustomRuntimeObject) {
|
||||
const childrenInstanceContainer =
|
||||
runtimeObject.getChildrenContainer();
|
||||
@@ -1443,15 +1612,18 @@ namespace gdjs {
|
||||
CustomObjectConfiguration &
|
||||
InstanceContainerData;
|
||||
|
||||
// Reload the content of custom objects that were created at runtime.
|
||||
this._hotReloadRuntimeInstanceContainer(
|
||||
oldProjectData,
|
||||
newProjectData,
|
||||
oldCustomObjectData,
|
||||
newCustomObjectData,
|
||||
changedRuntimeBehaviors,
|
||||
childrenInstanceContainer
|
||||
);
|
||||
// Variant swapping is handled by `CustomRuntimeObject.updateFromObjectData`.
|
||||
if (newCustomObjectData.variant === oldCustomObjectData.variant) {
|
||||
// Reload the content of custom objects that were created at runtime.
|
||||
this._hotReloadRuntimeInstanceContainer(
|
||||
oldProjectData,
|
||||
newProjectData,
|
||||
oldCustomObjectData,
|
||||
newCustomObjectData,
|
||||
changedRuntimeBehaviors,
|
||||
childrenInstanceContainer
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1513,22 +1685,16 @@ namespace gdjs {
|
||||
somethingChanged = true;
|
||||
}
|
||||
if (gdjs.Base3DHandler && gdjs.Base3DHandler.is3D(runtimeObject)) {
|
||||
if (oldInstance.z !== newInstance.z && newInstance.z !== undefined) {
|
||||
runtimeObject.setZ(newInstance.z);
|
||||
if (oldInstance.z !== newInstance.z) {
|
||||
runtimeObject.setZ(newInstance.z || 0);
|
||||
somethingChanged = true;
|
||||
}
|
||||
if (
|
||||
oldInstance.rotationX !== newInstance.rotationX &&
|
||||
newInstance.rotationX !== undefined
|
||||
) {
|
||||
runtimeObject.setRotationX(newInstance.rotationX);
|
||||
if (oldInstance.rotationX !== newInstance.rotationX) {
|
||||
runtimeObject.setRotationX(newInstance.rotationX || 0);
|
||||
somethingChanged = true;
|
||||
}
|
||||
if (
|
||||
oldInstance.rotationY !== newInstance.rotationY &&
|
||||
newInstance.rotationY !== undefined
|
||||
) {
|
||||
runtimeObject.setRotationY(newInstance.rotationY);
|
||||
if (oldInstance.rotationY !== newInstance.rotationY) {
|
||||
runtimeObject.setRotationY(newInstance.rotationY || 0);
|
||||
somethingChanged = true;
|
||||
}
|
||||
}
|
||||
@@ -1583,8 +1749,6 @@ namespace gdjs {
|
||||
}
|
||||
}
|
||||
if (runtimeObject instanceof gdjs.CustomRuntimeObject) {
|
||||
const childrenInstanceContainer = runtimeObject.getChildrenContainer();
|
||||
|
||||
// The `objects` attribute is already resolved by `resolveCustomObjectConfigurations()`.
|
||||
const oldCustomObjectData = oldObjectData as ObjectData &
|
||||
CustomObjectConfiguration &
|
||||
@@ -1593,14 +1757,19 @@ namespace gdjs {
|
||||
CustomObjectConfiguration &
|
||||
InstanceContainerData;
|
||||
|
||||
this._hotReloadRuntimeInstanceContainer(
|
||||
oldProjectData,
|
||||
newProjectData,
|
||||
oldCustomObjectData,
|
||||
newCustomObjectData,
|
||||
changedRuntimeBehaviors,
|
||||
childrenInstanceContainer
|
||||
);
|
||||
// Variant swapping is handled by `CustomRuntimeObject.updateFromObjectData`.
|
||||
if (newCustomObjectData.variant === oldCustomObjectData.variant) {
|
||||
const childrenInstanceContainer =
|
||||
runtimeObject.getChildrenContainer();
|
||||
this._hotReloadRuntimeInstanceContainer(
|
||||
oldProjectData,
|
||||
newProjectData,
|
||||
oldCustomObjectData,
|
||||
newCustomObjectData,
|
||||
changedRuntimeBehaviors,
|
||||
childrenInstanceContainer
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Update variables
|
||||
@@ -1727,5 +1896,23 @@ namespace gdjs {
|
||||
// true if both NaN, false otherwise
|
||||
return a !== a && b !== b;
|
||||
}
|
||||
|
||||
static assignOrDelete(
|
||||
target: any,
|
||||
source: any,
|
||||
ignoreKeys: string[] = []
|
||||
): void {
|
||||
Object.assign(target, source);
|
||||
for (const key in target) {
|
||||
if (ignoreKeys.includes(key)) {
|
||||
continue;
|
||||
}
|
||||
if (Object.prototype.hasOwnProperty.call(target, key)) {
|
||||
if (source[key] === undefined) {
|
||||
delete target[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -56,6 +56,19 @@ namespace gdjs {
|
||||
};
|
||||
this._ws.onclose = function close() {
|
||||
logger.info('Debugger connection closed');
|
||||
|
||||
if (that._runtimegame.isInGameEdition()) {
|
||||
// Sometimes, for example if the editor is launched for a long time and the device goes to sleep,
|
||||
// the WebSocket connection between the editor and the game is closed. When we are in in-game edition,
|
||||
// we can't afford to lose the connection because it means the editor is unusable.
|
||||
// In this case, we hard reload the game to re-establish a new connection.
|
||||
setTimeout(() => {
|
||||
logger.info(
|
||||
'Debugger connection closed while in in-game edition - this is suspicious so hard reloading to re-establish a new connection.'
|
||||
);
|
||||
that.launchHardReload();
|
||||
}, 1000);
|
||||
}
|
||||
};
|
||||
this._ws.onerror = function errored(error) {
|
||||
logger.warn('Debugger client error:', error);
|
||||
|
@@ -11,7 +11,13 @@ namespace gdjs {
|
||||
constructor(runtimeGame: RuntimeGame) {
|
||||
super(runtimeGame);
|
||||
|
||||
// Opener is either the `opener` for popups, or the `parent` if the game
|
||||
// is running as an iframe (notably: in-game edition).
|
||||
this._opener = window.opener || null;
|
||||
if (!this._opener && window.parent !== window) {
|
||||
this._opener = window.parent;
|
||||
}
|
||||
|
||||
if (!this._opener) {
|
||||
logger.info("`window.opener` not existing, the debugger won't work.");
|
||||
return;
|
||||
|
@@ -153,7 +153,7 @@ namespace gdjs {
|
||||
};
|
||||
|
||||
/**
|
||||
* Return true if the specified key is pressed
|
||||
* Return true if the specified key is pressed (i.e: just pressed or held down).
|
||||
*
|
||||
*/
|
||||
export const isKeyPressed = function (
|
||||
@@ -170,8 +170,22 @@ namespace gdjs {
|
||||
};
|
||||
|
||||
/**
|
||||
* Return true if the specified key was just released
|
||||
*
|
||||
* Return true if the specified key was just pressed (i.e: it started being pressed
|
||||
* during this frame).
|
||||
*/
|
||||
export const wasKeyJustPressed = function (
|
||||
instanceContainer: gdjs.RuntimeInstanceContainer,
|
||||
key: string
|
||||
) {
|
||||
return instanceContainer
|
||||
.getGame()
|
||||
.getInputManager()
|
||||
.wasKeyJustPressed(gdjs.evtTools.input.keysNameToCode[key]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Return true if the specified key was just released (i.e: it stopped being pressed
|
||||
* during this frame).
|
||||
*/
|
||||
export const wasKeyReleased = function (
|
||||
instanceContainer: gdjs.RuntimeInstanceContainer,
|
||||
@@ -187,7 +201,7 @@ namespace gdjs {
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the name of the last key pressed in the game
|
||||
* Return the name of the last key pressed in the game.
|
||||
*/
|
||||
export const lastPressedKey = function (
|
||||
instanceContainer: gdjs.RuntimeInstanceContainer
|
||||
|
@@ -206,26 +206,16 @@ namespace gdjs {
|
||||
this._loadedFontFamilySet.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Unload the specified list of resources:
|
||||
* this clears the caches of loaded font families.
|
||||
*
|
||||
* Usually called when scene resoures are unloaded.
|
||||
*
|
||||
* @param resourcesList The list of specific resources
|
||||
*/
|
||||
unloadResourcesList(resourcesList: ResourceData[]): void {
|
||||
resourcesList.forEach((resourceData) => {
|
||||
const resource = this._loadedFontFamily.get(resourceData);
|
||||
if (resource) {
|
||||
this._loadedFontFamily.delete(resourceData);
|
||||
}
|
||||
unloadResource(resourceData: ResourceData): void {
|
||||
const resource = this._loadedFontFamily.getFromName(resourceData.name);
|
||||
if (resource) {
|
||||
this._loadedFontFamily.delete(resourceData);
|
||||
}
|
||||
|
||||
const fontName = this._getFontFamilyFromFilename(resourceData);
|
||||
if (fontName) {
|
||||
this._loadedFontFamilySet.delete(fontName);
|
||||
}
|
||||
});
|
||||
const fontName = this._getFontFamilyFromFilename(resourceData);
|
||||
if (fontName) {
|
||||
this._loadedFontFamilySet.delete(fontName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -502,21 +502,18 @@ namespace gdjs {
|
||||
* @returns {Array}
|
||||
*/
|
||||
export const objectsListsToArray = function (
|
||||
objectsLists: Hashtable<RuntimeObject>,
|
||||
result: Array<RuntimeObject> = []
|
||||
objectsLists: Hashtable<RuntimeObject>
|
||||
): Array<RuntimeObject> {
|
||||
const lists = gdjs.staticArray(gdjs.objectsListsToArray);
|
||||
var lists = gdjs.staticArray(gdjs.objectsListsToArray);
|
||||
objectsLists.values(lists);
|
||||
|
||||
let resultIndex = 0;
|
||||
for (let i = 0; i < lists.length; ++i) {
|
||||
const arr = lists[i];
|
||||
for (let k = 0; k < arr.length; ++k) {
|
||||
result[resultIndex] = arr[k];
|
||||
resultIndex++;
|
||||
var result: Array<RuntimeObject> = [];
|
||||
for (var i = 0; i < lists.length; ++i) {
|
||||
var arr = lists[i];
|
||||
for (var k = 0; k < arr.length; ++k) {
|
||||
result.push(arr[k]);
|
||||
}
|
||||
}
|
||||
result.length = resultIndex;
|
||||
return result;
|
||||
};
|
||||
|
||||
@@ -527,8 +524,8 @@ namespace gdjs {
|
||||
* @param dst The destination array
|
||||
*/
|
||||
export const copyArray = function <T>(src: Array<T>, dst: Array<T>): void {
|
||||
const len = src.length;
|
||||
for (let i = 0; i < len; ++i) {
|
||||
var len = src.length;
|
||||
for (var i = 0; i < len; ++i) {
|
||||
dst[i] = src[i];
|
||||
}
|
||||
dst.length = len;
|
||||
|
@@ -365,7 +365,7 @@ namespace gdjs {
|
||||
* It is basically a container to associate channels to sounds and keep a list
|
||||
* of all sounds being played.
|
||||
*/
|
||||
export class HowlerSoundManager {
|
||||
export class HowlerSoundManager implements gdjs.ResourceManager {
|
||||
_loadedMusics = new gdjs.ResourceCache<Howl>();
|
||||
_loadedSounds = new gdjs.ResourceCache<Howl>();
|
||||
_availableResources: Record<string, ResourceData> = {};
|
||||
@@ -940,26 +940,16 @@ namespace gdjs {
|
||||
this.unloadAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Unload the specified list of resources:
|
||||
* this unloads all audio from the specified resources from memory.
|
||||
*
|
||||
* Usually called when scene resoures are unloaded.
|
||||
*
|
||||
* @param resourcesList The list of specific resources
|
||||
*/
|
||||
unloadResourcesList(resourcesList: ResourceData[]): void {
|
||||
resourcesList.forEach((resourceData) => {
|
||||
const musicRes = this._loadedMusics.get(resourceData);
|
||||
if (musicRes) {
|
||||
this.unloadAudio(resourceData.name, true);
|
||||
}
|
||||
unloadResource(resourceData: ResourceData): void {
|
||||
const musicRes = this._loadedMusics.getFromName(resourceData.name);
|
||||
if (musicRes) {
|
||||
this.unloadAudio(resourceData.name, true);
|
||||
}
|
||||
|
||||
const soundRes = this._loadedSounds.get(resourceData);
|
||||
if (soundRes) {
|
||||
this.unloadAudio(resourceData.name, false);
|
||||
}
|
||||
});
|
||||
const soundRes = this._loadedSounds.getFromName(resourceData.name);
|
||||
if (soundRes) {
|
||||
this.unloadAudio(resourceData.name, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -23,33 +23,38 @@ namespace gdjs {
|
||||
* variants and should default to their left variant values
|
||||
* if location is not specified.
|
||||
*/
|
||||
static _DEFAULT_LEFT_VARIANT_KEYS: integer[] = [16, 17, 18, 91];
|
||||
_pressedKeys: Hashtable<boolean>;
|
||||
_releasedKeys: Hashtable<boolean>;
|
||||
_lastPressedKey: float = 0;
|
||||
_pressedMouseButtons: Array<boolean>;
|
||||
_releasedMouseButtons: Array<boolean>;
|
||||
private static _DEFAULT_LEFT_VARIANT_KEYS: integer[] = [16, 17, 18, 91];
|
||||
private _pressedKeys: Hashtable<boolean>;
|
||||
private _justPressedKeys: Hashtable<boolean>;
|
||||
private _releasedKeys: Hashtable<boolean>;
|
||||
private _lastPressedKey: float = 0;
|
||||
private _pressedMouseButtons: Array<boolean>;
|
||||
private _releasedMouseButtons: Array<boolean>;
|
||||
/**
|
||||
* The cursor X position (moved by mouse and touch events).
|
||||
*/
|
||||
_cursorX: float = 0;
|
||||
private _cursorX: float = 0;
|
||||
/**
|
||||
* The cursor Y position (moved by mouse and touch events).
|
||||
*/
|
||||
_cursorY: float = 0;
|
||||
private _cursorY: float = 0;
|
||||
/**
|
||||
* The mouse X position (only moved by mouse events).
|
||||
*/
|
||||
_mouseX: float = 0;
|
||||
private _mouseX: float = 0;
|
||||
/**
|
||||
* The mouse Y position (only moved by mouse events).
|
||||
*/
|
||||
_mouseY: float = 0;
|
||||
_isMouseInsideCanvas: boolean = true;
|
||||
_mouseWheelDelta: float = 0;
|
||||
private _mouseY: float = 0;
|
||||
private _isMouseInsideCanvas: boolean = true;
|
||||
private _wheelDeltaX: float = 0;
|
||||
private _wheelDeltaY: float = 0;
|
||||
private _wheelDeltaZ: float = 0;
|
||||
|
||||
// TODO Remove _touches when there is no longer SpritePanelButton 1.2.0
|
||||
// extension in the wild.
|
||||
_touches = {
|
||||
// @ts-ignore
|
||||
private _touches = {
|
||||
firstKey: (): string | number | null => {
|
||||
for (const key in this._mouseOrTouches.items) {
|
||||
// Exclude mouse key.
|
||||
@@ -60,25 +65,27 @@ namespace gdjs {
|
||||
return null;
|
||||
},
|
||||
};
|
||||
_mouseOrTouches: Hashtable<Touch>;
|
||||
|
||||
private _mouseOrTouches: Hashtable<Touch>;
|
||||
//Identifiers of the touches that started during/before the frame.
|
||||
_startedTouches: Array<integer> = [];
|
||||
private _startedTouches: Array<integer> = [];
|
||||
|
||||
//Identifiers of the touches that ended during/before the frame.
|
||||
_endedTouches: Array<integer> = [];
|
||||
_touchSimulateMouse: boolean = true;
|
||||
private _endedTouches: Array<integer> = [];
|
||||
private _touchSimulateMouse: boolean = true;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
_lastStartedTouchIndex = 0;
|
||||
private _lastStartedTouchIndex = 0;
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
_lastEndedTouchIndex = 0;
|
||||
private _lastEndedTouchIndex = 0;
|
||||
|
||||
constructor() {
|
||||
this._pressedKeys = new Hashtable();
|
||||
this._justPressedKeys = new Hashtable();
|
||||
this._releasedKeys = new Hashtable();
|
||||
this._pressedMouseButtons = new Array(5);
|
||||
this._releasedMouseButtons = new Array(5);
|
||||
@@ -94,7 +101,7 @@ namespace gdjs {
|
||||
* @param keyCode The raw key code
|
||||
* @param location The location
|
||||
*/
|
||||
_getLocationAwareKeyCode(
|
||||
static getLocationAwareKeyCode(
|
||||
keyCode: number,
|
||||
location: number | null | undefined
|
||||
): integer {
|
||||
@@ -119,11 +126,12 @@ namespace gdjs {
|
||||
* @param location The location of the event.
|
||||
*/
|
||||
onKeyPressed(keyCode: number, location?: number): void {
|
||||
const locationAwareKeyCode = this._getLocationAwareKeyCode(
|
||||
const locationAwareKeyCode = InputManager.getLocationAwareKeyCode(
|
||||
keyCode,
|
||||
location
|
||||
);
|
||||
this._pressedKeys.put(locationAwareKeyCode, true);
|
||||
this._justPressedKeys.put(locationAwareKeyCode, true);
|
||||
this._lastPressedKey = locationAwareKeyCode;
|
||||
}
|
||||
|
||||
@@ -135,14 +143,39 @@ namespace gdjs {
|
||||
* @param location The location of the event.
|
||||
*/
|
||||
onKeyReleased(keyCode: number, location?: number): void {
|
||||
const locationAwareKeyCode = this._getLocationAwareKeyCode(
|
||||
const locationAwareKeyCode = InputManager.getLocationAwareKeyCode(
|
||||
keyCode,
|
||||
location
|
||||
);
|
||||
this._pressedKeys.put(locationAwareKeyCode, false);
|
||||
this._justPressedKeys.put(locationAwareKeyCode, false);
|
||||
this._releasedKeys.put(locationAwareKeyCode, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Release all keys that are currently pressed.
|
||||
* Note: if you want to discard pressed keys without considering them as
|
||||
* released, check `clearAllPressedKeys` instead.
|
||||
*/
|
||||
releaseAllPressedKeys(): void {
|
||||
for (const locationAwareKeyCode in this._pressedKeys.items) {
|
||||
this._pressedKeys.put(locationAwareKeyCode, false);
|
||||
this._justPressedKeys.put(locationAwareKeyCode, false);
|
||||
this._releasedKeys.put(locationAwareKeyCode, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all stored pressed keys without making the keys go through
|
||||
* the release state.
|
||||
* Note: prefer to use `releaseAllPressedKeys` instead, as it corresponds
|
||||
* to a normal key release.
|
||||
*/
|
||||
clearAllPressedKeys(): void {
|
||||
this._pressedKeys.clear();
|
||||
this._justPressedKeys.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the location-aware code of the last key that was pressed.
|
||||
* @return The location-aware code of the last key pressed.
|
||||
@@ -152,14 +185,21 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the key corresponding to the location-aware keyCode is pressed.
|
||||
* Return true if the key corresponding to the location-aware keyCode is pressed
|
||||
* (either it was just pressed or is still held down).
|
||||
* @param locationAwareKeyCode The location-aware key code to be tested.
|
||||
*/
|
||||
isKeyPressed(locationAwareKeyCode: number): boolean {
|
||||
return (
|
||||
this._pressedKeys.containsKey(locationAwareKeyCode) &&
|
||||
this._pressedKeys.get(locationAwareKeyCode)
|
||||
);
|
||||
return !!this._pressedKeys.get(locationAwareKeyCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the key corresponding to the location-aware keyCode
|
||||
* was just pressed during the last frame.
|
||||
* @param locationAwareKeyCode The location-aware key code to be tested.
|
||||
*/
|
||||
wasKeyJustPressed(locationAwareKeyCode: number): boolean {
|
||||
return !!this._justPressedKeys.get(locationAwareKeyCode);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -167,10 +207,7 @@ namespace gdjs {
|
||||
* @param locationAwareKeyCode The location-aware key code to be tested.
|
||||
*/
|
||||
wasKeyReleased(locationAwareKeyCode: number) {
|
||||
return (
|
||||
this._releasedKeys.containsKey(locationAwareKeyCode) &&
|
||||
this._releasedKeys.get(locationAwareKeyCode)
|
||||
);
|
||||
return !!this._releasedKeys.get(locationAwareKeyCode);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -303,6 +340,19 @@ namespace gdjs {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if any mouse button is pressed.
|
||||
* @return true if any mouse button is pressed.
|
||||
*/
|
||||
anyMouseButtonPressed(): boolean {
|
||||
for (const buttonCode in this._pressedMouseButtons) {
|
||||
if (this._pressedMouseButtons[buttonCode]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
_setMouseButtonPressed(buttonCode: number): void {
|
||||
this._pressedMouseButtons[buttonCode] = true;
|
||||
this._releasedMouseButtons[buttonCode] = false;
|
||||
@@ -348,17 +398,37 @@ namespace gdjs {
|
||||
|
||||
/**
|
||||
* Should be called whenever the mouse wheel is used
|
||||
* @param wheelDelta The mouse wheel delta
|
||||
* @param wheelDeltaY The mouse wheel delta
|
||||
*/
|
||||
onMouseWheel(wheelDelta: number): void {
|
||||
this._mouseWheelDelta = wheelDelta;
|
||||
onMouseWheel(
|
||||
wheelDeltaY: number,
|
||||
wheelDeltaX: number,
|
||||
wheelDeltaZ: number
|
||||
): void {
|
||||
this._wheelDeltaY = wheelDeltaY;
|
||||
if (wheelDeltaX !== undefined) this._wheelDeltaX = wheelDeltaX;
|
||||
if (wheelDeltaZ !== undefined) this._wheelDeltaZ = wheelDeltaZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mouse wheel delta
|
||||
* Return the mouse wheel delta on Y axis.
|
||||
*/
|
||||
getMouseWheelDelta(): float {
|
||||
return this._mouseWheelDelta;
|
||||
return this._wheelDeltaY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mouse wheel delta on X axis.
|
||||
*/
|
||||
getMouseWheelDeltaX(): float {
|
||||
return this._wheelDeltaX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mouse wheel delta on Z axis.
|
||||
*/
|
||||
getMouseWheelDeltaZ(): float {
|
||||
return this._wheelDeltaZ;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -544,8 +614,11 @@ namespace gdjs {
|
||||
this._startedTouches.length = 0;
|
||||
this._endedTouches.length = 0;
|
||||
this._releasedKeys.clear();
|
||||
this._justPressedKeys.clear();
|
||||
this._releasedMouseButtons.length = 0;
|
||||
this._mouseWheelDelta = 0;
|
||||
this._wheelDeltaX = 0;
|
||||
this._wheelDeltaY = 0;
|
||||
this._wheelDeltaZ = 0;
|
||||
this._lastStartedTouchIndex = 0;
|
||||
this._lastEndedTouchIndex = 0;
|
||||
}
|
||||
@@ -564,14 +637,6 @@ namespace gdjs {
|
||||
return this.getMouseWheelDelta() < 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all stored pressed keys without making the keys go through
|
||||
* the release state.
|
||||
*/
|
||||
clearAllPressedKeys(): void {
|
||||
this._pressedKeys.clear();
|
||||
}
|
||||
|
||||
static _allTouchIds: Array<integer> = [];
|
||||
}
|
||||
}
|
||||
|
@@ -209,25 +209,16 @@ namespace gdjs {
|
||||
this._callbacks.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Unload the specified list of resources:
|
||||
* this clears the JSONs 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 loadedJson = this._loadedJsons.get(resourceData);
|
||||
if (loadedJson) {
|
||||
this._loadedJsons.delete(resourceData);
|
||||
}
|
||||
unloadResource(resourceData: ResourceData): void {
|
||||
const loadedJson = this._loadedJsons.getFromName(resourceData.name);
|
||||
if (loadedJson) {
|
||||
this._loadedJsons.delete(resourceData);
|
||||
}
|
||||
|
||||
const callback = this._callbacks.get(resourceData);
|
||||
if (callback) {
|
||||
this._callbacks.delete(resourceData);
|
||||
}
|
||||
});
|
||||
const callback = this._callbacks.getFromName(resourceData.name);
|
||||
if (callback) {
|
||||
this._callbacks.delete(resourceData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user