mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
Compare commits
484 Commits
cursor/sco
...
shape-pain
Author | SHA1 | Date | |
---|---|---|---|
![]() |
62ed7a4998 | ||
![]() |
dc5dab7086 | ||
![]() |
dad7546099 | ||
![]() |
0a55eb46d3 | ||
![]() |
2678d86783 | ||
![]() |
b31618568a | ||
![]() |
b3b4681c51 | ||
![]() |
b1b7643efc | ||
![]() |
d43479cbe5 | ||
![]() |
11d3c52197 | ||
![]() |
2ee1050083 | ||
![]() |
ca00afb918 | ||
![]() |
02953a1436 | ||
![]() |
e14215c679 | ||
![]() |
c60ea0701a | ||
![]() |
de6ae1cc8f | ||
![]() |
ad24acd72f | ||
![]() |
3c0bb83032 | ||
![]() |
d5c96d74ed | ||
![]() |
43aada8ae7 | ||
![]() |
17a7c2815f | ||
![]() |
9441774a22 | ||
![]() |
d279276cc0 | ||
![]() |
c7f8a7a2eb | ||
![]() |
0fb92e000b | ||
![]() |
76d43e1695 | ||
![]() |
d70e3f71a6 | ||
![]() |
fe50ea3c01 | ||
![]() |
77f821250a | ||
![]() |
c3ee9d9891 | ||
![]() |
313f7857e2 | ||
![]() |
ff028ffa62 | ||
![]() |
3ac2aec229 | ||
![]() |
92e8dfdf04 | ||
![]() |
80726da56d | ||
![]() |
3098fb034f | ||
![]() |
64cc788d43 | ||
![]() |
a85668c8d9 | ||
![]() |
9d0f172663 | ||
![]() |
e0d2f16dc1 | ||
![]() |
0578c45b0f | ||
![]() |
a8866a7bd6 | ||
![]() |
ec62e33530 | ||
![]() |
e1f12837f1 | ||
![]() |
732f3c14b9 | ||
![]() |
7fdad45192 | ||
![]() |
add042ec16 | ||
![]() |
94590d3713 | ||
![]() |
de310512a3 | ||
![]() |
a9edd2f21a | ||
![]() |
0cc03bd4b5 | ||
![]() |
cdbcaf59d9 | ||
![]() |
4e9b09e426 | ||
![]() |
e174136fb4 | ||
![]() |
3d843a0170 | ||
![]() |
ee2f7fb8dc | ||
![]() |
a780601230 | ||
![]() |
cc42923e16 | ||
![]() |
0e8a223b24 | ||
![]() |
c2f17f9348 | ||
![]() |
0ba59d5d7f | ||
![]() |
6878a4cd75 | ||
![]() |
33eed58c62 | ||
![]() |
f516eff739 | ||
![]() |
baefc272f6 | ||
![]() |
2badc72dfb | ||
![]() |
5e54f02061 | ||
![]() |
ff8697ed71 | ||
![]() |
542a841791 | ||
![]() |
b9640f0049 | ||
![]() |
827bb2d08d | ||
![]() |
0fda0baa48 | ||
![]() |
e655cc0661 | ||
![]() |
a6cc3dc85b | ||
![]() |
225d1b37ab | ||
![]() |
5a0ab9dffa | ||
![]() |
6083c71e0e | ||
![]() |
e844ea819b | ||
![]() |
27f0300bc7 | ||
![]() |
9300604d56 | ||
![]() |
02fcf1dbc2 | ||
![]() |
35cea2eaba | ||
![]() |
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 | ||
![]() |
a3ef07c163 | ||
![]() |
a8ede5eee7 | ||
![]() |
ce965ca31c | ||
![]() |
039fbb8b1b | ||
![]() |
80c1e67146 | ||
![]() |
2591cabdd0 | ||
![]() |
45620d6bf4 | ||
![]() |
89a59cdd35 | ||
![]() |
17b819a423 | ||
![]() |
afb4e3c1c6 | ||
![]() |
e5f8fe1bf8 | ||
![]() |
30de5c6fa9 | ||
![]() |
79a29cf7d5 | ||
![]() |
98a1323bf5 | ||
![]() |
0fe6585897 | ||
![]() |
33dd605c57 | ||
![]() |
bab0c227f8 | ||
![]() |
5ad2be4a8a | ||
![]() |
39e18678f5 | ||
![]() |
f54b13a9f5 | ||
![]() |
703adc090a | ||
![]() |
81f0047dab | ||
![]() |
ef70add27b | ||
![]() |
d45932cc04 | ||
![]() |
081a97a5fc | ||
![]() |
2b3cedb441 | ||
![]() |
76426117ff | ||
![]() |
e8720780eb | ||
![]() |
e5ed642121 | ||
![]() |
a6602292b7 | ||
![]() |
859d8e08a0 | ||
![]() |
8128adfd7b | ||
![]() |
f9317dd17f | ||
![]() |
32a169014a | ||
![]() |
37d3fd99eb | ||
![]() |
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 |
@@ -42,15 +42,19 @@ gd::String EventsCodeGenerator::GenerateRelationalOperatorCall(
|
||||
const vector<gd::String>& arguments,
|
||||
const gd::String& callStartString,
|
||||
std::size_t startFromArgument) {
|
||||
std::size_t relationalOperatorIndex = instrInfos.parameters.GetParametersCount();
|
||||
for (std::size_t i = startFromArgument; i < instrInfos.parameters.GetParametersCount();
|
||||
std::size_t relationalOperatorIndex =
|
||||
instrInfos.parameters.GetParametersCount();
|
||||
for (std::size_t i = startFromArgument;
|
||||
i < instrInfos.parameters.GetParametersCount();
|
||||
++i) {
|
||||
if (instrInfos.parameters.GetParameter(i).GetType() == "relationalOperator") {
|
||||
if (instrInfos.parameters.GetParameter(i).GetType() ==
|
||||
"relationalOperator") {
|
||||
relationalOperatorIndex = i;
|
||||
}
|
||||
}
|
||||
// Ensure that there is at least one parameter after the relational operator
|
||||
if (relationalOperatorIndex + 1 >= instrInfos.parameters.GetParametersCount()) {
|
||||
if (relationalOperatorIndex + 1 >=
|
||||
instrInfos.parameters.GetParametersCount()) {
|
||||
ReportError();
|
||||
return "";
|
||||
}
|
||||
@@ -87,20 +91,23 @@ gd::String EventsCodeGenerator::GenerateRelationalOperation(
|
||||
const gd::String& relationalOperator,
|
||||
const gd::String& lhs,
|
||||
const gd::String& rhs) {
|
||||
return lhs + " " + GenerateRelationalOperatorCodes(relationalOperator) + " " + rhs;
|
||||
return lhs + " " + GenerateRelationalOperatorCodes(relationalOperator) + " " +
|
||||
rhs;
|
||||
}
|
||||
|
||||
const gd::String EventsCodeGenerator::GenerateRelationalOperatorCodes(const gd::String &operatorString) {
|
||||
if (operatorString == "=") {
|
||||
return "==";
|
||||
}
|
||||
if (operatorString != "<" && operatorString != ">" &&
|
||||
operatorString != "<=" && operatorString != ">=" && operatorString != "!=" &&
|
||||
operatorString != "startsWith" && operatorString != "endsWith" && operatorString != "contains") {
|
||||
cout << "Warning: Bad relational operator: Set to == by default." << endl;
|
||||
return "==";
|
||||
}
|
||||
return operatorString;
|
||||
const gd::String EventsCodeGenerator::GenerateRelationalOperatorCodes(
|
||||
const gd::String& operatorString) {
|
||||
if (operatorString == "=") {
|
||||
return "==";
|
||||
}
|
||||
if (operatorString != "<" && operatorString != ">" &&
|
||||
operatorString != "<=" && operatorString != ">=" &&
|
||||
operatorString != "!=" && operatorString != "startsWith" &&
|
||||
operatorString != "endsWith" && operatorString != "contains") {
|
||||
cout << "Warning: Bad relational operator: Set to == by default." << endl;
|
||||
return "==";
|
||||
}
|
||||
return operatorString;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -124,7 +131,8 @@ gd::String EventsCodeGenerator::GenerateOperatorCall(
|
||||
const gd::String& getterStartString,
|
||||
std::size_t startFromArgument) {
|
||||
std::size_t operatorIndex = instrInfos.parameters.GetParametersCount();
|
||||
for (std::size_t i = startFromArgument; i < instrInfos.parameters.GetParametersCount();
|
||||
for (std::size_t i = startFromArgument;
|
||||
i < instrInfos.parameters.GetParametersCount();
|
||||
++i) {
|
||||
if (instrInfos.parameters.GetParameter(i).GetType() == "operator") {
|
||||
operatorIndex = i;
|
||||
@@ -195,7 +203,8 @@ gd::String EventsCodeGenerator::GenerateCompoundOperatorCall(
|
||||
const gd::String& callStartString,
|
||||
std::size_t startFromArgument) {
|
||||
std::size_t operatorIndex = instrInfos.parameters.GetParametersCount();
|
||||
for (std::size_t i = startFromArgument; i < instrInfos.parameters.GetParametersCount();
|
||||
for (std::size_t i = startFromArgument;
|
||||
i < instrInfos.parameters.GetParametersCount();
|
||||
++i) {
|
||||
if (instrInfos.parameters.GetParameter(i).GetType() == "operator") {
|
||||
operatorIndex = i;
|
||||
@@ -248,7 +257,8 @@ gd::String EventsCodeGenerator::GenerateMutatorCall(
|
||||
const gd::String& callStartString,
|
||||
std::size_t startFromArgument) {
|
||||
std::size_t operatorIndex = instrInfos.parameters.GetParametersCount();
|
||||
for (std::size_t i = startFromArgument; i < instrInfos.parameters.GetParametersCount();
|
||||
for (std::size_t i = startFromArgument;
|
||||
i < instrInfos.parameters.GetParametersCount();
|
||||
++i) {
|
||||
if (instrInfos.parameters.GetParameter(i).GetType() == "operator") {
|
||||
operatorIndex = i;
|
||||
@@ -323,83 +333,97 @@ gd::String EventsCodeGenerator::GenerateConditionCode(
|
||||
}
|
||||
|
||||
// Insert code only parameters and be sure there is no lack of parameter.
|
||||
while (condition.GetParameters().size() < instrInfos.parameters.GetParametersCount()) {
|
||||
while (condition.GetParameters().size() <
|
||||
instrInfos.parameters.GetParametersCount()) {
|
||||
vector<gd::Expression> parameters = condition.GetParameters();
|
||||
parameters.push_back(gd::Expression(""));
|
||||
condition.SetParameters(parameters);
|
||||
}
|
||||
|
||||
gd::EventsCodeGenerator::CheckBehaviorParameters(condition, instrInfos);
|
||||
// Verify that there are no mismatches between object type in parameters.
|
||||
for (std::size_t pNb = 0; pNb < instrInfos.parameters.GetParametersCount(); ++pNb) {
|
||||
if (ParameterMetadata::IsObject(instrInfos.parameters.GetParameter(pNb).GetType())) {
|
||||
for (std::size_t pNb = 0; pNb < instrInfos.parameters.GetParametersCount();
|
||||
++pNb) {
|
||||
if (ParameterMetadata::IsObject(
|
||||
instrInfos.parameters.GetParameter(pNb).GetType())) {
|
||||
gd::String objectInParameter =
|
||||
condition.GetParameter(pNb).GetPlainString();
|
||||
|
||||
const auto &expectedObjectType =
|
||||
const auto& expectedObjectType =
|
||||
instrInfos.parameters.GetParameter(pNb).GetExtraInfo();
|
||||
const auto &actualObjectType =
|
||||
const auto& actualObjectType =
|
||||
GetObjectsContainersList().GetTypeOfObject(objectInParameter);
|
||||
if (!GetObjectsContainersList().HasObjectOrGroupNamed(
|
||||
objectInParameter)) {
|
||||
gd::ProjectDiagnostic projectDiagnostic(
|
||||
gd::ProjectDiagnostic::ErrorType::UnknownObject, "",
|
||||
objectInParameter, "");
|
||||
gd::ProjectDiagnostic::ErrorType::UnknownObject,
|
||||
"",
|
||||
objectInParameter,
|
||||
"");
|
||||
if (diagnosticReport) diagnosticReport->Add(projectDiagnostic);
|
||||
return "/* Unknown object - skipped. */";
|
||||
} else if (!expectedObjectType.empty() &&
|
||||
actualObjectType != expectedObjectType) {
|
||||
gd::ProjectDiagnostic projectDiagnostic(
|
||||
gd::ProjectDiagnostic::ErrorType::MismatchedObjectType, "",
|
||||
actualObjectType, expectedObjectType, objectInParameter);
|
||||
gd::ProjectDiagnostic::ErrorType::MismatchedObjectType,
|
||||
"",
|
||||
actualObjectType,
|
||||
expectedObjectType,
|
||||
objectInParameter);
|
||||
if (diagnosticReport) diagnosticReport->Add(projectDiagnostic);
|
||||
return "/* Mismatched object type - skipped. */";
|
||||
}
|
||||
}
|
||||
}
|
||||
bool isAnyBehaviorMissing =
|
||||
gd::EventsCodeGenerator::CheckBehaviorParameters(condition, instrInfos);
|
||||
if (isAnyBehaviorMissing) {
|
||||
return "/* Missing behavior - skipped. */";
|
||||
}
|
||||
|
||||
if (instrInfos.IsObjectInstruction()) {
|
||||
gd::String objectName = condition.GetParameter(0).GetPlainString();
|
||||
if (!objectName.empty() && instrInfos.parameters.GetParametersCount() > 0) {
|
||||
std::vector<gd::String> realObjects =
|
||||
GetObjectsContainersList().ExpandObjectName(objectName, context.GetCurrentObject());
|
||||
GetObjectsContainersList().ExpandObjectName(
|
||||
objectName, context.GetCurrentObject());
|
||||
for (std::size_t i = 0; i < realObjects.size(); ++i) {
|
||||
// Set up the context
|
||||
gd::String objectType = GetObjectsContainersList().GetTypeOfObject(realObjects[i]);
|
||||
gd::String objectType =
|
||||
GetObjectsContainersList().GetTypeOfObject(realObjects[i]);
|
||||
const ObjectMetadata& objInfo =
|
||||
MetadataProvider::GetObjectMetadata(platform, objectType);
|
||||
|
||||
AddIncludeFiles(objInfo.includeFiles);
|
||||
context.SetCurrentObject(realObjects[i]);
|
||||
context.ObjectsListNeeded(realObjects[i]);
|
||||
AddIncludeFiles(objInfo.includeFiles);
|
||||
context.SetCurrentObject(realObjects[i]);
|
||||
context.ObjectsListNeeded(realObjects[i]);
|
||||
|
||||
// Prepare arguments and generate the condition whole code
|
||||
vector<gd::String> arguments = GenerateParametersCodes(
|
||||
condition.GetParameters(), instrInfos.parameters, context);
|
||||
conditionCode += GenerateObjectCondition(realObjects[i],
|
||||
objInfo,
|
||||
arguments,
|
||||
instrInfos,
|
||||
returnBoolean,
|
||||
condition.IsInverted(),
|
||||
context);
|
||||
// Prepare arguments and generate the condition whole code
|
||||
vector<gd::String> arguments = GenerateParametersCodes(
|
||||
condition.GetParameters(), instrInfos.parameters, context);
|
||||
conditionCode += GenerateObjectCondition(realObjects[i],
|
||||
objInfo,
|
||||
arguments,
|
||||
instrInfos,
|
||||
returnBoolean,
|
||||
condition.IsInverted(),
|
||||
context);
|
||||
|
||||
context.SetNoCurrentObject();
|
||||
context.SetNoCurrentObject();
|
||||
}
|
||||
}
|
||||
} else if (instrInfos.IsBehaviorInstruction()) {
|
||||
if (instrInfos.parameters.GetParametersCount() >= 2) {
|
||||
const gd::String &objectName = condition.GetParameter(0).GetPlainString();
|
||||
const gd::String &behaviorName =
|
||||
const gd::String& objectName = condition.GetParameter(0).GetPlainString();
|
||||
const gd::String& behaviorName =
|
||||
condition.GetParameter(1).GetPlainString();
|
||||
const gd::String &actualBehaviorType =
|
||||
const gd::String& actualBehaviorType =
|
||||
GetObjectsContainersList().GetTypeOfBehavior(behaviorName);
|
||||
|
||||
std::vector<gd::String> realObjects =
|
||||
GetObjectsContainersList().ExpandObjectName(
|
||||
objectName, context.GetCurrentObject());
|
||||
|
||||
const BehaviorMetadata &autoInfo =
|
||||
const BehaviorMetadata& autoInfo =
|
||||
MetadataProvider::GetBehaviorMetadata(platform, actualBehaviorType);
|
||||
|
||||
for (std::size_t i = 0; i < realObjects.size(); ++i) {
|
||||
@@ -411,15 +435,14 @@ gd::String EventsCodeGenerator::GenerateConditionCode(
|
||||
// Prepare arguments and generate the whole condition code
|
||||
vector<gd::String> arguments = GenerateParametersCodes(
|
||||
condition.GetParameters(), instrInfos.parameters, context);
|
||||
conditionCode += GenerateBehaviorCondition(
|
||||
realObjects[i],
|
||||
behaviorName,
|
||||
autoInfo,
|
||||
arguments,
|
||||
instrInfos,
|
||||
returnBoolean,
|
||||
condition.IsInverted(),
|
||||
context);
|
||||
conditionCode += GenerateBehaviorCondition(realObjects[i],
|
||||
behaviorName,
|
||||
autoInfo,
|
||||
arguments,
|
||||
instrInfos,
|
||||
returnBoolean,
|
||||
condition.IsInverted(),
|
||||
context);
|
||||
|
||||
context.SetNoCurrentObject();
|
||||
}
|
||||
@@ -488,31 +511,50 @@ gd::String EventsCodeGenerator::GenerateConditionsListCode(
|
||||
return outputCode;
|
||||
}
|
||||
|
||||
void EventsCodeGenerator::CheckBehaviorParameters(
|
||||
const gd::Instruction &instruction,
|
||||
const gd::InstructionMetadata &instrInfos) {
|
||||
gd::ParameterMetadataTools::IterateOverParameters(
|
||||
instruction.GetParameters(), instrInfos.parameters,
|
||||
[this](const gd::ParameterMetadata ¶meterMetadata,
|
||||
const gd::Expression ¶meterValue,
|
||||
const gd::String &lastObjectName) {
|
||||
bool EventsCodeGenerator::CheckBehaviorParameters(
|
||||
const gd::Instruction& instruction,
|
||||
const gd::InstructionMetadata& instrInfos) {
|
||||
bool isAnyBehaviorMissing = false;
|
||||
gd::ParameterMetadataTools::IterateOverParametersWithIndex(
|
||||
instruction.GetParameters(),
|
||||
instrInfos.parameters,
|
||||
[this, &isAnyBehaviorMissing, &instrInfos](
|
||||
const gd::ParameterMetadata& parameterMetadata,
|
||||
const gd::Expression& parameterValue,
|
||||
size_t parameterIndex,
|
||||
const gd::String& lastObjectName,
|
||||
size_t lastObjectIndex) {
|
||||
if (ParameterMetadata::IsBehavior(parameterMetadata.GetType())) {
|
||||
const gd::String &behaviorName = parameterValue.GetPlainString();
|
||||
const gd::String &actualBehaviorType =
|
||||
const gd::String& behaviorName = parameterValue.GetPlainString();
|
||||
const gd::String& actualBehaviorType =
|
||||
GetObjectsContainersList().GetTypeOfBehaviorInObjectOrGroup(
|
||||
lastObjectName, behaviorName);
|
||||
const gd::String &expectedBehaviorType =
|
||||
const gd::String& expectedBehaviorType =
|
||||
parameterMetadata.GetExtraInfo();
|
||||
|
||||
if (!expectedBehaviorType.empty() &&
|
||||
actualBehaviorType != expectedBehaviorType) {
|
||||
const auto& objectParameterMetadata =
|
||||
instrInfos.GetParameter(lastObjectIndex);
|
||||
// Event functions crash if some objects in a group are missing
|
||||
// the required behaviors, since they lose reference to the original
|
||||
// objects. Missing behaviors are considered "fatal" only for
|
||||
// ObjectList parameters, in order to minimize side effects on
|
||||
// built-in functions.
|
||||
if (objectParameterMetadata.GetType() == "objectList") {
|
||||
isAnyBehaviorMissing = true;
|
||||
}
|
||||
gd::ProjectDiagnostic projectDiagnostic(
|
||||
gd::ProjectDiagnostic::ErrorType::MissingBehavior, "",
|
||||
actualBehaviorType, expectedBehaviorType, lastObjectName);
|
||||
gd::ProjectDiagnostic::ErrorType::MissingBehavior,
|
||||
"",
|
||||
actualBehaviorType,
|
||||
expectedBehaviorType,
|
||||
lastObjectName);
|
||||
if (diagnosticReport) diagnosticReport->Add(projectDiagnostic);
|
||||
}
|
||||
}
|
||||
});
|
||||
return isAnyBehaviorMissing;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -521,7 +563,8 @@ void EventsCodeGenerator::CheckBehaviorParameters(
|
||||
gd::String EventsCodeGenerator::GenerateActionCode(
|
||||
gd::Instruction& action,
|
||||
EventsCodeGenerationContext& context,
|
||||
const gd::String& optionalAsyncCallbackName) {
|
||||
const gd::String& optionalAsyncCallbackName,
|
||||
const gd::String& optionalAsyncCallbackId) {
|
||||
gd::String actionCode;
|
||||
|
||||
const gd::InstructionMetadata& instrInfos =
|
||||
@@ -546,39 +589,51 @@ gd::String EventsCodeGenerator::GenerateActionCode(
|
||||
: instrInfos.codeExtraInformation.functionCallName;
|
||||
|
||||
// Be sure there is no lack of parameter.
|
||||
while (action.GetParameters().size() < instrInfos.parameters.GetParametersCount()) {
|
||||
while (action.GetParameters().size() <
|
||||
instrInfos.parameters.GetParametersCount()) {
|
||||
vector<gd::Expression> parameters = action.GetParameters();
|
||||
parameters.push_back(gd::Expression(""));
|
||||
action.SetParameters(parameters);
|
||||
}
|
||||
|
||||
gd::EventsCodeGenerator::CheckBehaviorParameters(action, instrInfos);
|
||||
// Verify that there are no mismatches between object type in parameters.
|
||||
for (std::size_t pNb = 0; pNb < instrInfos.parameters.GetParametersCount(); ++pNb) {
|
||||
if (ParameterMetadata::IsObject(instrInfos.parameters.GetParameter(pNb).GetType())) {
|
||||
for (std::size_t pNb = 0; pNb < instrInfos.parameters.GetParametersCount();
|
||||
++pNb) {
|
||||
if (ParameterMetadata::IsObject(
|
||||
instrInfos.parameters.GetParameter(pNb).GetType())) {
|
||||
gd::String objectInParameter = action.GetParameter(pNb).GetPlainString();
|
||||
|
||||
const auto &expectedObjectType =
|
||||
const auto& expectedObjectType =
|
||||
instrInfos.parameters.GetParameter(pNb).GetExtraInfo();
|
||||
const auto &actualObjectType =
|
||||
const auto& actualObjectType =
|
||||
GetObjectsContainersList().GetTypeOfObject(objectInParameter);
|
||||
if (!GetObjectsContainersList().HasObjectOrGroupNamed(
|
||||
objectInParameter)) {
|
||||
gd::ProjectDiagnostic projectDiagnostic(
|
||||
gd::ProjectDiagnostic::ErrorType::UnknownObject, "",
|
||||
objectInParameter, "");
|
||||
gd::ProjectDiagnostic::ErrorType::UnknownObject,
|
||||
"",
|
||||
objectInParameter,
|
||||
"");
|
||||
if (diagnosticReport) diagnosticReport->Add(projectDiagnostic);
|
||||
return "/* Unknown object - skipped. */";
|
||||
} else if (!expectedObjectType.empty() &&
|
||||
actualObjectType != expectedObjectType) {
|
||||
gd::ProjectDiagnostic projectDiagnostic(
|
||||
gd::ProjectDiagnostic::ErrorType::MismatchedObjectType, "",
|
||||
actualObjectType, expectedObjectType, objectInParameter);
|
||||
gd::ProjectDiagnostic::ErrorType::MismatchedObjectType,
|
||||
"",
|
||||
actualObjectType,
|
||||
expectedObjectType,
|
||||
objectInParameter);
|
||||
if (diagnosticReport) diagnosticReport->Add(projectDiagnostic);
|
||||
return "/* Mismatched object type - skipped. */";
|
||||
}
|
||||
}
|
||||
}
|
||||
bool isAnyBehaviorMissing =
|
||||
gd::EventsCodeGenerator::CheckBehaviorParameters(action, instrInfos);
|
||||
if (isAnyBehaviorMissing) {
|
||||
return "/* Missing behavior - skipped. */";
|
||||
}
|
||||
|
||||
// Call free function first if available
|
||||
if (instrInfos.IsObjectInstruction()) {
|
||||
@@ -586,43 +641,46 @@ gd::String EventsCodeGenerator::GenerateActionCode(
|
||||
|
||||
if (instrInfos.parameters.GetParametersCount() > 0) {
|
||||
std::vector<gd::String> realObjects =
|
||||
GetObjectsContainersList().ExpandObjectName(objectName, context.GetCurrentObject());
|
||||
GetObjectsContainersList().ExpandObjectName(
|
||||
objectName, context.GetCurrentObject());
|
||||
for (std::size_t i = 0; i < realObjects.size(); ++i) {
|
||||
// Setup context
|
||||
gd::String objectType = GetObjectsContainersList().GetTypeOfObject(realObjects[i]);
|
||||
gd::String objectType =
|
||||
GetObjectsContainersList().GetTypeOfObject(realObjects[i]);
|
||||
const ObjectMetadata& objInfo =
|
||||
MetadataProvider::GetObjectMetadata(platform, objectType);
|
||||
|
||||
AddIncludeFiles(objInfo.includeFiles);
|
||||
context.SetCurrentObject(realObjects[i]);
|
||||
context.ObjectsListNeeded(realObjects[i]);
|
||||
AddIncludeFiles(objInfo.includeFiles);
|
||||
context.SetCurrentObject(realObjects[i]);
|
||||
context.ObjectsListNeeded(realObjects[i]);
|
||||
|
||||
// Prepare arguments and generate the whole action code
|
||||
vector<gd::String> arguments = GenerateParametersCodes(
|
||||
action.GetParameters(), instrInfos.parameters, context);
|
||||
actionCode += GenerateObjectAction(realObjects[i],
|
||||
objInfo,
|
||||
functionCallName,
|
||||
arguments,
|
||||
instrInfos,
|
||||
context,
|
||||
optionalAsyncCallbackName);
|
||||
// Prepare arguments and generate the whole action code
|
||||
vector<gd::String> arguments = GenerateParametersCodes(
|
||||
action.GetParameters(), instrInfos.parameters, context);
|
||||
actionCode += GenerateObjectAction(realObjects[i],
|
||||
objInfo,
|
||||
functionCallName,
|
||||
arguments,
|
||||
instrInfos,
|
||||
context,
|
||||
optionalAsyncCallbackName,
|
||||
optionalAsyncCallbackId);
|
||||
|
||||
context.SetNoCurrentObject();
|
||||
context.SetNoCurrentObject();
|
||||
}
|
||||
}
|
||||
} else if (instrInfos.IsBehaviorInstruction()) {
|
||||
if (instrInfos.parameters.GetParametersCount() >= 2) {
|
||||
const gd::String &objectName = action.GetParameter(0).GetPlainString();
|
||||
const gd::String &behaviorName = action.GetParameter(1).GetPlainString();
|
||||
const gd::String &actualBehaviorType =
|
||||
const gd::String& objectName = action.GetParameter(0).GetPlainString();
|
||||
const gd::String& behaviorName = action.GetParameter(1).GetPlainString();
|
||||
const gd::String& actualBehaviorType =
|
||||
GetObjectsContainersList().GetTypeOfBehavior(behaviorName);
|
||||
|
||||
std::vector<gd::String> realObjects =
|
||||
GetObjectsContainersList().ExpandObjectName(
|
||||
objectName, context.GetCurrentObject());
|
||||
|
||||
const BehaviorMetadata &autoInfo =
|
||||
const BehaviorMetadata& autoInfo =
|
||||
MetadataProvider::GetBehaviorMetadata(platform, actualBehaviorType);
|
||||
|
||||
AddIncludeFiles(autoInfo.includeFiles);
|
||||
@@ -634,15 +692,15 @@ gd::String EventsCodeGenerator::GenerateActionCode(
|
||||
// Prepare arguments and generate the whole action code
|
||||
vector<gd::String> arguments = GenerateParametersCodes(
|
||||
action.GetParameters(), instrInfos.parameters, context);
|
||||
actionCode +=
|
||||
GenerateBehaviorAction(realObjects[i],
|
||||
behaviorName,
|
||||
autoInfo,
|
||||
functionCallName,
|
||||
arguments,
|
||||
instrInfos,
|
||||
context,
|
||||
optionalAsyncCallbackName);
|
||||
actionCode += GenerateBehaviorAction(realObjects[i],
|
||||
behaviorName,
|
||||
autoInfo,
|
||||
functionCallName,
|
||||
arguments,
|
||||
instrInfos,
|
||||
context,
|
||||
optionalAsyncCallbackName,
|
||||
optionalAsyncCallbackId);
|
||||
|
||||
context.SetNoCurrentObject();
|
||||
}
|
||||
@@ -654,7 +712,8 @@ gd::String EventsCodeGenerator::GenerateActionCode(
|
||||
arguments,
|
||||
instrInfos,
|
||||
context,
|
||||
optionalAsyncCallbackName);
|
||||
optionalAsyncCallbackName,
|
||||
optionalAsyncCallbackId);
|
||||
}
|
||||
|
||||
return actionCode;
|
||||
@@ -667,8 +726,8 @@ gd::String EventsCodeGenerator::GenerateLocalVariablesStackAccessor() {
|
||||
}
|
||||
|
||||
gd::String EventsCodeGenerator::GenerateAnyOrSceneVariableGetter(
|
||||
const gd::Expression &variableExpression,
|
||||
EventsCodeGenerationContext &context) {
|
||||
const gd::Expression& variableExpression,
|
||||
EventsCodeGenerationContext& context) {
|
||||
const auto variableName = gd::ExpressionVariableNameFinder::GetVariableName(
|
||||
*variableExpression.GetRootNode());
|
||||
|
||||
@@ -679,8 +738,12 @@ gd::String EventsCodeGenerator::GenerateAnyOrSceneVariableGetter(
|
||||
: "scenevar";
|
||||
|
||||
return gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
*this, context, variableParameterType,
|
||||
variableExpression.GetPlainString(), "", "AllowUndeclaredVariable");
|
||||
*this,
|
||||
context,
|
||||
variableParameterType,
|
||||
variableExpression.GetPlainString(),
|
||||
"",
|
||||
"AllowUndeclaredVariable");
|
||||
}
|
||||
|
||||
const EventsCodeGenerator::CallbackDescriptor
|
||||
@@ -727,6 +790,11 @@ EventsCodeGenerator::GenerateCallback(
|
||||
|
||||
AddCustomCodeOutsideMain(callbackCode);
|
||||
|
||||
const gd::String idToCallbackMapUpdate = GetCodeNamespaceAccessor() +
|
||||
"idToCallbackMap.set(" + callbackID +
|
||||
", " + callbackFunctionName + ");\n";
|
||||
AddCustomCodeOutsideMain(idToCallbackMapUpdate);
|
||||
|
||||
std::set<gd::String> requiredObjects;
|
||||
// Build the list of all objects required by the callback. Any object that has
|
||||
// already been declared could have gone through previous object picking, so
|
||||
@@ -769,7 +837,7 @@ gd::String EventsCodeGenerator::GenerateActionsListCode(
|
||||
} else {
|
||||
outputCode += actionCode;
|
||||
}
|
||||
outputCode += "}";
|
||||
outputCode += "}\n";
|
||||
}
|
||||
|
||||
return outputCode;
|
||||
@@ -786,13 +854,28 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
|
||||
|
||||
if (ParameterMetadata::IsExpression("number", metadata.GetType())) {
|
||||
argOutput = gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
*this, context, "number", parameter, lastObjectName, metadata.GetExtraInfo());
|
||||
*this,
|
||||
context,
|
||||
"number",
|
||||
parameter,
|
||||
lastObjectName,
|
||||
metadata.GetExtraInfo());
|
||||
} else if (ParameterMetadata::IsExpression("string", metadata.GetType())) {
|
||||
argOutput = gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
*this, context, "string", parameter, lastObjectName, metadata.GetExtraInfo());
|
||||
*this,
|
||||
context,
|
||||
"string",
|
||||
parameter,
|
||||
lastObjectName,
|
||||
metadata.GetExtraInfo());
|
||||
} else if (ParameterMetadata::IsExpression("variable", metadata.GetType())) {
|
||||
argOutput = gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
*this, context, metadata.GetType(), parameter, lastObjectName, metadata.GetExtraInfo());
|
||||
*this,
|
||||
context,
|
||||
metadata.GetType(),
|
||||
parameter,
|
||||
lastObjectName,
|
||||
metadata.GetExtraInfo());
|
||||
} else if (ParameterMetadata::IsObject(metadata.GetType())) {
|
||||
// It would be possible to run a gd::ExpressionCodeGenerator if later
|
||||
// objects can have nested objects, or function returning objects.
|
||||
@@ -827,7 +910,8 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
|
||||
metadata.GetType() == "atlasResource" ||
|
||||
metadata.GetType() == "spineResource" ||
|
||||
// Deprecated, old parameter names:
|
||||
metadata.GetType() == "password" || metadata.GetType() == "musicfile" ||
|
||||
metadata.GetType() == "password" ||
|
||||
metadata.GetType() == "musicfile" ||
|
||||
metadata.GetType() == "soundfile") {
|
||||
argOutput = "\"" + ConvertToString(parameter.GetPlainString()) + "\"";
|
||||
} else if (metadata.GetType() == "mouse") {
|
||||
@@ -977,7 +1061,8 @@ gd::String EventsCodeGenerator::GenerateEventsListCode(
|
||||
for (std::size_t eId = 0; eId < events.size(); ++eId) {
|
||||
auto& event = events[eId];
|
||||
if (event.HasVariables()) {
|
||||
GetProjectScopedContainers().GetVariablesContainersList().Push(event.GetVariables());
|
||||
GetProjectScopedContainers().GetVariablesContainersList().Push(
|
||||
event.GetVariables());
|
||||
}
|
||||
|
||||
// Each event has its own context : Objects picked in an event are totally
|
||||
@@ -1102,7 +1187,7 @@ gd::String EventsCodeGenerator::GenerateFreeCondition(
|
||||
instrInfos.codeExtraInformation.functionCallName);
|
||||
} else {
|
||||
predicate = instrInfos.codeExtraInformation.functionCallName + "(" +
|
||||
GenerateArgumentsList(arguments, 0) + ")";
|
||||
GenerateArgumentsList(arguments, 0) + ")";
|
||||
}
|
||||
|
||||
// Add logical not if needed
|
||||
@@ -1146,7 +1231,7 @@ gd::String EventsCodeGenerator::GenerateObjectCondition(
|
||||
instrInfos, arguments, objectFunctionCallNamePart, 1);
|
||||
} else {
|
||||
predicate = objectFunctionCallNamePart + "(" +
|
||||
GenerateArgumentsList(arguments, 1) + ")";
|
||||
GenerateArgumentsList(arguments, 1) + ")";
|
||||
}
|
||||
if (conditionInverted) predicate = GenerateNegatedPredicate(predicate);
|
||||
|
||||
@@ -1178,18 +1263,20 @@ gd::String EventsCodeGenerator::GenerateBehaviorCondition(
|
||||
}
|
||||
|
||||
gd::String EventsCodeGenerator::GenerateFreeAction(
|
||||
const gd::String& functionCallName,
|
||||
const gd::String& functionCallName,
|
||||
const std::vector<gd::String>& arguments,
|
||||
const gd::InstructionMetadata& instrInfos,
|
||||
gd::EventsCodeGenerationContext& context,
|
||||
const gd::String& optionalAsyncCallbackName) {
|
||||
const gd::String& optionalAsyncCallbackName,
|
||||
const gd::String& optionalAsyncCallbackId) {
|
||||
// Generate call
|
||||
gd::String call;
|
||||
if (instrInfos.codeExtraInformation.type == "number" ||
|
||||
instrInfos.codeExtraInformation.type == "string" ||
|
||||
// Boolean actions declared with addExpressionAndConditionAndAction uses
|
||||
// MutatorAndOrAccessor even though they don't declare an operator parameter.
|
||||
// Boolean operators are only used with SetMutators or SetCustomCodeGenerator.
|
||||
// MutatorAndOrAccessor even though they don't declare an operator
|
||||
// parameter. Boolean operators are only used with SetMutators or
|
||||
// SetCustomCodeGenerator.
|
||||
(instrInfos.codeExtraInformation.type == "boolean" &&
|
||||
instrInfos.codeExtraInformation.accessType ==
|
||||
gd::InstructionMetadata::ExtraInformation::AccessType::Mutators)) {
|
||||
@@ -1202,23 +1289,19 @@ gd::String EventsCodeGenerator::GenerateFreeAction(
|
||||
instrInfos.codeExtraInformation.optionalAssociatedInstruction);
|
||||
else if (instrInfos.codeExtraInformation.accessType ==
|
||||
gd::InstructionMetadata::ExtraInformation::Mutators)
|
||||
call =
|
||||
GenerateMutatorCall(instrInfos,
|
||||
arguments,
|
||||
functionCallName);
|
||||
call = GenerateMutatorCall(instrInfos, arguments, functionCallName);
|
||||
else
|
||||
call = GenerateCompoundOperatorCall(
|
||||
instrInfos,
|
||||
arguments,
|
||||
functionCallName);
|
||||
call =
|
||||
GenerateCompoundOperatorCall(instrInfos, arguments, functionCallName);
|
||||
} else {
|
||||
call = functionCallName + "(" +
|
||||
GenerateArgumentsList(arguments) + ")";
|
||||
call = functionCallName + "(" + GenerateArgumentsList(arguments) + ")";
|
||||
}
|
||||
|
||||
if (!optionalAsyncCallbackName.empty())
|
||||
if (!optionalAsyncCallbackName.empty() && !optionalAsyncCallbackId.empty()) {
|
||||
call = "runtimeScene.getAsyncTasksManager().addTask(" + call + ", " +
|
||||
optionalAsyncCallbackName + ")";
|
||||
optionalAsyncCallbackName + ", " + optionalAsyncCallbackId +
|
||||
", asyncObjectsList)";
|
||||
}
|
||||
|
||||
return call + ";\n";
|
||||
}
|
||||
@@ -1230,7 +1313,8 @@ gd::String EventsCodeGenerator::GenerateObjectAction(
|
||||
const std::vector<gd::String>& arguments,
|
||||
const gd::InstructionMetadata& instrInfos,
|
||||
gd::EventsCodeGenerationContext& context,
|
||||
const gd::String& optionalAsyncCallbackName) {
|
||||
const gd::String& optionalAsyncCallbackName,
|
||||
const gd::String& optionalAsyncCallbackId) {
|
||||
// Create call
|
||||
gd::String call;
|
||||
if ((instrInfos.codeExtraInformation.type == "number" ||
|
||||
@@ -1271,7 +1355,8 @@ gd::String EventsCodeGenerator::GenerateBehaviorAction(
|
||||
const std::vector<gd::String>& arguments,
|
||||
const gd::InstructionMetadata& instrInfos,
|
||||
gd::EventsCodeGenerationContext& context,
|
||||
const gd::String& optionalAsyncCallbackName) {
|
||||
const gd::String& optionalAsyncCallbackName,
|
||||
const gd::String& optionalAsyncCallbackId) {
|
||||
// Create call
|
||||
gd::String call;
|
||||
if ((instrInfos.codeExtraInformation.type == "number" ||
|
||||
@@ -1286,17 +1371,13 @@ gd::String EventsCodeGenerator::GenerateBehaviorAction(
|
||||
2);
|
||||
else
|
||||
call = GenerateCompoundOperatorCall(
|
||||
instrInfos,
|
||||
arguments,
|
||||
functionCallName,
|
||||
2);
|
||||
instrInfos, arguments, functionCallName, 2);
|
||||
return "For each picked object \"" + objectName + "\", call " + call +
|
||||
" for behavior \"" + behaviorName + "\".\n";
|
||||
} else {
|
||||
gd::String argumentsStr = GenerateArgumentsList(arguments, 2);
|
||||
|
||||
call = functionCallName + "(" +
|
||||
argumentsStr + ")";
|
||||
call = functionCallName + "(" + argumentsStr + ")";
|
||||
|
||||
return "For each picked object \"" + objectName + "\", call " + call + "(" +
|
||||
argumentsStr + ")" + " for behavior \"" + behaviorName + "\"" +
|
||||
@@ -1351,42 +1432,47 @@ gd::String EventsCodeGenerator::GenerateArgumentsList(
|
||||
return argumentsStr;
|
||||
}
|
||||
|
||||
gd::String EventsCodeGenerator::GeneratePropertyGetter(const gd::PropertiesContainer& propertiesContainer,
|
||||
const gd::NamedPropertyDescriptor& property,
|
||||
const gd::String& type,
|
||||
gd::EventsCodeGenerationContext& context) {
|
||||
gd::String EventsCodeGenerator::GeneratePropertyGetter(
|
||||
const gd::PropertiesContainer& propertiesContainer,
|
||||
const gd::NamedPropertyDescriptor& property,
|
||||
const gd::String& type,
|
||||
gd::EventsCodeGenerationContext& context) {
|
||||
return "getProperty" + property.GetName() + "As" + type + "()";
|
||||
}
|
||||
|
||||
gd::String EventsCodeGenerator::GeneratePropertyGetterWithoutCasting(
|
||||
const gd::PropertiesContainer &propertiesContainer,
|
||||
const gd::NamedPropertyDescriptor &property) {
|
||||
const gd::PropertiesContainer& propertiesContainer,
|
||||
const gd::NamedPropertyDescriptor& property) {
|
||||
return "getProperty" + property.GetName() + "()";
|
||||
}
|
||||
|
||||
gd::String EventsCodeGenerator::GeneratePropertySetterWithoutCasting(
|
||||
const gd::PropertiesContainer &propertiesContainer,
|
||||
const gd::NamedPropertyDescriptor &property,
|
||||
const gd::String &operandCode) {
|
||||
const gd::PropertiesContainer& propertiesContainer,
|
||||
const gd::NamedPropertyDescriptor& property,
|
||||
const gd::String& operandCode) {
|
||||
return "setProperty" + property.GetName() + "(" + operandCode + ")";
|
||||
}
|
||||
}
|
||||
|
||||
gd::String EventsCodeGenerator::GenerateParameterGetter(const gd::ParameterMetadata& parameter,
|
||||
const gd::String& type,
|
||||
gd::EventsCodeGenerationContext& context) {
|
||||
gd::String EventsCodeGenerator::GenerateParameterGetter(
|
||||
const gd::ParameterMetadata& parameter,
|
||||
const gd::String& type,
|
||||
gd::EventsCodeGenerationContext& context) {
|
||||
return "getParameter" + parameter.GetName() + "As" + type + "()";
|
||||
}
|
||||
|
||||
gd::String EventsCodeGenerator::GenerateParameterGetterWithoutCasting(
|
||||
const gd::ParameterMetadata ¶meter) {
|
||||
return "getParameter" + parameter.GetName() + "()";
|
||||
}
|
||||
const gd::ParameterMetadata& parameter) {
|
||||
return "getParameter" + parameter.GetName() + "()";
|
||||
}
|
||||
|
||||
EventsCodeGenerator::EventsCodeGenerator(const gd::Project& project_,
|
||||
const gd::Layout& layout,
|
||||
const gd::Platform& platform_)
|
||||
: platform(platform_),
|
||||
projectScopedContainers(gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project_, layout)),
|
||||
projectScopedContainers(
|
||||
gd::ProjectScopedContainers::
|
||||
MakeNewProjectScopedContainersForProjectAndLayout(project_,
|
||||
layout)),
|
||||
hasProjectAndLayout(true),
|
||||
project(&project_),
|
||||
scene(&layout),
|
||||
@@ -1395,7 +1481,7 @@ EventsCodeGenerator::EventsCodeGenerator(const gd::Project& project_,
|
||||
maxCustomConditionsDepth(0),
|
||||
maxConditionsListsSize(0),
|
||||
eventsListNextUniqueId(0),
|
||||
diagnosticReport(nullptr){};
|
||||
diagnosticReport(nullptr) {};
|
||||
|
||||
EventsCodeGenerator::EventsCodeGenerator(
|
||||
const gd::Platform& platform_,
|
||||
@@ -1410,6 +1496,6 @@ EventsCodeGenerator::EventsCodeGenerator(
|
||||
maxCustomConditionsDepth(0),
|
||||
maxConditionsListsSize(0),
|
||||
eventsListNextUniqueId(0),
|
||||
diagnosticReport(nullptr){};
|
||||
diagnosticReport(nullptr) {};
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -9,9 +9,9 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "GDCore/Events/CodeGeneration/DiagnosticReport.h"
|
||||
#include "GDCore/Events/Event.h"
|
||||
#include "GDCore/Events/Instruction.h"
|
||||
#include "GDCore/Events/CodeGeneration/DiagnosticReport.h"
|
||||
#include "GDCore/Project/ProjectScopedContainers.h"
|
||||
#include "GDCore/String.h"
|
||||
|
||||
@@ -62,7 +62,7 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
EventsCodeGenerator(
|
||||
const gd::Platform& platform,
|
||||
const gd::ProjectScopedContainers& projectScopedContainers_);
|
||||
virtual ~EventsCodeGenerator(){};
|
||||
virtual ~EventsCodeGenerator() {};
|
||||
|
||||
/**
|
||||
* \brief Preprocess an events list (replacing for example links with the
|
||||
@@ -160,7 +160,8 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
gd::String GenerateActionCode(
|
||||
gd::Instruction& action,
|
||||
EventsCodeGenerationContext& context,
|
||||
const gd::String& optionalAsyncCallbackName = "");
|
||||
const gd::String& optionalAsyncCallbackName = "",
|
||||
const gd::String& optionalAsyncCallbackId = "");
|
||||
|
||||
struct CallbackDescriptor {
|
||||
CallbackDescriptor(const gd::String functionName_,
|
||||
@@ -168,7 +169,7 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
const std::set<gd::String> requiredObjects_)
|
||||
: functionName(functionName_),
|
||||
argumentsList(argumentsList_),
|
||||
requiredObjects(requiredObjects_){};
|
||||
requiredObjects(requiredObjects_) {};
|
||||
/**
|
||||
* The name by which the function can be invoked.
|
||||
*/
|
||||
@@ -338,9 +339,9 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Give access to the project scoped containers as code generation might
|
||||
* push and pop variable containers (for local variables).
|
||||
* This could be passed as a parameter recursively in code generation, but this requires
|
||||
* @brief Give access to the project scoped containers as code generation
|
||||
* might push and pop variable containers (for local variables). This could be
|
||||
* passed as a parameter recursively in code generation, but this requires
|
||||
* heavy refactoring. Instead, we use this single instance.
|
||||
*/
|
||||
gd::ProjectScopedContainers& GetProjectScopedContainers() {
|
||||
@@ -387,9 +388,7 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
diagnosticReport = diagnosticReport_;
|
||||
}
|
||||
|
||||
gd::DiagnosticReport* GetDiagnosticReport() {
|
||||
return diagnosticReport;
|
||||
}
|
||||
gd::DiagnosticReport* GetDiagnosticReport() { return diagnosticReport; }
|
||||
|
||||
/**
|
||||
* \brief Generate the full name for accessing to a boolean variable used for
|
||||
@@ -513,16 +512,16 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
* \brief Generate an any variable getter that fallbacks on scene variable for
|
||||
* compatibility reason.
|
||||
*/
|
||||
gd::String
|
||||
GenerateAnyOrSceneVariableGetter(const gd::Expression &variableExpression,
|
||||
EventsCodeGenerationContext &context);
|
||||
gd::String GenerateAnyOrSceneVariableGetter(
|
||||
const gd::Expression& variableExpression,
|
||||
EventsCodeGenerationContext& context);
|
||||
|
||||
virtual gd::String GeneratePropertySetterWithoutCasting(
|
||||
const gd::PropertiesContainer &propertiesContainer,
|
||||
const gd::NamedPropertyDescriptor &property,
|
||||
const gd::String &operandCode);
|
||||
const gd::PropertiesContainer& propertiesContainer,
|
||||
const gd::NamedPropertyDescriptor& property,
|
||||
const gd::String& operandCode);
|
||||
|
||||
protected:
|
||||
protected:
|
||||
virtual const gd::String GenerateRelationalOperatorCodes(
|
||||
const gd::String& operatorString);
|
||||
|
||||
@@ -643,16 +642,16 @@ protected:
|
||||
gd::EventsCodeGenerationContext& context);
|
||||
|
||||
virtual gd::String GeneratePropertyGetterWithoutCasting(
|
||||
const gd::PropertiesContainer &propertiesContainer,
|
||||
const gd::NamedPropertyDescriptor &property);
|
||||
const gd::PropertiesContainer& propertiesContainer,
|
||||
const gd::NamedPropertyDescriptor& property);
|
||||
|
||||
virtual gd::String GenerateParameterGetter(
|
||||
const gd::ParameterMetadata& parameter,
|
||||
const gd::String& type,
|
||||
gd::EventsCodeGenerationContext& context);
|
||||
|
||||
virtual gd::String
|
||||
GenerateParameterGetterWithoutCasting(const gd::ParameterMetadata ¶meter);
|
||||
virtual gd::String GenerateParameterGetterWithoutCasting(
|
||||
const gd::ParameterMetadata& parameter);
|
||||
|
||||
/**
|
||||
* \brief Generate the code to reference an object which is
|
||||
@@ -769,7 +768,8 @@ protected:
|
||||
const std::vector<gd::String>& arguments,
|
||||
const gd::InstructionMetadata& instrInfos,
|
||||
gd::EventsCodeGenerationContext& context,
|
||||
const gd::String& optionalAsyncCallbackName = "");
|
||||
const gd::String& optionalAsyncCallbackName = "",
|
||||
const gd::String& optionalAsyncCallbackId = "");
|
||||
|
||||
virtual gd::String GenerateObjectAction(
|
||||
const gd::String& objectName,
|
||||
@@ -778,7 +778,8 @@ protected:
|
||||
const std::vector<gd::String>& arguments,
|
||||
const gd::InstructionMetadata& instrInfos,
|
||||
gd::EventsCodeGenerationContext& context,
|
||||
const gd::String& optionalAsyncCallbackName = "");
|
||||
const gd::String& optionalAsyncCallbackName = "",
|
||||
const gd::String& optionalAsyncCallbackId = "");
|
||||
|
||||
virtual gd::String GenerateBehaviorAction(
|
||||
const gd::String& objectName,
|
||||
@@ -788,7 +789,8 @@ protected:
|
||||
const std::vector<gd::String>& arguments,
|
||||
const gd::InstructionMetadata& instrInfos,
|
||||
gd::EventsCodeGenerationContext& context,
|
||||
const gd::String& optionalAsyncCallbackName = "");
|
||||
const gd::String& optionalAsyncCallbackName = "",
|
||||
const gd::String& optionalAsyncCallbackId = "");
|
||||
|
||||
gd::String GenerateRelationalOperatorCall(
|
||||
const gd::InstructionMetadata& instrInfos,
|
||||
@@ -837,9 +839,8 @@ protected:
|
||||
virtual gd::String GenerateGetBehaviorNameCode(
|
||||
const gd::String& behaviorName);
|
||||
|
||||
void CheckBehaviorParameters(
|
||||
const gd::Instruction &instruction,
|
||||
const gd::InstructionMetadata &instrInfos);
|
||||
bool CheckBehaviorParameters(const gd::Instruction& instruction,
|
||||
const gd::InstructionMetadata& instrInfos);
|
||||
|
||||
const gd::Platform& platform; ///< The platform being used.
|
||||
|
||||
@@ -876,4 +877,3 @@ protected:
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
||||
|
@@ -293,6 +293,25 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.MarkAsAdvanced();
|
||||
|
||||
obj.AddAction(
|
||||
"RotateTowardObject",
|
||||
_("Rotate toward another object"),
|
||||
_("Rotate an object towards another object, with the specified speed. "
|
||||
"Note that if multiple instances of the target object are picked, "
|
||||
"only the first one will be used. Use a For Each event or actions "
|
||||
"like \"Pick nearest object\", \"Pick a random object\" to refine "
|
||||
"the choice of the target object."),
|
||||
_("Rotate _PARAM0_ towards _PARAM1_ at speed _PARAM2_ deg/second"),
|
||||
_("Angle"),
|
||||
"res/actions/rotate24_black.png",
|
||||
"res/actions/rotate_black.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("objectPtr", _("Target object"))
|
||||
.AddParameter("expression", _("Angular speed (in degrees per second)"))
|
||||
.SetParameterLongDescription(_("Enter 0 for an immediate rotation."))
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.MarkAsAdvanced();
|
||||
|
||||
obj.AddAction(
|
||||
"AddForceXY",
|
||||
_("Add a force"),
|
||||
@@ -1617,7 +1636,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
|
||||
extension
|
||||
.AddAction("AjoutObjConcern",
|
||||
_("Pick all instances"),
|
||||
_("Pick all object instances"),
|
||||
_("Pick all instances of the specified object(s). When you "
|
||||
"pick all instances, "
|
||||
"the next conditions and actions of this event work on all "
|
||||
@@ -1631,20 +1650,34 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
.MarkAsAdvanced();
|
||||
|
||||
extension
|
||||
.AddAction(
|
||||
"AjoutHasard",
|
||||
_("Pick a random object"),
|
||||
_("Pick one object from all the specified objects. When an object "
|
||||
"is picked, the next conditions and actions of this event work "
|
||||
"only on that object."),
|
||||
_("Pick a random _PARAM1_"),
|
||||
_("Objects"),
|
||||
"res/actions/ajouthasard24.png",
|
||||
"res/actions/ajouthasard.png")
|
||||
.AddAction("AjoutHasard",
|
||||
_("Pick a random object"),
|
||||
_("Pick one instance from all the specified objects. When an "
|
||||
"instance is picked, the next conditions and actions of "
|
||||
"this event work only on that object instance."),
|
||||
_("Pick a random _PARAM1_"),
|
||||
_("Objects"),
|
||||
"res/actions/ajouthasard24.png",
|
||||
"res/actions/ajouthasard.png")
|
||||
.AddCodeOnlyParameter("objectsContext", "")
|
||||
.AddParameter("objectList", _("Object"))
|
||||
.MarkAsSimple();
|
||||
|
||||
extension
|
||||
.AddAction(
|
||||
"PickNearest",
|
||||
_("Pick nearest object"),
|
||||
_("Pick the instance of this object that is nearest to the specified "
|
||||
"position."),
|
||||
_("Pick the _PARAM0_ that is nearest to _PARAM1_;_PARAM2_"),
|
||||
_("Objects"),
|
||||
"res/conditions/distance24.png",
|
||||
"res/conditions/distance.png")
|
||||
.AddParameter("objectList", _("Object"))
|
||||
.AddParameter("expression", _("X position"))
|
||||
.AddParameter("expression", _("Y position"))
|
||||
.MarkAsSimple();
|
||||
|
||||
extension
|
||||
.AddAction(
|
||||
"MoveObjects",
|
||||
@@ -1694,11 +1727,12 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
extension
|
||||
.AddCondition(
|
||||
"AjoutObjConcern",
|
||||
_("Pick all objects"),
|
||||
_("Pick all the specified objects. When you pick all objects, "
|
||||
_("Pick all object instances"),
|
||||
_("Pick all instances of the specified object(s). When you "
|
||||
"pick all instances, "
|
||||
"the next conditions and actions of this event work on all "
|
||||
"of them."),
|
||||
_("Pick all _PARAM1_ objects"),
|
||||
_("Pick all instances of _PARAM1_"),
|
||||
_("Objects"),
|
||||
"res/conditions/add24.png",
|
||||
"res/conditions/add.png")
|
||||
@@ -1707,16 +1741,15 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
.MarkAsAdvanced();
|
||||
|
||||
extension
|
||||
.AddCondition(
|
||||
"AjoutHasard",
|
||||
_("Pick a random object"),
|
||||
_("Pick one object from all the specified objects. When an object "
|
||||
"is picked, the next conditions and actions of this event work "
|
||||
"only on that object."),
|
||||
_("Pick a random _PARAM1_"),
|
||||
_("Objects"),
|
||||
"res/conditions/ajouthasard24.png",
|
||||
"res/conditions/ajouthasard.png")
|
||||
.AddCondition("AjoutHasard",
|
||||
_("Pick a random object"),
|
||||
_("Pick one instance from all the specified objects. When "
|
||||
"an instance is picked, the next conditions and actions "
|
||||
"of this event work only on that object instance."),
|
||||
_("Pick a random _PARAM1_"),
|
||||
_("Objects"),
|
||||
"res/conditions/ajouthasard24.png",
|
||||
"res/conditions/ajouthasard.png")
|
||||
.AddCodeOnlyParameter("objectsContext", "")
|
||||
.AddParameter("objectList", _("Object"))
|
||||
.MarkAsSimple();
|
||||
@@ -1725,9 +1758,9 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
.AddCondition(
|
||||
"PickNearest",
|
||||
_("Pick nearest object"),
|
||||
_("Pick the object of this type that is nearest to the specified "
|
||||
"position. If the condition is inverted, the object farthest from "
|
||||
"the specified position is picked instead."),
|
||||
_("Pick the instance of this object that is nearest to the specified "
|
||||
"position. If the condition is inverted, the instance farthest "
|
||||
"from the specified position is picked instead."),
|
||||
_("Pick the _PARAM0_ that is nearest to _PARAM1_;_PARAM2_"),
|
||||
_("Objects"),
|
||||
"res/conditions/distance24.png",
|
||||
|
@@ -42,13 +42,17 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsFileExtension(
|
||||
extension
|
||||
.AddAction(
|
||||
"LoadFile",
|
||||
_("Load a storage in memory"),
|
||||
_("This action loads the specified storage in memory, so you can "
|
||||
"write and read it.\nYou can open and write without using this "
|
||||
"action, but it will be slower.\nIf you use this action, do not "
|
||||
"forget to unload the storage from memory."),
|
||||
_("Load storage _PARAM0_ in memory"),
|
||||
"",
|
||||
_("Manually preload a storage in memory"),
|
||||
_("Forces the specified storage to be loaded and kept in "
|
||||
"memory, allowing faster reads/writes. "
|
||||
"However, it requires manual management: if you use this "
|
||||
"action, you *must* also unload the storage manually when "
|
||||
"it's no longer needed to ensure data is persisted.\n\n"
|
||||
"Unless you have a specific performance need, avoid using this "
|
||||
"action. The system already handles loading/unloading "
|
||||
"automatically."),
|
||||
_("Load data storage _PARAM0_ in memory"),
|
||||
_("Advanced"),
|
||||
"res/actions/fichier24.png",
|
||||
"res/actions/fichier.png")
|
||||
.AddParameter("string", _("Storage name"))
|
||||
@@ -56,11 +60,11 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsFileExtension(
|
||||
|
||||
extension
|
||||
.AddAction("UnloadFile",
|
||||
_("Close a storage"),
|
||||
_("This action closes the structured data previously loaded "
|
||||
_("Manually unload and persist a storage"),
|
||||
_("Close the specified storage previously loaded "
|
||||
"in memory, saving all changes made."),
|
||||
_("Close structured data _PARAM0_"),
|
||||
"",
|
||||
_("Unload and persist data storage _PARAM0_"),
|
||||
_("Advanced"),
|
||||
"res/actions/fichier24.png",
|
||||
"res/actions/fichier.png")
|
||||
.AddParameter("string", _("Storage name"))
|
||||
|
@@ -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
|
||||
|
@@ -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));
|
||||
|
@@ -24,7 +24,7 @@ The rest of this page is an introduction to the main concepts of GDevelop archit
|
||||
|
||||
Extensions do have the same distinction between the "**IDE**" part and the "**Runtime**" part. For example, most extensions have:
|
||||
|
||||
- A file called [`JsExtension.js`(https://github.com/4ian/GDevelop/blob/master/Extensions/ExampleJsExtension/JsExtension.js)], which contains the _declaration_ of the extension for the **IDE**
|
||||
- A file called [`JsExtension.js`](https://github.com/4ian/GDevelop/blob/master/Extensions/ExampleJsExtension/JsExtension.js), which contains the _declaration_ of the extension for the **IDE**
|
||||
- One or more files implementing the feature for the game, in other words for **Runtime**. This can be a [Runtime Object](https://github.com/4ian/GDevelop/blob/master/Extensions/ExampleJsExtension/dummyruntimeobject.ts) or a [Runtime Behavior](https://github.com/4ian/GDevelop/blob/master/Extensions/ExampleJsExtension/dummyruntimebehavior.ts), [functions called by actions or conditions](https://github.com/4ian/GDevelop/blob/master/Extensions/ExampleJsExtension/examplejsextensiontools.ts) or by the game engine.
|
||||
|
||||
### "Runtime" and "IDE" difference using an example: the `gd::Variable` class
|
||||
|
@@ -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") {
|
||||
|
@@ -97,6 +97,14 @@ namespace gdjs {
|
||||
oldObjectData: Object3DData,
|
||||
newObjectData: Object3DData
|
||||
): boolean {
|
||||
this.updateOriginalDimensionsFromObjectData(oldObjectData, newObjectData);
|
||||
return true;
|
||||
}
|
||||
|
||||
updateOriginalDimensionsFromObjectData(
|
||||
oldObjectData: Object3DData,
|
||||
newObjectData: Object3DData
|
||||
): void {
|
||||
// There is no need to check if they changed because events can't modify them.
|
||||
this._setOriginalWidth(
|
||||
getValidDimensionValue(newObjectData.content.width)
|
||||
@@ -107,12 +115,13 @@ namespace gdjs {
|
||||
this._setOriginalDepth(
|
||||
getValidDimensionValue(newObjectData.content.depth)
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
getNetworkSyncData(): Object3DNetworkSyncData {
|
||||
getNetworkSyncData(
|
||||
syncOptions: GetNetworkSyncDataOptions
|
||||
): Object3DNetworkSyncData {
|
||||
return {
|
||||
...super.getNetworkSyncData(),
|
||||
...super.getNetworkSyncData(syncOptions),
|
||||
z: this.getZ(),
|
||||
d: this.getDepth(),
|
||||
rx: this.getRotationX(),
|
||||
@@ -123,8 +132,11 @@ namespace gdjs {
|
||||
};
|
||||
}
|
||||
|
||||
updateFromNetworkSyncData(networkSyncData: Object3DNetworkSyncData) {
|
||||
super.updateFromNetworkSyncData(networkSyncData);
|
||||
updateFromNetworkSyncData(
|
||||
networkSyncData: Object3DNetworkSyncData,
|
||||
options: UpdateFromNetworkSyncDataOptions
|
||||
) {
|
||||
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||
if (networkSyncData.z !== undefined) this.setZ(networkSyncData.z);
|
||||
if (networkSyncData.d !== undefined) this.setDepth(networkSyncData.d);
|
||||
if (networkSyncData.rx !== undefined)
|
||||
@@ -147,15 +159,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 +328,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 +386,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);
|
||||
|
@@ -452,9 +452,11 @@ namespace gdjs {
|
||||
return true;
|
||||
}
|
||||
|
||||
getNetworkSyncData(): Cube3DObjectNetworkSyncData {
|
||||
getNetworkSyncData(
|
||||
syncOptions: GetNetworkSyncDataOptions
|
||||
): Cube3DObjectNetworkSyncData {
|
||||
return {
|
||||
...super.getNetworkSyncData(),
|
||||
...super.getNetworkSyncData(syncOptions),
|
||||
mt: this._materialType,
|
||||
fo: this._facesOrientation,
|
||||
bfu: this._backFaceUpThroughWhichAxisRotation,
|
||||
@@ -466,9 +468,10 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
updateFromNetworkSyncData(
|
||||
networkSyncData: Cube3DObjectNetworkSyncData
|
||||
networkSyncData: Cube3DObjectNetworkSyncData,
|
||||
options: UpdateFromNetworkSyncDataOptions
|
||||
): void {
|
||||
super.updateFromNetworkSyncData(networkSyncData);
|
||||
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||
|
||||
if (networkSyncData.mt !== undefined) {
|
||||
this._materialType = networkSyncData.mt;
|
||||
|
@@ -1,12 +1,16 @@
|
||||
namespace gdjs {
|
||||
type CustomObject3DNetworkSyncDataType = CustomObjectNetworkSyncDataType & {
|
||||
type CustomObject3DNetworkSyncDataType = {
|
||||
z: float;
|
||||
d: float;
|
||||
rx: float;
|
||||
ry: float;
|
||||
ifz: boolean;
|
||||
ccz: float;
|
||||
};
|
||||
|
||||
type CustomObject3DNetworkSyncData = CustomObjectNetworkSyncData &
|
||||
CustomObject3DNetworkSyncDataType;
|
||||
|
||||
/**
|
||||
* Base class for 3D custom objects.
|
||||
*/
|
||||
@@ -74,32 +78,30 @@ 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 {
|
||||
getNetworkSyncData(
|
||||
syncOptions: GetNetworkSyncDataOptions
|
||||
): CustomObject3DNetworkSyncData {
|
||||
return {
|
||||
...super.getNetworkSyncData(),
|
||||
...super.getNetworkSyncData(syncOptions),
|
||||
z: this.getZ(),
|
||||
d: this.getDepth(),
|
||||
rx: this.getRotationX(),
|
||||
ry: this.getRotationY(),
|
||||
ifz: this.isFlippedZ(),
|
||||
ccz: this._customCenterZ,
|
||||
};
|
||||
}
|
||||
|
||||
updateFromNetworkSyncData(
|
||||
networkSyncData: CustomObject3DNetworkSyncDataType
|
||||
networkSyncData: CustomObject3DNetworkSyncData,
|
||||
options: UpdateFromNetworkSyncDataOptions
|
||||
): void {
|
||||
super.updateFromNetworkSyncData(networkSyncData);
|
||||
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||
if (networkSyncData.z !== undefined) this.setZ(networkSyncData.z);
|
||||
if (networkSyncData.d !== undefined) this.setDepth(networkSyncData.d);
|
||||
if (networkSyncData.rx !== undefined)
|
||||
@@ -107,6 +109,8 @@ namespace gdjs {
|
||||
if (networkSyncData.ry !== undefined)
|
||||
this.setRotationY(networkSyncData.ry);
|
||||
if (networkSyncData.ifz !== undefined) this.flipZ(networkSyncData.ifz);
|
||||
if (networkSyncData.ccz !== undefined)
|
||||
this._customCenterZ = networkSyncData.ccz;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -315,6 +319,10 @@ namespace gdjs {
|
||||
return this._maxZ - this._minZ;
|
||||
}
|
||||
|
||||
getOriginalDepth(): float {
|
||||
return this._instanceContainer._getInitialInnerAreaDepth();
|
||||
}
|
||||
|
||||
override _updateUntransformedHitBoxes(): void {
|
||||
super._updateUntransformedHitBoxes();
|
||||
|
||||
|
@@ -23,6 +23,8 @@ namespace gdjs {
|
||||
|
||||
this._threeGroup = new THREE.Group();
|
||||
this._threeGroup.rotation.order = 'ZYX';
|
||||
//@ts-ignore
|
||||
this._threeGroup.gdjsRuntimeObject = object;
|
||||
|
||||
const layer = parent.getLayer('');
|
||||
if (layer) {
|
||||
|
@@ -1901,6 +1901,11 @@ module.exports = {
|
||||
.getOrCreate('density')
|
||||
.setValue('0.0012')
|
||||
.setLabel(_('Density'))
|
||||
.setDescription(
|
||||
_(
|
||||
'Density of the fog. Usual values are between 0.0005 (far away) and 0.005 (very thick fog).'
|
||||
)
|
||||
)
|
||||
.setType('number');
|
||||
}
|
||||
{
|
||||
@@ -1908,7 +1913,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 +1936,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 +2024,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 +2068,48 @@ module.exports = {
|
||||
.setType('number')
|
||||
.setGroup(_('Orientation'));
|
||||
}
|
||||
{
|
||||
const effect = extension
|
||||
.addEffect('Skybox')
|
||||
.setFullName(_('Skybox'))
|
||||
.setDescription(
|
||||
_('Display a background on a cube surrounding the scene.')
|
||||
)
|
||||
.markAsNotWorkingForObjects()
|
||||
.markAsOnlyWorkingFor3D()
|
||||
.addIncludeFile('Extensions/3D/Skybox.js');
|
||||
const properties = effect.getProperties();
|
||||
properties
|
||||
.getOrCreate('rightFaceResourceName')
|
||||
.setType('resource')
|
||||
.addExtraInfo('image')
|
||||
.setLabel(_('Right face (X+)'));
|
||||
properties
|
||||
.getOrCreate('leftFaceResourceName')
|
||||
.setType('resource')
|
||||
.addExtraInfo('image')
|
||||
.setLabel(_('Left face (X-)'));
|
||||
properties
|
||||
.getOrCreate('bottomFaceResourceName')
|
||||
.setType('resource')
|
||||
.addExtraInfo('image')
|
||||
.setLabel(_('Bottom face (Y+)'));
|
||||
properties
|
||||
.getOrCreate('topFaceResourceName')
|
||||
.setType('resource')
|
||||
.addExtraInfo('image')
|
||||
.setLabel(_('Top face (Y-)'));
|
||||
properties
|
||||
.getOrCreate('frontFaceResourceName')
|
||||
.setType('resource')
|
||||
.addExtraInfo('image')
|
||||
.setLabel(_('Front face (Z+)'));
|
||||
properties
|
||||
.getOrCreate('backFaceResourceName')
|
||||
.setType('resource')
|
||||
.addExtraInfo('image')
|
||||
.setLabel(_('Back face (Z-)'));
|
||||
}
|
||||
{
|
||||
const effect = extension
|
||||
.addEffect('HueAndSaturation')
|
||||
|
@@ -8,6 +8,7 @@ namespace gdjs {
|
||||
anis: Model3DAnimation[];
|
||||
ai: integer;
|
||||
ass: float;
|
||||
aet: float;
|
||||
ap: boolean;
|
||||
cfd: float;
|
||||
};
|
||||
@@ -152,6 +153,14 @@ namespace gdjs {
|
||||
}
|
||||
}
|
||||
|
||||
override updateOriginalDimensionsFromObjectData(
|
||||
oldObjectData: Object3DData,
|
||||
newObjectData: Object3DData
|
||||
): void {
|
||||
// Original dimensions must not be reset by `super.updateFromObjectData`.
|
||||
// `_updateModel` has a different logic to evaluate them using `keepAspectRatio`.
|
||||
}
|
||||
|
||||
updateFromObjectData(
|
||||
oldObjectData: Model3DObjectData,
|
||||
newObjectData: Model3DObjectData
|
||||
@@ -181,8 +190,14 @@ namespace gdjs {
|
||||
oldObjectData.content.keepAspectRatio !==
|
||||
newObjectData.content.keepAspectRatio ||
|
||||
oldObjectData.content.materialType !==
|
||||
newObjectData.content.materialType
|
||||
newObjectData.content.materialType ||
|
||||
oldObjectData.content.centerLocation !==
|
||||
newObjectData.content.centerLocation
|
||||
) {
|
||||
// The center is applied to the model by `_updateModel`.
|
||||
this._centerPoint = getPointForLocation(
|
||||
newObjectData.content.centerLocation
|
||||
);
|
||||
this._updateModel(newObjectData);
|
||||
}
|
||||
if (
|
||||
@@ -192,14 +207,7 @@ namespace gdjs {
|
||||
this._originPoint = getPointForLocation(
|
||||
newObjectData.content.originLocation
|
||||
);
|
||||
}
|
||||
if (
|
||||
oldObjectData.content.centerLocation !==
|
||||
newObjectData.content.centerLocation
|
||||
) {
|
||||
this._centerPoint = getPointForLocation(
|
||||
newObjectData.content.centerLocation
|
||||
);
|
||||
this._renderer.updatePosition();
|
||||
}
|
||||
if (
|
||||
oldObjectData.content.isCastingShadow !==
|
||||
@@ -216,24 +224,28 @@ namespace gdjs {
|
||||
return true;
|
||||
}
|
||||
|
||||
getNetworkSyncData(): Model3DObjectNetworkSyncData {
|
||||
getNetworkSyncData(
|
||||
syncOptions: GetNetworkSyncDataOptions
|
||||
): Model3DObjectNetworkSyncData {
|
||||
return {
|
||||
...super.getNetworkSyncData(),
|
||||
...super.getNetworkSyncData(syncOptions),
|
||||
mt: this._materialType,
|
||||
op: this._originPoint,
|
||||
cp: this._centerPoint,
|
||||
anis: this._animations,
|
||||
ai: this._currentAnimationIndex,
|
||||
ass: this._animationSpeedScale,
|
||||
aet: this.getAnimationElapsedTime(),
|
||||
ap: this._animationPaused,
|
||||
cfd: this._crossfadeDuration,
|
||||
};
|
||||
}
|
||||
|
||||
updateFromNetworkSyncData(
|
||||
networkSyncData: Model3DObjectNetworkSyncData
|
||||
networkSyncData: Model3DObjectNetworkSyncData,
|
||||
options: UpdateFromNetworkSyncDataOptions
|
||||
): void {
|
||||
super.updateFromNetworkSyncData(networkSyncData);
|
||||
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||
|
||||
if (networkSyncData.mt !== undefined) {
|
||||
this._materialType = networkSyncData.mt;
|
||||
@@ -247,11 +259,14 @@ namespace gdjs {
|
||||
if (networkSyncData.anis !== undefined) {
|
||||
this._animations = networkSyncData.anis;
|
||||
}
|
||||
if (networkSyncData.ass !== undefined) {
|
||||
this.setAnimationSpeedScale(networkSyncData.ass);
|
||||
}
|
||||
if (networkSyncData.ai !== undefined) {
|
||||
this.setAnimationIndex(networkSyncData.ai);
|
||||
}
|
||||
if (networkSyncData.ass !== undefined) {
|
||||
this.setAnimationSpeedScale(networkSyncData.ass);
|
||||
if (networkSyncData.aet !== undefined) {
|
||||
this.setAnimationElapsedTime(networkSyncData.aet);
|
||||
}
|
||||
if (networkSyncData.ap !== undefined) {
|
||||
if (networkSyncData.ap !== this.isAnimationPaused()) {
|
||||
@@ -273,14 +288,17 @@ namespace gdjs {
|
||||
const rotationX = objectData.content.rotationX || 0;
|
||||
const rotationY = objectData.content.rotationY || 0;
|
||||
const rotationZ = objectData.content.rotationZ || 0;
|
||||
const width = objectData.content.width || 100;
|
||||
const height = objectData.content.height || 100;
|
||||
const depth = objectData.content.depth || 100;
|
||||
const keepAspectRatio = objectData.content.keepAspectRatio;
|
||||
this._renderer._updateModel(
|
||||
rotationX,
|
||||
rotationY,
|
||||
rotationZ,
|
||||
this._getOriginalWidth(),
|
||||
this._getOriginalHeight(),
|
||||
this._getOriginalDepth(),
|
||||
width,
|
||||
height,
|
||||
depth,
|
||||
keepAspectRatio
|
||||
);
|
||||
}
|
||||
|
@@ -233,6 +233,10 @@ namespace gdjs {
|
||||
this._object._setOriginalWidth(scaleRatio * modelWidth);
|
||||
this._object._setOriginalHeight(scaleRatio * modelHeight);
|
||||
this._object._setOriginalDepth(scaleRatio * modelDepth);
|
||||
} else {
|
||||
this._object._setOriginalWidth(originalWidth);
|
||||
this._object._setOriginalHeight(originalHeight);
|
||||
this._object._setOriginalDepth(originalDepth);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,6 +290,7 @@ namespace gdjs {
|
||||
this.get3DRendererObject().remove(this._threeObject);
|
||||
this.get3DRendererObject().add(threeObject);
|
||||
this._threeObject = threeObject;
|
||||
this.updatePosition();
|
||||
this._updateShadow();
|
||||
|
||||
// Start the current animation on the new 3D object.
|
||||
|
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,
|
||||
|
@@ -40,6 +40,19 @@ describe('gdjs.AnchorRuntimeBehavior', () => {
|
||||
objects: [],
|
||||
instances: [],
|
||||
usedResources: [],
|
||||
uiSettings: {
|
||||
grid: false,
|
||||
gridType: 'rectangular',
|
||||
gridWidth: 10,
|
||||
gridHeight: 10,
|
||||
gridDepth: 10,
|
||||
gridOffsetX: 0,
|
||||
gridOffsetY: 0,
|
||||
gridOffsetZ: 0,
|
||||
gridColor: 0,
|
||||
gridAlpha: 1,
|
||||
snap: false,
|
||||
},
|
||||
},
|
||||
usedExtensionsWithVariablesData: [],
|
||||
});
|
||||
|
@@ -145,9 +145,11 @@ namespace gdjs {
|
||||
return true;
|
||||
}
|
||||
|
||||
override getNetworkSyncData(): BBTextObjectNetworkSyncData {
|
||||
override getNetworkSyncData(
|
||||
syncOptions: GetNetworkSyncDataOptions
|
||||
): BBTextObjectNetworkSyncData {
|
||||
return {
|
||||
...super.getNetworkSyncData(),
|
||||
...super.getNetworkSyncData(syncOptions),
|
||||
text: this._text,
|
||||
o: this._opacity,
|
||||
c: this._color,
|
||||
@@ -162,9 +164,10 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
override updateFromNetworkSyncData(
|
||||
networkSyncData: BBTextObjectNetworkSyncData
|
||||
networkSyncData: BBTextObjectNetworkSyncData,
|
||||
options: UpdateFromNetworkSyncDataOptions
|
||||
): void {
|
||||
super.updateFromNetworkSyncData(networkSyncData);
|
||||
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||
if (this._text !== undefined) {
|
||||
this.setBBText(networkSyncData.text);
|
||||
}
|
||||
|
@@ -155,9 +155,11 @@ namespace gdjs {
|
||||
return true;
|
||||
}
|
||||
|
||||
override getNetworkSyncData(): BitmapTextObjectNetworkSyncData {
|
||||
override getNetworkSyncData(
|
||||
syncOptions: GetNetworkSyncDataOptions
|
||||
): BitmapTextObjectNetworkSyncData {
|
||||
return {
|
||||
...super.getNetworkSyncData(),
|
||||
...super.getNetworkSyncData(syncOptions),
|
||||
text: this._text,
|
||||
opa: this._opacity,
|
||||
tint: this._tint,
|
||||
@@ -172,9 +174,10 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
override updateFromNetworkSyncData(
|
||||
networkSyncData: BitmapTextObjectNetworkSyncData
|
||||
networkSyncData: BitmapTextObjectNetworkSyncData,
|
||||
options: UpdateFromNetworkSyncDataOptions
|
||||
): void {
|
||||
super.updateFromNetworkSyncData(networkSyncData);
|
||||
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||
if (this._text !== undefined) {
|
||||
this.setText(networkSyncData.text);
|
||||
}
|
||||
@@ -218,9 +221,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 {
|
||||
|
@@ -21,7 +21,9 @@ module.exports = {
|
||||
.setExtensionInformation(
|
||||
'DebuggerTools',
|
||||
_('Debugger Tools'),
|
||||
_('Allow to interact with the editor debugger from the game.'),
|
||||
_(
|
||||
'Allow to interact with the editor debugger from the game (notably: enable 2D debug draw, log a message in the debugger console).'
|
||||
),
|
||||
'Arthur Pacaud (arthuro555), Aurélien Vivet (Bouh)',
|
||||
'MIT'
|
||||
)
|
||||
|
@@ -12,7 +12,8 @@ This project is released under the MIT License.
|
||||
#include "GDCore/Tools/Localization.h"
|
||||
|
||||
void DestroyOutsideBehavior::InitializeContent(gd::SerializerElement& content) {
|
||||
content.SetAttribute("extraBorder", 300);
|
||||
content.SetAttribute("extraBorder", 200);
|
||||
content.SetAttribute("unseenGraceDistance", 10000);
|
||||
}
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
@@ -27,7 +28,15 @@ DestroyOutsideBehavior::GetProperties(
|
||||
.SetType("Number")
|
||||
.SetMeasurementUnit(gd::MeasurementUnit::GetPixel())
|
||||
.SetLabel(_("Deletion margin"))
|
||||
.SetDescription(_("Margin before deleting the object, in pixels"));
|
||||
.SetDescription(_("Margin before deleting the object, in pixels."));
|
||||
|
||||
properties["unseenGraceDistance"]
|
||||
.SetValue(gd::String::From(
|
||||
behaviorContent.GetDoubleAttribute("unseenGraceDistance", 0)))
|
||||
.SetType("Number")
|
||||
.SetMeasurementUnit(gd::MeasurementUnit::GetPixel())
|
||||
.SetLabel(_("Unseen object grace distance"))
|
||||
.SetDescription(_("If the object hasn't been visible yet, don't delete it until it travels this far beyond the screen (in pixels). Useful to avoid objects being deleted before they are visible when they spawn."));
|
||||
|
||||
return properties;
|
||||
}
|
||||
@@ -38,6 +47,8 @@ bool DestroyOutsideBehavior::UpdateProperty(
|
||||
const gd::String& value) {
|
||||
if (name == "extraBorder")
|
||||
behaviorContent.SetAttribute("extraBorder", value.To<double>());
|
||||
else if (name == "unseenGraceDistance")
|
||||
behaviorContent.SetAttribute("unseenGraceDistance", value.To<double>());
|
||||
else
|
||||
return false;
|
||||
|
||||
|
@@ -6,6 +6,7 @@ This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
#include "DestroyOutsideBehavior.h"
|
||||
#include "GDCore/Extensions/Metadata/MultipleInstructionMetadata.h"
|
||||
#include "GDCore/Extensions/PlatformExtension.h"
|
||||
#include "GDCore/Project/BehaviorsSharedData.h"
|
||||
#include "GDCore/Tools/Localization.h"
|
||||
@@ -19,11 +20,10 @@ void DeclareDestroyOutsideBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
"outside of the bounds of the 2D camera. Useful for 2D bullets or "
|
||||
"other short-lived objects. Don't use it for 3D objects in a "
|
||||
"FPS/TPS game or any game with a camera not being a top view "
|
||||
"(for 3D objects, prefer comparing "
|
||||
"the position, for example Z position to see if an object goes "
|
||||
"outside of the bound of the map). Be careful when using this "
|
||||
"behavior because if the object appears outside of the screen, it "
|
||||
"will be immediately removed."),
|
||||
"(for 3D objects, prefer comparing the position, for example Z "
|
||||
"position to see if an object goes outside of the bound of the "
|
||||
"map). If the object appears outside of the screen, it's not "
|
||||
"removed unless it goes beyond the unseen object grace distance."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetCategory("Game mechanic")
|
||||
@@ -44,34 +44,39 @@ void DeclareDestroyOutsideBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
std::shared_ptr<gd::BehaviorsSharedData>())
|
||||
.SetQuickCustomizationVisibility(gd::QuickCustomization::Hidden);
|
||||
|
||||
aut.AddCondition("ExtraBorder",
|
||||
_("Additional border (extra distance before deletion)"),
|
||||
_("Compare the extra distance (in pixels) the object must "
|
||||
"travel beyond the screen before it gets deleted."),
|
||||
_("the additional border"),
|
||||
_("Destroy outside configuration"),
|
||||
"CppPlatform/Extensions/destroyoutsideicon24.png",
|
||||
"CppPlatform/Extensions/destroyoutsideicon16.png")
|
||||
aut.AddExpressionAndConditionAndAction(
|
||||
"number",
|
||||
"ExtraBorder",
|
||||
_("Additional border (extra distance before deletion)"),
|
||||
_("the extra distance (in pixels) the object must "
|
||||
"travel beyond the screen before it gets deleted"),
|
||||
_("the additional border"),
|
||||
_("Destroy outside configuration"),
|
||||
"CppPlatform/Extensions/destroyoutsideicon24.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "DestroyOutside")
|
||||
.UseStandardRelationalOperatorParameters(
|
||||
"number", gd::ParameterOptions::MakeNewOptions())
|
||||
.MarkAsAdvanced()
|
||||
.SetFunctionName("GetExtraBorder");
|
||||
.UseStandardParameters("number", gd::ParameterOptions::MakeNewOptions())
|
||||
.MarkAsAdvanced();
|
||||
|
||||
aut.AddAction("ExtraBorder",
|
||||
_("Additional border (extra distance before deletion)"),
|
||||
_("Change the extra distance (in pixels) the object must "
|
||||
"travel beyond the screen before it gets deleted."),
|
||||
_("the additional border"),
|
||||
_("Destroy outside configuration"),
|
||||
"CppPlatform/Extensions/destroyoutsideicon24.png",
|
||||
"CppPlatform/Extensions/destroyoutsideicon16.png")
|
||||
// Deprecated:
|
||||
aut.AddDuplicatedAction("ExtraBorder", "DestroyOutside::SetExtraBorder")
|
||||
.SetHidden();
|
||||
aut.AddDuplicatedCondition("ExtraBorder", "DestroyOutside::ExtraBorder")
|
||||
.SetHidden();
|
||||
|
||||
aut.AddExpressionAndConditionAndAction(
|
||||
"number",
|
||||
"UnseenGraceDistance",
|
||||
_("Unseen object grace distance"),
|
||||
_("the grace distance (in pixels) before deleting the object if it "
|
||||
"has "
|
||||
"never been visible on the screen. Useful to avoid objects being "
|
||||
"deleted before they are visible when they spawn"),
|
||||
_("the unseen grace distance"),
|
||||
_("Destroy outside configuration"),
|
||||
"CppPlatform/Extensions/destroyoutsideicon24.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "DestroyOutside")
|
||||
.UseStandardOperatorParameters("number",
|
||||
gd::ParameterOptions::MakeNewOptions())
|
||||
.MarkAsAdvanced()
|
||||
.SetFunctionName("SetExtraBorder")
|
||||
.SetGetter("GetExtraBorder");
|
||||
.UseStandardParameters("number", gd::ParameterOptions::MakeNewOptions())
|
||||
.MarkAsAdvanced();
|
||||
}
|
||||
|
@@ -5,11 +5,11 @@ Copyright (c) 2014-2016 Florian Rival (Florian.Rival@gmail.com)
|
||||
This project is released under the MIT License.
|
||||
*/
|
||||
#if defined(GD_IDE_ONLY)
|
||||
#include <iostream>
|
||||
|
||||
#include "GDCore/Extensions/PlatformExtension.h"
|
||||
#include "GDCore/Tools/Localization.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
void DeclareDestroyOutsideBehaviorExtension(gd::PlatformExtension& extension);
|
||||
|
||||
/**
|
||||
@@ -29,19 +29,36 @@ class DestroyOutsideBehaviorJsExtension : public gd::PlatformExtension {
|
||||
"Extensions/DestroyOutsideBehavior/"
|
||||
"destroyoutsideruntimebehavior.js");
|
||||
|
||||
GetAllExpressionsForBehavior(
|
||||
"DestroyOutsideBehavior::DestroyOutside")["ExtraBorder"]
|
||||
.SetFunctionName("getExtraBorder");
|
||||
GetAllConditionsForBehavior("DestroyOutsideBehavior::DestroyOutside")
|
||||
["DestroyOutsideBehavior::DestroyOutside::ExtraBorder"]
|
||||
.SetFunctionName("getExtraBorder");
|
||||
GetAllActionsForBehavior("DestroyOutsideBehavior::DestroyOutside")
|
||||
["DestroyOutsideBehavior::DestroyOutside::SetExtraBorder"]
|
||||
.SetFunctionName("setExtraBorder")
|
||||
.SetGetter("getExtraBorder");
|
||||
|
||||
// Deprecated:
|
||||
GetAllConditionsForBehavior("DestroyOutsideBehavior::DestroyOutside")
|
||||
["DestroyOutsideBehavior::ExtraBorder"]
|
||||
.SetFunctionName("getExtraBorder")
|
||||
.SetIncludeFile(
|
||||
"Extensions/DestroyOutsideBehavior/"
|
||||
"destroyoutsideruntimebehavior.js");
|
||||
.SetFunctionName("getExtraBorder");
|
||||
GetAllActionsForBehavior("DestroyOutsideBehavior::DestroyOutside")
|
||||
["DestroyOutsideBehavior::ExtraBorder"]
|
||||
.SetFunctionName("setExtraBorder")
|
||||
.SetGetter("getExtraBorder")
|
||||
.SetIncludeFile(
|
||||
"Extensions/DestroyOutsideBehavior/"
|
||||
"destroyoutsideruntimebehavior.js");
|
||||
.SetGetter("getExtraBorder");
|
||||
|
||||
GetAllExpressionsForBehavior(
|
||||
"DestroyOutsideBehavior::DestroyOutside")["UnseenGraceDistance"]
|
||||
.SetFunctionName("getUnseenGraceDistance");
|
||||
GetAllConditionsForBehavior("DestroyOutsideBehavior::DestroyOutside")
|
||||
["DestroyOutsideBehavior::DestroyOutside::UnseenGraceDistance"]
|
||||
.SetFunctionName("getUnseenGraceDistance");
|
||||
GetAllActionsForBehavior("DestroyOutsideBehavior::DestroyOutside")
|
||||
["DestroyOutsideBehavior::DestroyOutside::SetUnseenGraceDistance"]
|
||||
.SetFunctionName("setUnseenGraceDistance")
|
||||
.SetGetter("getUnseenGraceDistance");
|
||||
|
||||
GD_COMPLETE_EXTENSION_COMPILATION_INFORMATION();
|
||||
};
|
||||
|
@@ -8,7 +8,9 @@ namespace gdjs {
|
||||
* The DestroyOutsideRuntimeBehavior represents a behavior that destroys the object when it leaves the screen.
|
||||
*/
|
||||
export class DestroyOutsideRuntimeBehavior extends gdjs.RuntimeBehavior {
|
||||
_extraBorder: any;
|
||||
_extraBorder: float;
|
||||
_unseenGraceDistance: float;
|
||||
_hasBeenOnScreen: boolean;
|
||||
|
||||
constructor(
|
||||
instanceContainer: gdjs.RuntimeInstanceContainer,
|
||||
@@ -17,12 +19,20 @@ namespace gdjs {
|
||||
) {
|
||||
super(instanceContainer, behaviorData, owner);
|
||||
this._extraBorder = behaviorData.extraBorder || 0;
|
||||
this._unseenGraceDistance = behaviorData.unseenGraceDistance || 0;
|
||||
this._hasBeenOnScreen = false;
|
||||
}
|
||||
|
||||
updateFromBehaviorData(oldBehaviorData, newBehaviorData): boolean {
|
||||
if (oldBehaviorData.extraBorder !== newBehaviorData.extraBorder) {
|
||||
this._extraBorder = newBehaviorData.extraBorder;
|
||||
}
|
||||
if (
|
||||
oldBehaviorData.unseenGraceDistance !==
|
||||
newBehaviorData.unseenGraceDistance
|
||||
) {
|
||||
this._unseenGraceDistance = newBehaviorData.unseenGraceDistance;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -35,23 +45,47 @@ namespace gdjs {
|
||||
const ocy = this.owner.getDrawableY() + this.owner.getCenterY();
|
||||
const layer = instanceContainer.getLayer(this.owner.getLayer());
|
||||
const boundingCircleRadius = Math.sqrt(ow * ow + oh * oh) / 2.0;
|
||||
|
||||
const cameraLeft = layer.getCameraX() - layer.getCameraWidth() / 2;
|
||||
const cameraRight = layer.getCameraX() + layer.getCameraWidth() / 2;
|
||||
const cameraTop = layer.getCameraY() - layer.getCameraHeight() / 2;
|
||||
const cameraBottom = layer.getCameraY() + layer.getCameraHeight() / 2;
|
||||
|
||||
if (
|
||||
ocx + boundingCircleRadius + this._extraBorder <
|
||||
layer.getCameraX() - layer.getCameraWidth() / 2 ||
|
||||
ocx - boundingCircleRadius - this._extraBorder >
|
||||
layer.getCameraX() + layer.getCameraWidth() / 2 ||
|
||||
ocy + boundingCircleRadius + this._extraBorder <
|
||||
layer.getCameraY() - layer.getCameraHeight() / 2 ||
|
||||
ocy - boundingCircleRadius - this._extraBorder >
|
||||
layer.getCameraY() + layer.getCameraHeight() / 2
|
||||
ocx + boundingCircleRadius + this._extraBorder < cameraLeft ||
|
||||
ocx - boundingCircleRadius - this._extraBorder > cameraRight ||
|
||||
ocy + boundingCircleRadius + this._extraBorder < cameraTop ||
|
||||
ocy - boundingCircleRadius - this._extraBorder > cameraBottom
|
||||
) {
|
||||
//We are outside the camera area.
|
||||
this.owner.deleteFromScene();
|
||||
if (this._hasBeenOnScreen) {
|
||||
// Object is outside the camera area and object was previously seen inside it:
|
||||
// delete it now.
|
||||
this.owner.deleteFromScene();
|
||||
} else if (
|
||||
ocx + boundingCircleRadius + this._unseenGraceDistance < cameraLeft ||
|
||||
ocx - boundingCircleRadius - this._unseenGraceDistance >
|
||||
cameraRight ||
|
||||
ocy + boundingCircleRadius + this._unseenGraceDistance < cameraTop ||
|
||||
ocy - boundingCircleRadius - this._unseenGraceDistance > cameraBottom
|
||||
) {
|
||||
// Object is outside the camera area and also outside the grace distance:
|
||||
// force deletion.
|
||||
this.owner.deleteFromScene();
|
||||
} else {
|
||||
// Object is outside the camera area but inside the grace distance
|
||||
// and was never seen inside the camera area: don't delete it yet.
|
||||
}
|
||||
} else {
|
||||
this._hasBeenOnScreen = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an additional border to the camera viewport as a buffer before the object gets destroyed.
|
||||
* Set the additional border outside the camera area.
|
||||
*
|
||||
* If the object goes beyond the camera area and this border, it will be deleted (unless it was
|
||||
* never seen inside the camera area and this border before, in which case it will be deleted
|
||||
* according to the grace distance).
|
||||
* @param val Border in pixels.
|
||||
*/
|
||||
setExtraBorder(val: number): void {
|
||||
@@ -59,12 +93,36 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the additional border of the camera viewport buffer which triggers the destruction of an object.
|
||||
* Get the additional border outside the camera area.
|
||||
* @return The additional border around the camera viewport in pixels
|
||||
*/
|
||||
getExtraBorder(): number {
|
||||
return this._extraBorder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the grace distance before an object is deleted if it's outside the camera area
|
||||
* and was never seen inside the camera area. Typically useful to avoid objects being deleted
|
||||
* before they are visible when they spawn.
|
||||
*/
|
||||
setUnseenGraceDistance(val: number): void {
|
||||
this._unseenGraceDistance = val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the grace distance before an object is deleted if it's outside the camera area
|
||||
* and was never seen inside the camera area.
|
||||
*/
|
||||
getUnseenGraceDistance(): number {
|
||||
return this._unseenGraceDistance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this object has been visible on screen (precisely: inside the camera area *including* the extra border).
|
||||
*/
|
||||
hasBeenOnScreen(): boolean {
|
||||
return this._hasBeenOnScreen;
|
||||
}
|
||||
}
|
||||
gdjs.registerBehavior(
|
||||
'DestroyOutsideBehavior::DestroyOutside',
|
||||
|
@@ -29,6 +29,19 @@ describe('gdjs.DraggableRuntimeBehavior', function () {
|
||||
objects: [],
|
||||
instances: [],
|
||||
usedResources: [],
|
||||
uiSettings: {
|
||||
grid: false,
|
||||
gridType: 'rectangular',
|
||||
gridWidth: 10,
|
||||
gridHeight: 10,
|
||||
gridDepth: 10,
|
||||
gridOffsetX: 0,
|
||||
gridOffsetY: 0,
|
||||
gridOffsetZ: 0,
|
||||
gridColor: 0,
|
||||
gridAlpha: 1,
|
||||
snap: false,
|
||||
},
|
||||
},
|
||||
usedExtensionsWithVariablesData: [],
|
||||
});
|
||||
|
@@ -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) {
|
||||
|
@@ -87,16 +87,21 @@ namespace gdjs {
|
||||
return true;
|
||||
}
|
||||
|
||||
getNetworkSyncData(): LightNetworkSyncData {
|
||||
getNetworkSyncData(
|
||||
syncOptions: GetNetworkSyncDataOptions
|
||||
): LightNetworkSyncData {
|
||||
return {
|
||||
...super.getNetworkSyncData(),
|
||||
...super.getNetworkSyncData(syncOptions),
|
||||
rad: this.getRadius(),
|
||||
col: this.getColor(),
|
||||
};
|
||||
}
|
||||
|
||||
updateFromNetworkSyncData(networkSyncData: LightNetworkSyncData): void {
|
||||
super.updateFromNetworkSyncData(networkSyncData);
|
||||
updateFromNetworkSyncData(
|
||||
networkSyncData: LightNetworkSyncData,
|
||||
options: UpdateFromNetworkSyncDataOptions
|
||||
): void {
|
||||
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||
|
||||
if (networkSyncData.rad !== undefined) {
|
||||
this.setRadius(networkSyncData.rad);
|
||||
|
@@ -35,6 +35,19 @@ describe('gdjs.LinksManager', function () {
|
||||
stopSoundsOnStartup: false,
|
||||
title: '',
|
||||
usedResources: [],
|
||||
uiSettings: {
|
||||
grid: false,
|
||||
gridType: 'rectangular',
|
||||
gridWidth: 10,
|
||||
gridHeight: 10,
|
||||
gridDepth: 10,
|
||||
gridOffsetX: 0,
|
||||
gridOffsetY: 0,
|
||||
gridOffsetZ: 0,
|
||||
gridColor: 0,
|
||||
gridAlpha: 1,
|
||||
snap: false,
|
||||
},
|
||||
},
|
||||
usedExtensionsWithVariablesData: [],
|
||||
});
|
||||
|
@@ -729,7 +729,9 @@ namespace gdjs {
|
||||
behavior.playerNumber = ownerPlayerNumber;
|
||||
}
|
||||
|
||||
instance.updateFromNetworkSyncData(messageData);
|
||||
instance.updateFromNetworkSyncData(messageData, {
|
||||
clearInputs: false,
|
||||
});
|
||||
|
||||
setLastClockReceivedForInstanceOnScene({
|
||||
sceneNetworkId,
|
||||
@@ -1737,7 +1739,7 @@ namespace gdjs {
|
||||
return;
|
||||
}
|
||||
|
||||
runtimeScene.updateFromNetworkSyncData(messageData);
|
||||
runtimeScene.updateFromNetworkSyncData(messageData, {});
|
||||
} else {
|
||||
// If the game is not ready to receive game update messages, we need to save the data for later use.
|
||||
// This can happen when joining a game that is already running.
|
||||
@@ -1890,7 +1892,7 @@ namespace gdjs {
|
||||
const messageData = message.getData();
|
||||
const messageSender = message.getSender();
|
||||
if (gdjs.multiplayer.isReadyToSendOrReceiveGameUpdateMessages()) {
|
||||
runtimeScene.getGame().updateFromNetworkSyncData(messageData);
|
||||
runtimeScene.getGame().updateFromNetworkSyncData(messageData, {});
|
||||
} else {
|
||||
// If the game is not ready to receive game update messages, we need to save the data for later use.
|
||||
// This can happen when joining a game that is already running.
|
||||
@@ -1918,7 +1920,7 @@ namespace gdjs {
|
||||
// Reapply the game saved updates.
|
||||
lastReceivedGameSyncDataUpdates.getUpdates().forEach((messageData) => {
|
||||
debugLogger.info(`Reapplying saved update of game.`);
|
||||
runtimeScene.getGame().updateFromNetworkSyncData(messageData);
|
||||
runtimeScene.getGame().updateFromNetworkSyncData(messageData, {});
|
||||
});
|
||||
// Game updates are always applied properly, so we can clear them.
|
||||
lastReceivedGameSyncDataUpdates.clear();
|
||||
@@ -1937,7 +1939,7 @@ namespace gdjs {
|
||||
|
||||
debugLogger.info(`Reapplying saved update of scene ${sceneNetworkId}.`);
|
||||
|
||||
runtimeScene.updateFromNetworkSyncData(messageData);
|
||||
runtimeScene.updateFromNetworkSyncData(messageData, {});
|
||||
// We only remove the message if it was successfully applied, so it can be reapplied later,
|
||||
// in case we were not on the right scene.
|
||||
lastReceivedSceneSyncDataUpdates.remove(messageData);
|
||||
|
@@ -278,7 +278,7 @@ namespace gdjs {
|
||||
|
||||
const instanceNetworkId = this._getOrCreateInstanceNetworkId();
|
||||
const objectName = this.owner.getName();
|
||||
const objectNetworkSyncData = this.owner.getNetworkSyncData();
|
||||
const objectNetworkSyncData = this.owner.getNetworkSyncData({});
|
||||
|
||||
// this._logToConsoleWithThrottle(
|
||||
// `Synchronizing object ${this.owner.getName()} (instance ${
|
||||
@@ -448,7 +448,7 @@ namespace gdjs {
|
||||
objectOwner: this.playerNumber,
|
||||
objectName,
|
||||
instanceNetworkId,
|
||||
objectNetworkSyncData: this.owner.getNetworkSyncData(),
|
||||
objectNetworkSyncData: this.owner.getNetworkSyncData({}),
|
||||
sceneNetworkId,
|
||||
});
|
||||
this._sendDataToPeersWithIncreasedClock(
|
||||
@@ -598,7 +598,7 @@ namespace gdjs {
|
||||
debugLogger.info(
|
||||
'Sending update message to move the object immediately.'
|
||||
);
|
||||
const objectNetworkSyncData = this.owner.getNetworkSyncData();
|
||||
const objectNetworkSyncData = this.owner.getNetworkSyncData({});
|
||||
const {
|
||||
messageName: updateMessageName,
|
||||
messageData: updateMessageData,
|
||||
|
@@ -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);
|
||||
}
|
||||
@@ -119,18 +123,21 @@ namespace gdjs {
|
||||
return true;
|
||||
}
|
||||
|
||||
getNetworkSyncData(): PanelSpriteNetworkSyncData {
|
||||
getNetworkSyncData(
|
||||
syncOptions: GetNetworkSyncDataOptions
|
||||
): PanelSpriteNetworkSyncData {
|
||||
return {
|
||||
...super.getNetworkSyncData(),
|
||||
...super.getNetworkSyncData(syncOptions),
|
||||
op: this.getOpacity(),
|
||||
color: this.getColor(),
|
||||
};
|
||||
}
|
||||
|
||||
updateFromNetworkSyncData(
|
||||
networkSyncData: PanelSpriteNetworkSyncData
|
||||
networkSyncData: PanelSpriteNetworkSyncData,
|
||||
options: UpdateFromNetworkSyncDataOptions
|
||||
): void {
|
||||
super.updateFromNetworkSyncData(networkSyncData);
|
||||
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||
|
||||
// Texture is not synchronized, see if this is asked or not.
|
||||
|
||||
@@ -163,9 +170,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 +253,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;
|
||||
|
@@ -18,12 +18,15 @@ namespace gdjs {
|
||||
renderer: PIXI.Container;
|
||||
emitter: PIXI.particles.Emitter;
|
||||
started: boolean = false;
|
||||
helperGraphics: PIXI.Graphics | null = null;
|
||||
runtimeObject: gdjs.ParticleEmitterObject;
|
||||
|
||||
constructor(
|
||||
instanceContainer: gdjs.RuntimeInstanceContainer,
|
||||
runtimeObject: gdjs.RuntimeObject,
|
||||
runtimeObject: gdjs.ParticleEmitterObject,
|
||||
objectData: any
|
||||
) {
|
||||
this.runtimeObject = runtimeObject;
|
||||
const pixiRenderer = instanceContainer
|
||||
.getGame()
|
||||
.getRenderer()
|
||||
@@ -223,6 +226,44 @@ namespace gdjs {
|
||||
if (!this.started && wasEmitting) {
|
||||
this.started = true;
|
||||
}
|
||||
if (this.helperGraphics) {
|
||||
this.helperGraphics.clear();
|
||||
this.helperGraphics.position.x = this.runtimeObject.getX();
|
||||
this.helperGraphics.position.y = this.runtimeObject.getY();
|
||||
|
||||
const emitterAngle = gdjs.toRad(this.runtimeObject.getAngle());
|
||||
const sprayConeAngle = gdjs.toRad(
|
||||
this.runtimeObject.getConeSprayAngle()
|
||||
);
|
||||
const line1Angle = emitterAngle - sprayConeAngle / 2;
|
||||
const line2Angle = emitterAngle + sprayConeAngle / 2;
|
||||
const length = 64;
|
||||
|
||||
this.helperGraphics.beginFill(0, 0);
|
||||
this.helperGraphics.lineStyle(
|
||||
3,
|
||||
this.runtimeObject.getParticleColorEnd(),
|
||||
1
|
||||
);
|
||||
this.helperGraphics.moveTo(0, 0);
|
||||
this.helperGraphics.lineTo(
|
||||
Math.cos(line1Angle) * length,
|
||||
Math.sin(line1Angle) * length
|
||||
);
|
||||
this.helperGraphics.moveTo(0, 0);
|
||||
this.helperGraphics.lineTo(
|
||||
Math.cos(line2Angle) * length,
|
||||
Math.sin(line2Angle) * length
|
||||
);
|
||||
this.helperGraphics.endFill();
|
||||
|
||||
this.helperGraphics.lineStyle(0, 0x000000, 1);
|
||||
this.helperGraphics.beginFill(
|
||||
this.runtimeObject.getParticleColorStart()
|
||||
);
|
||||
this.helperGraphics.drawCircle(0, 0, 8);
|
||||
this.helperGraphics.endFill();
|
||||
}
|
||||
}
|
||||
|
||||
setPosition(x: number, y: number): void {
|
||||
@@ -443,6 +484,17 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
private static readonly frequencyMinimumValue = 0.0001;
|
||||
|
||||
setHelperVisible(visible: boolean) {
|
||||
if (visible && !this.helperGraphics) {
|
||||
this.helperGraphics = new PIXI.Graphics();
|
||||
this.renderer.addChild(this.helperGraphics);
|
||||
} else if (!visible && this.helperGraphics) {
|
||||
this.helperGraphics.removeFromParent();
|
||||
this.helperGraphics.destroy();
|
||||
this.helperGraphics = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-ignore - Register the class to let the engine use it.
|
||||
|
@@ -174,6 +174,10 @@ namespace gdjs {
|
||||
this,
|
||||
particleObjectData
|
||||
);
|
||||
if (instanceContainer.getGame().isInGameEdition()) {
|
||||
// TODO Disable the particles rendering
|
||||
this._renderer.setHelperVisible(true);
|
||||
}
|
||||
this.angleA = particleObjectData.emitterAngleA;
|
||||
this.angleB = particleObjectData.emitterAngleB;
|
||||
this.forceMin = particleObjectData.emitterForceMin;
|
||||
@@ -370,9 +374,11 @@ namespace gdjs {
|
||||
return true;
|
||||
}
|
||||
|
||||
getNetworkSyncData(): ParticleEmitterObjectNetworkSyncData {
|
||||
getNetworkSyncData(
|
||||
syncOptions: GetNetworkSyncDataOptions
|
||||
): ParticleEmitterObjectNetworkSyncData {
|
||||
return {
|
||||
...super.getNetworkSyncData(),
|
||||
...super.getNetworkSyncData(syncOptions),
|
||||
prms: this.particleRotationMinSpeed,
|
||||
prmx: this.particleRotationMaxSpeed,
|
||||
mpc: this.maxParticlesCount,
|
||||
@@ -399,9 +405,10 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
updateFromNetworkSyncData(
|
||||
syncData: ParticleEmitterObjectNetworkSyncData
|
||||
syncData: ParticleEmitterObjectNetworkSyncData,
|
||||
options: UpdateFromNetworkSyncDataOptions
|
||||
): void {
|
||||
super.updateFromNetworkSyncData(syncData);
|
||||
super.updateFromNetworkSyncData(syncData, options);
|
||||
if (syncData.x !== undefined) {
|
||||
this.setX(syncData.x);
|
||||
}
|
||||
@@ -799,6 +806,14 @@ namespace gdjs {
|
||||
}
|
||||
}
|
||||
|
||||
getParticleColorStart(): number {
|
||||
return this.color1;
|
||||
}
|
||||
|
||||
getParticleColorEnd(): number {
|
||||
return this.color2;
|
||||
}
|
||||
|
||||
getParticleRed1(): number {
|
||||
return gdjs.hexNumberToRGBArray(this.color1)[0];
|
||||
}
|
||||
|
@@ -6,7 +6,7 @@ namespace gdjs {
|
||||
const logger = new gdjs.Logger('Pathfinding behavior');
|
||||
|
||||
interface PathfindingNetworkSyncDataType {
|
||||
// Syncing the path should be enough to have a good prediction.
|
||||
// Syncing the path and its position on it should be enough to have a good prediction.
|
||||
path: FloatPoint[];
|
||||
pf: boolean;
|
||||
sp: number;
|
||||
@@ -15,6 +15,7 @@ namespace gdjs {
|
||||
tss: number;
|
||||
re: boolean;
|
||||
ma: number;
|
||||
dos: number;
|
||||
}
|
||||
|
||||
export interface PathfindingNetworkSyncData extends BehaviorNetworkSyncData {
|
||||
@@ -133,9 +134,11 @@ namespace gdjs {
|
||||
return true;
|
||||
}
|
||||
|
||||
getNetworkSyncData(): PathfindingNetworkSyncData {
|
||||
getNetworkSyncData(
|
||||
options: GetNetworkSyncDataOptions
|
||||
): PathfindingNetworkSyncData {
|
||||
return {
|
||||
...super.getNetworkSyncData(),
|
||||
...super.getNetworkSyncData(options),
|
||||
props: {
|
||||
path: this._path,
|
||||
pf: this._pathFound,
|
||||
@@ -145,14 +148,16 @@ namespace gdjs {
|
||||
tss: this._totalSegmentDistance,
|
||||
re: this._reachedEnd,
|
||||
ma: this._movementAngle,
|
||||
dos: this._distanceOnSegment,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
updateFromNetworkSyncData(
|
||||
networkSyncData: PathfindingNetworkSyncData
|
||||
networkSyncData: PathfindingNetworkSyncData,
|
||||
options: UpdateFromNetworkSyncDataOptions
|
||||
): void {
|
||||
super.updateFromNetworkSyncData(networkSyncData);
|
||||
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||
const behaviorSpecificProps = networkSyncData.props;
|
||||
if (behaviorSpecificProps.path !== undefined) {
|
||||
this._path = behaviorSpecificProps.path;
|
||||
@@ -181,6 +186,9 @@ namespace gdjs {
|
||||
if (behaviorSpecificProps.ma !== undefined) {
|
||||
this._movementAngle = behaviorSpecificProps.ma;
|
||||
}
|
||||
if (behaviorSpecificProps.dos !== undefined) {
|
||||
this._distanceOnSegment = behaviorSpecificProps.dos;
|
||||
}
|
||||
}
|
||||
|
||||
setCellWidth(width: float): void {
|
||||
|
@@ -36,6 +36,19 @@ describe('gdjs.PathfindingRuntimeBehavior', function () {
|
||||
objects: [],
|
||||
instances: [],
|
||||
usedResources: [],
|
||||
uiSettings: {
|
||||
grid: false,
|
||||
gridType: 'rectangular',
|
||||
gridWidth: 10,
|
||||
gridHeight: 10,
|
||||
gridDepth: 10,
|
||||
gridOffsetX: 0,
|
||||
gridOffsetY: 0,
|
||||
gridOffsetZ: 0,
|
||||
gridColor: 0,
|
||||
gridAlpha: 1,
|
||||
snap: false,
|
||||
},
|
||||
},
|
||||
usedExtensionsWithVariablesData: [],
|
||||
});
|
||||
|
@@ -39,6 +39,19 @@ describe('gdjs.PathfindingRuntimeBehavior', function () {
|
||||
objects: [],
|
||||
instances: [],
|
||||
usedResources: [],
|
||||
uiSettings: {
|
||||
grid: false,
|
||||
gridType: 'rectangular',
|
||||
gridWidth: 10,
|
||||
gridHeight: 10,
|
||||
gridDepth: 10,
|
||||
gridOffsetX: 0,
|
||||
gridOffsetY: 0,
|
||||
gridOffsetZ: 0,
|
||||
gridColor: 0,
|
||||
gridAlpha: 1,
|
||||
snap: false,
|
||||
},
|
||||
},
|
||||
usedExtensionsWithVariablesData: [],
|
||||
});
|
||||
|
@@ -41,6 +41,19 @@ describe('gdjs.PathfindingRuntimeBehavior', function () {
|
||||
objects: [],
|
||||
instances: [],
|
||||
usedResources: [],
|
||||
uiSettings: {
|
||||
grid: false,
|
||||
gridType: 'rectangular',
|
||||
gridWidth: 10,
|
||||
gridHeight: 10,
|
||||
gridDepth: 10,
|
||||
gridOffsetX: 0,
|
||||
gridOffsetY: 0,
|
||||
gridOffsetZ: 0,
|
||||
gridColor: 0,
|
||||
gridAlpha: 1,
|
||||
snap: false,
|
||||
},
|
||||
},
|
||||
usedExtensionsWithVariablesData: [],
|
||||
});
|
||||
|
@@ -623,10 +623,46 @@ Zv();a.b2Manifold.e_faceA=$v();a.b2Manifold.e_faceB=aw();a.b2_staticBody=bw();a.
|
||||
})();
|
||||
|
||||
gdjs.registerAsynchronouslyLoadingLibraryPromise(initializeBox2D({locateFile: function(path, prefix) {
|
||||
return location.protocol === 'file:' ?
|
||||
// This is needed to run on Electron.
|
||||
prefix + "Extensions/Physics2Behavior/" + path :
|
||||
prefix + path;
|
||||
// Path should always be "Box2D_v2.3.1_min.wasm.wasm" (and if it's not, we should probably hardcode it).
|
||||
if (path !== 'Box2D_v2.3.1_min.wasm.wasm') {
|
||||
console.warn("'path' argument sent to locateFile in Box2D_v2.3.1_min.wasm.js is not the expected string 'Box2D_v2.3.1_min.wasm.wasm'. Loading may fail.")
|
||||
}
|
||||
|
||||
// Prefix is typically:
|
||||
// Games ("exported", standalone game):
|
||||
// - Web game: "https://games.gdevelop-app.com/[...]/Extensions/Physics2Behavior/"
|
||||
// - Cordova Android: "https://localhost/Extensions/Physics2Behavior/".
|
||||
// - Cordova iOS: "ionic://localhost/Extensions/Physics2Behavior/".
|
||||
// - Electron macOS: "/private/var/[...]/Contents/Resources/app.asar/app/" (notice the missing folder).
|
||||
// - Electron Windows: "C:\Users\[...]\AppData\Local\[...]\resources\app.asar\app/" (notice the missing folder).
|
||||
// Preview (in the editor):
|
||||
// - Web app preview (dev editor): "http://localhost:5002/Runtime/Extensions/Physics2Behavior/"
|
||||
// - Web app preview (production editor): "https://resources.gdevelop-app.com/[...]/Runtime/Extensions/Physics2Behavior/"
|
||||
// - Electron app preview (dev editor): "/var/[...]/preview/" (notice the missing folder).
|
||||
// - Electron app preview (production editor): "/var/[...]/preview/" (notice the missing folder).
|
||||
// In-game editor:
|
||||
// - Web app (dev editor): "http://localhost:5002/Runtime/Extensions/Physics2Behavior/"
|
||||
// - Web app (production editor): "https://resources.gdevelop-app.com/[...]/Runtime/Extensions/Physics2Behavior/"
|
||||
// - Electron app (dev editor): "file:///var/[...]/in-game-editor-preview/Extensions/Physics2Behavior/"
|
||||
// - Electron app (production editor): "file:///var/[...]/in-game-editor-preview/Extensions/Physics2Behavior/"
|
||||
|
||||
// If the prefix is a full URL, it's a full URL to the folder containing this JS file.
|
||||
// Sill consider the case where the folder could have been missing.
|
||||
let url;
|
||||
if (prefix.startsWith('http:') || prefix.startsWith('https:')) {
|
||||
url = prefix.endsWith('Extensions/Physics2Behavior/') ?
|
||||
prefix + path :
|
||||
prefix + 'Extensions/Physics2Behavior/' + path;
|
||||
} else {
|
||||
// Electron or Cordova iOS will fall in this case.
|
||||
// We can't use this simple solution for http/https because
|
||||
// on the web-app, the runtime is not necessarily hosted
|
||||
// on the same domain as where the game generated files are served (so "prefix" is needed).
|
||||
url = "Extensions/Physics2Behavior/" + path;
|
||||
}
|
||||
|
||||
console.info(`Box2D wasm file is being loaded from path "${path}" with prefix "${prefix}". Resolved URL: "${url}".`);
|
||||
return url;
|
||||
}}).then(box2d => {
|
||||
window.Box2D = box2d;
|
||||
}));
|
||||
|
@@ -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')
|
||||
|
@@ -499,7 +499,9 @@ namespace gdjs {
|
||||
return true;
|
||||
}
|
||||
|
||||
getNetworkSyncData(): Physics2NetworkSyncData {
|
||||
getNetworkSyncData(
|
||||
options: GetNetworkSyncDataOptions
|
||||
): Physics2NetworkSyncData {
|
||||
const bodyProps = this._body
|
||||
? {
|
||||
tpx: this._body.GetTransform().get_p().get_x(),
|
||||
@@ -520,7 +522,7 @@ namespace gdjs {
|
||||
aw: undefined,
|
||||
};
|
||||
return {
|
||||
...super.getNetworkSyncData(),
|
||||
...super.getNetworkSyncData(options),
|
||||
props: {
|
||||
...bodyProps,
|
||||
layers: this.layers,
|
||||
@@ -529,45 +531,13 @@ namespace gdjs {
|
||||
};
|
||||
}
|
||||
|
||||
updateFromNetworkSyncData(networkSyncData: Physics2NetworkSyncData) {
|
||||
super.updateFromNetworkSyncData(networkSyncData);
|
||||
updateFromNetworkSyncData(
|
||||
networkSyncData: Physics2NetworkSyncData,
|
||||
options: UpdateFromNetworkSyncDataOptions
|
||||
) {
|
||||
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||
|
||||
const behaviorSpecificProps = networkSyncData.props;
|
||||
if (
|
||||
behaviorSpecificProps.tpx !== undefined &&
|
||||
behaviorSpecificProps.tpy !== undefined &&
|
||||
behaviorSpecificProps.tqa !== undefined
|
||||
) {
|
||||
if (this._body) {
|
||||
this._body.SetTransform(
|
||||
this.b2Vec2(behaviorSpecificProps.tpx, behaviorSpecificProps.tpy),
|
||||
behaviorSpecificProps.tqa
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
behaviorSpecificProps.lvx !== undefined &&
|
||||
behaviorSpecificProps.lvy !== undefined
|
||||
) {
|
||||
if (this._body) {
|
||||
this._body.SetLinearVelocity(
|
||||
this.b2Vec2(behaviorSpecificProps.lvx, behaviorSpecificProps.lvy)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (behaviorSpecificProps.av !== undefined) {
|
||||
if (this._body) {
|
||||
this._body.SetAngularVelocity(behaviorSpecificProps.av);
|
||||
}
|
||||
}
|
||||
|
||||
if (behaviorSpecificProps.aw !== undefined) {
|
||||
if (this._body) {
|
||||
this._body.SetAwake(behaviorSpecificProps.aw);
|
||||
}
|
||||
}
|
||||
|
||||
if (behaviorSpecificProps.layers !== undefined) {
|
||||
this.layers = behaviorSpecificProps.layers;
|
||||
@@ -576,6 +546,38 @@ namespace gdjs {
|
||||
if (behaviorSpecificProps.masks !== undefined) {
|
||||
this.masks = behaviorSpecificProps.masks;
|
||||
}
|
||||
|
||||
this.updateBodyFromObject();
|
||||
|
||||
if (!this._body) return;
|
||||
|
||||
if (
|
||||
behaviorSpecificProps.tpx !== undefined &&
|
||||
behaviorSpecificProps.tpy !== undefined &&
|
||||
behaviorSpecificProps.tqa !== undefined
|
||||
) {
|
||||
this._body.SetTransform(
|
||||
this.b2Vec2(behaviorSpecificProps.tpx, behaviorSpecificProps.tpy),
|
||||
behaviorSpecificProps.tqa
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
behaviorSpecificProps.lvx !== undefined &&
|
||||
behaviorSpecificProps.lvy !== undefined
|
||||
) {
|
||||
this._body.SetLinearVelocity(
|
||||
this.b2Vec2(behaviorSpecificProps.lvx, behaviorSpecificProps.lvy)
|
||||
);
|
||||
}
|
||||
|
||||
if (behaviorSpecificProps.av !== undefined) {
|
||||
this._body.SetAngularVelocity(behaviorSpecificProps.av);
|
||||
}
|
||||
|
||||
if (behaviorSpecificProps.aw !== undefined) {
|
||||
this._body.SetAwake(behaviorSpecificProps.aw);
|
||||
}
|
||||
}
|
||||
|
||||
onDeActivate() {
|
||||
|
@@ -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',
|
||||
|
@@ -495,7 +495,9 @@ namespace gdjs {
|
||||
return true;
|
||||
}
|
||||
|
||||
override getNetworkSyncData(): Physics3DNetworkSyncData {
|
||||
override getNetworkSyncData(
|
||||
options: GetNetworkSyncDataOptions
|
||||
): Physics3DNetworkSyncData {
|
||||
let bodyProps;
|
||||
if (this._body) {
|
||||
const position = this._body.GetPosition();
|
||||
@@ -537,7 +539,7 @@ namespace gdjs {
|
||||
};
|
||||
}
|
||||
return {
|
||||
...super.getNetworkSyncData(),
|
||||
...super.getNetworkSyncData(options),
|
||||
props: {
|
||||
...bodyProps,
|
||||
layers: this.layers,
|
||||
@@ -547,27 +549,40 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
override updateFromNetworkSyncData(
|
||||
networkSyncData: Physics3DNetworkSyncData
|
||||
networkSyncData: Physics3DNetworkSyncData,
|
||||
options: UpdateFromNetworkSyncDataOptions
|
||||
) {
|
||||
super.updateFromNetworkSyncData(networkSyncData);
|
||||
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||
|
||||
const behaviorSpecificProps = networkSyncData.props;
|
||||
|
||||
if (behaviorSpecificProps.layers !== undefined) {
|
||||
this.layers = behaviorSpecificProps.layers;
|
||||
}
|
||||
if (behaviorSpecificProps.masks !== undefined) {
|
||||
this.masks = behaviorSpecificProps.masks;
|
||||
}
|
||||
|
||||
this._needToRecreateShape = true;
|
||||
this._needToRecreateBody = true;
|
||||
this.updateBodyFromObject();
|
||||
|
||||
if (!this._body) return;
|
||||
|
||||
if (
|
||||
behaviorSpecificProps.px !== undefined &&
|
||||
behaviorSpecificProps.py !== undefined &&
|
||||
behaviorSpecificProps.pz !== undefined
|
||||
) {
|
||||
if (this._body) {
|
||||
this._sharedData.bodyInterface.SetPosition(
|
||||
this._body.GetID(),
|
||||
this.getRVec3(
|
||||
behaviorSpecificProps.px,
|
||||
behaviorSpecificProps.py,
|
||||
behaviorSpecificProps.pz
|
||||
),
|
||||
Jolt.EActivation_DontActivate
|
||||
);
|
||||
}
|
||||
this._sharedData.bodyInterface.SetPosition(
|
||||
this._body.GetID(),
|
||||
this.getRVec3(
|
||||
behaviorSpecificProps.px,
|
||||
behaviorSpecificProps.py,
|
||||
behaviorSpecificProps.pz
|
||||
),
|
||||
Jolt.EActivation_DontActivate
|
||||
);
|
||||
}
|
||||
if (
|
||||
behaviorSpecificProps.rx !== undefined &&
|
||||
@@ -575,56 +590,44 @@ namespace gdjs {
|
||||
behaviorSpecificProps.rz !== undefined &&
|
||||
behaviorSpecificProps.rw !== undefined
|
||||
) {
|
||||
if (this._body) {
|
||||
this._sharedData.bodyInterface.SetRotation(
|
||||
this._body.GetID(),
|
||||
this.getQuat(
|
||||
behaviorSpecificProps.rx,
|
||||
behaviorSpecificProps.ry,
|
||||
behaviorSpecificProps.rz,
|
||||
behaviorSpecificProps.rw
|
||||
),
|
||||
Jolt.EActivation_DontActivate
|
||||
);
|
||||
}
|
||||
this._sharedData.bodyInterface.SetRotation(
|
||||
this._body.GetID(),
|
||||
this.getQuat(
|
||||
behaviorSpecificProps.rx,
|
||||
behaviorSpecificProps.ry,
|
||||
behaviorSpecificProps.rz,
|
||||
behaviorSpecificProps.rw
|
||||
),
|
||||
Jolt.EActivation_DontActivate
|
||||
);
|
||||
}
|
||||
if (
|
||||
behaviorSpecificProps.lvx !== undefined &&
|
||||
behaviorSpecificProps.lvy !== undefined &&
|
||||
behaviorSpecificProps.lvz !== undefined
|
||||
) {
|
||||
if (this._body) {
|
||||
this._sharedData.bodyInterface.SetLinearVelocity(
|
||||
this._body.GetID(),
|
||||
this.getVec3(
|
||||
behaviorSpecificProps.lvx,
|
||||
behaviorSpecificProps.lvy,
|
||||
behaviorSpecificProps.lvz
|
||||
)
|
||||
);
|
||||
}
|
||||
this._sharedData.bodyInterface.SetLinearVelocity(
|
||||
this._body.GetID(),
|
||||
this.getVec3(
|
||||
behaviorSpecificProps.lvx,
|
||||
behaviorSpecificProps.lvy,
|
||||
behaviorSpecificProps.lvz
|
||||
)
|
||||
);
|
||||
}
|
||||
if (
|
||||
behaviorSpecificProps.avx !== undefined &&
|
||||
behaviorSpecificProps.avy !== undefined &&
|
||||
behaviorSpecificProps.avz !== undefined
|
||||
) {
|
||||
if (this._body) {
|
||||
this._sharedData.bodyInterface.SetAngularVelocity(
|
||||
this._body.GetID(),
|
||||
this.getVec3(
|
||||
behaviorSpecificProps.avx,
|
||||
behaviorSpecificProps.avy,
|
||||
behaviorSpecificProps.avz
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
if (behaviorSpecificProps.layers !== undefined) {
|
||||
this.layers = behaviorSpecificProps.layers;
|
||||
}
|
||||
if (behaviorSpecificProps.masks !== undefined) {
|
||||
this.masks = behaviorSpecificProps.masks;
|
||||
this._sharedData.bodyInterface.SetAngularVelocity(
|
||||
this._body.GetID(),
|
||||
this.getVec3(
|
||||
behaviorSpecificProps.avx,
|
||||
behaviorSpecificProps.avy,
|
||||
behaviorSpecificProps.avz
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -921,31 +924,58 @@ namespace gdjs {
|
||||
this.updateBodyFromObject();
|
||||
}
|
||||
|
||||
recreateBody() {
|
||||
if (!this._body) {
|
||||
this._createBody();
|
||||
return;
|
||||
}
|
||||
|
||||
recreateBody(previousBodyData?: {
|
||||
linearVelocityX: float;
|
||||
linearVelocityY: float;
|
||||
linearVelocityZ: float;
|
||||
angularVelocityX: float;
|
||||
angularVelocityY: float;
|
||||
angularVelocityZ: float;
|
||||
}) {
|
||||
const bodyInterface = this._sharedData.bodyInterface;
|
||||
const linearVelocity = this._body.GetLinearVelocity();
|
||||
const linearVelocityX = linearVelocity.GetX();
|
||||
const linearVelocityY = linearVelocity.GetY();
|
||||
const linearVelocityZ = linearVelocity.GetZ();
|
||||
const angularVelocity = this._body.GetAngularVelocity();
|
||||
const angularVelocityX = angularVelocity.GetX();
|
||||
const angularVelocityY = angularVelocity.GetY();
|
||||
const angularVelocityZ = angularVelocity.GetZ();
|
||||
const linearVelocityX = previousBodyData
|
||||
? previousBodyData.linearVelocityX
|
||||
: this._body
|
||||
? this._body.GetLinearVelocity().GetX()
|
||||
: 0;
|
||||
const linearVelocityY = previousBodyData
|
||||
? previousBodyData.linearVelocityY
|
||||
: this._body
|
||||
? this._body.GetLinearVelocity().GetY()
|
||||
: 0;
|
||||
const linearVelocityZ = previousBodyData
|
||||
? previousBodyData.linearVelocityZ
|
||||
: this._body
|
||||
? this._body.GetLinearVelocity().GetZ()
|
||||
: 0;
|
||||
const angularVelocityX = previousBodyData
|
||||
? previousBodyData.angularVelocityX
|
||||
: this._body
|
||||
? this._body.GetAngularVelocity().GetX()
|
||||
: 0;
|
||||
const angularVelocityY = previousBodyData
|
||||
? previousBodyData.angularVelocityY
|
||||
: this._body
|
||||
? this._body.GetAngularVelocity().GetY()
|
||||
: 0;
|
||||
const angularVelocityZ = previousBodyData
|
||||
? previousBodyData.angularVelocityZ
|
||||
: this._body
|
||||
? this._body.GetAngularVelocity().GetZ()
|
||||
: 0;
|
||||
|
||||
this.bodyUpdater.destroyBody();
|
||||
this._contactsEndedThisFrame.length = 0;
|
||||
this._contactsStartedThisFrame.length = 0;
|
||||
this._currentContacts.length = 0;
|
||||
if (this._body) {
|
||||
this.bodyUpdater.destroyBody();
|
||||
this._contactsEndedThisFrame.length = 0;
|
||||
this._contactsStartedThisFrame.length = 0;
|
||||
this._currentContacts.length = 0;
|
||||
}
|
||||
|
||||
this._createBody();
|
||||
if (!this._body) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bodyID = this._body.GetID();
|
||||
bodyInterface.SetLinearVelocity(
|
||||
bodyID,
|
||||
|
@@ -12,6 +12,7 @@ namespace gdjs {
|
||||
etm: float;
|
||||
esm: float;
|
||||
ei: float;
|
||||
es: float;
|
||||
}
|
||||
|
||||
export interface PhysicsCar3DNetworkSyncData extends BehaviorNetworkSyncData {
|
||||
@@ -96,7 +97,7 @@ namespace gdjs {
|
||||
// This is useful when the object is synchronized by an external source
|
||||
// like in a multiplayer game, and we want to be able to predict the
|
||||
// movement of the object, even if the inputs are not updated every frame.
|
||||
private _dontClearInputsBetweenFrames: boolean = false;
|
||||
private _clearInputsBetweenFrames: boolean = true;
|
||||
|
||||
constructor(
|
||||
instanceContainer: gdjs.RuntimeInstanceContainer,
|
||||
@@ -168,12 +169,37 @@ namespace gdjs {
|
||||
this._isHookedToPhysicsStep = true;
|
||||
}
|
||||
|
||||
// Destroy the body before switching the bodyUpdater,
|
||||
// to ensure the body of the previous bodyUpdater is not left alive.
|
||||
// (would be a memory leak and would create a phantom body in the physics world)
|
||||
// But transfer the linear and angular velocity to the new body,
|
||||
// so the body doesn't stop when it is recreated.
|
||||
let previousBodyData = {
|
||||
linearVelocityX: 0,
|
||||
linearVelocityY: 0,
|
||||
linearVelocityZ: 0,
|
||||
angularVelocityX: 0,
|
||||
angularVelocityY: 0,
|
||||
angularVelocityZ: 0,
|
||||
};
|
||||
if (behavior._body) {
|
||||
const linearVelocity = behavior._body.GetLinearVelocity();
|
||||
previousBodyData.linearVelocityX = linearVelocity.GetX();
|
||||
previousBodyData.linearVelocityY = linearVelocity.GetY();
|
||||
previousBodyData.linearVelocityZ = linearVelocity.GetZ();
|
||||
const angularVelocity = behavior._body.GetAngularVelocity();
|
||||
previousBodyData.angularVelocityX = angularVelocity.GetX();
|
||||
previousBodyData.angularVelocityY = angularVelocity.GetY();
|
||||
previousBodyData.angularVelocityZ = angularVelocity.GetZ();
|
||||
behavior.bodyUpdater.destroyBody();
|
||||
}
|
||||
|
||||
behavior.bodyUpdater =
|
||||
new gdjs.PhysicsCar3DRuntimeBehavior.VehicleBodyUpdater(
|
||||
this,
|
||||
behavior.bodyUpdater
|
||||
);
|
||||
behavior.recreateBody();
|
||||
behavior.recreateBody(previousBodyData);
|
||||
|
||||
return this._physics3D;
|
||||
}
|
||||
@@ -273,13 +299,15 @@ namespace gdjs {
|
||||
return true;
|
||||
}
|
||||
|
||||
override getNetworkSyncData(): PhysicsCar3DNetworkSyncData {
|
||||
override getNetworkSyncData(
|
||||
syncOptions: GetNetworkSyncDataOptions
|
||||
): PhysicsCar3DNetworkSyncData {
|
||||
// This method is called, so we are synchronizing this object.
|
||||
// Let's clear the inputs between frames as we control it.
|
||||
this._dontClearInputsBetweenFrames = false;
|
||||
this._clearInputsBetweenFrames = true;
|
||||
|
||||
return {
|
||||
...super.getNetworkSyncData(),
|
||||
...super.getNetworkSyncData(syncOptions),
|
||||
props: {
|
||||
lek: this._wasLeftKeyPressed,
|
||||
rik: this._wasRightKeyPressed,
|
||||
@@ -291,14 +319,16 @@ namespace gdjs {
|
||||
etm: this._engineTorqueMax,
|
||||
esm: this._engineSpeedMax,
|
||||
ei: this._engineInertia,
|
||||
es: this.getEngineSpeed(),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
override updateFromNetworkSyncData(
|
||||
networkSyncData: PhysicsCar3DNetworkSyncData
|
||||
networkSyncData: PhysicsCar3DNetworkSyncData,
|
||||
options: UpdateFromNetworkSyncDataOptions
|
||||
) {
|
||||
super.updateFromNetworkSyncData(networkSyncData);
|
||||
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||
|
||||
const behaviorSpecificProps = networkSyncData.props;
|
||||
this._hasPressedForwardKey = behaviorSpecificProps.upk;
|
||||
@@ -311,9 +341,15 @@ namespace gdjs {
|
||||
this._engineTorqueMax = behaviorSpecificProps.etm;
|
||||
this._engineSpeedMax = behaviorSpecificProps.esm;
|
||||
this._engineInertia = behaviorSpecificProps.ei;
|
||||
if (this._vehicleController) {
|
||||
this._vehicleController
|
||||
.GetEngine()
|
||||
.SetCurrentRPM(behaviorSpecificProps.es);
|
||||
}
|
||||
|
||||
// When the object is synchronized from the network, the inputs must not be cleared.
|
||||
this._dontClearInputsBetweenFrames = true;
|
||||
// When the object is synchronized from the network, the inputs must not be cleared,
|
||||
// except if asked specifically.
|
||||
this._clearInputsBetweenFrames = !!options.clearInputs;
|
||||
}
|
||||
|
||||
_getPhysicsPosition(result: Jolt.RVec3): Jolt.RVec3 {
|
||||
@@ -490,7 +526,7 @@ namespace gdjs {
|
||||
this._previousAcceleratorStickForce = this._acceleratorStickForce;
|
||||
this._previousSteeringStickForce = this._steeringStickForce;
|
||||
|
||||
if (!this._dontClearInputsBetweenFrames) {
|
||||
if (this._clearInputsBetweenFrames) {
|
||||
this._hasPressedForwardKey = false;
|
||||
this._hasPressedBackwardKey = false;
|
||||
this._hasPressedRightKey = false;
|
||||
|
@@ -2,11 +2,24 @@
|
||||
|
||||
namespace gdjs {
|
||||
interface PhysicsCharacter3DNetworkSyncDataType {
|
||||
sma: float;
|
||||
shm: float;
|
||||
grav: float;
|
||||
mfs: float;
|
||||
facc: float;
|
||||
fdec: float;
|
||||
fsm: float;
|
||||
sacc: float;
|
||||
sdec: float;
|
||||
ssm: float;
|
||||
jumpspeed: float;
|
||||
jumpsustime: float;
|
||||
sbpa: boolean;
|
||||
fwa: float;
|
||||
fws: float;
|
||||
sws: float;
|
||||
fs: float;
|
||||
js: float;
|
||||
cfs: float;
|
||||
cjs: float;
|
||||
cj: boolean;
|
||||
lek: boolean;
|
||||
rik: boolean;
|
||||
@@ -102,7 +115,7 @@ namespace gdjs {
|
||||
// This is useful when the object is synchronized by an external source
|
||||
// like in a multiplayer game, and we want to be able to predict the
|
||||
// movement of the object, even if the inputs are not updated every frame.
|
||||
private _dontClearInputsBetweenFrames: boolean = false;
|
||||
private _clearInputsBetweenFrames: boolean = true;
|
||||
|
||||
/**
|
||||
* A very small value compare to 1 pixel, yet very huge compare to rounding errors.
|
||||
@@ -207,10 +220,35 @@ namespace gdjs {
|
||||
this._isHookedToPhysicsStep = true;
|
||||
}
|
||||
|
||||
// Destroy the body before switching the bodyUpdater,
|
||||
// to ensure the body of the previous bodyUpdater is not left alive.
|
||||
// (would be a memory leak and would create a phantom body in the physics world)
|
||||
// But transfer the linear and angular velocity to the new body,
|
||||
// so the body doesn't stop when it is recreated.
|
||||
let previousBodyData = {
|
||||
linearVelocityX: 0,
|
||||
linearVelocityY: 0,
|
||||
linearVelocityZ: 0,
|
||||
angularVelocityX: 0,
|
||||
angularVelocityY: 0,
|
||||
angularVelocityZ: 0,
|
||||
};
|
||||
if (behavior._body) {
|
||||
const linearVelocity = behavior._body.GetLinearVelocity();
|
||||
previousBodyData.linearVelocityX = linearVelocity.GetX();
|
||||
previousBodyData.linearVelocityY = linearVelocity.GetY();
|
||||
previousBodyData.linearVelocityZ = linearVelocity.GetZ();
|
||||
const angularVelocity = behavior._body.GetAngularVelocity();
|
||||
previousBodyData.angularVelocityX = angularVelocity.GetX();
|
||||
previousBodyData.angularVelocityY = angularVelocity.GetY();
|
||||
previousBodyData.angularVelocityZ = angularVelocity.GetZ();
|
||||
behavior.bodyUpdater.destroyBody();
|
||||
}
|
||||
|
||||
behavior.bodyUpdater =
|
||||
new gdjs.PhysicsCharacter3DRuntimeBehavior.CharacterBodyUpdater(this);
|
||||
behavior.collisionChecker = this.collisionChecker;
|
||||
behavior.recreateBody();
|
||||
behavior.recreateBody(previousBodyData);
|
||||
|
||||
// Always begin in the direction of the object.
|
||||
this._forwardAngle = this.owner.getAngle();
|
||||
@@ -277,19 +315,34 @@ namespace gdjs {
|
||||
return true;
|
||||
}
|
||||
|
||||
override getNetworkSyncData(): PhysicsCharacter3DNetworkSyncData {
|
||||
override getNetworkSyncData(
|
||||
options: GetNetworkSyncDataOptions
|
||||
): PhysicsCharacter3DNetworkSyncData {
|
||||
// This method is called, so we are synchronizing this object.
|
||||
// Let's clear the inputs between frames as we control it.
|
||||
this._dontClearInputsBetweenFrames = false;
|
||||
this._clearInputsBetweenFrames = true;
|
||||
|
||||
return {
|
||||
...super.getNetworkSyncData(),
|
||||
...super.getNetworkSyncData(options),
|
||||
props: {
|
||||
sma: this._slopeMaxAngle,
|
||||
shm: this._stairHeightMax,
|
||||
grav: this._gravity,
|
||||
mfs: this._maxFallingSpeed,
|
||||
facc: this._forwardAcceleration,
|
||||
fdec: this._forwardDeceleration,
|
||||
fsm: this._forwardSpeedMax,
|
||||
sacc: this._sidewaysAcceleration,
|
||||
sdec: this._sidewaysDeceleration,
|
||||
ssm: this._sidewaysSpeedMax,
|
||||
jumpspeed: this._jumpSpeed,
|
||||
jumpsustime: this._jumpSustainTime,
|
||||
fwa: this._forwardAngle,
|
||||
sbpa: this._shouldBindObjectAndForwardAngle,
|
||||
fws: this._currentForwardSpeed,
|
||||
sws: this._currentSidewaysSpeed,
|
||||
fs: this._currentFallSpeed,
|
||||
js: this._currentJumpSpeed,
|
||||
cfs: this._currentFallSpeed,
|
||||
cjs: this._currentJumpSpeed,
|
||||
cj: this._canJump,
|
||||
lek: this._wasLeftKeyPressed,
|
||||
rik: this._wasRightKeyPressed,
|
||||
@@ -306,16 +359,30 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
override updateFromNetworkSyncData(
|
||||
networkSyncData: PhysicsCharacter3DNetworkSyncData
|
||||
networkSyncData: PhysicsCharacter3DNetworkSyncData,
|
||||
options: UpdateFromNetworkSyncDataOptions
|
||||
) {
|
||||
super.updateFromNetworkSyncData(networkSyncData);
|
||||
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||
|
||||
const behaviorSpecificProps = networkSyncData.props;
|
||||
this._slopeMaxAngle = behaviorSpecificProps.sma;
|
||||
this._stairHeightMax = behaviorSpecificProps.shm;
|
||||
this._gravity = behaviorSpecificProps.grav;
|
||||
this._maxFallingSpeed = behaviorSpecificProps.mfs;
|
||||
this._forwardAcceleration = behaviorSpecificProps.facc;
|
||||
this._forwardDeceleration = behaviorSpecificProps.fdec;
|
||||
this._forwardSpeedMax = behaviorSpecificProps.fsm;
|
||||
this._sidewaysAcceleration = behaviorSpecificProps.sacc;
|
||||
this._sidewaysDeceleration = behaviorSpecificProps.sdec;
|
||||
this._sidewaysSpeedMax = behaviorSpecificProps.ssm;
|
||||
this._jumpSpeed = behaviorSpecificProps.jumpspeed;
|
||||
this._jumpSustainTime = behaviorSpecificProps.jumpsustime;
|
||||
this._forwardAngle = behaviorSpecificProps.fwa;
|
||||
this._shouldBindObjectAndForwardAngle = behaviorSpecificProps.sbpa;
|
||||
this._currentForwardSpeed = behaviorSpecificProps.fws;
|
||||
this._currentSidewaysSpeed = behaviorSpecificProps.sws;
|
||||
this._currentFallSpeed = behaviorSpecificProps.fs;
|
||||
this._currentJumpSpeed = behaviorSpecificProps.js;
|
||||
this._currentFallSpeed = behaviorSpecificProps.cfs;
|
||||
this._currentJumpSpeed = behaviorSpecificProps.cjs;
|
||||
this._canJump = behaviorSpecificProps.cj;
|
||||
this._hasPressedForwardKey = behaviorSpecificProps.upk;
|
||||
this._hasPressedBackwardKey = behaviorSpecificProps.dok;
|
||||
@@ -328,8 +395,8 @@ namespace gdjs {
|
||||
this._timeSinceCurrentJumpStart = behaviorSpecificProps.tscjs;
|
||||
this._jumpKeyHeldSinceJumpStart = behaviorSpecificProps.jkhsjs;
|
||||
|
||||
// When the object is synchronized from the network, the inputs must not be cleared.
|
||||
this._dontClearInputsBetweenFrames = true;
|
||||
// Clear user inputs between frames only if requested.
|
||||
this._clearInputsBetweenFrames = !!options.clearInputs;
|
||||
}
|
||||
|
||||
_getPhysicsPosition(result: Jolt.RVec3): Jolt.RVec3 {
|
||||
@@ -650,7 +717,7 @@ namespace gdjs {
|
||||
this._wasJumpKeyPressed = this._hasPressedJumpKey;
|
||||
this._wasStickUsed = this._hasUsedStick;
|
||||
|
||||
if (!this._dontClearInputsBetweenFrames) {
|
||||
if (this._clearInputsBetweenFrames) {
|
||||
this._hasPressedForwardKey = false;
|
||||
this._hasPressedBackwardKey = false;
|
||||
this._hasPressedRightKey = false;
|
||||
|
@@ -37,7 +37,8 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
"res/physics-deprecated32.png",
|
||||
"PhysicsBehavior",
|
||||
std::make_shared<PhysicsBehavior>(),
|
||||
std::make_shared<ScenePhysicsDatas>());
|
||||
std::make_shared<ScenePhysicsDatas>())
|
||||
.MarkAsIrrelevantForChildObjects();
|
||||
|
||||
aut.AddAction("SetStatic",
|
||||
("Make the object static"),
|
||||
|
@@ -147,7 +147,7 @@ namespace gdjs {
|
||||
// This is useful when the object is synchronized by an external source
|
||||
// like in a multiplayer game, and we want to be able to predict the
|
||||
// movement of the object, even if the inputs are not updated every frame.
|
||||
_dontClearInputsBetweenFrames: boolean = false;
|
||||
private _clearInputsBetweenFrames: boolean = true;
|
||||
// This is useful when the object is synchronized over the network,
|
||||
// object is controlled by the network and we want to ensure the current player
|
||||
// cannot control it.
|
||||
@@ -227,14 +227,16 @@ namespace gdjs {
|
||||
this._state = this._falling;
|
||||
}
|
||||
|
||||
getNetworkSyncData(): PlatformerObjectNetworkSyncData {
|
||||
getNetworkSyncData(
|
||||
syncOptions: GetNetworkSyncDataOptions
|
||||
): PlatformerObjectNetworkSyncData {
|
||||
// This method is called, so we are synchronizing this object.
|
||||
// Let's clear the inputs between frames as we control it.
|
||||
this._dontClearInputsBetweenFrames = false;
|
||||
this._clearInputsBetweenFrames = true;
|
||||
this._ignoreDefaultControlsAsSyncedByNetwork = false;
|
||||
|
||||
return {
|
||||
...super.getNetworkSyncData(),
|
||||
...super.getNetworkSyncData(syncOptions),
|
||||
props: {
|
||||
cs: this._currentSpeed,
|
||||
|
||||
@@ -263,11 +265,52 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
updateFromNetworkSyncData(
|
||||
networkSyncData: PlatformerObjectNetworkSyncData
|
||||
networkSyncData: PlatformerObjectNetworkSyncData,
|
||||
options: UpdateFromNetworkSyncDataOptions
|
||||
) {
|
||||
super.updateFromNetworkSyncData(networkSyncData);
|
||||
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||
|
||||
const behaviorSpecificProps = networkSyncData.props;
|
||||
|
||||
switch (behaviorSpecificProps.sn) {
|
||||
case 'Falling':
|
||||
if (behaviorSpecificProps.sn !== this._state.toString()) {
|
||||
this._setFalling();
|
||||
}
|
||||
this._falling.updateFromNetworkSyncData(behaviorSpecificProps.ssd);
|
||||
break;
|
||||
case 'OnFloor':
|
||||
// Let it handle automatically as we don't know which platform to land on.
|
||||
// @ts-ignore - we assume it's OnFloorStateNetworkSyncData
|
||||
this._onFloor.updateFromNetworkSyncData(behaviorSpecificProps.ssd);
|
||||
break;
|
||||
case 'Jumping':
|
||||
if (behaviorSpecificProps.sn !== this._state.toString()) {
|
||||
this._setJumping();
|
||||
}
|
||||
// @ts-ignore - we assume it's JumpingStateNetworkSyncData
|
||||
this._jumping.updateFromNetworkSyncData(behaviorSpecificProps.ssd);
|
||||
break;
|
||||
case 'GrabbingPlatform':
|
||||
// Let it handle automatically as we don't know which platform to grab.
|
||||
this._grabbingPlatform.updateFromNetworkSyncData(
|
||||
// @ts-ignore - we assume it's GrabbingPlatformStateNetworkSyncData
|
||||
behaviorSpecificProps.ssd
|
||||
);
|
||||
break;
|
||||
case 'OnLadder':
|
||||
if (behaviorSpecificProps.sn !== this._state.toString()) {
|
||||
this._setOnLadder();
|
||||
}
|
||||
this._onLadder.updateFromNetworkSyncData(behaviorSpecificProps.ssd);
|
||||
break;
|
||||
default:
|
||||
console.error(
|
||||
'Unknown state name: ' + behaviorSpecificProps.sn + '.'
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
if (behaviorSpecificProps.cs !== this._currentSpeed) {
|
||||
this._currentSpeed = behaviorSpecificProps.cs;
|
||||
}
|
||||
@@ -317,39 +360,10 @@ namespace gdjs {
|
||||
this._jumpKeyHeldSinceJumpStart = behaviorSpecificProps.jkhsjs;
|
||||
}
|
||||
|
||||
if (behaviorSpecificProps.sn !== this._state.toString()) {
|
||||
switch (behaviorSpecificProps.sn) {
|
||||
case 'Falling':
|
||||
this._setFalling();
|
||||
break;
|
||||
case 'OnFloor':
|
||||
// Let it handle automatically as we don't know which platform to land on.
|
||||
break;
|
||||
case 'Jumping':
|
||||
this._setJumping();
|
||||
break;
|
||||
case 'GrabbingPlatform':
|
||||
// Let it handle automatically as we don't know which platform to grab.
|
||||
break;
|
||||
case 'OnLadder':
|
||||
this._setOnLadder();
|
||||
break;
|
||||
default:
|
||||
console.error(
|
||||
'Unknown state name: ' + behaviorSpecificProps.sn + '.'
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (behaviorSpecificProps.sn === this._state.toString()) {
|
||||
this._state.updateFromNetworkSyncData(behaviorSpecificProps.ssd);
|
||||
}
|
||||
|
||||
// When the object is synchronized from the network, the inputs must not be cleared.
|
||||
this._dontClearInputsBetweenFrames = true;
|
||||
// Clear user inputs between frames only if requested.
|
||||
this._clearInputsBetweenFrames = !!options.clearInputs;
|
||||
// And we are not using the default controls.
|
||||
this._ignoreDefaultControlsAsSyncedByNetwork = true;
|
||||
this._ignoreDefaultControlsAsSyncedByNetwork = !options.keepControl;
|
||||
}
|
||||
|
||||
updateFromBehaviorData(oldBehaviorData, newBehaviorData): boolean {
|
||||
@@ -545,8 +559,9 @@ namespace gdjs {
|
||||
this._wasJumpKeyPressed = this._jumpKey;
|
||||
this._wasReleasePlatformKeyPressed = this._releasePlatformKey;
|
||||
this._wasReleaseLadderKeyPressed = this._releaseLadderKey;
|
||||
|
||||
//4) Do not forget to reset pressed keys
|
||||
if (!this._dontClearInputsBetweenFrames) {
|
||||
if (this._clearInputsBetweenFrames) {
|
||||
// Reset the keys only if the inputs are not supposed to survive between frames.
|
||||
// (Most of the time, except if this object is synchronized by an external source)
|
||||
this._leftKey = false;
|
||||
|
@@ -157,6 +157,57 @@ namespace gdjs {
|
||||
return true;
|
||||
}
|
||||
|
||||
trackChangesAndUpdateManagerIfNeeded() {
|
||||
if (!this.activated() && this._registeredInManager) {
|
||||
this._manager.removePlatform(this);
|
||||
this._registeredInManager = false;
|
||||
} else {
|
||||
if (this.activated() && !this._registeredInManager) {
|
||||
this._manager.addPlatform(this);
|
||||
this._registeredInManager = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
this._oldX !== this.owner.getX() ||
|
||||
this._oldY !== this.owner.getY() ||
|
||||
this._oldWidth !== this.owner.getWidth() ||
|
||||
this._oldHeight !== this.owner.getHeight() ||
|
||||
this._oldAngle !== this.owner.getAngle()
|
||||
) {
|
||||
if (this._registeredInManager) {
|
||||
this._manager.removePlatform(this);
|
||||
this._manager.addPlatform(this);
|
||||
}
|
||||
this._oldX = this.owner.getX();
|
||||
this._oldY = this.owner.getY();
|
||||
this._oldWidth = this.owner.getWidth();
|
||||
this._oldHeight = this.owner.getHeight();
|
||||
this._oldAngle = this.owner.getAngle();
|
||||
}
|
||||
}
|
||||
|
||||
getNetworkSyncData(
|
||||
syncOptions: GetNetworkSyncDataOptions
|
||||
): BehaviorNetworkSyncData {
|
||||
return super.getNetworkSyncData(syncOptions);
|
||||
}
|
||||
|
||||
updateFromNetworkSyncData(
|
||||
networkSyncData: BehaviorNetworkSyncData,
|
||||
options: UpdateFromNetworkSyncDataOptions
|
||||
): void {
|
||||
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||
|
||||
this.trackChangesAndUpdateManagerIfNeeded();
|
||||
}
|
||||
|
||||
onCreated(): void {
|
||||
// Register it right away if activated,
|
||||
// so it can be used by platformer objects in that same frame.
|
||||
this.trackChangesAndUpdateManagerIfNeeded();
|
||||
}
|
||||
|
||||
onDestroy() {
|
||||
if (this._manager && this._registeredInManager) {
|
||||
this._manager.removePlatform(this);
|
||||
@@ -175,34 +226,7 @@ namespace gdjs {
|
||||
}*/
|
||||
|
||||
//Make sure the platform is or is not in the platforms manager.
|
||||
if (!this.activated() && this._registeredInManager) {
|
||||
this._manager.removePlatform(this);
|
||||
this._registeredInManager = false;
|
||||
} else {
|
||||
if (this.activated() && !this._registeredInManager) {
|
||||
this._manager.addPlatform(this);
|
||||
this._registeredInManager = true;
|
||||
}
|
||||
}
|
||||
|
||||
//Track changes in size or position
|
||||
if (
|
||||
this._oldX !== this.owner.getX() ||
|
||||
this._oldY !== this.owner.getY() ||
|
||||
this._oldWidth !== this.owner.getWidth() ||
|
||||
this._oldHeight !== this.owner.getHeight() ||
|
||||
this._oldAngle !== this.owner.getAngle()
|
||||
) {
|
||||
if (this._registeredInManager) {
|
||||
this._manager.removePlatform(this);
|
||||
this._manager.addPlatform(this);
|
||||
}
|
||||
this._oldX = this.owner.getX();
|
||||
this._oldY = this.owner.getY();
|
||||
this._oldWidth = this.owner.getWidth();
|
||||
this._oldHeight = this.owner.getHeight();
|
||||
this._oldAngle = this.owner.getAngle();
|
||||
}
|
||||
this.trackChangesAndUpdateManagerIfNeeded();
|
||||
}
|
||||
|
||||
doStepPostEvents(instanceContainer: gdjs.RuntimeInstanceContainer) {}
|
||||
|
@@ -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()
|
||||
|
@@ -20,6 +20,8 @@ namespace gdjs {
|
||||
|
||||
_antialiasingFilter: null | PIXI.Filter = null;
|
||||
|
||||
_placeholder: PIXI.Sprite | null = null;
|
||||
|
||||
private static readonly _positionForTransformation: PIXI.IPointData = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
@@ -405,6 +407,25 @@ namespace gdjs {
|
||||
|
||||
updatePreRender(): void {
|
||||
this.updatePositionIfNeeded();
|
||||
|
||||
const game = this._object.getRuntimeScene().getGame();
|
||||
if (
|
||||
game.isInGameEdition() &&
|
||||
this._graphics.geometry.graphicsData.length === 0
|
||||
) {
|
||||
if (!this._placeholder) {
|
||||
console.log(game.getGameData().resources.resources);
|
||||
const texture = game
|
||||
.getImageManager()
|
||||
.getPIXITexture('InGameEditor-ShapePainterIcon');
|
||||
this._placeholder = new PIXI.Sprite(texture);
|
||||
}
|
||||
this._graphics.addChild(this._placeholder);
|
||||
} else if (this._placeholder) {
|
||||
this._placeholder.removeFromParent();
|
||||
this._placeholder.destroy();
|
||||
this._placeholder = null;
|
||||
}
|
||||
}
|
||||
|
||||
updatePositionX(): void {
|
||||
|
@@ -37,6 +37,24 @@ namespace gdjs {
|
||||
|
||||
export type ShapePainterObjectData = ObjectData & ShapePainterObjectDataType;
|
||||
|
||||
type ShapePainterNetworkSyncDataType = {
|
||||
cbf: boolean; // clearBetweenFrames
|
||||
aa: Antialiasing; // antialiasing
|
||||
ac: boolean; // absoluteCoordinates
|
||||
fc: integer; // fillColor
|
||||
oc: integer; // outlineColor
|
||||
os: float; // outlineSize
|
||||
fo: float; // fillOpacity
|
||||
oo: float; // outlineOpacity
|
||||
scaleX: number;
|
||||
scaleY: number;
|
||||
ifx: boolean; // isFlippedX
|
||||
ify: boolean; // isFlippedY
|
||||
};
|
||||
|
||||
export type ShapePainterNetworkSyncData = ObjectNetworkSyncData &
|
||||
ShapePainterNetworkSyncDataType;
|
||||
|
||||
/**
|
||||
* The ShapePainterRuntimeObject allows to draw graphics shapes on screen.
|
||||
*/
|
||||
@@ -191,17 +209,77 @@ namespace gdjs {
|
||||
return true;
|
||||
}
|
||||
|
||||
getNetworkSyncData(
|
||||
syncOptions: GetNetworkSyncDataOptions
|
||||
): ShapePainterNetworkSyncData {
|
||||
return {
|
||||
...super.getNetworkSyncData(syncOptions),
|
||||
cbf: this._clearBetweenFrames,
|
||||
aa: this._antialiasing,
|
||||
ac: this._useAbsoluteCoordinates,
|
||||
fc: this._fillColor,
|
||||
oc: this._outlineColor,
|
||||
os: this._outlineSize,
|
||||
fo: this._fillOpacity,
|
||||
oo: this._outlineOpacity,
|
||||
scaleX: this.getScaleX(),
|
||||
scaleY: this.getScaleY(),
|
||||
ifx: this._flippedX,
|
||||
ify: this._flippedY,
|
||||
};
|
||||
}
|
||||
|
||||
updateFromNetworkSyncData(
|
||||
networkSyncData: ShapePainterNetworkSyncData,
|
||||
options: UpdateFromNetworkSyncDataOptions
|
||||
): void {
|
||||
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||
|
||||
if (networkSyncData.cbf !== undefined) {
|
||||
this._clearBetweenFrames = networkSyncData.cbf;
|
||||
}
|
||||
if (networkSyncData.aa !== undefined) {
|
||||
this.setAntialiasing(networkSyncData.aa);
|
||||
}
|
||||
if (networkSyncData.ac !== undefined) {
|
||||
this.setCoordinatesRelative(!networkSyncData.ac);
|
||||
}
|
||||
if (networkSyncData.fc !== undefined) {
|
||||
this._fillColor = networkSyncData.fc;
|
||||
}
|
||||
if (networkSyncData.oc !== undefined) {
|
||||
this._outlineColor = networkSyncData.oc;
|
||||
}
|
||||
if (networkSyncData.os !== undefined) {
|
||||
this.setOutlineSize(networkSyncData.os);
|
||||
}
|
||||
if (networkSyncData.fo !== undefined) {
|
||||
this.setFillOpacity(networkSyncData.fo);
|
||||
}
|
||||
if (networkSyncData.oo !== undefined) {
|
||||
this.setOutlineOpacity(networkSyncData.oo);
|
||||
}
|
||||
if (networkSyncData.scaleX !== undefined) {
|
||||
this.setScaleX(networkSyncData.scaleX);
|
||||
}
|
||||
if (networkSyncData.scaleY !== undefined) {
|
||||
this.setScaleY(networkSyncData.scaleY);
|
||||
}
|
||||
if (networkSyncData.ifx !== undefined) {
|
||||
this.flipX(networkSyncData.ifx);
|
||||
}
|
||||
if (networkSyncData.ify !== undefined) {
|
||||
this.flipY(networkSyncData.ify);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the extra parameters that could be set for an instance.
|
||||
* @param initialInstanceData The extra parameters
|
||||
*/
|
||||
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) {
|
||||
|
@@ -50,6 +50,19 @@ describe('gdjs.ShapePainterRuntimeObject (using a PixiJS RuntimeGame with assets
|
||||
instances: [],
|
||||
variables: [],
|
||||
usedResources: [],
|
||||
uiSettings: {
|
||||
grid: false,
|
||||
gridType: 'rectangular',
|
||||
gridWidth: 10,
|
||||
gridHeight: 10,
|
||||
gridDepth: 10,
|
||||
gridOffsetX: 0,
|
||||
gridOffsetY: 0,
|
||||
gridOffsetZ: 0,
|
||||
gridColor: 0,
|
||||
gridAlpha: 1,
|
||||
snap: false,
|
||||
},
|
||||
},
|
||||
usedExtensionsWithVariablesData: [],
|
||||
});
|
||||
|
190
Extensions/SaveState/JsExtension.js
Normal file
190
Extensions/SaveState/JsExtension.js
Normal file
@@ -0,0 +1,190 @@
|
||||
//@ts-check
|
||||
/// <reference path="../JsExtensionTypes.d.ts" />
|
||||
/**
|
||||
* This is a declaration of an extension for GDevelop 5.
|
||||
*
|
||||
* ℹ️ Changes in this file are watched and automatically imported if the editor
|
||||
* is running. You can also manually run `node import-GDJS-Runtime.js` (in newIDE/app/scripts).
|
||||
*
|
||||
* The file must be named "JsExtension.js", otherwise GDevelop won't load it.
|
||||
* ⚠️ If you make a change and the extension is not loaded, open the developer console
|
||||
* and search for any errors.
|
||||
*
|
||||
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
|
||||
*/
|
||||
|
||||
/** @type {ExtensionModule} */
|
||||
module.exports = {
|
||||
createExtension: function (_, gd) {
|
||||
const extension = new gd.PlatformExtension();
|
||||
extension
|
||||
.setExtensionInformation(
|
||||
'SaveState',
|
||||
_('Save State (experimental)'),
|
||||
_('Allows to save and load the full state of a game.'),
|
||||
'Neyl Mahfouf',
|
||||
'Open source (MIT License)'
|
||||
)
|
||||
.setExtensionHelpPath('/all-features/save-state')
|
||||
.setCategory('Game mechanic')
|
||||
.addInstructionOrExpressionGroupMetadata(_('Save State (experimental)'))
|
||||
.setIcon('res/actions/saveDown.svg');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'SaveGameSnapshotToVariable',
|
||||
_('Save game to a variable'),
|
||||
_('Takes a snapshot of the game and save it to a variable.'),
|
||||
_('Save the game in variable _PARAM1_'),
|
||||
_('Save'),
|
||||
'res/actions/saveDown.svg',
|
||||
'res/actions/saveDown.svg'
|
||||
)
|
||||
.addCodeOnlyParameter('currentScene', '')
|
||||
.addParameter('variable', _('Variable to store the save to'), '', false)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/SaveState/savestatetools.js')
|
||||
.setFunctionName('gdjs.saveState.saveVariableGameSnapshot');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'SaveGameSnapshotToStorage',
|
||||
_('Save game to device storage'),
|
||||
_('Takes a snapshot of the game and save it to device storage.'),
|
||||
_('Save the game to device storage under key _PARAM1_'),
|
||||
_('Save'),
|
||||
'res/actions/saveDown.svg',
|
||||
'res/actions/saveDown.svg'
|
||||
)
|
||||
.addCodeOnlyParameter('currentScene', '')
|
||||
.addParameter('string', _('Storage key to save to'), '', false)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/SaveState/savestatetools.js')
|
||||
.setFunctionName('gdjs.saveState.saveStorageGameSnapshot');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'LoadGameSnapshotFromVariable',
|
||||
_('Load game from variable'),
|
||||
_('Load game from a variable save snapshot.'),
|
||||
_('Load the game from variable _PARAM0_'),
|
||||
_('Load'),
|
||||
'res/actions/saveUp.svg',
|
||||
'res/actions/saveUp.svg'
|
||||
)
|
||||
.addParameter('variable', _('Variable to load the game from'), '', false)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/SaveState/savestatetools.js')
|
||||
.setFunctionName('gdjs.saveState.loadGameFromVariableSnapshot');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'LoadGameSnapshotFromStorage',
|
||||
_('Load game from device storage'),
|
||||
_('Load game from device storage save snapshot.'),
|
||||
_('Load the game from device storage under key _PARAM0_.'),
|
||||
_('Load'),
|
||||
'res/actions/saveUp.svg',
|
||||
'res/actions/saveUp.svg'
|
||||
)
|
||||
.addParameter('string', _('Storage key to load the game from'), '', false)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/SaveState/savestatetools.js')
|
||||
.setFunctionName('gdjs.saveState.loadGameFromStorageSnapshot');
|
||||
|
||||
extension
|
||||
.addExpressionAndCondition(
|
||||
'number',
|
||||
'TimeSinceLastSave',
|
||||
_('Time since last save'),
|
||||
_(
|
||||
'Time since the last save, in seconds. Returns -1 if no save happened, and a positive number otherwise.'
|
||||
),
|
||||
_('Time since the last save'),
|
||||
'',
|
||||
'res/actions/saveDown.svg'
|
||||
)
|
||||
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
|
||||
.setIncludeFile('Extensions/SaveState/savestatetools.js')
|
||||
.setFunctionName('gdjs.saveState.getSecondsSinceLastSave')
|
||||
.setGetter('gdjs.saveState.getSecondsSinceLastSave');
|
||||
|
||||
extension
|
||||
.addExpressionAndCondition(
|
||||
'number',
|
||||
'TimeSinceLastLoad',
|
||||
_('Time since last load'),
|
||||
_(
|
||||
'Time since the last load, in seconds. Returns -1 if no load happened, and a positive number otherwise.'
|
||||
),
|
||||
_('Time since the last load'),
|
||||
'',
|
||||
'res/actions/saveDown.svg'
|
||||
)
|
||||
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
|
||||
.setIncludeFile('Extensions/SaveState/savestatetools.js')
|
||||
.setFunctionName('gdjs.saveState.getSecondsSinceLastLoad')
|
||||
.setGetter('gdjs.saveState.getSecondsSinceLastLoad');
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'SaveJustSucceeded',
|
||||
_('Save just succeeded'),
|
||||
_('the save just succeeded'),
|
||||
_('the save just succeeded'),
|
||||
_('Save'),
|
||||
'res/actions/saveDown.svg',
|
||||
'res/actions/saveDown.svg'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/SaveState/savestatetools.js')
|
||||
.setFunctionName('gdjs.saveState.hasSaveJustSucceeded');
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'SaveJustFailed',
|
||||
_('Save just failed'),
|
||||
_('the save just failed'),
|
||||
_('the save just failed'),
|
||||
_('Save'),
|
||||
'res/actions/saveDown.svg',
|
||||
'res/actions/saveDown.svg'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/SaveState/savestatetools.js')
|
||||
.setFunctionName('gdjs.saveState.hasSaveJustFailed');
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'LoadJustSucceeded',
|
||||
_('Load just succeeded'),
|
||||
_('the load just succeeded'),
|
||||
_('the load just succeeded'),
|
||||
_('Load'),
|
||||
'res/actions/saveUp.svg',
|
||||
'res/actions/saveUp.svg'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/SaveState/savestatetools.js')
|
||||
.setFunctionName('gdjs.saveState.hasLoadJustSucceeded');
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'LoadJustFailed',
|
||||
_('Load just failed'),
|
||||
_('the load just failed'),
|
||||
_('the load just failed'),
|
||||
_('Load'),
|
||||
'res/actions/saveUp.svg',
|
||||
'res/actions/saveUp.svg'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/SaveState/savestatetools.js')
|
||||
.setFunctionName('gdjs.saveState.hasLoadJustFailed');
|
||||
|
||||
return extension;
|
||||
},
|
||||
runExtensionSanityTests: function (gd, extension) {
|
||||
return [];
|
||||
},
|
||||
};
|
360
Extensions/SaveState/savestatetools.ts
Normal file
360
Extensions/SaveState/savestatetools.ts
Normal file
@@ -0,0 +1,360 @@
|
||||
namespace gdjs {
|
||||
const logger = new gdjs.Logger('Save state');
|
||||
export type LoadRequestOptions = {
|
||||
loadStorageName?: string;
|
||||
loadVariable?: gdjs.Variable;
|
||||
};
|
||||
|
||||
export namespace saveState {
|
||||
export const getIndexedDbDatabaseName = () => {
|
||||
const gameId = gdjs.projectData.properties.projectUuid;
|
||||
return `gdevelop-game-${gameId}`;
|
||||
};
|
||||
export const getIndexedDbObjectStore = () => {
|
||||
return `game-saves`;
|
||||
};
|
||||
export const getIndexedDbStorageKey = (key: string) => {
|
||||
return `save-${key}`;
|
||||
};
|
||||
|
||||
const getNetworkSyncOptions: GetNetworkSyncDataOptions = {
|
||||
syncObjectIdentifiers: true,
|
||||
syncAllVariables: true,
|
||||
syncAllBehaviors: true,
|
||||
syncSceneTimers: true,
|
||||
syncOnceTriggers: true,
|
||||
syncSounds: true,
|
||||
syncTweens: true,
|
||||
syncLayers: true,
|
||||
syncAsyncTasks: true,
|
||||
syncSceneVisualProps: true,
|
||||
syncFullTileMaps: true,
|
||||
};
|
||||
const updateFromNetworkSyncDataOptions: UpdateFromNetworkSyncDataOptions = {
|
||||
clearSceneStack: true,
|
||||
preventInitialInstancesCreation: true,
|
||||
preventSoundsStoppingOnStartup: true,
|
||||
clearInputs: true,
|
||||
keepControl: true,
|
||||
ignoreVariableOwnership: true,
|
||||
};
|
||||
|
||||
let lastSaveTime: number | null = null;
|
||||
let lastLoadTime: number | null = null;
|
||||
let saveJustSucceeded: boolean = false;
|
||||
let saveJustFailed: boolean = false;
|
||||
let loadJustSucceeded: boolean = false;
|
||||
let loadJustFailed: boolean = false;
|
||||
|
||||
let loadRequestOptions: LoadRequestOptions | null = null;
|
||||
|
||||
export const getSecondsSinceLastSave = (): number => {
|
||||
if (!lastSaveTime) return -1;
|
||||
return Math.floor((Date.now() - lastSaveTime) / 1000);
|
||||
};
|
||||
export const getSecondsSinceLastLoad = (): number => {
|
||||
if (!lastLoadTime) return -1;
|
||||
return Math.floor((Date.now() - lastLoadTime) / 1000);
|
||||
};
|
||||
export const hasSaveJustSucceeded = () => {
|
||||
return saveJustSucceeded;
|
||||
};
|
||||
export const hasLoadJustSucceeded = () => {
|
||||
return loadJustSucceeded;
|
||||
};
|
||||
export const hasSaveJustFailed = () => {
|
||||
return saveJustFailed;
|
||||
};
|
||||
export const hasLoadJustFailed = () => {
|
||||
return loadJustFailed;
|
||||
};
|
||||
export const markSaveJustSucceeded = () => {
|
||||
saveJustSucceeded = true;
|
||||
lastSaveTime = Date.now();
|
||||
};
|
||||
export const markLoadJustSucceeded = () => {
|
||||
loadJustSucceeded = true;
|
||||
lastLoadTime = Date.now();
|
||||
};
|
||||
export const markSaveJustFailed = () => {
|
||||
saveJustFailed = true;
|
||||
};
|
||||
export const markLoadJustFailed = () => {
|
||||
loadJustFailed = true;
|
||||
};
|
||||
|
||||
// Ensure that the condition "save/load just succeeded/failed" are valid only for one frame.
|
||||
gdjs.registerRuntimeScenePostEventsCallback(() => {
|
||||
saveJustSucceeded = false;
|
||||
saveJustFailed = false;
|
||||
loadJustSucceeded = false;
|
||||
loadJustFailed = false;
|
||||
});
|
||||
|
||||
gdjs.registerRuntimeScenePostEventsCallback(
|
||||
(runtimeScene: gdjs.RuntimeScene) => {
|
||||
loadGameSnapshotAtTheEndOfFrameIfAny(runtimeScene);
|
||||
}
|
||||
);
|
||||
|
||||
const getGameSaveState = (runtimeScene: RuntimeScene) => {
|
||||
const gameSaveState: GameSaveState = {
|
||||
gameNetworkSyncData: {},
|
||||
layoutNetworkSyncDatas: [],
|
||||
};
|
||||
|
||||
const gameData = runtimeScene
|
||||
.getGame()
|
||||
.getNetworkSyncData(getNetworkSyncOptions);
|
||||
const scenes = runtimeScene.getGame().getSceneStack().getAllScenes();
|
||||
gameSaveState.gameNetworkSyncData = gameData || {};
|
||||
|
||||
scenes.forEach((scene, index) => {
|
||||
gameSaveState.layoutNetworkSyncDatas[index] = {
|
||||
sceneData: {} as LayoutNetworkSyncData,
|
||||
objectDatas: {},
|
||||
};
|
||||
|
||||
// First collect all object sync data, as they may generate unique
|
||||
// identifiers like their networkId.
|
||||
const sceneRuntimeObjects = scene.getAdhocListOfAllInstances();
|
||||
for (const key in sceneRuntimeObjects) {
|
||||
if (sceneRuntimeObjects.hasOwnProperty(key)) {
|
||||
const object = sceneRuntimeObjects[key];
|
||||
const objectSyncData = object.getNetworkSyncData(
|
||||
getNetworkSyncOptions
|
||||
);
|
||||
gameSaveState.layoutNetworkSyncDatas[index].objectDatas[object.id] =
|
||||
objectSyncData;
|
||||
}
|
||||
}
|
||||
|
||||
// Collect all scene data in the end.
|
||||
const sceneDatas = (scene.getNetworkSyncData(getNetworkSyncOptions) ||
|
||||
[]) as LayoutNetworkSyncData;
|
||||
|
||||
gameSaveState.layoutNetworkSyncDatas[index].sceneData = sceneDatas;
|
||||
});
|
||||
|
||||
return gameSaveState;
|
||||
};
|
||||
|
||||
export const saveVariableGameSnapshot = async function (
|
||||
currentScene: RuntimeScene,
|
||||
variable: gdjs.Variable
|
||||
) {
|
||||
try {
|
||||
const gameSaveState = getGameSaveState(currentScene);
|
||||
variable.fromJSObject(gameSaveState);
|
||||
markSaveJustSucceeded();
|
||||
} catch (error) {
|
||||
logger.error('Error saving to variable:', error);
|
||||
markSaveJustFailed();
|
||||
}
|
||||
};
|
||||
|
||||
export const saveStorageGameSnapshot = async function (
|
||||
currentScene: RuntimeScene,
|
||||
storageKey: string
|
||||
) {
|
||||
try {
|
||||
const gameSaveState = getGameSaveState(currentScene);
|
||||
await gdjs.indexedDb.saveToIndexedDB(
|
||||
getIndexedDbDatabaseName(),
|
||||
getIndexedDbObjectStore(),
|
||||
getIndexedDbStorageKey(storageKey),
|
||||
gameSaveState
|
||||
);
|
||||
markSaveJustSucceeded();
|
||||
} catch (error) {
|
||||
logger.error('Error saving to IndexedDB:', error);
|
||||
markSaveJustFailed();
|
||||
}
|
||||
};
|
||||
|
||||
export const loadGameFromVariableSnapshot = async function (
|
||||
variable: gdjs.Variable
|
||||
) {
|
||||
// The information is saved, so that the load can be done
|
||||
// at the end of the frame,
|
||||
// and avoid possible conflicts with running events.
|
||||
loadRequestOptions = {
|
||||
loadVariable: variable,
|
||||
};
|
||||
};
|
||||
|
||||
export const loadGameFromStorageSnapshot = async function (
|
||||
storageName: string
|
||||
) {
|
||||
// The information is saved, so that the load can be done
|
||||
// at the end of the frame,
|
||||
// and avoid possible conflicts with running events.
|
||||
loadRequestOptions = {
|
||||
loadStorageName: storageName,
|
||||
};
|
||||
};
|
||||
|
||||
const loadGameSnapshotAtTheEndOfFrameIfAny = function (
|
||||
runtimeScene: RuntimeScene
|
||||
) {
|
||||
if (!loadRequestOptions) return;
|
||||
|
||||
const optionsToApply = loadRequestOptions;
|
||||
// Reset it so we don't load it twice.
|
||||
loadRequestOptions = null;
|
||||
|
||||
if (optionsToApply.loadVariable) {
|
||||
const sceneVariables = runtimeScene.getVariables();
|
||||
const variablePathInScene =
|
||||
sceneVariables.getVariablePathInContainerByLoopingThroughAllVariables(
|
||||
optionsToApply.loadVariable
|
||||
);
|
||||
const gameVariables = runtimeScene.getGame().getVariables();
|
||||
const variablePathIngame =
|
||||
gameVariables.getVariablePathInContainerByLoopingThroughAllVariables(
|
||||
optionsToApply.loadVariable
|
||||
);
|
||||
const saveState =
|
||||
optionsToApply.loadVariable.toJSObject() as GameSaveState;
|
||||
|
||||
try {
|
||||
loadGameFromSave(runtimeScene, saveState, {
|
||||
variableToRehydrate: optionsToApply.loadVariable,
|
||||
variablePathInScene: variablePathInScene,
|
||||
variablePathInGame: variablePathIngame,
|
||||
});
|
||||
markLoadJustSucceeded();
|
||||
} catch (error) {
|
||||
logger.error('Error loading from variable:', error);
|
||||
markLoadJustFailed();
|
||||
}
|
||||
} else if (optionsToApply.loadStorageName) {
|
||||
gdjs.indexedDb
|
||||
.loadFromIndexedDB(
|
||||
getIndexedDbDatabaseName(),
|
||||
getIndexedDbObjectStore(),
|
||||
getIndexedDbStorageKey(optionsToApply.loadStorageName)
|
||||
)
|
||||
.then((jsonData) => {
|
||||
const saveState = jsonData as GameSaveState;
|
||||
loadGameFromSave(runtimeScene, saveState);
|
||||
markLoadJustSucceeded();
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.error('Error loading from IndexedDB:', error);
|
||||
markLoadJustFailed();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const loadGameFromSave = (
|
||||
runtimeScene: RuntimeScene,
|
||||
saveState: GameSaveState,
|
||||
saveOptions?: {
|
||||
variableToRehydrate: gdjs.Variable;
|
||||
variablePathInScene: string[] | null;
|
||||
variablePathInGame: string[] | null;
|
||||
}
|
||||
): void => {
|
||||
// Save the content of the save, as it will be erased after the load.
|
||||
const variableToRehydrateNetworkSyncData = saveOptions
|
||||
? saveOptions.variableToRehydrate.getNetworkSyncData(
|
||||
getNetworkSyncOptions
|
||||
)
|
||||
: null;
|
||||
|
||||
// First update the game, which will update the variables,
|
||||
// and set the scene stack to update when ready.
|
||||
const runtimeGame = runtimeScene.getGame();
|
||||
runtimeGame.updateFromNetworkSyncData(
|
||||
saveState.gameNetworkSyncData,
|
||||
updateFromNetworkSyncDataOptions
|
||||
);
|
||||
|
||||
// Apply the scene stack updates, as we are at the end of a frame,
|
||||
// we can safely do it.
|
||||
const sceneStack = runtimeGame.getSceneStack();
|
||||
sceneStack.applyUpdateFromNetworkSyncDataIfAny(
|
||||
updateFromNetworkSyncDataOptions
|
||||
);
|
||||
|
||||
// Then get all scenes, which we assume will be the expected ones
|
||||
// after the load has been done, so we can update them,
|
||||
// and create their objects.
|
||||
const runtimeScenes = sceneStack.getAllScenes();
|
||||
runtimeScenes.forEach((scene, index) => {
|
||||
const layoutSyncData = saveState.layoutNetworkSyncDatas[index];
|
||||
if (!layoutSyncData) return;
|
||||
|
||||
// Create objects first, so they are available for the scene update,
|
||||
// especially so that they have a networkId defined.
|
||||
const objectDatas = layoutSyncData.objectDatas;
|
||||
for (const id in objectDatas) {
|
||||
const objectNetworkSyncData = objectDatas[id];
|
||||
const objectName = objectNetworkSyncData.n;
|
||||
if (!objectName) {
|
||||
logger.warn('Tried to recreate an object without a name.');
|
||||
continue;
|
||||
}
|
||||
const object = scene.createObject(objectName);
|
||||
if (object) {
|
||||
object.updateFromNetworkSyncData(
|
||||
objectNetworkSyncData,
|
||||
updateFromNetworkSyncDataOptions
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Update the scene last.
|
||||
scene.updateFromNetworkSyncData(
|
||||
layoutSyncData.sceneData,
|
||||
updateFromNetworkSyncDataOptions
|
||||
);
|
||||
});
|
||||
|
||||
// Finally, if the save was done in a variable,
|
||||
// rehydrate the variable where the save was done,
|
||||
// as it has been erased by the load.
|
||||
if (saveOptions && variableToRehydrateNetworkSyncData) {
|
||||
const currentScene = sceneStack.getCurrentScene();
|
||||
if (!currentScene) return;
|
||||
const sceneVariables = currentScene.getVariables();
|
||||
const gameVariables = currentScene.getGame().getVariables();
|
||||
const { variablePathInScene, variablePathInGame } = saveOptions;
|
||||
|
||||
if (variablePathInScene && variablePathInScene.length > 0) {
|
||||
const variableName =
|
||||
variablePathInScene[variablePathInScene.length - 1];
|
||||
const variableInScene =
|
||||
sceneVariables.getVariableFromPath(variablePathInScene);
|
||||
if (variableInScene) {
|
||||
const variableNetworkSyncData: VariableNetworkSyncData = {
|
||||
name: variableName,
|
||||
...variableToRehydrateNetworkSyncData,
|
||||
};
|
||||
variableInScene.updateFromNetworkSyncData(
|
||||
variableNetworkSyncData,
|
||||
updateFromNetworkSyncDataOptions
|
||||
);
|
||||
}
|
||||
}
|
||||
if (variablePathInGame && variablePathInGame.length > 0) {
|
||||
const variableName =
|
||||
variablePathInGame[variablePathInGame.length - 1];
|
||||
const variableInGame =
|
||||
gameVariables.getVariableFromPath(variablePathInGame);
|
||||
if (variableInGame) {
|
||||
const variableNetworkSyncData: VariableNetworkSyncData = {
|
||||
name: variableName,
|
||||
...variableToRehydrateNetworkSyncData,
|
||||
};
|
||||
variableInGame.updateFromNetworkSyncData(
|
||||
variableNetworkSyncData,
|
||||
updateFromNetworkSyncDataOptions
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@@ -203,13 +203,17 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
unloadResource(resourceData: ResourceData): void {
|
||||
const loadedSpineAtlas = this._loadedSpineAtlases.get(resourceData);
|
||||
const loadedSpineAtlas = this._loadedSpineAtlases.getFromName(
|
||||
resourceData.name
|
||||
);
|
||||
if (loadedSpineAtlas) {
|
||||
loadedSpineAtlas.dispose();
|
||||
this._loadedSpineAtlases.delete(resourceData);
|
||||
}
|
||||
|
||||
const loadingSpineAtlas = this._loadingSpineAtlases.get(resourceData);
|
||||
const loadingSpineAtlas = this._loadingSpineAtlases.getFromName(
|
||||
resourceData.name
|
||||
);
|
||||
if (loadingSpineAtlas) {
|
||||
loadingSpineAtlas.then((atl) => atl.dispose());
|
||||
this._loadingSpineAtlases.delete(resourceData);
|
||||
|
@@ -53,6 +53,8 @@ namespace gdjs {
|
||||
|
||||
readonly spineResourceName: string;
|
||||
|
||||
static isHitBoxesUpdateDisabled = false;
|
||||
|
||||
/**
|
||||
* @param instanceContainer The container the object belongs to.
|
||||
* @param objectData The object data used to initialize the object
|
||||
@@ -74,6 +76,10 @@ namespace gdjs {
|
||||
this.setAnimationIndex(0);
|
||||
this._renderer.updateAnimation(0);
|
||||
|
||||
if (SpineRuntimeObject.isHitBoxesUpdateDisabled) {
|
||||
this.hitBoxes.length = 0;
|
||||
}
|
||||
|
||||
// *ALWAYS* call `this.onCreated()` at the very end of your object constructor.
|
||||
this.onCreated();
|
||||
}
|
||||
@@ -111,9 +117,11 @@ namespace gdjs {
|
||||
return true;
|
||||
}
|
||||
|
||||
getNetworkSyncData(): SpineNetworkSyncData {
|
||||
getNetworkSyncData(
|
||||
syncOptions: GetNetworkSyncDataOptions
|
||||
): SpineNetworkSyncData {
|
||||
return {
|
||||
...super.getNetworkSyncData(),
|
||||
...super.getNetworkSyncData(syncOptions),
|
||||
opa: this._opacity,
|
||||
scaX: this.getScaleX(),
|
||||
scaY: this.getScaleY(),
|
||||
@@ -127,8 +135,11 @@ namespace gdjs {
|
||||
};
|
||||
}
|
||||
|
||||
updateFromNetworkSyncData(syncData: SpineNetworkSyncData): void {
|
||||
super.updateFromNetworkSyncData(syncData);
|
||||
updateFromNetworkSyncData(
|
||||
syncData: SpineNetworkSyncData,
|
||||
options: UpdateFromNetworkSyncDataOptions
|
||||
): void {
|
||||
super.updateFromNetworkSyncData(syncData, options);
|
||||
|
||||
if (syncData.opa !== undefined && syncData.opa !== this._opacity) {
|
||||
this.setOpacity(syncData.opa);
|
||||
@@ -183,6 +194,14 @@ namespace gdjs {
|
||||
}
|
||||
}
|
||||
|
||||
updateHitBoxes(): void {
|
||||
if (SpineRuntimeObject.isHitBoxesUpdateDisabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
super.updateHitBoxes();
|
||||
}
|
||||
|
||||
extraInitializationFromInitialInstance(
|
||||
initialInstanceData: InstanceData
|
||||
): void {
|
||||
@@ -199,15 +218,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 {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user