mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
Compare commits
3875 Commits
qt_ui
...
develop-8.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4da4956df8 | ||
|
|
618159c1fa | ||
|
|
d8c662e7f8 | ||
|
|
12c41b57c9 | ||
|
|
85a27845bc | ||
|
|
e3f6347e16 | ||
|
|
fffe1b6e94 | ||
|
|
5a7a730e23 | ||
|
|
576f777320 | ||
|
|
87f7fe5307 | ||
|
|
e1434378a8 | ||
|
|
e03b0d99d8 | ||
|
|
e4eb3dec1b | ||
|
|
b365016496 | ||
|
|
c359b3f7fc | ||
|
|
302613069e | ||
|
|
5a22b62e3b | ||
|
|
001e7dfed9 | ||
|
|
cf985d3d37 | ||
|
|
1044a1f45f | ||
|
|
9fe31859d3 | ||
|
|
09417322e7 | ||
|
|
136a9b5f02 | ||
|
|
02f22d4930 | ||
|
|
ca133b9fd1 | ||
|
|
b1af6dfbe1 | ||
|
|
647d1f57f9 | ||
|
|
b250fe2f1e | ||
|
|
3be57bf6bb | ||
|
|
3c8d0b023e | ||
|
|
adceb3a224 | ||
|
|
ab02cd34c5 | ||
|
|
c74b603d81 | ||
|
|
5815401e73 | ||
|
|
f463fe50f2 | ||
|
|
f60bf62897 | ||
|
|
9d43eb8f03 | ||
|
|
8f0ca08b89 | ||
|
|
0534f66b30 | ||
|
|
1162e0aa29 | ||
|
|
36c4bb88be | ||
|
|
dc6624a159 | ||
|
|
8b55331326 | ||
|
|
33ca77e3d1 | ||
|
|
b92b01b245 | ||
|
|
b18b371904 | ||
|
|
9c7e16d121 | ||
|
|
87e869d963 | ||
|
|
4a059a4f8b | ||
|
|
674254e55b | ||
|
|
9fd0e06c05 | ||
|
|
ecaf84ea55 | ||
|
|
e4028cb013 | ||
|
|
c45ac50370 | ||
|
|
6640609caf | ||
|
|
e44b6b416b | ||
|
|
8a861d3da5 | ||
|
|
380d6551be | ||
|
|
4cb035b955 | ||
|
|
e50be9bbde | ||
|
|
ec49a10135 | ||
|
|
23e3630169 | ||
|
|
e20ab5fbc0 | ||
|
|
4fd2bb131b | ||
|
|
42a7102948 | ||
|
|
d271ff17c2 | ||
|
|
cb61dfccc4 | ||
|
|
56f93c76eb | ||
|
|
36cb3a386c | ||
|
|
c25e830e6c | ||
|
|
5d08990cd0 | ||
|
|
2a45cd8899 | ||
|
|
90b880ec3c | ||
|
|
5f0c570d65 | ||
|
|
ce102fcc50 | ||
|
|
30c792c15a | ||
|
|
2f45b856d6 | ||
|
|
31d2b756ab | ||
|
|
b5cf889c09 | ||
|
|
19958f91ca | ||
|
|
c775a898a4 | ||
|
|
535244f6f3 | ||
|
|
9d1d3bdcfa | ||
|
|
36eef2b1b9 | ||
|
|
7788425c5c | ||
|
|
ee0c21b3e5 | ||
|
|
54cd619f75 | ||
|
|
051940e23c | ||
|
|
4fbd7defa3 | ||
|
|
90bda9383d | ||
|
|
7798e2970c | ||
|
|
410c25b331 | ||
|
|
cff74525d6 | ||
|
|
8b7f107044 | ||
|
|
c365a0d739 | ||
|
|
1f4fd0fd04 | ||
|
|
4bb60cb500 | ||
|
|
fe96a415be | ||
|
|
6699289bf7 | ||
|
|
a85d3243fb | ||
|
|
7f2607cf08 | ||
|
|
e50ee976ed | ||
|
|
29ffb526f2 | ||
|
|
e024013093 | ||
|
|
257dabe4fa | ||
|
|
406fb61fa4 | ||
|
|
49dfa95c61 | ||
|
|
c80e5b259f | ||
|
|
64e2213f28 | ||
|
|
ced93afd49 | ||
|
|
f719a5ec34 | ||
|
|
6f4ac1dc39 | ||
|
|
f831c8efdd | ||
|
|
e3c6b03603 | ||
|
|
7a2e8279cd | ||
|
|
24e72475b4 | ||
|
|
f10350dac4 | ||
|
|
f068976749 | ||
|
|
4b4c45e90f | ||
|
|
527eac1f4a | ||
|
|
92c3087187 | ||
|
|
8dc3fca290 | ||
|
|
0d18b57074 | ||
|
|
b745e7c8ec | ||
|
|
800ca598ef | ||
|
|
212813e31d | ||
|
|
571fe21d57 | ||
|
|
cd952312b7 | ||
|
|
23982fdac6 | ||
|
|
8724b458a8 | ||
|
|
ef64899701 | ||
|
|
51e4dc5c22 | ||
|
|
ac5edeb936 | ||
|
|
f7364d04ed | ||
|
|
483bf73213 | ||
|
|
0b87f90d4f | ||
|
|
202fe1109b | ||
|
|
5a8863b07e | ||
|
|
889e1f5da2 | ||
|
|
418d78f99b | ||
|
|
a4b5bc198c | ||
|
|
fcb2f21d36 | ||
|
|
15abf3d6fe | ||
|
|
a8b7aca4fb | ||
|
|
799dbfa99c | ||
|
|
eb31a0f038 | ||
|
|
78e2da9196 | ||
|
|
ca96a232f0 | ||
|
|
03671bbfb0 | ||
|
|
97c4168d13 | ||
|
|
e0edfa68b1 | ||
|
|
97c238a4bb | ||
|
|
a6c5b03212 | ||
|
|
5b148a74aa | ||
|
|
33242048e7 | ||
|
|
4f7932ad8a | ||
|
|
8158cc7112 | ||
|
|
50d7a3e46f | ||
|
|
b6b9a22668 | ||
|
|
bd2ec12e0f | ||
|
|
752a90cddb | ||
|
|
004594639e | ||
|
|
6943adf6df | ||
|
|
5ad57d2878 | ||
|
|
acb2d01d92 | ||
|
|
15fa73a514 | ||
|
|
7f94b34277 | ||
|
|
4d2ed64a70 | ||
|
|
e444761059 | ||
|
|
c9e4b5eba4 | ||
|
|
c14c7cc73d | ||
|
|
5e459c2390 | ||
|
|
4ee6de2c84 | ||
|
|
a7d2eca209 | ||
|
|
57a4a7c282 | ||
|
|
b4c5236d8b | ||
|
|
de2a779715 | ||
|
|
352c2ddc56 | ||
|
|
76e6aff9d7 | ||
|
|
b4c02767ac | ||
|
|
aa2a888ed0 | ||
|
|
b50d82feff | ||
|
|
cce9592ac8 | ||
|
|
b69def652e | ||
|
|
6df83485e1 | ||
|
|
e297fcbff8 | ||
|
|
2f2ebff674 | ||
|
|
06b74c4ca6 | ||
|
|
7ddfc5e5ad | ||
|
|
f86709ebd0 | ||
|
|
7266de42f5 | ||
|
|
47831d43b5 | ||
|
|
cf47dd82d7 | ||
|
|
081c97583b | ||
|
|
77f1706cbb | ||
|
|
664efa3ace | ||
|
|
b6059f692e | ||
|
|
4bf8f25d31 | ||
|
|
e2c6d6788c | ||
|
|
1c20bc3966 | ||
|
|
c31d76ec83 | ||
|
|
ada8f9f8ee | ||
|
|
1b72598803 | ||
|
|
4bc8bf52e7 | ||
|
|
0d257a2c3b | ||
|
|
0ba602d3aa | ||
|
|
38d18ba767 | ||
|
|
94b8aa7213 | ||
|
|
1ac36d03da | ||
|
|
dca256364a | ||
|
|
652e7d8d7b | ||
|
|
42e9a6294b | ||
|
|
f3d2952579 | ||
|
|
ee1d4cd3e4 | ||
|
|
4e067eaaa8 | ||
|
|
4a0975b21b | ||
|
|
dd9ad2f0be | ||
|
|
d1fe267072 | ||
|
|
28ba100864 | ||
|
|
99ea06c0d5 | ||
|
|
fa070b2126 | ||
|
|
5405632434 | ||
|
|
ca2cec5d7d | ||
|
|
8150176fc6 | ||
|
|
5c72e0754a | ||
|
|
af2e195f90 | ||
|
|
8523c11357 | ||
|
|
b860d72c2d | ||
|
|
bcb7d059c0 | ||
|
|
bc0dacf974 | ||
|
|
2d9b0177ec | ||
|
|
2fed84c676 | ||
|
|
a73c06223d | ||
|
|
e129c02109 | ||
|
|
67dae80b76 | ||
|
|
088b69a6ef | ||
|
|
db64f37a95 | ||
|
|
af6c42f49b | ||
|
|
4503170075 | ||
|
|
c07f343d0e | ||
|
|
725f6c55a5 | ||
|
|
364742a98b | ||
|
|
7b35a749e2 | ||
|
|
23ac510d26 | ||
|
|
ba10298dbc | ||
|
|
c6a8aeac1d | ||
|
|
b41ef0ab13 | ||
|
|
c33a0d5deb | ||
|
|
70b9d4c174 | ||
|
|
1462bedd97 | ||
|
|
937bacacb7 | ||
|
|
e8824e5d03 | ||
|
|
e030cfebb8 | ||
|
|
eea98b01f6 | ||
|
|
f8c1d291ed | ||
|
|
0df268f331 | ||
|
|
be2ad226f4 | ||
|
|
a4df23361e | ||
|
|
0f34946127 | ||
|
|
575470ae1b | ||
|
|
31c59e7380 | ||
|
|
4b542b70ae | ||
|
|
9cb641bddf | ||
|
|
7a8b3591cd | ||
|
|
fd2ba6b2b2 | ||
|
|
ac6cc39616 | ||
|
|
0be9e1985a | ||
|
|
a1af4e563a | ||
|
|
7167e84a8f | ||
|
|
7eeb84de47 | ||
|
|
45aabf369b | ||
|
|
e396a21791 | ||
|
|
c6635a4885 | ||
|
|
306971230b | ||
|
|
e0c13846a7 | ||
|
|
4aa42e6573 | ||
|
|
24a04fb8c6 | ||
|
|
321de8d4ec | ||
|
|
5db82f733f | ||
|
|
d65fbf299c | ||
|
|
7c2bb3bd85 | ||
|
|
f9903f1e19 | ||
|
|
4a4935f165 | ||
|
|
09f92cc5e4 | ||
|
|
887e5997c2 | ||
|
|
f88a50dd07 | ||
|
|
3f12a5ae3d | ||
|
|
f2946817bf | ||
|
|
935a9b0631 | ||
|
|
c0dc411102 | ||
|
|
55037626a4 | ||
|
|
fc3e72bacf | ||
|
|
0fd0f0e7c0 | ||
|
|
22503d4e95 | ||
|
|
de9236e93a | ||
|
|
66523301aa | ||
|
|
9a81121ac1 | ||
|
|
a245ba80c3 | ||
|
|
9a1860fc5e | ||
|
|
f3f5ab70ea | ||
|
|
6ce7638fdc | ||
|
|
7f916d55e7 | ||
|
|
43ea019091 | ||
|
|
6025cad716 | ||
|
|
9365aea724 | ||
|
|
28859a8a9c | ||
|
|
304fd7ea80 | ||
|
|
5e345263a7 | ||
|
|
445ee25bbf | ||
|
|
20937815f8 | ||
|
|
3863b8ef40 | ||
|
|
d91ccaa70f | ||
|
|
7673ca5481 | ||
|
|
774a37a7d2 | ||
|
|
905094f63f | ||
|
|
fd5b7ba49d | ||
|
|
1b828b95b3 | ||
|
|
54546aaefb | ||
|
|
ded5fc8b1d | ||
|
|
68fc4f6950 | ||
|
|
5d07238ab8 | ||
|
|
5e7e5e2636 | ||
|
|
ca5c0055d1 | ||
|
|
e208df16b2 | ||
|
|
11632b0ef1 | ||
|
|
b0bc46f539 | ||
|
|
627ed45065 | ||
|
|
fc9ad5b519 | ||
|
|
40ddad1d9a | ||
|
|
eb997db703 | ||
|
|
e53dc5b80b | ||
|
|
ab64655f05 | ||
|
|
e1b530e4fc | ||
|
|
4414853e45 | ||
|
|
35adcd2c7f | ||
|
|
5ec487a832 | ||
|
|
0cc56edc95 | ||
|
|
3de8b7e022 | ||
|
|
e5946e59a8 | ||
|
|
0f48a48a4e | ||
|
|
1a255969a7 | ||
|
|
bf4728fded | ||
|
|
d5e91c7168 | ||
|
|
2ded922b5d | ||
|
|
193ab0ff63 | ||
|
|
9356449f45 | ||
|
|
670683b47f | ||
|
|
517212225e | ||
|
|
6315beb7cd | ||
|
|
c1653b7ee1 | ||
|
|
8ed0efe241 | ||
|
|
7b50894ca6 | ||
|
|
7dfd99a0b0 | ||
|
|
c78e6dc231 | ||
|
|
93f3e81cfc | ||
|
|
38f4b27d10 | ||
|
|
541ac6f8dd | ||
|
|
774eb48828 | ||
|
|
1eccedb74d | ||
|
|
bc6f953f76 | ||
|
|
261f939896 | ||
|
|
c5f0f1ef9f | ||
|
|
dc843a811e | ||
|
|
c8ada1ac46 | ||
|
|
d82ac8f355 | ||
|
|
7ab8683d72 | ||
|
|
d0d56aceb6 | ||
|
|
c33ba2c5af | ||
|
|
72e67d7b71 | ||
|
|
8dabac916b | ||
|
|
56da2dfd4c | ||
|
|
624ca3c308 | ||
|
|
123db516ad | ||
|
|
4531fc7f37 | ||
|
|
ea6662c38b | ||
|
|
9139f84c33 | ||
|
|
9e625b0e5e | ||
|
|
336df10da2 | ||
|
|
79e241730b | ||
|
|
251d329a71 | ||
|
|
8b384f184c | ||
|
|
d81ba04ba5 | ||
|
|
8f9270e9fe | ||
|
|
b4b9bbf476 | ||
|
|
a47cb865fb | ||
|
|
4f9719abc4 | ||
|
|
9785cf9fe6 | ||
|
|
91100a75db | ||
|
|
fdac9e99e1 | ||
|
|
3204466295 | ||
|
|
aac68436d9 | ||
|
|
085bf65d45 | ||
|
|
3e1312b53a | ||
|
|
60ab332235 | ||
|
|
16c1fd83bc | ||
|
|
455854f91d | ||
|
|
62f7a9f112 | ||
|
|
a1f2685629 | ||
|
|
3f2ec65c2c | ||
|
|
73b7be0606 | ||
|
|
73ee2ba4c0 | ||
|
|
3d4d9af3f4 | ||
|
|
41fb28ae80 | ||
|
|
5c18af4638 | ||
|
|
66a5878fc6 | ||
|
|
5b93149c7b | ||
|
|
83c084e476 | ||
|
|
60b92a5577 | ||
|
|
202dbb6259 | ||
|
|
75b19a5f06 | ||
|
|
ab6eebab43 | ||
|
|
b482dbb031 | ||
|
|
59af080bfb | ||
|
|
db7cd17c10 | ||
|
|
2f0a7e4f12 | ||
|
|
b63ecc59fb | ||
|
|
575cbf659c | ||
|
|
b50219ba0b | ||
|
|
e4d76b3b13 | ||
|
|
99d9a2e4b9 | ||
|
|
04a6782b48 | ||
|
|
cb3257e704 | ||
|
|
daf11c01c9 | ||
|
|
207d56c2e9 | ||
|
|
3629fa1b36 | ||
|
|
21f8550fc7 | ||
|
|
9bd0be20a6 | ||
|
|
d6b1c1409d | ||
|
|
eee0039add | ||
|
|
c9d49f6f40 | ||
|
|
0f5e35a2eb | ||
|
|
979851aac9 | ||
|
|
3a2eb182f9 | ||
|
|
2338c26392 | ||
|
|
c835cda5b6 | ||
|
|
6295f3fd71 | ||
|
|
84d0a40547 | ||
|
|
36ef1479a6 | ||
|
|
67dcc6e7f5 | ||
|
|
edd162c3d2 | ||
|
|
982656bd5a | ||
|
|
469d8b7b12 | ||
|
|
f1562a7b94 | ||
|
|
1e12f1cc80 | ||
|
|
123d8fcaa6 | ||
|
|
4b4738c58f | ||
|
|
037ff85396 | ||
|
|
e0160ac876 | ||
|
|
08abe36443 | ||
|
|
2461a66ad8 | ||
|
|
9ab3430cc4 | ||
|
|
a9348154af | ||
|
|
5621b4cbd4 | ||
|
|
028576f208 | ||
|
|
746bada0d4 | ||
|
|
c89327586d | ||
|
|
bbfe1657d6 | ||
|
|
c39a094d88 | ||
|
|
13f0dd8b01 | ||
|
|
ba7b3aa473 | ||
|
|
08d4fe92d1 | ||
|
|
09786c6d29 | ||
|
|
1150750c09 | ||
|
|
b9b1f51957 | ||
|
|
e53a487948 | ||
|
|
cd19f2ab21 | ||
|
|
124e2d5e10 | ||
|
|
bb2ceb9968 | ||
|
|
ddd203a79f | ||
|
|
017a673211 | ||
|
|
b011870c03 | ||
|
|
db4672f4af | ||
|
|
cc5c625a99 | ||
|
|
8480dadba8 | ||
|
|
fd88a6a22f | ||
|
|
7a230c90f0 | ||
|
|
f15c2ada1b | ||
|
|
fa7dbc587a | ||
|
|
5e40042ace | ||
|
|
df85d2627d | ||
|
|
229008577b | ||
|
|
a679c19af1 | ||
|
|
8b1cd5965a | ||
|
|
1881fecc83 | ||
|
|
d133809bc0 | ||
|
|
49001bb558 | ||
|
|
18f1048dc4 | ||
|
|
0afc4d2af6 | ||
|
|
0e62e50b1c | ||
|
|
07960bd65a | ||
|
|
647529f35f | ||
|
|
90e9e3ecd6 | ||
|
|
87f88f4c50 | ||
|
|
49fd9c0c36 | ||
|
|
7e4f81d541 | ||
|
|
eb3d2ef049 | ||
|
|
080782e011 | ||
|
|
10d0dd861c | ||
|
|
693afb7949 | ||
|
|
d16f6692b8 | ||
|
|
a74add96b7 | ||
|
|
643d1be6d7 | ||
|
|
1717bc98cb | ||
|
|
0c5e548892 | ||
|
|
f4ecfe6da9 | ||
|
|
71f68b3103 | ||
|
|
d8486568b7 | ||
|
|
24a6c5995b | ||
|
|
452848fd2a | ||
|
|
4f1e3da70a | ||
|
|
c630226e2d | ||
|
|
82939a446b | ||
|
|
f49833646d | ||
|
|
422e335328 | ||
|
|
c803a49134 | ||
|
|
236812cc81 | ||
|
|
a101527906 | ||
|
|
7a45391c22 | ||
|
|
4521053804 | ||
|
|
3dd0e4a66f | ||
|
|
9cd511da3d | ||
|
|
6bbe583e82 | ||
|
|
6519f48149 | ||
|
|
d02afdf22e | ||
|
|
be38969c40 | ||
|
|
7c2690ca54 | ||
|
|
36bd628378 | ||
|
|
38ff691eac | ||
|
|
582fcf8b19 | ||
|
|
60f772081c | ||
|
|
267da47f6e | ||
|
|
151cf17e35 | ||
|
|
6437700a61 | ||
|
|
07ac8957c8 | ||
|
|
2bd39bd9f5 | ||
|
|
2ecb3506b5 | ||
|
|
35fc43cda0 | ||
|
|
71311eb157 | ||
|
|
02c52f0801 | ||
|
|
ee7a0ade9e | ||
|
|
1bee29de83 | ||
|
|
d1c1977a9c | ||
|
|
16b03ec90e | ||
|
|
7162bf4d24 | ||
|
|
3fb31c9d78 | ||
|
|
52c0be63ea | ||
|
|
a54c217f88 | ||
|
|
622e5d65aa | ||
|
|
21199d9a24 | ||
|
|
51b9d80488 | ||
|
|
ccfd82e1c9 | ||
|
|
cbb81bbba7 | ||
|
|
e60cde892a | ||
|
|
7355162d80 | ||
|
|
9823f7b96f | ||
|
|
a20b95bb26 | ||
|
|
0fc2e8872d | ||
|
|
746eda70ee | ||
|
|
ec425501cd | ||
|
|
64e68706ad | ||
|
|
2f97d948b8 | ||
|
|
cc4237d076 | ||
|
|
13546d77e7 | ||
|
|
27dff95df5 | ||
|
|
5f071a6138 | ||
|
|
def611ef89 | ||
|
|
8c2c353071 | ||
|
|
d45bba19c8 | ||
|
|
9ab1dbab78 | ||
|
|
83af032bee | ||
|
|
2a5e12dfc5 | ||
|
|
e751b53241 | ||
|
|
61488627a4 | ||
|
|
da90a40bc4 | ||
|
|
d578e763c0 | ||
|
|
289545e777 | ||
|
|
3eafd0cb62 | ||
|
|
75cc8bfd50 | ||
|
|
76bc0fde33 | ||
|
|
7f05f6bc7d | ||
|
|
8f0d071afb | ||
|
|
30bdcfac29 | ||
|
|
8a1c0c041c | ||
|
|
ad7032064d | ||
|
|
c5ff8777be | ||
|
|
70e5c578ae | ||
|
|
6557864697 | ||
|
|
04cb53a9c8 | ||
|
|
5f1ae30f19 | ||
|
|
aa77cfe4b9 | ||
|
|
de148dbb61 | ||
|
|
0a7ded4052 | ||
|
|
c401ac7560 | ||
|
|
22c3d4ebc5 | ||
|
|
c5efc908de | ||
|
|
046c863768 | ||
|
|
3335cafa94 | ||
|
|
ee3fb6df09 | ||
|
|
d59653eed9 | ||
|
|
50b82f6383 | ||
|
|
72682e4db3 | ||
|
|
10e7ce6363 | ||
|
|
e874f47920 | ||
|
|
92992fc068 | ||
|
|
48075ace90 | ||
|
|
88a8caa023 | ||
|
|
f20d0effe9 | ||
|
|
7acc418489 | ||
|
|
47b6c2608c | ||
|
|
00dc8df0de | ||
|
|
244425381d | ||
|
|
a10e55cfd7 | ||
|
|
2d39fb496c | ||
|
|
9e3edd7208 | ||
|
|
015103f613 | ||
|
|
321bd4e874 | ||
|
|
799e01c7b3 | ||
|
|
e4f91fd3ba | ||
|
|
41d1ae099f | ||
|
|
2836a89f91 | ||
|
|
f04030858b | ||
|
|
5be92cd75e | ||
|
|
36d9dda500 | ||
|
|
3008d9a512 | ||
|
|
c437fa329c | ||
|
|
004bcce58e | ||
|
|
4664a7bbd4 | ||
|
|
fd7bd28381 | ||
|
|
5b090c20ec | ||
|
|
5569f49456 | ||
|
|
5cdfe62e2d | ||
|
|
138e48dc2d | ||
|
|
8f16f242b1 | ||
|
|
679dfc3441 | ||
|
|
4115ca6040 | ||
|
|
88ea647c3a | ||
|
|
69a5b4f227 | ||
|
|
aae314ae1d | ||
|
|
4b89220a7b | ||
|
|
759b934184 | ||
|
|
25d0dcd08e | ||
|
|
1d20e6277e | ||
|
|
9c9dc1c976 | ||
|
|
c6d1f31108 | ||
|
|
8362797381 | ||
|
|
1bb7e1bf47 | ||
|
|
23ba3215d4 | ||
|
|
ad8fef2fda | ||
|
|
2274cef68c | ||
|
|
bc76efaea6 | ||
|
|
de76276a4d | ||
|
|
4738a722a6 | ||
|
|
6de50d1515 | ||
|
|
088073b257 | ||
|
|
c9df8cc803 | ||
|
|
5f010bb94d | ||
|
|
f7f565477c | ||
|
|
6a6df8936e | ||
|
|
d30701c1bb | ||
|
|
5492a45b67 | ||
|
|
d2d62c350c | ||
|
|
9050e705ff | ||
|
|
7b0676025b | ||
|
|
8eb97136b0 | ||
|
|
e9c5cac20c | ||
|
|
1e19afe0e5 | ||
|
|
bbb08aa1db | ||
|
|
274f689f70 | ||
|
|
892bd9f069 | ||
|
|
d0fe058a24 | ||
|
|
14218f4d09 | ||
|
|
c7270e8654 | ||
|
|
d67d32610d | ||
|
|
e93639e1ab | ||
|
|
f0a3fd1e3a | ||
|
|
9a2fa50b0f | ||
|
|
4ace13c857 | ||
|
|
923549ef69 | ||
|
|
437fdd6d12 | ||
|
|
4014a4e250 | ||
|
|
46e220cecc | ||
|
|
78e901cbf7 | ||
|
|
123a44fefc | ||
|
|
b683246647 | ||
|
|
f72b2a21f7 | ||
|
|
3af3bd606c | ||
|
|
6c1e1e1e95 | ||
|
|
039ac9ec74 | ||
|
|
941a7d441c | ||
|
|
ad37dcce44 | ||
|
|
2c041081c9 | ||
|
|
8dddffb8b5 | ||
|
|
78c3da99f7 | ||
|
|
47d54fd295 | ||
|
|
bc39d5eaa8 | ||
|
|
29cfff70ee | ||
|
|
8c0be1099c | ||
|
|
e6a3bf9885 | ||
|
|
31bc5eb2aa | ||
|
|
be67d6dbc6 | ||
|
|
01f872c960 | ||
|
|
995ccadc5b | ||
|
|
c5918d5531 | ||
|
|
4a666705c4 | ||
|
|
f7ced1aea6 | ||
|
|
46694e458d | ||
|
|
c238e50e41 | ||
|
|
5aa358c27b | ||
|
|
1207b082dc | ||
|
|
da4bd8120b | ||
|
|
2c10d3f5b2 | ||
|
|
827c68bf75 | ||
|
|
769fe12159 | ||
|
|
fa8c0d9660 | ||
|
|
c5fd3df235 | ||
|
|
4993353184 | ||
|
|
7fe73ad2eb | ||
|
|
f63a107c11 | ||
|
|
e36c62b30e | ||
|
|
73a8ec02b2 | ||
|
|
453f6ac74a | ||
|
|
895a4eb0dc | ||
|
|
005090fbcd | ||
|
|
053a1287c9 | ||
|
|
f5955dafaf | ||
|
|
e95a9e0685 | ||
|
|
a6a44ef433 | ||
|
|
cf7c7d853f | ||
|
|
ca640ebabe | ||
|
|
baae65919f | ||
|
|
a70ab8cc1d | ||
|
|
f7f0cf942c | ||
|
|
54b9392d4b | ||
|
|
cf3ef5b403 | ||
|
|
8c63274f57 | ||
|
|
d7e62d0b0b | ||
|
|
4b4336391a | ||
|
|
6ee235545f | ||
|
|
15176223fa | ||
|
|
b08b91ca2e | ||
|
|
0afe1f69d4 | ||
|
|
6f21067ddb | ||
|
|
ccce801dc4 | ||
|
|
dc4762a03b | ||
|
|
30aebf2546 | ||
|
|
2310ef0f80 | ||
|
|
de284c2bf6 | ||
|
|
b7439cbd17 | ||
|
|
4053356e13 | ||
|
|
904602510d | ||
|
|
8165d3bd8c | ||
|
|
3c9acea31c | ||
|
|
17f2f007d2 | ||
|
|
7e213dbfbe | ||
|
|
fcb897a0e8 | ||
|
|
5230591dc2 | ||
|
|
605d8f057f | ||
|
|
738cf1f381 | ||
|
|
73fcfcec7b | ||
|
|
995e28cb32 | ||
|
|
2c6e8c414c | ||
|
|
b4edd5d841 | ||
|
|
35df036eb8 | ||
|
|
05fbdae54c | ||
|
|
b6457ae434 | ||
|
|
59f734dd07 | ||
|
|
88cd9e19c5 | ||
|
|
980d8f3092 | ||
|
|
811f46c289 | ||
|
|
6933470ce0 | ||
|
|
cba39df5da | ||
|
|
34111cfc67 | ||
|
|
fef123c2d4 | ||
|
|
a710ce5e1b | ||
|
|
13ca5352c7 | ||
|
|
92236a5bc3 | ||
|
|
4539e91fa9 | ||
|
|
c5c596dc2f | ||
|
|
d0ad554e14 | ||
|
|
3037b540d4 | ||
|
|
0b1365a04b | ||
|
|
ccb510fe47 | ||
|
|
89b987fc87 | ||
|
|
8d806def5b | ||
|
|
e5f4974e9a | ||
|
|
4dfc42528d | ||
|
|
79b471b41c | ||
|
|
f9f18dd38b | ||
|
|
d53fc46ffc | ||
|
|
781f8fb0e8 | ||
|
|
b39a44ae37 | ||
|
|
9a2c10a98f | ||
|
|
0bdb4ac894 | ||
|
|
510dcd762f | ||
|
|
64b01c471b | ||
|
|
1cd77a4a77 | ||
|
|
dba2699b7e | ||
|
|
78b080063e | ||
|
|
98c36c8b03 | ||
|
|
aac333e132 | ||
|
|
030675812e | ||
|
|
8e8bbe84f3 | ||
|
|
6d29bfdf65 | ||
|
|
625f36c780 | ||
|
|
6ff9208d46 | ||
|
|
406a64ae3f | ||
|
|
bd8aa0296b | ||
|
|
21ba1bea36 | ||
|
|
e51662526b | ||
|
|
c628695a4e | ||
|
|
155f9d4052 | ||
|
|
abadfef5a7 | ||
|
|
59e98b31df | ||
|
|
4e348dd99a | ||
|
|
0056747aee | ||
|
|
e3adcada52 | ||
|
|
02383763ec | ||
|
|
e7398af877 | ||
|
|
e1cdbed2e1 | ||
|
|
41158543cf | ||
|
|
05f3f3636b | ||
|
|
0e324115be | ||
|
|
a3a5c59327 | ||
|
|
762bc328e1 | ||
|
|
2585dcc130 | ||
|
|
bc41261009 | ||
|
|
a53812c0fb | ||
|
|
af4a718fc7 | ||
|
|
2efe4f6c80 | ||
|
|
332959128a | ||
|
|
d6e82d44fc | ||
|
|
45e76e12b6 | ||
|
|
ad0d3412fb | ||
|
|
6f8c30ec81 | ||
|
|
c88fa6d2af | ||
|
|
e4aedc9e83 | ||
|
|
b1356551c6 | ||
|
|
1c543666b5 | ||
|
|
95836a217c | ||
|
|
2ae820fb20 | ||
|
|
0e6a303c17 | ||
|
|
1a9930b93a | ||
|
|
bb72acd3ac | ||
|
|
9e2e4ffa74 | ||
|
|
ff12b37431 | ||
|
|
ac80c4adc1 | ||
|
|
3e08e0e8b6 | ||
|
|
c5ab0431a9 | ||
|
|
df7be8603b | ||
|
|
bafc9dc65e | ||
|
|
0a13839155 | ||
|
|
54e24dff39 | ||
|
|
2c17a9a52e | ||
|
|
1ae6503ceb | ||
|
|
f72f669d00 | ||
|
|
f10f792c4b | ||
|
|
344d4e31b7 | ||
|
|
60c8c80480 | ||
|
|
daf4704fe7 | ||
|
|
5febcdd4e4 | ||
|
|
d154069877 | ||
|
|
6baf36c587 | ||
|
|
9e6b1cf716 | ||
|
|
c16ca40894 | ||
|
|
b533633494 | ||
|
|
2af274dc77 | ||
|
|
5cd9af32fa | ||
|
|
4c3509a455 | ||
|
|
77d29e314c | ||
|
|
09457d8aab | ||
|
|
2168143fea | ||
|
|
cba68549d8 | ||
|
|
ab6f44cb6f | ||
|
|
9b20a6d053 | ||
|
|
5cd2b91e28 | ||
|
|
9bdb81019b | ||
|
|
ddfe4c00b1 | ||
|
|
4f64329f25 | ||
|
|
b4742ad54c | ||
|
|
36f74ae0a9 | ||
|
|
758feab413 | ||
|
|
52ed6f3f94 | ||
|
|
c1cb32de21 | ||
|
|
e3ee988225 | ||
|
|
4e030c4a3a | ||
|
|
4f73c47dcb | ||
|
|
21f7912458 | ||
|
|
350f08be2f | ||
|
|
1df31b2496 | ||
|
|
05ae78a671 | ||
|
|
6ebda41922 | ||
|
|
1adafac35f | ||
|
|
d488bcffbd | ||
|
|
f153e75f03 | ||
|
|
51961294dd | ||
|
|
9f7f391609 | ||
|
|
33ed127cba | ||
|
|
8a66bf2e09 | ||
|
|
2e901f3586 | ||
|
|
85e7b1762d | ||
|
|
7c4d1e2f60 | ||
|
|
5d0fa0fbb3 | ||
|
|
011d8a4e12 | ||
|
|
079f19a66e | ||
|
|
7a19a7d696 | ||
|
|
c0890b2347 | ||
|
|
61c1d12a86 | ||
|
|
dc0562b3be | ||
|
|
05d69ba003 | ||
|
|
ffedd2e1ad | ||
|
|
3932e28417 | ||
|
|
33a75b5450 | ||
|
|
a024be6b1d | ||
|
|
3987f26689 | ||
|
|
ac5d20ff82 | ||
|
|
9d14333cc0 | ||
|
|
a70a951192 | ||
|
|
3afc6ba24b | ||
|
|
3231d008cc | ||
|
|
205929bfc9 | ||
|
|
8128b2ed17 | ||
|
|
32dd0f543a | ||
|
|
e80851b0a1 | ||
|
|
2499276b2a | ||
|
|
f95795d547 | ||
|
|
b3a3eb414a | ||
|
|
0300b77ff5 | ||
|
|
93a0db3112 | ||
|
|
b312242cb8 | ||
|
|
5d291846d5 | ||
|
|
7387c2ed8f | ||
|
|
bf034e18eb | ||
|
|
a97a4b2c15 | ||
|
|
4b99ae957e | ||
|
|
cd97565cce | ||
|
|
0106e1c64a | ||
|
|
420779fb4e | ||
|
|
11328ea241 | ||
|
|
ecd2f2b6e5 | ||
|
|
0036dca773 | ||
|
|
cefc36a6a9 | ||
|
|
194b4dfd6b | ||
|
|
d9b4342293 | ||
|
|
39152eab3c | ||
|
|
eb6c187180 | ||
|
|
808db05d23 | ||
|
|
795df1a93f | ||
|
|
54745e786e | ||
|
|
82dfeb8afa | ||
|
|
8df9f6989a | ||
|
|
19713e6159 | ||
|
|
f526681f3d | ||
|
|
77edeac990 | ||
|
|
2c0d7c8e55 | ||
|
|
5c0227b86f | ||
|
|
8e5d7d9f20 | ||
|
|
c844c364fa | ||
|
|
4139258508 | ||
|
|
2d07ef717c | ||
|
|
383d1b2c9c | ||
|
|
c31ace409f | ||
|
|
8dbd2e2561 | ||
|
|
abe76ea003 | ||
|
|
a013d27d17 | ||
|
|
641c21627e | ||
|
|
1bad0f045e | ||
|
|
209afd3adf | ||
|
|
deb7227ce9 | ||
|
|
abeebe9c8b | ||
|
|
81cbf807cb | ||
|
|
4528233830 | ||
|
|
5684570880 | ||
|
|
c0dfa77d76 | ||
|
|
d81c6a0426 | ||
|
|
656a98675e | ||
|
|
43d5dc0528 | ||
|
|
0c7d549e59 | ||
|
|
fc4022a76d | ||
|
|
de87b4be92 | ||
|
|
d2f7785f9f | ||
|
|
e4ba9a8b72 | ||
|
|
4803ae5f78 | ||
|
|
d739d830ab | ||
|
|
3626fa79b4 | ||
|
|
dea2c883a7 | ||
|
|
2193af1a95 | ||
|
|
88bc4fd852 | ||
|
|
515efd0598 | ||
|
|
fb10a8d28e | ||
|
|
ce4628b64f | ||
|
|
d9108a7ca6 | ||
|
|
8867aaeb6d | ||
|
|
490a4e8097 | ||
|
|
f9ed61d199 | ||
|
|
61015b127e | ||
|
|
e99c36a21f | ||
|
|
0608147cd9 | ||
|
|
c40f538782 | ||
|
|
3afaa90183 | ||
|
|
bdf2976a89 | ||
|
|
697e2c2b11 | ||
|
|
c643adf57b | ||
|
|
2c21644a2c | ||
|
|
d11d3b58f7 | ||
|
|
690705ff8f | ||
|
|
34100d1c76 | ||
|
|
676a25631b | ||
|
|
9e92c87351 | ||
|
|
2a75d14e0e | ||
|
|
f3bf9c0c3c | ||
|
|
acd63fdeac | ||
|
|
bc819d59f4 | ||
|
|
8c2e25339f | ||
|
|
ef73f712ec | ||
|
|
08d670a882 | ||
|
|
477d3fb197 | ||
|
|
4a7dae9cc2 | ||
|
|
69649d7496 | ||
|
|
12f420f50e | ||
|
|
66c8b96c9a | ||
|
|
a51b3b4621 | ||
|
|
d69ebc728b | ||
|
|
f13795b743 | ||
|
|
1dfc625f79 | ||
|
|
0c731e4856 | ||
|
|
9c0f72921f | ||
|
|
a94db0ec43 | ||
|
|
0ff00acf9d | ||
|
|
45b7b4b2cb | ||
|
|
59d178f99e | ||
|
|
c59c87c3e8 | ||
|
|
c21476b262 | ||
|
|
5b4d65db23 | ||
|
|
b351551568 | ||
|
|
ea868b7079 | ||
|
|
613008b56e | ||
|
|
2ea2ecec94 | ||
|
|
a3038e75cf | ||
|
|
5dcd4580c3 | ||
|
|
68b48ad610 | ||
|
|
532ac261ff | ||
|
|
94f65d8f70 | ||
|
|
46e5299c60 | ||
|
|
56e1eb33af | ||
|
|
753b301e88 | ||
|
|
2c14a140fe | ||
|
|
d31f0e22e3 | ||
|
|
30cfd8a769 | ||
|
|
d645b4fe73 | ||
|
|
68c794f608 | ||
|
|
475d18b701 | ||
|
|
e9634b7066 | ||
|
|
c60bba1166 | ||
|
|
35900c2350 | ||
|
|
e6fc817f49 | ||
|
|
7a18d160c8 | ||
|
|
a33104d7c4 | ||
|
|
7f57180da4 | ||
|
|
a23e7fe83d | ||
|
|
c854508381 | ||
|
|
d1cf8915e3 | ||
|
|
d9b5b87f2b | ||
|
|
5923ba21de | ||
|
|
8fe805bc68 | ||
|
|
1944a172a3 | ||
|
|
de0b267568 | ||
|
|
03430a4df5 | ||
|
|
87bf3110c8 | ||
|
|
748d80ff3b | ||
|
|
33f00fb811 | ||
|
|
ae99558f40 | ||
|
|
85cbffb845 | ||
|
|
70dbe7c9ca | ||
|
|
2d0b5023c9 | ||
|
|
92fdd0b80d | ||
|
|
dac3533654 | ||
|
|
2d93ac58fc | ||
|
|
79924a59bc | ||
|
|
545f974552 | ||
|
|
2699a38f7b | ||
|
|
13d52803d6 | ||
|
|
1a3b8d1dd6 | ||
|
|
b2cbf4b6f4 | ||
|
|
9839787b6d | ||
|
|
cd0c0f1b01 | ||
|
|
5db1b94ac4 | ||
|
|
c8f30b3289 | ||
|
|
88b4039e47 | ||
|
|
49033f67f3 | ||
|
|
410077467b | ||
|
|
14769c0350 | ||
|
|
b728fcc2d6 | ||
|
|
74291271e3 | ||
|
|
b0787d9a3f | ||
|
|
be69d17345 | ||
|
|
52b9656a1a | ||
|
|
46bf952562 | ||
|
|
822d737f65 | ||
|
|
d656ec3220 | ||
|
|
a2140b915f | ||
|
|
6dae5b98d5 | ||
|
|
626740366b | ||
|
|
a618f00662 | ||
|
|
7bec4c62f7 | ||
|
|
39fae9effc | ||
|
|
3c145cf2ff | ||
|
|
ad6f3ef8cc | ||
|
|
79839f83a0 | ||
|
|
17011820de | ||
|
|
551ea728fc | ||
|
|
d4f77f6588 | ||
|
|
8fe7551176 | ||
|
|
e47276ce2e | ||
|
|
fd1463eb4c | ||
|
|
d5eaa4d091 | ||
|
|
b174e668f4 | ||
|
|
fd49d213c2 | ||
|
|
bbeb80fc48 | ||
|
|
702e29b54b | ||
|
|
0fd911feb1 | ||
|
|
39234adff7 | ||
|
|
e45505b406 | ||
|
|
b1eb876572 | ||
|
|
7e66aa16f7 | ||
|
|
35b30a01ed | ||
|
|
76b3ff5f6e | ||
|
|
5e5f249bd2 | ||
|
|
ce57acb9d6 | ||
|
|
2a79e4a4e5 | ||
|
|
7f948465a4 | ||
|
|
a24ab63fc7 | ||
|
|
643718be23 | ||
|
|
d4d6ee3d26 | ||
|
|
4399e10cef | ||
|
|
519f0542dd | ||
|
|
9456fd77d1 | ||
|
|
37874d82f2 | ||
|
|
9f2a9bf458 | ||
|
|
43a8897c28 | ||
|
|
6980f96697 | ||
|
|
4ffb294d65 | ||
|
|
ae46631b2b | ||
|
|
c42bdd256f | ||
|
|
8990e0c1ff | ||
|
|
f26452c07d | ||
|
|
89789f16d2 | ||
|
|
3bb4c9c29a | ||
|
|
3b8e392395 | ||
|
|
3e6d63e8f7 | ||
|
|
90ca619839 | ||
|
|
8c2aa78b9f | ||
|
|
e544b2d1ba | ||
|
|
82f5287282 | ||
|
|
589a353f02 | ||
|
|
e84e36fd22 | ||
|
|
a4b03c5cfe | ||
|
|
6aee4c2ec4 | ||
|
|
65abef7979 | ||
|
|
45b52f4dea | ||
|
|
18336f58d3 | ||
|
|
12ad4fbf63 | ||
|
|
b1fee9fe56 | ||
|
|
2a6f250706 | ||
|
|
ab2bb6814e | ||
|
|
94fb0d8c66 | ||
|
|
a192e4c872 | ||
|
|
99acd52e89 | ||
|
|
a1ee9d7476 | ||
|
|
757363e372 | ||
|
|
7d0b3a096d | ||
|
|
24a0211d8c | ||
|
|
2c8f960696 | ||
|
|
16d397db1c | ||
|
|
9c3171f1ce | ||
|
|
8a60fa5c83 | ||
|
|
15ce48e712 | ||
|
|
c252fd6a77 | ||
|
|
90a8bb63dc | ||
|
|
1a4be911c0 | ||
|
|
f9f0b429b6 | ||
|
|
a404792bd2 | ||
|
|
e0047b1bbc | ||
|
|
18eb661e84 | ||
|
|
c2e5cba061 | ||
|
|
cd15de6d42 | ||
|
|
60a5ee42fe | ||
|
|
46e2d8c1f9 | ||
|
|
824745c11d | ||
|
|
33709b0558 | ||
|
|
aac91c15d9 | ||
|
|
67405e4af5 | ||
|
|
41aa743947 | ||
|
|
7aca108ef5 | ||
|
|
380d6bf47a | ||
|
|
8fea8e7b47 | ||
|
|
469b1e5efe | ||
|
|
5fae178081 | ||
|
|
4715773bba | ||
|
|
74577752e0 | ||
|
|
056e6b28da | ||
|
|
0cb10e4224 | ||
|
|
34ff5fbc6a | ||
|
|
f63a35b1fa | ||
|
|
57e78d5c55 | ||
|
|
2ee604d2a4 | ||
|
|
a7c3a0f7fd | ||
|
|
5445c41f81 | ||
|
|
e1e1e471a1 | ||
|
|
2e3b43b28b | ||
|
|
fe118d81db | ||
|
|
d3b2a751e2 | ||
|
|
b856a84adc | ||
|
|
707d13a65c | ||
|
|
7417429fdb | ||
|
|
8f5b6f58d1 | ||
|
|
08365bcbda | ||
|
|
4423288a53 | ||
|
|
99274133ff | ||
|
|
55c6728c42 | ||
|
|
357487b767 | ||
|
|
9768fb3493 | ||
|
|
90ad1f4a61 | ||
|
|
51e056a765 | ||
|
|
8e1b33bc51 | ||
|
|
d2e22ef8bf | ||
|
|
103675e5bb | ||
|
|
b5b0d82a1a | ||
|
|
c80d0e5378 | ||
|
|
adeebbc422 | ||
|
|
ee8e8d4a9a | ||
|
|
bc5ffdec8e | ||
|
|
88d52003b3 | ||
|
|
6c7b62b8b1 | ||
|
|
37491ceffb | ||
|
|
b3dedbdf75 | ||
|
|
42d09292b7 | ||
|
|
c80293d9e0 | ||
|
|
b31c09c4ff | ||
|
|
91daabc9d2 | ||
|
|
def5454e5f | ||
|
|
74e6226d13 | ||
|
|
72d83e2fe4 | ||
|
|
85ccbf34c7 | ||
|
|
5e715daded | ||
|
|
5412487178 | ||
|
|
c4937e95e9 | ||
|
|
4f53e2beea | ||
|
|
9ea1edf9db | ||
|
|
ce1c416b20 | ||
|
|
fdbc3c55c7 | ||
|
|
71559154a8 | ||
|
|
07f8a203ea | ||
|
|
b67fd16081 | ||
|
|
9792c17c69 | ||
|
|
8488a5ec1a | ||
|
|
ff571db494 | ||
|
|
aaa932f725 | ||
|
|
c58ecd96f0 | ||
|
|
8608b73009 | ||
|
|
30801dff9f | ||
|
|
912311ad55 | ||
|
|
14615f9976 | ||
|
|
9121cf7ecb | ||
|
|
a831800a05 | ||
|
|
399c739fd7 | ||
|
|
63f687a20e | ||
|
|
fbd0198771 | ||
|
|
00e85280fd | ||
|
|
5b37698d36 | ||
|
|
483640b0c6 | ||
|
|
1e96aad484 | ||
|
|
b88e0e8a52 | ||
|
|
6028009aac | ||
|
|
9aa9b72557 | ||
|
|
8c023c5727 | ||
|
|
d9edaede89 | ||
|
|
0220b37c2d | ||
|
|
f2d2d1cc8d | ||
|
|
437eeef9a0 | ||
|
|
8e4f291389 | ||
|
|
8f27222b07 | ||
|
|
9e05991908 | ||
|
|
1c76bf93a2 | ||
|
|
d5cedee6c5 | ||
|
|
eb1b7176a6 | ||
|
|
71143536bf | ||
|
|
f2608cecd5 | ||
|
|
c95d5464d8 | ||
|
|
0ac7466a81 | ||
|
|
77e62d5a54 | ||
|
|
bef015eb57 | ||
|
|
d99d95217f | ||
|
|
5cbf8db272 | ||
|
|
6ee0c7600b | ||
|
|
6621421a6f | ||
|
|
edf95ea9fb | ||
|
|
a3e3e9046f | ||
|
|
04cdb6fbfc | ||
|
|
8c7e56a2bd | ||
|
|
3e08574fbe | ||
|
|
73ba7933da | ||
|
|
fc45c3b98c | ||
|
|
0d6f420f97 | ||
|
|
bef85963a6 | ||
|
|
3be57efa97 | ||
|
|
981d8510c2 | ||
|
|
5d8f655243 | ||
|
|
0cb41469ab | ||
|
|
971d7e730a | ||
|
|
06f8b9b817 | ||
|
|
db51384b63 | ||
|
|
ac088ea692 | ||
|
|
2a5793e8ce | ||
|
|
6c60ff88a3 | ||
|
|
8d68c10905 | ||
|
|
119d4b9514 | ||
|
|
0370aa8df5 | ||
|
|
6034c899d3 | ||
|
|
d2fe11ba6f | ||
|
|
58c96e1329 | ||
|
|
4c51b4b822 | ||
|
|
f5dea4935c | ||
|
|
0117ab8aa4 | ||
|
|
a5ade0c41a | ||
|
|
4df12ae675 | ||
|
|
274a41f052 | ||
|
|
3670c8f879 | ||
|
|
e88bb442f3 | ||
|
|
a0d1bf4b5c | ||
|
|
32f05dccd9 | ||
|
|
4aac2d2b7b | ||
|
|
e5a40bfb69 | ||
|
|
741ae36d4c | ||
|
|
67fa4a8910 | ||
|
|
80bf3c97b2 | ||
|
|
9f23cb35a9 | ||
|
|
458de17b8f | ||
|
|
dd50ee92a9 | ||
|
|
1094085872 | ||
|
|
edbd3de4a4 | ||
|
|
91d430085e | ||
|
|
fab550157a | ||
|
|
5e2ed04d72 | ||
|
|
e87aa83666 | ||
|
|
c9b6b5d4a8 | ||
|
|
ce01ad2083 | ||
|
|
0eb8ec70d9 | ||
|
|
270f87f193 | ||
|
|
c2951e5e41 | ||
|
|
e22e8669e1 | ||
|
|
2580fe6b79 | ||
|
|
c11c6f40d5 | ||
|
|
3c90a92641 | ||
|
|
4c0a97e62f | ||
|
|
0a57bb5029 | ||
|
|
c65ac5a7cf | ||
|
|
04a8040292 | ||
|
|
adab00bc0e | ||
|
|
9bb8e00c3d | ||
|
|
f2dc95b86d | ||
|
|
28f98aed88 | ||
|
|
d11174da21 | ||
|
|
8e977f994f | ||
|
|
11c2d4ab25 | ||
|
|
b733e6855b | ||
|
|
aa3d644f97 | ||
|
|
bb46d00f22 | ||
|
|
771c74ee75 | ||
|
|
04a346678c | ||
|
|
e5c0fc92ec | ||
|
|
1b640f40dc | ||
|
|
ee77516716 | ||
|
|
82cca0a602 | ||
|
|
d444d716f5 | ||
|
|
e03d710d53 | ||
|
|
b46d44c3a5 | ||
|
|
7a459fd5b8 | ||
|
|
2b696144e3 | ||
|
|
62036a273e | ||
|
|
d25befabdd | ||
|
|
56b17dfbcf | ||
|
|
72c181a399 | ||
|
|
b1b60f4286 | ||
|
|
7648716199 | ||
|
|
9177588220 | ||
|
|
9bbcee645e | ||
|
|
a7d49b986d | ||
|
|
7c3e08050f | ||
|
|
076df7cf66 | ||
|
|
415b8c6317 | ||
|
|
c1d3c93dbb | ||
|
|
8e59c99666 | ||
|
|
dfcd372d2d | ||
|
|
f7bbe0fa94 | ||
|
|
a1910f49a8 | ||
|
|
5f8be5fa91 | ||
|
|
587034ad03 | ||
|
|
9568bc7ea6 | ||
|
|
ccf6b6ef5f | ||
|
|
24f6aff8c8 | ||
|
|
17c19d453b | ||
|
|
4534758c21 | ||
|
|
c180eb466d | ||
|
|
0a416ab758 | ||
|
|
575aca5886 | ||
|
|
c0cc5657a7 | ||
|
|
78514b6c2e | ||
|
|
7e4390d743 | ||
|
|
cd558daf5a | ||
|
|
dda5955121 | ||
|
|
783ac18222 | ||
|
|
81c8052449 | ||
|
|
6ce02282e7 | ||
|
|
a19a0b6789 | ||
|
|
9de08dc83f | ||
|
|
96c7b87ac7 | ||
|
|
469dd49def | ||
|
|
53f6a0b32b | ||
|
|
fb9a0fe833 | ||
|
|
69c3d41a8a | ||
|
|
fc32b98341 | ||
|
|
299ed88f09 | ||
|
|
29753a6aa9 | ||
|
|
7983cd8d62 | ||
|
|
05fab1f79d | ||
|
|
7229b886e0 | ||
|
|
8b70d2674f | ||
|
|
8ba27cdaea | ||
|
|
1c2411a0fc | ||
|
|
ec88d07ef1 | ||
|
|
aa328d3ef7 | ||
|
|
727facfb90 | ||
|
|
4add853473 | ||
|
|
b2db27f9aa | ||
|
|
96be6c0efe | ||
|
|
3f42f1281d | ||
|
|
bab8384803 | ||
|
|
ceb77c990b | ||
|
|
3f65928e9d | ||
|
|
4e6659e7e8 | ||
|
|
9e22d4b5df | ||
|
|
357361de3d | ||
|
|
de443fa3f0 | ||
|
|
20839853b7 | ||
|
|
bc2539b566 | ||
|
|
c89416702d | ||
|
|
b2dd8c68e1 | ||
|
|
2ef2eafdd3 | ||
|
|
568655d503 | ||
|
|
9bd6f9ef47 | ||
|
|
c8e5cefd36 | ||
|
|
7ba4077f9f | ||
|
|
151f8bf329 | ||
|
|
e94d48c265 | ||
|
|
2a5c523afd | ||
|
|
f80696b724 | ||
|
|
5f5b5f69e3 | ||
|
|
d99f8fef09 | ||
|
|
97e59db5e6 | ||
|
|
1c813c0e0e | ||
|
|
e39f17b3de | ||
|
|
0b90b53e09 | ||
|
|
847d729ba4 | ||
|
|
aa86a6e53b | ||
|
|
34470336e4 | ||
|
|
5a2a89f19e | ||
|
|
7eb4df770e | ||
|
|
550bb5fd33 | ||
|
|
ffcae66f59 | ||
|
|
d2df795ba7 | ||
|
|
b930e13964 | ||
|
|
1b9da9cdd8 | ||
|
|
e6bf318cdf | ||
|
|
4cfed08247 | ||
|
|
4460b526cb | ||
|
|
01e6a87968 | ||
|
|
7667a4f8c0 | ||
|
|
6fbfb83e6c | ||
|
|
123d3e182a | ||
|
|
fd8d16035c | ||
|
|
1ff45b55d6 | ||
|
|
0ce02d7766 | ||
|
|
959a13a514 | ||
|
|
b601d713d2 | ||
|
|
dc96d8699a | ||
|
|
f38cdd8432 | ||
|
|
91655a3d5a | ||
|
|
7774a9b2ab | ||
|
|
80cf8f484d | ||
|
|
cb7c075a61 | ||
|
|
4d0fb67c53 | ||
|
|
380d1d4f18 | ||
|
|
71832859a5 | ||
|
|
a31432ad9e | ||
|
|
26743154d8 | ||
|
|
a50a6fa917 | ||
|
|
b43e5bac0b | ||
|
|
ddaef1fb64 | ||
|
|
6f264ff5de | ||
|
|
a06fc6d80f | ||
|
|
3ddfc47d3a | ||
|
|
905bd05ba8 | ||
|
|
aa19787654 | ||
|
|
3274f3ec35 | ||
|
|
c3b8c48ca2 | ||
|
|
d365094616 | ||
|
|
7c76684076 | ||
|
|
0ef27b038a | ||
|
|
610a27c0e4 | ||
|
|
752c91a721 | ||
|
|
d3d655da07 | ||
|
|
db36cf248e | ||
|
|
153d8e106e | ||
|
|
df8829b477 | ||
|
|
569bc297a8 | ||
|
|
099cbbdb64 | ||
|
|
ca7469b92e | ||
|
|
6db4145927 | ||
|
|
ca93f2baff | ||
|
|
84a0a3caeb | ||
|
|
7b327693e2 | ||
|
|
dba70dc6d5 | ||
|
|
bd1618e41d | ||
|
|
08b7aff0d8 | ||
|
|
a75688f89c | ||
|
|
30763b5401 | ||
|
|
814519248c | ||
|
|
8c71be5257 | ||
|
|
91763b233e | ||
|
|
ab51f5e69a | ||
|
|
d278d58f6c | ||
|
|
47e038c9fa | ||
|
|
e96210f48c | ||
|
|
aa3811ad02 | ||
|
|
963ab38b2e | ||
|
|
11069cc219 | ||
|
|
d074500109 | ||
|
|
63af28b016 | ||
|
|
d2cc3f673e | ||
|
|
dc85644d71 | ||
|
|
0b5bdf8151 | ||
|
|
b27238a69a | ||
|
|
bb2bf78e8a | ||
|
|
7e17533cc6 | ||
|
|
7808da118a | ||
|
|
4259cf8764 | ||
|
|
994c55945e | ||
|
|
f20c145ece | ||
|
|
5b31026e1c | ||
|
|
39fe5951f7 | ||
|
|
9d767c3dd8 | ||
|
|
2a3f9bf81c | ||
|
|
82bb2fcf6a | ||
|
|
e56e765450 | ||
|
|
3fd4359cb1 | ||
|
|
c70169b4a0 | ||
|
|
ca1be580df | ||
|
|
28820f2e64 | ||
|
|
6c3987ec86 | ||
|
|
089eb9e86b | ||
|
|
0793e9afc5 | ||
|
|
1e2522375b | ||
|
|
e09f53da8f | ||
|
|
29b4b62a44 | ||
|
|
b1a63db1fc | ||
|
|
9940dc8451 | ||
|
|
703c68eb66 | ||
|
|
3338df9836 | ||
|
|
dc4794b246 | ||
|
|
b130c9882a | ||
|
|
5f8b838652 | ||
|
|
4efd1b5d3e | ||
|
|
ad6ed21b6b | ||
|
|
2ffaa71bb5 | ||
|
|
1763f59320 | ||
|
|
08d32ffc77 | ||
|
|
7e3cebb96d | ||
|
|
930fb404af | ||
|
|
6cd711a1e2 | ||
|
|
1bcc332885 | ||
|
|
9bb986cff9 | ||
|
|
1247942bf1 | ||
|
|
95d3ff4cbe | ||
|
|
0a874a28ef | ||
|
|
2dee702060 | ||
|
|
4ea66477fe | ||
|
|
d3be732566 | ||
|
|
933517055e | ||
|
|
040a3d9b36 | ||
|
|
2c859bf280 | ||
|
|
fe227e02b8 | ||
|
|
c68e583c20 | ||
|
|
6620d56859 | ||
|
|
1ec72d3e94 | ||
|
|
5c3bb75786 | ||
|
|
a90cb0dad9 | ||
|
|
8854a491ab | ||
|
|
74e8073328 | ||
|
|
a6d62a7596 | ||
|
|
980a224d02 | ||
|
|
0c6b83fc35 | ||
|
|
6d2310f59d | ||
|
|
05107fab1c | ||
|
|
285bed65c6 | ||
|
|
b523c23e7a | ||
|
|
4c9a028a4e | ||
|
|
cea970f065 | ||
|
|
d8511fab1d | ||
|
|
b2d10e92e9 | ||
|
|
0582d5e2b6 | ||
|
|
da2b56b5b1 | ||
|
|
46c15f37c5 | ||
|
|
4ddc02d7fe | ||
|
|
4c3ac0af91 | ||
|
|
edd0b90576 | ||
|
|
11dca41945 | ||
|
|
75c4724200 | ||
|
|
09704b6f37 | ||
|
|
8a0824880e | ||
|
|
d84abf021e | ||
|
|
e7223da19f | ||
|
|
499d143199 | ||
|
|
fefeb3c006 | ||
|
|
6aeee933d2 | ||
|
|
34c0698c48 | ||
|
|
1cc1a00820 | ||
|
|
62f6b57948 | ||
|
|
5387acf533 | ||
|
|
077b3ef04d | ||
|
|
9c654254d3 | ||
|
|
4bb8bbbad8 | ||
|
|
39adafb1be | ||
|
|
e19bfcdd04 | ||
|
|
6fde92f5ac | ||
|
|
7170a7b302 | ||
|
|
24884e4a77 | ||
|
|
384be8ceae | ||
|
|
ee9a5e8482 | ||
|
|
34453fa3be | ||
|
|
f727712bfa | ||
|
|
3bb974b9e0 | ||
|
|
021445216e | ||
|
|
c13bf3ccd1 | ||
|
|
8d53f42421 | ||
|
|
ace42019fb | ||
|
|
54aa161da0 | ||
|
|
25c289deaa | ||
|
|
3c802e7d55 | ||
|
|
ba3cf4d2bd | ||
|
|
4aa905716b | ||
|
|
0fc1e8ec10 | ||
|
|
0875d35129 | ||
|
|
8c62a081fe | ||
|
|
f811ae6c61 | ||
|
|
4a3ef42e67 | ||
|
|
88abaef7f9 | ||
|
|
21fe746f2f | ||
|
|
c3c6915fa0 | ||
|
|
b2705c1a13 | ||
|
|
75e3b4cc84 | ||
|
|
78f5235eca | ||
|
|
adad88681e | ||
|
|
78cd17e279 | ||
|
|
1b9ac088e4 | ||
|
|
e00951e5b9 | ||
|
|
c51c8aae5c | ||
|
|
40aa7734e1 | ||
|
|
0594e1148e | ||
|
|
9eacd1563f | ||
|
|
a53a648a63 | ||
|
|
a9dacf4a29 | ||
|
|
66f82b6ff9 | ||
|
|
0e68884493 | ||
|
|
f8d885fc9a | ||
|
|
366190ee99 | ||
|
|
42d56a324f | ||
|
|
7d1f1ea2f7 | ||
|
|
30cab8e3a7 | ||
|
|
e0e2162c6d | ||
|
|
f1582fcc10 | ||
|
|
eb6206ea57 | ||
|
|
3ad51cafa8 | ||
|
|
b8c14d69c3 | ||
|
|
725b5083c7 | ||
|
|
87dd6b19bf | ||
|
|
3188994261 | ||
|
|
e4c9d8799e | ||
|
|
bc938db7f9 | ||
|
|
b7a0feba5b | ||
|
|
51fa0a0891 | ||
|
|
f4c54bb9e6 | ||
|
|
e00ca5d096 | ||
|
|
0a9dc49e7f | ||
|
|
07cdfc16d0 | ||
|
|
622a171ac4 | ||
|
|
fd85efbf55 | ||
|
|
ae2a818d8c | ||
|
|
6966c16dd2 | ||
|
|
27b5f24a0f | ||
|
|
ea15421308 | ||
|
|
ef35ad90b8 | ||
|
|
914691eaa7 | ||
|
|
37bb83dfa6 | ||
|
|
d8881e2734 | ||
|
|
45869c428e | ||
|
|
40832bd3a1 | ||
|
|
126a8e8efb | ||
|
|
07b93167f0 | ||
|
|
29c0a8d054 | ||
|
|
73b1be36a2 | ||
|
|
1796c21f48 | ||
|
|
363d4af639 | ||
|
|
f1c881378c | ||
|
|
d316e13fa6 | ||
|
|
1ea98a6ed1 | ||
|
|
3d4415d5d2 | ||
|
|
3e43414d9c | ||
|
|
6d682d509f | ||
|
|
3a592aee8b | ||
|
|
b74f60fe0e | ||
|
|
34f3a50234 | ||
|
|
6094179a40 | ||
|
|
e3bc2688ba | ||
|
|
96cdea2a94 | ||
|
|
cb159e3341 | ||
|
|
136e776b03 | ||
|
|
a0833e8943 | ||
|
|
8bb1b1da7c | ||
|
|
558502d8ea | ||
|
|
8edb952800 | ||
|
|
f3d79e58db | ||
|
|
f26ff085e1 | ||
|
|
7ea550738e | ||
|
|
6b1048590f | ||
|
|
203f0d3851 | ||
|
|
d9c38a716c | ||
|
|
24709d01bd | ||
|
|
2dc2681f84 | ||
|
|
d53a39860e | ||
|
|
ad2f084112 | ||
|
|
d59c42ed3f | ||
|
|
e022ffee62 | ||
|
|
77ddd5ed78 | ||
|
|
8604faffe6 | ||
|
|
45919200c4 | ||
|
|
d498bb9cff | ||
|
|
389f60786a | ||
|
|
2d0929cd69 | ||
|
|
e94ebd6ed2 | ||
|
|
77373606fe | ||
|
|
284f2bc323 | ||
|
|
355e6e1d15 | ||
|
|
f6909d2f98 | ||
|
|
c42974f7b3 | ||
|
|
230d80a2a5 | ||
|
|
551038b295 | ||
|
|
4055b06e71 | ||
|
|
6616359baf | ||
|
|
a5336bbe56 | ||
|
|
871e7f7a50 | ||
|
|
d1c7146a47 | ||
|
|
30f6220c3e | ||
|
|
acd3e87996 | ||
|
|
8c8814d07e | ||
|
|
417fc3af5b | ||
|
|
2218733da4 | ||
|
|
9d1060975e | ||
|
|
82281e2477 | ||
|
|
d0976c45e9 | ||
|
|
a888397bef | ||
|
|
7b2bb4a128 | ||
|
|
d440dc00f1 | ||
|
|
d61382f4e2 | ||
|
|
d4fe893539 | ||
|
|
1af95955b6 | ||
|
|
4eb78810c6 | ||
|
|
a43e926dd2 | ||
|
|
ff49046bfa | ||
|
|
95b0b851a5 | ||
|
|
077ca19912 | ||
|
|
089cc23648 | ||
|
|
e6b9a73d03 | ||
|
|
cea264e871 | ||
|
|
d0bde7b016 | ||
|
|
5b271df66f | ||
|
|
bc7faee880 | ||
|
|
a2abdcf5d3 | ||
|
|
d4e843983d | ||
|
|
6e41c36a44 | ||
|
|
1fe3451120 | ||
|
|
bc4a95d0a5 | ||
|
|
14dc6d1604 | ||
|
|
1795ed7617 | ||
|
|
e8edb31be3 | ||
|
|
58fd30e6ad | ||
|
|
9a34ada258 | ||
|
|
748a752e29 | ||
|
|
37748ef3bd | ||
|
|
d41007de8e | ||
|
|
45befd440c | ||
|
|
a2c10f1c7a | ||
|
|
d7768f86d3 | ||
|
|
dae3835eb0 | ||
|
|
e9b5784d30 | ||
|
|
1521f0a9b1 | ||
|
|
9a9c351f47 | ||
|
|
4ec11ddea5 | ||
|
|
f619b6b9fc | ||
|
|
bcccb3206d | ||
|
|
11a8ff7f70 | ||
|
|
f6ab1aad77 | ||
|
|
5a732acf64 | ||
|
|
e4e06e0a6e | ||
|
|
28f20d47d3 | ||
|
|
82ce688a0d | ||
|
|
f36757b650 | ||
|
|
ac4a7441e9 | ||
|
|
9091afe682 | ||
|
|
e2034b19e7 | ||
|
|
1b7a225f9d | ||
|
|
a52043ef29 | ||
|
|
b38d271f10 | ||
|
|
e480519855 | ||
|
|
8b8d1e87e7 | ||
|
|
cd6de191d1 | ||
|
|
8b8e018521 | ||
|
|
5277beede3 | ||
|
|
57a2457050 | ||
|
|
2f8656d54f | ||
|
|
49102e510d | ||
|
|
e7b8548698 | ||
|
|
9c2bad85d5 | ||
|
|
4147d2f684 | ||
|
|
6b30f47588 | ||
|
|
e49da6afd6 | ||
|
|
6fa0a29249 | ||
|
|
c163e2c981 | ||
|
|
372bf9d97f | ||
|
|
619d5dd1b9 | ||
|
|
4939faf5fa | ||
|
|
205e4aa707 | ||
|
|
81ce7fbb62 | ||
|
|
de9651533f | ||
|
|
e6e31fd234 | ||
|
|
d242079a74 | ||
|
|
48f26cb181 | ||
|
|
c37a5b2405 | ||
|
|
d15bfaac76 | ||
|
|
e94657875f | ||
|
|
f2bd7300aa | ||
|
|
c255aee3b9 | ||
|
|
305d1f0523 | ||
|
|
970f2c25dd | ||
|
|
b7b3b35816 | ||
|
|
e8f326ebce | ||
|
|
62b743025a | ||
|
|
7934463a53 | ||
|
|
d15ef63182 | ||
|
|
c7edba5120 | ||
|
|
188f871bc8 | ||
|
|
31eba975fd | ||
|
|
2ea0bccd25 | ||
|
|
fa321c7ddc | ||
|
|
1d7b0c9b17 | ||
|
|
a4fbcd2d02 | ||
|
|
d788b286aa | ||
|
|
eedb5c26a9 | ||
|
|
ddd6e7d18f | ||
|
|
eae0d6be94 | ||
|
|
5e68dbe1ca | ||
|
|
98e0be6be9 | ||
|
|
7450a6b7eb | ||
|
|
c3802e5a37 | ||
|
|
43cd9bce67 | ||
|
|
2f6ab6d2b0 | ||
|
|
2df17c32cd | ||
|
|
16fff8d87a | ||
|
|
1087069277 | ||
|
|
1b624e7e6f | ||
|
|
7223ae327a | ||
|
|
bcdefda0db | ||
|
|
fc56642631 | ||
|
|
69299d395c | ||
|
|
a789f58068 | ||
|
|
f68935735d | ||
|
|
ba2157cc43 | ||
|
|
57fe5c04ec | ||
|
|
3a08944c99 | ||
|
|
b6154b273c | ||
|
|
e332bff362 | ||
|
|
59e03434e4 | ||
|
|
2ca0edf5fd | ||
|
|
90dca9072e | ||
|
|
c0ead4a484 | ||
|
|
f8cb9e2bd3 | ||
|
|
df4dabf68f | ||
|
|
40720f9949 | ||
|
|
7e7a1dce7b | ||
|
|
f31861441b | ||
|
|
39b9a7f0ed | ||
|
|
43010779d4 | ||
|
|
a1a4fc8c7c | ||
|
|
621e4a513c | ||
|
|
6c821039b5 | ||
|
|
f80b948fb1 | ||
|
|
d4c27da892 | ||
|
|
11bf0ca868 | ||
|
|
664092c023 | ||
|
|
0cd2c4a90c | ||
|
|
2f6c04a86d | ||
|
|
a382e74a89 | ||
|
|
3c8c76f50d | ||
|
|
cbce379132 | ||
|
|
e795e96bfb | ||
|
|
e12e2c4b0b | ||
|
|
9a1b21a2fa | ||
|
|
79708f9ba6 | ||
|
|
102544877d | ||
|
|
1c32ae1227 | ||
|
|
55d7e444c7 | ||
|
|
cc93c686d9 | ||
|
|
9243fd499b | ||
|
|
d5990e60c9 | ||
|
|
844dc48d65 | ||
|
|
52d96b8518 | ||
|
|
8274e68846 | ||
|
|
f1adcd1836 | ||
|
|
2a77f57aa4 | ||
|
|
04ebe4c68a | ||
|
|
1c7e065c52 | ||
|
|
80f3857f44 | ||
|
|
3b62831401 | ||
|
|
a047e1d063 | ||
|
|
45985e1684 | ||
|
|
7dac886375 | ||
|
|
af3b8a9902 | ||
|
|
4e37666037 | ||
|
|
2769d32c81 | ||
|
|
4b004320a4 | ||
|
|
edfc879b41 | ||
|
|
0879d1da0d | ||
|
|
c5159f8a87 | ||
|
|
a0d9bf0f26 | ||
|
|
a3cce8ff72 | ||
|
|
242f00390d | ||
|
|
f4b64370bb | ||
|
|
ae57e4da83 | ||
|
|
cd391a360c | ||
|
|
dcbe12f1af | ||
|
|
5b61cfe922 | ||
|
|
739406614d | ||
|
|
8076206a90 | ||
|
|
f63d218aae | ||
|
|
f2e3ccd18c | ||
|
|
d41e69d770 | ||
|
|
0e3bc1ce43 | ||
|
|
6ca175345f | ||
|
|
c063a638cd | ||
|
|
2dfe1420bc | ||
|
|
7dd379c5c3 | ||
|
|
752eb6235d | ||
|
|
3f077727ae | ||
|
|
51d557524d | ||
|
|
5d9563304f | ||
|
|
53cb68f82c | ||
|
|
95b107ffad | ||
|
|
06dedf51aa | ||
|
|
ed7c8c11d9 | ||
|
|
643e5954f3 | ||
|
|
c7cc9d2a65 | ||
|
|
5050914d25 | ||
|
|
31fa2d866f | ||
|
|
4a096cb728 | ||
|
|
16b52f929c | ||
|
|
8ec133830f | ||
|
|
c144799a11 | ||
|
|
e56511a05a | ||
|
|
bdb959d986 | ||
|
|
dae9c368b7 | ||
|
|
2a401a302d | ||
|
|
ff3b8e5270 | ||
|
|
bb1a066ff7 | ||
|
|
9a9872812f | ||
|
|
969e4a2d65 | ||
|
|
77f0b87c54 | ||
|
|
eec56256e8 | ||
|
|
99dc91dcb4 | ||
|
|
5adcfbd7bd | ||
|
|
3c5f1f7c4b | ||
|
|
4415429661 | ||
|
|
92c404fbb6 | ||
|
|
956b9aaf95 | ||
|
|
c8348f1b44 | ||
|
|
d73ceb374c | ||
|
|
3e01953a3a | ||
|
|
1a65b1affb | ||
|
|
dd75078019 | ||
|
|
1ab205cb46 | ||
|
|
eb26d54ac1 | ||
|
|
d884645f37 | ||
|
|
45f0c3c85f | ||
|
|
4e498e6932 | ||
|
|
d9d68cd37c | ||
|
|
56abd0bb7f | ||
|
|
747683e9e8 | ||
|
|
5b191d72a6 | ||
|
|
b7619630cf | ||
|
|
de07f10e57 | ||
|
|
87e6080215 | ||
|
|
e721a234e1 | ||
|
|
67289bbba2 | ||
|
|
b0c24f6e51 | ||
|
|
12f474ecbe | ||
|
|
e2f20a7a65 | ||
|
|
58ffabe2d6 | ||
|
|
426f06045e | ||
|
|
2ca875192a | ||
|
|
36b2f24de9 | ||
|
|
2cf3b3be2b | ||
|
|
8320c6940b | ||
|
|
3c9d21e38d | ||
|
|
0d7f00aef6 | ||
|
|
1640763a7f | ||
|
|
0ec5346574 | ||
|
|
977845e2f4 | ||
|
|
4bb2ab73c1 | ||
|
|
af5584d244 | ||
|
|
b289e41a0d | ||
|
|
c0b4eef948 | ||
|
|
b8e6c2fe78 | ||
|
|
b10e86e484 | ||
|
|
1c31cffe4b | ||
|
|
b9822cd5d1 | ||
|
|
ef1c70123c | ||
|
|
c245531d65 | ||
|
|
522495fd11 | ||
|
|
b2a551dc63 | ||
|
|
840107c69e | ||
|
|
2a06a1ffdf | ||
|
|
8a01209ded | ||
|
|
2b8dfc9dbc | ||
|
|
e9f25eb562 | ||
|
|
475c7fd6db | ||
|
|
fa5d64022d | ||
|
|
5c0f6cf65e | ||
|
|
0f8d366e31 | ||
|
|
9e2e593825 | ||
|
|
028bfc11eb | ||
|
|
b6cf7a4534 | ||
|
|
0779679b99 | ||
|
|
a48ef69e41 | ||
|
|
64d7953e50 | ||
|
|
21c35b31d4 | ||
|
|
2d64acf299 | ||
|
|
e80819fc06 | ||
|
|
7e40d58d04 | ||
|
|
ba8fafcc95 | ||
|
|
42694d2004 | ||
|
|
5e67ce0ab2 | ||
|
|
97b73e1a01 | ||
|
|
4239257000 | ||
|
|
6bd94761d0 | ||
|
|
bc54e57fd4 | ||
|
|
17751e52fd | ||
|
|
6016ebd3b4 | ||
|
|
8a44fc19ee | ||
|
|
f69450e2ae | ||
|
|
3161ccced3 | ||
|
|
909aad22a6 | ||
|
|
c8b4fd1690 | ||
|
|
dac2271084 | ||
|
|
5320d20f71 | ||
|
|
20d8cc2b47 | ||
|
|
d3fdbdbca5 | ||
|
|
d80f7ebf3b | ||
|
|
d6c84e362f | ||
|
|
6aba07c33b | ||
|
|
45913b0add | ||
|
|
c258409a8d | ||
|
|
26cd2d3fef | ||
|
|
4069074f41 | ||
|
|
182422249f | ||
|
|
29b70b3247 | ||
|
|
e474748f4d | ||
|
|
132ba905c7 | ||
|
|
208d1b82b5 | ||
|
|
1fd7c95f1b | ||
|
|
481f195725 | ||
|
|
d6c1550a1d | ||
|
|
60b9ae0a70 | ||
|
|
bf71351e6d | ||
|
|
8e361a8776 | ||
|
|
e39fd53727 | ||
|
|
76efcca64b | ||
|
|
35f49d9bc0 | ||
|
|
a0a55797a9 | ||
|
|
696a429e9e | ||
|
|
29cd55e795 | ||
|
|
c2ebf61fd3 | ||
|
|
489b4d6acf | ||
|
|
6cffc47f3c | ||
|
|
50d8e08a34 | ||
|
|
3f16c0378a | ||
|
|
2b06d8a096 | ||
|
|
2a5b37b9ad | ||
|
|
cabbd234af | ||
|
|
480039ca50 | ||
|
|
81d5cddac9 | ||
|
|
bb3e83548c | ||
|
|
a3ff58c42d | ||
|
|
30bf4542f0 | ||
|
|
d11c9a4615 | ||
|
|
d4679e0029 | ||
|
|
3c4d6eb8e4 | ||
|
|
df98e1f8ac | ||
|
|
56fc2986e9 | ||
|
|
627f18c42b | ||
|
|
67b341cbd6 | ||
|
|
eff5b94db7 | ||
|
|
707323ca12 | ||
|
|
cb2ba2f53a | ||
|
|
777cd310ef | ||
|
|
9a4ec5a899 | ||
|
|
c92e4e06cc | ||
|
|
39135f8c80 | ||
|
|
5e054cfc77 | ||
|
|
3b72c13f9d | ||
|
|
65ed110ab7 | ||
|
|
5dd7ea3060 | ||
|
|
bd9cbf5e3b | ||
|
|
65f6a4eddd | ||
|
|
e9ff554f39 | ||
|
|
b65d178cf1 | ||
|
|
157a59e3c4 | ||
|
|
d24c65c3aa | ||
|
|
d4d441ff9b | ||
|
|
f43fb1223f | ||
|
|
3db275414d | ||
|
|
6e0ff6c805 | ||
|
|
9c359efbff | ||
|
|
c5cc1ea8e8 | ||
|
|
afb6a33131 | ||
|
|
539a11f54d | ||
|
|
9324e549e6 | ||
|
|
c8f6b6df87 | ||
|
|
38f632097e | ||
|
|
e63743f537 | ||
|
|
ce13295cf0 | ||
|
|
23c02a3510 | ||
|
|
01ea7b9ee1 | ||
|
|
6fed1284a1 | ||
|
|
5574d849bd | ||
|
|
c2ce3a6992 | ||
|
|
b61d15fdf4 | ||
|
|
ad5cc83fb3 | ||
|
|
2f53edd775 | ||
|
|
923459c88b | ||
|
|
1192d26448 | ||
|
|
2d5e827417 | ||
|
|
a30d9276b8 | ||
|
|
b963c2272f | ||
|
|
221cb8709b | ||
|
|
648857fc44 | ||
|
|
8091051bb4 | ||
|
|
1e468cd3e0 | ||
|
|
15d2a5bb2b | ||
|
|
5c76229ee5 | ||
|
|
0cd088122e | ||
|
|
b6f3467a89 | ||
|
|
52ce1a5959 | ||
|
|
7ce05762f5 | ||
|
|
cce736bc16 | ||
|
|
2a1127e637 | ||
|
|
8ca68b3d7a | ||
|
|
0f76d893b8 | ||
|
|
ab746b5195 | ||
|
|
828c87df39 | ||
|
|
888aeb621d | ||
|
|
ac2fddf87e | ||
|
|
614304cc81 | ||
|
|
f363d66aac | ||
|
|
1706c42695 | ||
|
|
2b44b2fc0b | ||
|
|
25986aa15c | ||
|
|
264eb01afc | ||
|
|
6db3c3f9f1 | ||
|
|
714992bdcb | ||
|
|
ca7a86b6d7 | ||
|
|
49e729e9ec | ||
|
|
7f0a690c7b | ||
|
|
d07afc603b | ||
|
|
5bd4c00257 | ||
|
|
13272aa280 | ||
|
|
260358c5fb | ||
|
|
0f07b2c095 | ||
|
|
b2fafc22dd | ||
|
|
6200ec8e0e | ||
|
|
831516c5f5 | ||
|
|
fb49d9e6ae | ||
|
|
e660726828 | ||
|
|
4519f47b19 | ||
|
|
47174bbb4d | ||
|
|
de18ce3e7b | ||
|
|
b5934633fa | ||
|
|
f314c08216 | ||
|
|
6704cded2d | ||
|
|
f11918fc41 | ||
|
|
58d5aa9944 | ||
|
|
60af2ad0a4 | ||
|
|
4ec8994c38 | ||
|
|
49d6cece50 | ||
|
|
03251d5bd5 | ||
|
|
a6e03184bc | ||
|
|
54f2a6f1b5 | ||
|
|
7eed7ae6ba | ||
|
|
b3d642fdf5 | ||
|
|
35dd9427a5 | ||
|
|
bf1087df3c | ||
|
|
523ef08697 | ||
|
|
c2310453d8 | ||
|
|
b4a6a5dc26 | ||
|
|
20ceb440fa | ||
|
|
4ee9c66524 | ||
|
|
9d04abd3af | ||
|
|
a562345876 | ||
|
|
2e7daceb3c | ||
|
|
a9aba3484a | ||
|
|
1b9bbb8eb6 | ||
|
|
25c44723a9 | ||
|
|
688a20c312 | ||
|
|
4c1b34461e | ||
|
|
c6939e7194 | ||
|
|
cd9432e395 | ||
|
|
7d5244a5bc | ||
|
|
1aa5d4f7de | ||
|
|
ad74204fe4 | ||
|
|
5cb1a47ed3 | ||
|
|
fff22d3cd1 | ||
|
|
665cd7b996 | ||
|
|
61173196d2 | ||
|
|
03e98ba562 | ||
|
|
6195290adf | ||
|
|
4f1b0055e1 | ||
|
|
dd9fe87ff4 | ||
|
|
5bda4abfce | ||
|
|
24f98aede5 | ||
|
|
f8ae1e9076 | ||
|
|
16b0dcad71 | ||
|
|
9c1265d50d | ||
|
|
8c0e781c94 | ||
|
|
a47bef1f13 | ||
|
|
053663bd76 | ||
|
|
e40b916b07 | ||
|
|
2ffe3bf722 | ||
|
|
1bc994c102 | ||
|
|
21be4d38e1 | ||
|
|
3b58c571b3 | ||
|
|
e0430cf607 | ||
|
|
f7889b785d | ||
|
|
27829a024a | ||
|
|
a0fda2552f | ||
|
|
65c185ebd2 | ||
|
|
fb425d3524 | ||
|
|
5792eb354c | ||
|
|
45300b64c5 | ||
|
|
dce7d91511 | ||
|
|
4b7ef46f82 | ||
|
|
b67e6d20f1 | ||
|
|
3d1afa74d4 | ||
|
|
0ae6575087 | ||
|
|
d8c94f5ece | ||
|
|
61f1e11a48 | ||
|
|
98249b1aca | ||
|
|
8e51b7fc1d | ||
|
|
71914b8a8b | ||
|
|
7a077a0d21 | ||
|
|
d23e4665e7 | ||
|
|
df12b54856 | ||
|
|
ae12053d74 | ||
|
|
0e7695f2d6 | ||
|
|
38cee87ee9 | ||
|
|
c64a6083e2 | ||
|
|
e0501e46e3 | ||
|
|
4a0ccc4c2f | ||
|
|
c92b7240eb | ||
|
|
6a74c3faeb | ||
|
|
c5ae872787 | ||
|
|
2e9ab0a9d7 | ||
|
|
08f67860be | ||
|
|
3fab1d92b7 | ||
|
|
6573157112 | ||
|
|
e83841eb0b | ||
|
|
6a0e18c0e9 | ||
|
|
b71b6473e3 | ||
|
|
a004f62fe8 | ||
|
|
c69e5e05a3 | ||
|
|
07ce20fd29 | ||
|
|
df74f7c6c7 | ||
|
|
2a6e2d470d | ||
|
|
643dd65113 | ||
|
|
cee0532b85 | ||
|
|
845e7fb956 | ||
|
|
f5cc2c3a37 | ||
|
|
d56c2f7a50 | ||
|
|
5c47a8f7e1 | ||
|
|
fb724e0150 | ||
|
|
94ef47c89d | ||
|
|
7e415b3fd7 | ||
|
|
fee497219e | ||
|
|
7624f09f98 | ||
|
|
946fe0a94c | ||
|
|
c7e1699546 | ||
|
|
de4a617743 | ||
|
|
228d62dd32 | ||
|
|
51f8a60096 | ||
|
|
83f1a95966 | ||
|
|
646ba94d10 | ||
|
|
8c5c08d678 | ||
|
|
3d0b47a181 | ||
|
|
ea7bece3b8 | ||
|
|
944a8e9cd6 | ||
|
|
88f7d1d572 | ||
|
|
6d78c1e302 | ||
|
|
791aa8b6d1 | ||
|
|
a078b67b36 | ||
|
|
34d4ecd4e6 | ||
|
|
5047b535c4 | ||
|
|
768d239840 | ||
|
|
89392553bd | ||
|
|
9a41217a59 | ||
|
|
f6557e4980 | ||
|
|
cc3cd95e2d | ||
|
|
8fb02136bb | ||
|
|
1c86585f03 | ||
|
|
242085966b | ||
|
|
6217075adc | ||
|
|
883f233c09 | ||
|
|
e4cc749180 | ||
|
|
9780798b75 | ||
|
|
225325cc29 | ||
|
|
2e3307065c | ||
|
|
f6a4316093 | ||
|
|
b80aad7449 | ||
|
|
5dbd1d093b | ||
|
|
64e2e1109e | ||
|
|
9c60140cec | ||
|
|
6099b664ac | ||
|
|
33d084eff0 | ||
|
|
07eb14eaa6 | ||
|
|
b2710fafd4 | ||
|
|
8f44b4571a | ||
|
|
169f010fae | ||
|
|
3e1547e0da | ||
|
|
825b9935ee | ||
|
|
2f2e086fbb | ||
|
|
2f6e0c15fe | ||
|
|
09e0c3dd63 | ||
|
|
4529ac9b92 | ||
|
|
8dac4eca55 | ||
|
|
bf56091dc7 | ||
|
|
f10f580f1c | ||
|
|
c09861d1ca | ||
|
|
8e8df2b846 | ||
|
|
d4a1d5bb9e | ||
|
|
84145aa7a7 | ||
|
|
25f32a4776 | ||
|
|
097c42d1dd | ||
|
|
91ac368a19 | ||
|
|
f959dd0519 | ||
|
|
444605920f | ||
|
|
a102d8b39f | ||
|
|
22eb861d28 | ||
|
|
779b36bf7b | ||
|
|
f36336403b | ||
|
|
b7fbade968 | ||
|
|
0535b20db7 | ||
|
|
ddd91e3078 | ||
|
|
97f734b8fc | ||
|
|
38941f02a8 | ||
|
|
84e09be199 | ||
|
|
995a89d370 | ||
|
|
6f11a269bc | ||
|
|
24a212a987 | ||
|
|
3282ba0302 | ||
|
|
a4db443f93 | ||
|
|
f8276f7e59 | ||
|
|
b545634d87 | ||
|
|
5da4cace94 | ||
|
|
1a2475dc25 | ||
|
|
2374239238 | ||
|
|
f84d77d334 | ||
|
|
5a275e6153 | ||
|
|
5f07069f1d | ||
|
|
1e1cebc3fc | ||
|
|
c40ad75fa2 | ||
|
|
c80db72bf7 | ||
|
|
727ec6bc28 | ||
|
|
9ebad734a9 | ||
|
|
0094628f6b | ||
|
|
abbb046566 | ||
|
|
a1136953d0 | ||
|
|
3298a5c6ad | ||
|
|
5e24fe9bb1 | ||
|
|
bc5b32ddef | ||
|
|
7269dbb79d | ||
|
|
6f5bb6ffa2 | ||
|
|
7d0b738918 | ||
|
|
1539d9c7ed | ||
|
|
4ae95e06ef | ||
|
|
fa1166d014 | ||
|
|
2d9e5fe984 | ||
|
|
7ae934e940 | ||
|
|
64b2eeface | ||
|
|
558dc591a3 | ||
|
|
0bfd766a0b | ||
|
|
7741713a7c | ||
|
|
454b540bce | ||
|
|
591c62b6d5 | ||
|
|
fdb4a7b055 | ||
|
|
f845ad9b31 | ||
|
|
f5f33ec865 | ||
|
|
d35faf15d7 | ||
|
|
7085bce6d4 | ||
|
|
a81890e844 | ||
|
|
8dbec21b02 | ||
|
|
a654c8229a | ||
|
|
f2e35c185b | ||
|
|
062c2643ad | ||
|
|
088c7b35ba | ||
|
|
ef439a6c42 | ||
|
|
40956a4042 | ||
|
|
8680e90e3b | ||
|
|
fac770424c | ||
|
|
042be9da6d | ||
|
|
81b0ea1eef | ||
|
|
52289d1283 | ||
|
|
1843d23203 | ||
|
|
1a32fef987 | ||
|
|
c740c8304b | ||
|
|
c3401d478b | ||
|
|
cf583bcd55 | ||
|
|
ef143a7ebb | ||
|
|
7aec483e73 | ||
|
|
db6a3b9849 | ||
|
|
657c5e1f52 | ||
|
|
3b0466d7cb | ||
|
|
8a9177b459 | ||
|
|
a0e63511d6 | ||
|
|
7c3f7d4b8e | ||
|
|
be1062c373 | ||
|
|
2bd5ab06a7 | ||
|
|
e174c1b147 | ||
|
|
7ef191be2a | ||
|
|
03a29aeedf | ||
|
|
d10b4c1e13 | ||
|
|
ab2046a2c2 | ||
|
|
bc6b2e0f3e | ||
|
|
746c99ebd6 | ||
|
|
34945e7eba | ||
|
|
507b217065 | ||
|
|
e222f17199 | ||
|
|
144cfecc0f | ||
|
|
64066bfc90 | ||
|
|
366ac4ee14 | ||
|
|
851984ee66 | ||
|
|
34bdc0e80b | ||
|
|
61d7d5e041 | ||
|
|
b5278550e7 | ||
|
|
461635c001 | ||
|
|
fcb1d8e104 | ||
|
|
6cbc2b707a | ||
|
|
9671542bdf | ||
|
|
de325c1208 | ||
|
|
802eff1faa | ||
|
|
068f9e42d7 | ||
|
|
4a483c3b27 | ||
|
|
e3bd958069 | ||
|
|
900cf0a9d0 | ||
|
|
64c424b9a6 | ||
|
|
5734c29312 | ||
|
|
362caa6ac1 | ||
|
|
b620976b70 | ||
|
|
daba4ef09e | ||
|
|
c697a34239 | ||
|
|
09b7cb3d85 | ||
|
|
d7e48662e0 | ||
|
|
9fd5c6f230 | ||
|
|
aa7825d4aa | ||
|
|
436725b38e | ||
|
|
922d935bc1 | ||
|
|
3716395453 | ||
|
|
69833f66e3 | ||
|
|
ec787b913c | ||
|
|
89f313295e | ||
|
|
7bc7a44c72 | ||
|
|
317a882386 | ||
|
|
3a9f585b6b | ||
|
|
7bbb1c0822 | ||
|
|
d3b1f6110f | ||
|
|
a6dc3d2aff | ||
|
|
d946a9e526 | ||
|
|
17dd1b193e | ||
|
|
d634fd3236 | ||
|
|
e861e5b3d6 | ||
|
|
6045f4dd91 | ||
|
|
8be2841bdf | ||
|
|
b6e37b9e67 | ||
|
|
0d0d582bd8 | ||
|
|
0c42227e5e | ||
|
|
98ac4bd5c8 | ||
|
|
a43b100781 | ||
|
|
c7f9bfbb43 | ||
|
|
b5f8e6925b | ||
|
|
993e59413a | ||
|
|
9f2fab78a1 | ||
|
|
3bdf1377c0 | ||
|
|
8f24cf07be | ||
|
|
17c40234e9 | ||
|
|
4cecddcdd0 | ||
|
|
1f4516b954 | ||
|
|
1d76ee4871 | ||
|
|
b53cac4c7a | ||
|
|
29a0644719 | ||
|
|
c833078e71 | ||
|
|
e4cba8d19f | ||
|
|
cd6620712f | ||
|
|
85619b156d | ||
|
|
10debbc286 | ||
|
|
dcac5b488a | ||
|
|
e1009bdafa | ||
|
|
38ce842ca8 | ||
|
|
aafd09569c | ||
|
|
67a9df686e | ||
|
|
9a374711fd | ||
|
|
b9138acbc8 | ||
|
|
2a65916f7c | ||
|
|
6aa1f1cca0 | ||
|
|
8c1ebfda02 | ||
|
|
81af5d7497 | ||
|
|
368bf08ade | ||
|
|
d95f623ca9 | ||
|
|
2856fbc42b | ||
|
|
ac59e15bd9 | ||
|
|
91d9bbdc97 | ||
|
|
575f4e1786 | ||
|
|
bff905fae5 | ||
|
|
c0fa135bf6 | ||
|
|
86394d8f19 | ||
|
|
72c233cb0d | ||
|
|
04e2c02eff | ||
|
|
7362744df2 | ||
|
|
01951b5c32 | ||
|
|
f2f52771bd | ||
|
|
b59167d3ca | ||
|
|
88e466562c | ||
|
|
1f85e5d7f8 | ||
|
|
50471d510e | ||
|
|
8b7cf2f725 | ||
|
|
282a5109ba | ||
|
|
3d3b4738d9 | ||
|
|
66149bb591 | ||
|
|
b0ad664ece | ||
|
|
7c29ea836c | ||
|
|
92e9e8c56a | ||
|
|
12bf26223d | ||
|
|
56d7993c8f | ||
|
|
52b63927b4 | ||
|
|
86558bdef6 | ||
|
|
e46262b021 | ||
|
|
c53feb5ccb | ||
|
|
fc6d4f0990 | ||
|
|
df948bde9d | ||
|
|
203a720ae1 | ||
|
|
3410f08cfb | ||
|
|
a553914ef4 | ||
|
|
21220141f2 | ||
|
|
caf2d8436b | ||
|
|
4cc305fa81 | ||
|
|
60f837d0b9 | ||
|
|
05bd7f8e6b | ||
|
|
e58ab34a15 | ||
|
|
d960758ef3 | ||
|
|
a36ccdcc39 | ||
|
|
d582948377 | ||
|
|
d806e0b1c3 | ||
|
|
b9e110a7e3 | ||
|
|
a2f218d56d | ||
|
|
2c475011a1 | ||
|
|
2d7fc33726 | ||
|
|
0c8d1e1dc4 | ||
|
|
bb04ce2abb | ||
|
|
9850b22c0a | ||
|
|
a2f65666a5 | ||
|
|
7730809dbb | ||
|
|
2ac818dcdd | ||
|
|
113947b9f0 | ||
|
|
44bc2d769b | ||
|
|
02ecfebb85 | ||
|
|
a1fed62591 | ||
|
|
778ed6ad91 | ||
|
|
7d539f5810 | ||
|
|
b407acbc07 | ||
|
|
3260260dce | ||
|
|
70c1290993 | ||
|
|
6bae60c51e | ||
|
|
a45adb6b3a | ||
|
|
476aaf5d3e | ||
|
|
58187b6969 | ||
|
|
f3a3d81d96 | ||
|
|
7a40b54153 | ||
|
|
9dd62d3538 | ||
|
|
76e4a6ed83 | ||
|
|
7a9eb06677 | ||
|
|
26f54e7619 | ||
|
|
117b7ae414 | ||
|
|
baeac324d6 | ||
|
|
0db0f003dc | ||
|
|
2d4f341710 | ||
|
|
b8a41dc937 | ||
|
|
2f2bb0de4f | ||
|
|
3b76d7f47e | ||
|
|
10b74e507f | ||
|
|
8a03a9462b | ||
|
|
e5bca224e9 | ||
|
|
197bf5d0cf | ||
|
|
d8b15ebcdb | ||
|
|
078466241f | ||
|
|
57c3eb5d2c | ||
|
|
a4876167c4 | ||
|
|
a38a5654a9 | ||
|
|
69a41879bb | ||
|
|
e3524a506b | ||
|
|
8447c563ea | ||
|
|
fd61a4b23a | ||
|
|
9257311896 | ||
|
|
23e870e416 | ||
|
|
8270b28d85 | ||
|
|
1a0889d3d9 | ||
|
|
5382d99a94 | ||
|
|
3e4bb88089 | ||
|
|
2f3f53a978 | ||
|
|
89755b1005 | ||
|
|
a7203ea90a | ||
|
|
afb0ac14c4 | ||
|
|
745dfc71bc | ||
|
|
82d9689d1b | ||
|
|
6afaef1654 | ||
|
|
bb42d86012 | ||
|
|
5b44580061 | ||
|
|
4eac743812 | ||
|
|
ed8ab37bd5 | ||
|
|
563c3f0f1b | ||
|
|
296e6e8e8f | ||
|
|
334aab2755 | ||
|
|
419f4f3156 | ||
|
|
ec5a26e8dd | ||
|
|
2b7cd36eea | ||
|
|
2f11731052 | ||
|
|
23a0846533 | ||
|
|
666858f8e2 | ||
|
|
2288b7f7b2 | ||
|
|
498af28efb | ||
|
|
3902ab3375 | ||
|
|
6bb0bdf66e | ||
|
|
b9ade2295e | ||
|
|
44b5f5a919 | ||
|
|
17d37494c2 | ||
|
|
f0d81e98a0 | ||
|
|
e3b13f7b4a | ||
|
|
ba2686630a | ||
|
|
e195cfa6a0 | ||
|
|
b9fbd1906f | ||
|
|
1f611bafef | ||
|
|
af7faa59dc | ||
|
|
0b21ee46ea | ||
|
|
f64996a350 | ||
|
|
d7cccd1980 | ||
|
|
b9467d9236 | ||
|
|
69096b15ae | ||
|
|
a075e62bad | ||
|
|
1ebe367e07 | ||
|
|
db229f25bf | ||
|
|
787c93b9d4 | ||
|
|
63953992a9 | ||
|
|
567cb0c0b6 | ||
|
|
f37999a3ef | ||
|
|
545761e3d4 | ||
|
|
97ea67d01d | ||
|
|
561d679a62 | ||
|
|
991cd91dd4 | ||
|
|
9e51ff0253 | ||
|
|
1a1e55e16c | ||
|
|
2fe4a39784 | ||
|
|
7d81b9ef5c | ||
|
|
b355b6dc60 | ||
|
|
819148762b | ||
|
|
4112a86fe9 | ||
|
|
7838c9b49b | ||
|
|
ca5a70e3bc | ||
|
|
d6376c3a91 | ||
|
|
e134143f16 | ||
|
|
0f1577d314 | ||
|
|
793b356c01 | ||
|
|
a36858f3ea | ||
|
|
aaa6637435 | ||
|
|
f0f6739cf8 | ||
|
|
25efdd3d4f | ||
|
|
0b2483ea15 | ||
|
|
2c38ce910c | ||
|
|
4d26ec0789 | ||
|
|
edba923f2f | ||
|
|
7d907aac0f | ||
|
|
d6981550a8 | ||
|
|
8b0636367b | ||
|
|
a6c9d0f9bc | ||
|
|
4b0d2f7abc | ||
|
|
dd28781b69 | ||
|
|
ece56032f1 | ||
|
|
61ebe9780e | ||
|
|
e887082501 | ||
|
|
8e3039dd37 | ||
|
|
1d1c130d19 | ||
|
|
1fd3f70eec | ||
|
|
d9ea33cbb9 | ||
|
|
80778aa267 | ||
|
|
445cb4f146 | ||
|
|
95db2aa14f | ||
|
|
4b0fc637eb | ||
|
|
48d6b4cfa1 | ||
|
|
fc11182bbe | ||
|
|
25b72e1af4 | ||
|
|
1848338ef7 | ||
|
|
ff0446cc12 | ||
|
|
08ceb57c31 | ||
|
|
affb332eb9 | ||
|
|
6455c38ff4 | ||
|
|
f62c2fbabb | ||
|
|
d5276c9d4a | ||
|
|
4c0fc5a407 | ||
|
|
f608cd5aef | ||
|
|
0371b62acb | ||
|
|
eddd66b5c4 | ||
|
|
c8e71d269b | ||
|
|
ae034d5387 | ||
|
|
07e5c568c4 | ||
|
|
817d6a0e15 | ||
|
|
31fdd24c3e | ||
|
|
bfa0e4ba49 | ||
|
|
dff98f0b53 | ||
|
|
d0856ff279 | ||
|
|
c89ff2c3d6 | ||
|
|
4ec88d524a | ||
|
|
d6b762efa7 | ||
|
|
b476a26759 | ||
|
|
48c218b430 | ||
|
|
1a062e2170 | ||
|
|
fe658eb877 | ||
|
|
d316836e90 | ||
|
|
8443f61f0a | ||
|
|
feed55186f | ||
|
|
56591b8655 | ||
|
|
7c52ca15f3 | ||
|
|
06c751f214 | ||
|
|
9d774eaad8 | ||
|
|
babfd4abda | ||
|
|
a029c165a0 | ||
|
|
9b51533d96 | ||
|
|
e1b7e0eb00 | ||
|
|
508a5693c9 | ||
|
|
67806f3d76 | ||
|
|
f687a30c7e | ||
|
|
15615a1077 | ||
|
|
905175c210 | ||
|
|
409e070887 | ||
|
|
f659dc1f76 | ||
|
|
b8922b39fd | ||
|
|
8137d57cdf | ||
|
|
bf290ac1a9 | ||
|
|
77fda00233 | ||
|
|
0255088e30 | ||
|
|
b74d8b12d0 | ||
|
|
2834f2982c | ||
|
|
7d07faa5fb | ||
|
|
fd70f0fc4a | ||
|
|
7744f84e85 | ||
|
|
441ef79aa4 | ||
|
|
c54f6ba4d2 | ||
|
|
61ecdfc48c | ||
|
|
5e2b259af1 | ||
|
|
1258f3e17c | ||
|
|
44ed895277 | ||
|
|
c74e18e449 | ||
|
|
2ea3f914f0 | ||
|
|
d6e4a50064 | ||
|
|
6296896471 | ||
|
|
473cda971a | ||
|
|
cf570adabe | ||
|
|
02196f2883 | ||
|
|
8b49752401 | ||
|
|
8c64867918 | ||
|
|
e544063c40 | ||
|
|
edfaaacd04 | ||
|
|
84b8613cf5 | ||
|
|
aea82e2266 | ||
|
|
b0b9c1c8e6 | ||
|
|
b5ff32c5b6 | ||
|
|
e0223ded54 | ||
|
|
2012ad0aa3 | ||
|
|
15d72a8dcb | ||
|
|
e525b11695 | ||
|
|
8f30e60e1b | ||
|
|
ce977ac937 | ||
|
|
aa9ffa0855 | ||
|
|
6a8ca810ff | ||
|
|
f2d2fd7014 | ||
|
|
ddd06b3162 | ||
|
|
d519aa1dad | ||
|
|
7226359e64 | ||
|
|
f396ff7f12 | ||
|
|
1adee0af17 | ||
|
|
bac47dad83 | ||
|
|
f1a2602cfd | ||
|
|
b8e64d4369 | ||
|
|
72ac8ca872 | ||
|
|
ccb41829c9 | ||
|
|
90697194a1 | ||
|
|
13f4baa34e | ||
|
|
76840ff5c2 | ||
|
|
72ac806cb8 | ||
|
|
bf275fe564 | ||
|
|
68818ae50d | ||
|
|
7315d097c2 | ||
|
|
cdf28700cf | ||
|
|
948c1d0bb0 | ||
|
|
de0a3f929c | ||
|
|
c3023a9f99 | ||
|
|
4f37610dfb | ||
|
|
aef4316f72 | ||
|
|
378dbf254a | ||
|
|
f0b6a37ce2 | ||
|
|
ff12a120e6 | ||
|
|
d04be4d71b | ||
|
|
581aaaad28 | ||
|
|
453eb9feb4 | ||
|
|
4059ee44b8 | ||
|
|
7a222ecfa0 | ||
|
|
be15e9adf2 | ||
|
|
2fd097c613 | ||
|
|
66ee5f5392 | ||
|
|
3bb08f8d30 | ||
|
|
d7787adddc | ||
|
|
1f37b879b1 | ||
|
|
45ce28f9bf | ||
|
|
4e87bed4e5 | ||
|
|
a7421fc670 | ||
|
|
208a7550ef | ||
|
|
37e23f70d6 | ||
|
|
7daefa8ae5 | ||
|
|
292ac42003 | ||
|
|
c501c45c52 | ||
|
|
29b894f8b0 | ||
|
|
55573bf40a | ||
|
|
f2c2ef82c5 | ||
|
|
d6b33d353c | ||
|
|
2ed1c36c54 | ||
|
|
7cbcbc1171 | ||
|
|
b74dcfa053 | ||
|
|
62bf7eb227 | ||
|
|
07bfe8e29a | ||
|
|
0b258997dd | ||
|
|
69421ad7a1 | ||
|
|
fdf571c016 | ||
|
|
bd60760f9d | ||
|
|
e8aa9839b0 | ||
|
|
fcdb22db5b | ||
|
|
e73cf68def | ||
|
|
fa5b842cc7 | ||
|
|
43a21cb341 | ||
|
|
45361b57a7 | ||
|
|
046c7a662a | ||
|
|
a9f1de13b1 | ||
|
|
edbe2d86f2 | ||
|
|
4e12a1cdad | ||
|
|
d24c7ea93e | ||
|
|
484f1e8d51 | ||
|
|
8d5abb877c | ||
|
|
fd454dce74 | ||
|
|
5d4fccd438 | ||
|
|
9f078e1483 | ||
|
|
0e807d84c2 | ||
|
|
3ad57d995b | ||
|
|
28cf42aeb8 | ||
|
|
7fcf74a8ed | ||
|
|
a0d38f7465 | ||
|
|
cd97526d2b | ||
|
|
87fdc16f9b | ||
|
|
b69eb02766 | ||
|
|
7636234649 | ||
|
|
2bd673a531 | ||
|
|
a1b64bc72d | ||
|
|
80bc9d6b23 | ||
|
|
ee768b9147 | ||
|
|
7dfb0c67e5 | ||
|
|
75ea5cc462 | ||
|
|
afabf6fd00 | ||
|
|
0eb4519797 | ||
|
|
611f04ab5a | ||
|
|
a9ba2deafa | ||
|
|
0c4e920af3 | ||
|
|
ef0e565337 | ||
|
|
02e7ab41b4 | ||
|
|
59bd4541c4 | ||
|
|
ca30af4238 | ||
|
|
718b3f2623 | ||
|
|
6e153c6451 | ||
|
|
4a1809d56e | ||
|
|
a2bf0c1bea | ||
|
|
f0d9dae33b | ||
|
|
b99462b628 | ||
|
|
65ac30acda | ||
|
|
2072b6fa63 | ||
|
|
4f604ba687 | ||
|
|
b9fe559b42 | ||
|
|
efcdbebda5 | ||
|
|
8886850c60 | ||
|
|
20276e5230 | ||
|
|
ed96bc83b4 | ||
|
|
c0147f5eb7 | ||
|
|
5bf5f024cb | ||
|
|
4628e8320a | ||
|
|
9c1d36d18a | ||
|
|
789b618e37 | ||
|
|
d0804a6f9e | ||
|
|
17fe977b06 | ||
|
|
60783ca390 | ||
|
|
34a7a37409 | ||
|
|
e68d2b5deb | ||
|
|
b0317055e7 | ||
|
|
6e0af7c144 | ||
|
|
9394ed663a | ||
|
|
967574820f | ||
|
|
da17d1e5d1 | ||
|
|
63bdbebcaa | ||
|
|
c67263662d | ||
|
|
2484457183 | ||
|
|
d7b328b887 | ||
|
|
493e53c28f | ||
|
|
7438c30885 | ||
|
|
fac43ba20b | ||
|
|
c2eb243026 | ||
|
|
2adaee8671 | ||
|
|
57edc5678c | ||
|
|
730130b19e | ||
|
|
17b0cee507 | ||
|
|
2557383946 | ||
|
|
29d3b5dfc6 | ||
|
|
f6fad30852 | ||
|
|
d5a081a15f | ||
|
|
6147e9ac96 | ||
|
|
b32ca4f92f | ||
|
|
b57dd51f86 | ||
|
|
fc6ca162af | ||
|
|
58ba9e9d1d | ||
|
|
939b6c468d | ||
|
|
bf7df6721a | ||
|
|
f6e0dbbb6a | ||
|
|
851c2d88a9 | ||
|
|
200c13dc31 | ||
|
|
8889e35f9e | ||
|
|
398fa1e73d | ||
|
|
316f73138c | ||
|
|
708b615ad7 | ||
|
|
866ff78518 | ||
|
|
f0480b033f | ||
|
|
4d19548736 | ||
|
|
fcf45554ef | ||
|
|
799b0fae94 | ||
|
|
0d95716545 | ||
|
|
8c5b808eba | ||
|
|
007dcf548e | ||
|
|
9a640bf7eb | ||
|
|
edd02d9dd6 | ||
|
|
75edbb62f1 | ||
|
|
2b6227f3b1 | ||
|
|
c4b8a41742 | ||
|
|
f8b2dbe283 | ||
|
|
20091292f4 | ||
|
|
a594f45aae | ||
|
|
d394d01ea8 | ||
|
|
70d982b0ed | ||
|
|
ae68a35a1a | ||
|
|
a9fcfe60f4 | ||
|
|
c3b028ef4b | ||
|
|
833399f068 | ||
|
|
5695cf4ac5 | ||
|
|
1553e5efd5 | ||
|
|
0f1b396dd2 | ||
|
|
976ee51bf5 | ||
|
|
7c22f6e83b | ||
|
|
18b6f7b84c | ||
|
|
206d09f7f8 | ||
|
|
4e910c4b09 | ||
|
|
bc3cd50a6c | ||
|
|
cecf611f91 | ||
|
|
5e4802f05e | ||
|
|
1e70e654ed | ||
|
|
20054b9825 | ||
|
|
5fb6a53cbd | ||
|
|
ff751c30f9 | ||
|
|
c1614ad5a7 | ||
|
|
13e372159a | ||
|
|
1ee0aafd9a | ||
|
|
df80ec635f | ||
|
|
6524286f04 | ||
|
|
87248fec53 | ||
|
|
6470d25d18 | ||
|
|
b7634a8ac3 | ||
|
|
434755a620 | ||
|
|
3eb2529b0b | ||
|
|
e6e4cca076 | ||
|
|
8d57bbc777 | ||
|
|
483db564f9 | ||
|
|
63d5862319 | ||
|
|
365b379798 | ||
|
|
20f97e48a9 | ||
|
|
fd473f0a46 | ||
|
|
94c5ed8bdc | ||
|
|
4b7b4bf110 | ||
|
|
a816877d08 | ||
|
|
2d56ae1cb6 | ||
|
|
a223da8f99 | ||
|
|
216adcc35a | ||
|
|
8d485d5fa2 | ||
|
|
fa549fcf94 | ||
|
|
98fd707aea | ||
|
|
5928f29f11 | ||
|
|
c3ebabbe44 | ||
|
|
29fd094dd0 | ||
|
|
f7966b8d8c | ||
|
|
ee2f4ecbc8 | ||
|
|
b08f6cad1d | ||
|
|
e851223733 | ||
|
|
1f12546ff4 | ||
|
|
8345063e84 | ||
|
|
482bedd739 | ||
|
|
7d7a334418 | ||
|
|
69dbe62b70 | ||
|
|
f61167cedf | ||
|
|
2ac92a75a4 | ||
|
|
fe80a9fd08 | ||
|
|
505af7635f | ||
|
|
426dc69e1d | ||
|
|
082e8c062c | ||
|
|
9f2409bb9e | ||
|
|
14dd8e43a4 | ||
|
|
9fb33526a7 | ||
|
|
8bd00bf450 | ||
|
|
f3553ced78 | ||
|
|
a52dc43c9e | ||
|
|
0b6b40a358 | ||
|
|
f6371d2ef1 | ||
|
|
ecd073e31d | ||
|
|
b7d160631a | ||
|
|
dc235f36c8 | ||
|
|
7503c1e1e9 | ||
|
|
253e8a209c | ||
|
|
e26b692631 | ||
|
|
658d808524 | ||
|
|
28e00055ab | ||
|
|
8eef1eaa7c | ||
|
|
e361a857a4 | ||
|
|
d369ce8847 | ||
|
|
d05897edcb | ||
|
|
1d98432c57 | ||
|
|
e60166dc89 | ||
|
|
87afc2fcef | ||
|
|
bd1457c435 | ||
|
|
c1f88b4a5f | ||
|
|
a080d4b692 | ||
|
|
c20e9e19cb | ||
|
|
d9056acc6d | ||
|
|
21cd764f66 | ||
|
|
dfc31dfd5c | ||
|
|
8ffbf32677 | ||
|
|
6e2124252c | ||
|
|
3dd07b8c23 | ||
|
|
2e067aada6 | ||
|
|
78cd60f3df | ||
|
|
2891649531 | ||
|
|
4b40739918 | ||
|
|
96c401e1b9 | ||
|
|
fad132dcca | ||
|
|
fae9650f56 | ||
|
|
e26e7f53c5 | ||
|
|
696710bf41 | ||
|
|
9019cbfd2b | ||
|
|
f8735927bf | ||
|
|
16cfc4e945 | ||
|
|
85f931316a | ||
|
|
717ea05d38 | ||
|
|
3987274764 | ||
|
|
73a97f9c2a | ||
|
|
80612ba97d | ||
|
|
c4d2b92e34 | ||
|
|
169fba9ab8 | ||
|
|
0b902e19ee | ||
|
|
ab26a76789 | ||
|
|
1f43fbe16e | ||
|
|
f29cb99530 | ||
|
|
ef84703da9 | ||
|
|
9e32ea7413 | ||
|
|
75769df8e2 | ||
|
|
95fd4cab05 | ||
|
|
a81254cd18 | ||
|
|
4cff838de0 | ||
|
|
3838b3ca4f | ||
|
|
7dc3e041c8 | ||
|
|
33b92423d8 | ||
|
|
6237fffa5a | ||
|
|
0b4e2d3b6b | ||
|
|
c4d08fa7b7 | ||
|
|
cec28351e7 | ||
|
|
398630d51e | ||
|
|
61400ba726 | ||
|
|
33885e2216 | ||
|
|
5719b136fe | ||
|
|
ede5ee60c3 | ||
|
|
9620ac7e7e | ||
|
|
c0bfdbf4bb | ||
|
|
efb544a303 | ||
|
|
adfc4b7244 | ||
|
|
7a5ce98569 | ||
|
|
1fcceb0901 | ||
|
|
22c552053f | ||
|
|
818c679d4f | ||
|
|
3ff36c45aa | ||
|
|
623d461b06 | ||
|
|
7535013848 | ||
|
|
a63bac8826 | ||
|
|
d2c831c4ee | ||
|
|
840b5ce071 | ||
|
|
be6abc0025 | ||
|
|
ef585c59dd | ||
|
|
16d9c1ccad | ||
|
|
9a9ef78583 | ||
|
|
fe7ee5b610 | ||
|
|
680804040a | ||
|
|
0d4fe73daa | ||
|
|
407190c6c5 | ||
|
|
73998dbde0 | ||
|
|
8827f7df34 | ||
|
|
4c394a9e2d | ||
|
|
5946fc7404 | ||
|
|
5b8ecb2c14 | ||
|
|
61253e4d4d | ||
|
|
2c0ca5803f | ||
|
|
690f3d0f13 | ||
|
|
42c259bc58 | ||
|
|
11426a0713 | ||
|
|
e6af1b8645 | ||
|
|
103f18191d | ||
|
|
fb312236a2 | ||
|
|
c850c0095d | ||
|
|
58481268f7 | ||
|
|
11604671f8 | ||
|
|
e8feded4c3 | ||
|
|
18f9b38d25 | ||
|
|
676eea3ccc | ||
|
|
ee113d080e | ||
|
|
8bc69415a7 | ||
|
|
3979ee57ff | ||
|
|
c2ee169d16 | ||
|
|
56b51c85bb | ||
|
|
853ee5aac4 | ||
|
|
4cf406aefa | ||
|
|
1abb341cb6 | ||
|
|
44dce9598c | ||
|
|
aefc8685a1 | ||
|
|
26761342f5 | ||
|
|
ca777bcebb | ||
|
|
5742075ff2 | ||
|
|
c7a6ec9691 | ||
|
|
e0153cfa6a | ||
|
|
3fd5e1bae7 | ||
|
|
d9511a7edd | ||
|
|
040db055fd | ||
|
|
b9f8cfd10d | ||
|
|
ff46556927 | ||
|
|
d1815a3d6e | ||
|
|
fdfa4827ab | ||
|
|
5d579ccef9 | ||
|
|
4145d5578e | ||
|
|
43eb041bb8 | ||
|
|
0b8ac8fc47 | ||
|
|
de3ba5908f | ||
|
|
bbb6251aa9 | ||
|
|
6f71d92a7b | ||
|
|
a8b59cc567 | ||
|
|
f4d3660eac | ||
|
|
1f165835c6 | ||
|
|
9087f3487d | ||
|
|
4ca92ea22d | ||
|
|
e2682d633f | ||
|
|
e6cb1b5970 | ||
|
|
de2d548139 | ||
|
|
4d1a0b85e4 | ||
|
|
5cfbd8c3ad | ||
|
|
95f72be8eb | ||
|
|
95c4dfa52f | ||
|
|
b72a2f4a5f | ||
|
|
844f8595d1 | ||
|
|
1c9d9be667 | ||
|
|
2a02a743a4 | ||
|
|
968d9365d6 | ||
|
|
cdb16cc591 | ||
|
|
86bc41c15c | ||
|
|
ac05c7cfaa | ||
|
|
9c07fe5963 | ||
|
|
ed05f995b5 | ||
|
|
85491dca20 | ||
|
|
465399f803 | ||
|
|
3550c8a8f6 | ||
|
|
739c0f8f52 | ||
|
|
49aa79c612 | ||
|
|
cdde75b517 | ||
|
|
dde74af6b5 | ||
|
|
eff9c77c9a | ||
|
|
5ba633c8a1 | ||
|
|
ab67a38ca5 | ||
|
|
08f0c9d30a | ||
|
|
9d747a9f9b | ||
|
|
31ca121498 | ||
|
|
44b5f5acf1 | ||
|
|
eb4878dfc4 | ||
|
|
3dc7dc3d1a | ||
|
|
6a6133e5cd | ||
|
|
65c85d7f0b | ||
|
|
d519dfa5da | ||
|
|
73ea83bbdd | ||
|
|
235a5ec538 | ||
|
|
f81a3d03c0 | ||
|
|
6878b57fba | ||
|
|
0143e5641f | ||
|
|
5adc92c601 | ||
|
|
0b2fbddbc5 | ||
|
|
28035bf02b | ||
|
|
6c9a9de3f3 | ||
|
|
62139fc4eb | ||
|
|
88b9ed29ba | ||
|
|
d94c57afd6 | ||
|
|
b6421646ff | ||
|
|
611b6fc272 | ||
|
|
9cdbef9faf | ||
|
|
b34de70fc7 | ||
|
|
f5047fc0cc | ||
|
|
258c34e61d | ||
|
|
f365487fd6 | ||
|
|
8f65b7ee7c | ||
|
|
9397f1f39c | ||
|
|
f03121af5a | ||
|
|
239b9f8234 | ||
|
|
1620c602cf | ||
|
|
fa01303460 | ||
|
|
1e5bd916d9 | ||
|
|
62d89239fc | ||
|
|
91bee4e6c2 | ||
|
|
a465dde32f | ||
|
|
254dd5f70f | ||
|
|
1a70ed5121 | ||
|
|
16d5a550ce | ||
|
|
794cc43a41 | ||
|
|
8583bbf893 | ||
|
|
63d510f2ea | ||
|
|
2c6b26003b | ||
|
|
fdaf3bc30f | ||
|
|
816d9696b5 | ||
|
|
bdaa6a294a | ||
|
|
a6b15b9529 | ||
|
|
878529e8f8 | ||
|
|
2cb37b5bd8 | ||
|
|
de95cfc981 | ||
|
|
0477247cf2 | ||
|
|
e0319a4047 | ||
|
|
839f163ac5 | ||
|
|
ba9ad4c371 | ||
|
|
bdcf7c1828 | ||
|
|
821e9fc114 | ||
|
|
334d21897b | ||
|
|
b405c3ab32 | ||
|
|
3cabb1e02d | ||
|
|
ec7f8f5710 | ||
|
|
93f0627c5e | ||
|
|
3d8c2d689e | ||
|
|
e1572c09ff | ||
|
|
910af12fb9 | ||
|
|
acbd45341f | ||
|
|
901c89371c | ||
|
|
743534bdda | ||
|
|
98bb1a9f65 | ||
|
|
bfc602f22f | ||
|
|
dd2b61edf3 | ||
|
|
0fd58135fd | ||
|
|
4672252242 | ||
|
|
a0c61bf73a | ||
|
|
d6c19a8aff | ||
|
|
b6a933e264 | ||
|
|
0191eca9dc | ||
|
|
f962fd55bc | ||
|
|
33fa719e8d | ||
|
|
adb9352905 | ||
|
|
58574c67df | ||
|
|
04d3ba4c47 | ||
|
|
0f1d2b8685 | ||
|
|
c06a855113 | ||
|
|
e9bfd58ee1 | ||
|
|
5f02febb6c | ||
|
|
15db12fb21 | ||
|
|
c3fca6696d | ||
|
|
dd4c37cde3 | ||
|
|
aa7ffdabb0 | ||
|
|
85f6616185 | ||
|
|
c8955bdca7 | ||
|
|
669a8c9e64 | ||
|
|
b4d0eb0b99 | ||
|
|
035bebaab8 | ||
|
|
b20200318b | ||
|
|
9caf83cda9 | ||
|
|
493f9df4e2 | ||
|
|
cc61893bca | ||
|
|
3ba2fb76aa | ||
|
|
e024da277b | ||
|
|
bc1e793ce6 | ||
|
|
aa7eacc043 | ||
|
|
dd7b9f1790 | ||
|
|
d6b94345d9 | ||
|
|
aa1ac56ec3 | ||
|
|
fd969020af | ||
|
|
95f486870d | ||
|
|
f6d049da3c | ||
|
|
69f15824ca | ||
|
|
8c70d1ab79 | ||
|
|
58fd651a0b | ||
|
|
177b505cb7 | ||
|
|
8ac5dbe22a | ||
|
|
b744238fb8 | ||
|
|
57b7402753 | ||
|
|
dcaa390d24 | ||
|
|
59010f6949 | ||
|
|
6a91fad10a | ||
|
|
f03029417d | ||
|
|
f5aa342602 | ||
|
|
53582ba539 | ||
|
|
44c976948d | ||
|
|
41d5020467 | ||
|
|
1bd26005f2 | ||
|
|
b1840ce2ca | ||
|
|
769246c55d | ||
|
|
24394d4d00 | ||
|
|
cab5825b72 | ||
|
|
84beb2dfe5 | ||
|
|
f8ac39fb82 | ||
|
|
eb69d01067 | ||
|
|
1c4f255c7f | ||
|
|
4125f6ec06 | ||
|
|
5023e0d30f | ||
|
|
f65595c626 | ||
|
|
916d1eec96 | ||
|
|
c2d615315e | ||
|
|
aa96ce7134 | ||
|
|
01f83e8451 | ||
|
|
ed92e9afb9 | ||
|
|
064890c0a2 | ||
|
|
8617f48fc2 | ||
|
|
2269cf0f08 | ||
|
|
3d41eb1ab4 | ||
|
|
cace523aa8 | ||
|
|
002f55dc04 | ||
|
|
49b6951ac3 | ||
|
|
7aa17e5ad6 | ||
|
|
9db41270f3 | ||
|
|
e4852c74ab | ||
|
|
613f84aa3c | ||
|
|
2814876976 | ||
|
|
69bf3999aa | ||
|
|
2fa3b26119 | ||
|
|
5a027c552e | ||
|
|
9efecf9514 | ||
|
|
8b87c43869 | ||
|
|
f7fec834e6 | ||
|
|
de43a1215c | ||
|
|
8dc531bb7f | ||
|
|
da2584d7ee | ||
|
|
373924a959 | ||
|
|
f75032bd79 | ||
|
|
4203dc5d41 | ||
|
|
9fe1f8ff90 | ||
|
|
bc825f760d | ||
|
|
191199d9de | ||
|
|
883a66a792 | ||
|
|
411e71b9a2 | ||
|
|
5463505787 | ||
|
|
ec6fc076de | ||
|
|
5a245bf362 | ||
|
|
d95912322c | ||
|
|
9c58e73b39 | ||
|
|
3c4ccd7d57 | ||
|
|
d22943d755 | ||
|
|
974b6590d8 | ||
|
|
d414c00b74 | ||
|
|
7b79d183eb | ||
|
|
edd56cb407 | ||
|
|
c777204f50 | ||
|
|
55f12f20c1 | ||
|
|
1ac062653d | ||
|
|
b0a176a22c | ||
|
|
f4b07cb518 | ||
|
|
a0ff78a810 | ||
|
|
f22391855b | ||
|
|
1fa18447e1 | ||
|
|
2d8c8c63c9 | ||
|
|
31d5e3151b | ||
|
|
c77bfe9da2 | ||
|
|
9a7dfc55e3 | ||
|
|
63bc3bd46e | ||
|
|
b5e5a3b2da | ||
|
|
7abe32be5c | ||
|
|
f0279a6866 | ||
|
|
5ce942c9a0 | ||
|
|
59d6cc7625 | ||
|
|
4abf806837 | ||
|
|
6bfb8cf2fd | ||
|
|
1d7f1082ea | ||
|
|
1c4aec83cb | ||
|
|
e537396fec | ||
|
|
944748a0ac | ||
|
|
023925d741 | ||
|
|
93db1254ec | ||
|
|
e0725ff139 | ||
|
|
9e96aee89f | ||
|
|
db6b660270 | ||
|
|
1808e5bccf | ||
|
|
5f1601a2da | ||
|
|
60ce6658ad | ||
|
|
e664652cc5 | ||
|
|
ca48a42701 | ||
|
|
00ea8ac4e1 | ||
|
|
de5238e89a | ||
|
|
3bb1327a65 | ||
|
|
71f77dd8fb | ||
|
|
9101dae38a | ||
|
|
1f18bf2bd8 | ||
|
|
41445c3092 | ||
|
|
2a3bf9821b | ||
|
|
819d775282 | ||
|
|
efbc6fe3ae | ||
|
|
262ba6c113 | ||
|
|
c41ecb6735 | ||
|
|
f5c32c6b98 | ||
|
|
a1886e37f8 | ||
|
|
f040804d02 | ||
|
|
e27625556c | ||
|
|
b13711ddef | ||
|
|
6ce82be46b | ||
|
|
56a5864600 | ||
|
|
07cbaa3e70 | ||
|
|
2aecea88b0 | ||
|
|
582c43fb6c | ||
|
|
cc7c2cc707 | ||
|
|
8b717c4f4c | ||
|
|
aa309af015 | ||
|
|
1e041b6249 | ||
|
|
1f240b02f4 | ||
|
|
6317f376b7 | ||
|
|
5ecf9aeed8 | ||
|
|
db36a76c2c | ||
|
|
3df8fb5fe9 | ||
|
|
7dd3367203 | ||
|
|
e72c82521a | ||
|
|
aca415db23 | ||
|
|
5ba2e8a7a1 | ||
|
|
07d4b126f5 | ||
|
|
98b2d8b3b9 | ||
|
|
f8ef5db5a3 | ||
|
|
6e14ec3227 | ||
|
|
028292a023 | ||
|
|
a38f2e36a2 | ||
|
|
a44cbe5972 | ||
|
|
2066a2e9bc | ||
|
|
f381bf85a4 | ||
|
|
44dcdcc8bb | ||
|
|
01220800f3 | ||
|
|
8ecb4cdcf4 | ||
|
|
8402d108c0 | ||
|
|
75af2d468e | ||
|
|
72ce37f008 | ||
|
|
e48e884286 | ||
|
|
f9d5c1f8de | ||
|
|
0873dcab0a | ||
|
|
a1343c2849 | ||
|
|
f732cc54d0 | ||
|
|
473a7d5fa4 | ||
|
|
8054a0b62f | ||
|
|
7f9cba5d37 | ||
|
|
5807fbf896 | ||
|
|
2c1dc6a18d | ||
|
|
f68e6387e6 | ||
|
|
f032001bee | ||
|
|
3bae591c04 | ||
|
|
18a1f0af94 | ||
|
|
afbd4a4716 | ||
|
|
a98da14c6f | ||
|
|
a2a70213a7 | ||
|
|
8b0f877041 | ||
|
|
bf7ad4cad2 | ||
|
|
66b659c0af | ||
|
|
8709ea948f | ||
|
|
7236c10403 | ||
|
|
dde703ec41 | ||
|
|
aa2e9b123c | ||
|
|
a3c06ce6e0 | ||
|
|
0e1dfb8ccb | ||
|
|
8a4a81a008 | ||
|
|
ff083942e8 | ||
|
|
737e04d09e | ||
|
|
0fe59efd72 | ||
|
|
ddb50e6254 | ||
|
|
72e6ae4186 | ||
|
|
4d510f643a | ||
|
|
66f607b5e6 | ||
|
|
1a125c62e7 | ||
|
|
84da44a27b | ||
|
|
5e35efcaef | ||
|
|
83f221c5e3 | ||
|
|
e86a10e9ba | ||
|
|
7eea328706 | ||
|
|
aa9dcec0ad | ||
|
|
e9bad2c7eb | ||
|
|
ce257a31bb | ||
|
|
c96b5cf4d7 | ||
|
|
fb40e9273d | ||
|
|
42c9af102b | ||
|
|
b7dff59542 | ||
|
|
266927aa9a | ||
|
|
0eee5747af | ||
|
|
80f2b7a1db | ||
|
|
dcaa8d4e96 | ||
|
|
0e2a449553 | ||
|
|
a70e035e02 | ||
|
|
4019da8ba9 | ||
|
|
5042ac1789 | ||
|
|
4031c9b978 | ||
|
|
65dd9bc286 | ||
|
|
e210dcb4df | ||
|
|
52a379229e | ||
|
|
93b2e91c10 | ||
|
|
b8afb01c46 | ||
|
|
59e7665b65 | ||
|
|
8a8a835a32 | ||
|
|
6ceab69656 | ||
|
|
9e98f05be0 | ||
|
|
5da1db91fd | ||
|
|
f0d58acd62 | ||
|
|
722ec00076 | ||
|
|
2db740e1ad | ||
|
|
70cd0e8c31 | ||
|
|
848c92ec25 | ||
|
|
98946d0a63 | ||
|
|
e0a39104b1 | ||
|
|
a4f66298a4 | ||
|
|
993bf50012 | ||
|
|
51bfc9a59b | ||
|
|
837795e87a | ||
|
|
d4820b2435 | ||
|
|
180537cd48 | ||
|
|
8bc77bbf18 | ||
|
|
474b606524 | ||
|
|
8a7e43ef42 | ||
|
|
7b5b486f0e | ||
|
|
ebedc02a0a | ||
|
|
ad42a3d956 | ||
|
|
98d75bc721 | ||
|
|
14fe68eb54 | ||
|
|
b6938c14ca | ||
|
|
3c96c1d5b1 | ||
|
|
2969ce2d56 | ||
|
|
bca92f5ee7 | ||
|
|
1c1982952e | ||
|
|
4b74b5a13d | ||
|
|
0fc00fac38 | ||
|
|
4446a7f060 | ||
|
|
9d31c478d3 | ||
|
|
b31e186d1d | ||
|
|
d02a3a0d3f | ||
|
|
a9e65cc83d | ||
|
|
010d505f04 | ||
|
|
95b9a3e1aa | ||
|
|
d051859371 | ||
|
|
af596c58c3 | ||
|
|
b4e3067718 | ||
|
|
40f8ae1cde | ||
|
|
f5f45a098e | ||
|
|
d429804c1f | ||
|
|
f5f770f401 | ||
|
|
91d6ed0ee7 | ||
|
|
06858b26c7 | ||
|
|
7e60a43f53 | ||
|
|
e7e82dcd0b | ||
|
|
66af6be063 | ||
|
|
69e1d2779d | ||
|
|
001752a81e | ||
|
|
1000041bce | ||
|
|
d50e791c30 | ||
|
|
7817d59989 | ||
|
|
139c4c1dd8 | ||
|
|
75bb6941d3 | ||
|
|
e92fb38271 | ||
|
|
e4eeef8f99 | ||
|
|
21c355bc9f | ||
|
|
04c878f57c | ||
|
|
ef23ce58d1 | ||
|
|
d213fa1b91 | ||
|
|
4b2804427e | ||
|
|
d707a59a71 | ||
|
|
923358b364 | ||
|
|
1d0c0ac19c | ||
|
|
7c97ecddac | ||
|
|
bcae51cc92 | ||
|
|
18896a69cf | ||
|
|
4c310d268d | ||
|
|
70babd9c32 | ||
|
|
15d0edce2e | ||
|
|
d7ab1774ac | ||
|
|
42d9607b0d | ||
|
|
bf82474fd7 | ||
|
|
64211275cc | ||
|
|
cf8060f7ff | ||
|
|
cee1d01d9a | ||
|
|
536d763a8e | ||
|
|
6d27b6ce41 | ||
|
|
0d05655e94 | ||
|
|
9cf697d72c | ||
|
|
f0f818c524 | ||
|
|
d2a3448819 | ||
|
|
1a25b15bce | ||
|
|
83808a63ed | ||
|
|
515d1d0dee | ||
|
|
2d47df96b6 | ||
|
|
ad4d71316f | ||
|
|
40932c9e84 | ||
|
|
59bee5f23e | ||
|
|
e6f00febea | ||
|
|
6ea694bc7e | ||
|
|
8f7a8edde4 | ||
|
|
11c7107152 | ||
|
|
d18a9f376f | ||
|
|
6ba95d8c70 | ||
|
|
1c00418d64 | ||
|
|
602e7fb530 | ||
|
|
e6a0a1d4a4 | ||
|
|
136cf143bd | ||
|
|
5afa2a23f6 | ||
|
|
513c81b508 | ||
|
|
45af66e000 | ||
|
|
b38f6c39e4 | ||
|
|
036874fbf0 | ||
|
|
9f3b49a7f5 | ||
|
|
9c35156db9 | ||
|
|
ab2edc6e59 | ||
|
|
9af278217c | ||
|
|
13dbc9c0fe | ||
|
|
14f7fbe794 | ||
|
|
a60e6aa860 | ||
|
|
220e72322c | ||
|
|
1a100dc54b | ||
|
|
04b95c986d | ||
|
|
8f6bb90945 | ||
|
|
50e78bd069 | ||
|
|
ce60e1a8f8 | ||
|
|
fb7ed19f7a | ||
|
|
970888b5ca | ||
|
|
8c934654ba | ||
|
|
ad3dc70cfd | ||
|
|
ae5949bc7f | ||
|
|
c7c563e68c | ||
|
|
3dac0af6c4 | ||
|
|
f9ce6966bb | ||
|
|
f61e23153b | ||
|
|
ff4f008a63 | ||
|
|
cbcf8a0a90 | ||
|
|
647e62059f | ||
|
|
6e3ef24e3a | ||
|
|
2559b27a6f | ||
|
|
421e2508d4 | ||
|
|
c9f8a93813 | ||
|
|
126849cf9a | ||
|
|
c08768f648 | ||
|
|
2c07257bf6 | ||
|
|
f797bbb97f | ||
|
|
a7f3b6e0dc | ||
|
|
60732c33c0 | ||
|
|
65b77e241f | ||
|
|
a167b95cec | ||
|
|
283cfd1ce9 | ||
|
|
e16db60d0f | ||
|
|
6a3b5bbe1d | ||
|
|
339c3f506c | ||
|
|
9fade70092 | ||
|
|
e18d84ae5e | ||
|
|
fa76e31640 | ||
|
|
2fd4fa25f7 | ||
|
|
c27d8e3b16 | ||
|
|
0841c52a75 | ||
|
|
669bff13c7 | ||
|
|
01ea4fa7a6 | ||
|
|
ef024b5118 | ||
|
|
a96a107ef9 | ||
|
|
8d3ab2be5d | ||
|
|
6ed407f656 | ||
|
|
6b3625f0ea | ||
|
|
db8e7f0474 | ||
|
|
d6398630df | ||
|
|
464bfccfb6 | ||
|
|
aa16da837d | ||
|
|
ce4478803a | ||
|
|
114239fe8e | ||
|
|
67d96061da | ||
|
|
2d5ad16399 | ||
|
|
4c7f79c6f8 | ||
|
|
2a50768db1 | ||
|
|
bff33e6992 | ||
|
|
448057a0b9 | ||
|
|
0a47669b14 | ||
|
|
b5893ce521 | ||
|
|
23537211b0 | ||
|
|
39f25db439 | ||
|
|
a551067c72 | ||
|
|
23f4df766c | ||
|
|
7354a34f1a | ||
|
|
2de48b3918 | ||
|
|
e823a7a7b0 | ||
|
|
ffc1f9d48e | ||
|
|
bb19bfb925 | ||
|
|
b3a2464249 | ||
|
|
09718e73a3 | ||
|
|
d5f20377ea | ||
|
|
f703d620c2 | ||
|
|
5ac680b37d | ||
|
|
9bf57e99fb | ||
|
|
b787f7cb11 | ||
|
|
7b35965dbf | ||
|
|
1618c1e677 | ||
|
|
0a5ce108b7 | ||
|
|
99c180441d | ||
|
|
79a7b0557c | ||
|
|
91cf192d2e | ||
|
|
66d7435ed6 | ||
|
|
19ea75b281 | ||
|
|
f0350b7045 | ||
|
|
9d4d3d0523 | ||
|
|
a76962e206 | ||
|
|
4060039440 | ||
|
|
c3a3a428a4 | ||
|
|
171e23bd09 | ||
|
|
6f4b7e0f1a | ||
|
|
ccf2cd3425 | ||
|
|
6ee444efd7 | ||
|
|
3e5be909a2 | ||
|
|
dac78f8f09 | ||
|
|
6b91e1b03c | ||
|
|
772295fc04 | ||
|
|
a4e93276b8 | ||
|
|
456a82acaa | ||
|
|
f2fb2cb363 | ||
|
|
2a7e5eecd7 | ||
|
|
79502f56a0 | ||
|
|
12dacfe2d2 | ||
|
|
d46337d694 | ||
|
|
f897cf745f | ||
|
|
cfa4f7da2e | ||
|
|
b21272cfab | ||
|
|
8ce0520101 | ||
|
|
4c17e1fd33 | ||
|
|
d5fb1f62f5 | ||
|
|
b34ede3795 | ||
|
|
4989d84693 | ||
|
|
2f210ab59f | ||
|
|
3ffea901f1 | ||
|
|
5e2e6520ce | ||
|
|
a8679c8eef | ||
|
|
469e842e96 | ||
|
|
cd945c625f | ||
|
|
1346192b75 | ||
|
|
45bebdd94e | ||
|
|
d0bbb025d3 | ||
|
|
8d0c53ef69 | ||
|
|
a59c2dfb10 | ||
|
|
9c7689f9b5 | ||
|
|
c580979fee | ||
|
|
e8e7bc95ea | ||
|
|
839e3e0833 | ||
|
|
4f451fab2f | ||
|
|
7d0413f41d | ||
|
|
bbac78195d | ||
|
|
20fef86b84 | ||
|
|
9581a8f1f4 | ||
|
|
58b4c36b6c | ||
|
|
15aaa5d9a1 | ||
|
|
b38332d061 | ||
|
|
4cfbbb6756 | ||
|
|
dec7db9e69 | ||
|
|
274be3bcfc | ||
|
|
d8a668ce60 | ||
|
|
a845ed1998 | ||
|
|
53bd147de2 | ||
|
|
4248b518a2 | ||
|
|
8f65a88d8b | ||
|
|
4d4a640f34 | ||
|
|
af9ead5937 | ||
|
|
ae3518f450 | ||
|
|
04e77a97f2 | ||
|
|
0ff3ce98e0 | ||
|
|
df2659787c | ||
|
|
d994673e91 | ||
|
|
dce781ef0e | ||
|
|
b0e2c73024 | ||
|
|
bda28f81cc | ||
|
|
d8dc3d48b1 | ||
|
|
5fdfa6339d | ||
|
|
9a799190ef | ||
|
|
8d32ba5b8e | ||
|
|
85a7f89ba9 | ||
|
|
17f5378326 | ||
|
|
9d67741310 | ||
|
|
ac6a106c6a | ||
|
|
d775b8baa0 | ||
|
|
2dc1b3ec43 | ||
|
|
d3d5160861 | ||
|
|
cdf8c3b6e5 | ||
|
|
29b1bbab9d | ||
|
|
c5d055c19b | ||
|
|
8f7b51a3df | ||
|
|
44e8cc810f | ||
|
|
b994878465 | ||
|
|
8d5d703cbe | ||
|
|
1c1936d8f8 | ||
|
|
7890fd5cc7 | ||
|
|
4bdf11eb79 | ||
|
|
81f4c7303f | ||
|
|
0641907ea0 | ||
|
|
ff0f32fcf5 | ||
|
|
768049ca36 | ||
|
|
88c8fe7cca | ||
|
|
5e7facfeb6 | ||
|
|
6e43467ef6 | ||
|
|
cd4ef8ae32 | ||
|
|
93a4463f22 | ||
|
|
56cf6bdaa4 | ||
|
|
fe02df27a2 | ||
|
|
a60ab68287 | ||
|
|
83e46ddc97 | ||
|
|
9c89ad7c72 | ||
|
|
0518abdedc | ||
|
|
93bcd07c7f | ||
|
|
5f1f4f8d81 | ||
|
|
e78120d9c6 | ||
|
|
cc5986d435 | ||
|
|
5192306b06 | ||
|
|
826935eb7d | ||
|
|
cd41bcf45c | ||
|
|
601375d06f | ||
|
|
c708abafc8 | ||
|
|
90d588353c | ||
|
|
2d4287df2a | ||
|
|
fde3a988b7 | ||
|
|
397164e667 |
8
.coveragerc
Normal file
8
.coveragerc
Normal file
@@ -0,0 +1,8 @@
|
||||
[report]
|
||||
exclude_lines =
|
||||
pragma: no cover
|
||||
if TYPE_CHECKING:
|
||||
|
||||
[run]
|
||||
branch = True
|
||||
source = game,pydcs_extensions,qt_ui,resources/tools
|
||||
2
.git-blame-ignore-revs
Normal file
2
.git-blame-ignore-revs
Normal file
@@ -0,0 +1,2 @@
|
||||
# Black
|
||||
a47bef1f1336fd264d0b175f4421758339a30acb
|
||||
92
.gitattributes
vendored
Normal file
92
.gitattributes
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
* text=auto
|
||||
*.pxd text diff=python
|
||||
*.py text diff=python
|
||||
*.py3 text diff=python
|
||||
*.pyw text diff=python
|
||||
*.pyx text diff=python
|
||||
*.pyz text diff=python
|
||||
*.pyi text diff=python
|
||||
*.db binary
|
||||
*.p binary
|
||||
*.pkl binary
|
||||
*.pickle binary
|
||||
*.pyc binary export-ignore
|
||||
*.pyo binary export-ignore
|
||||
*.pyd binary
|
||||
unshipped_data/arcgis_maps/ filter=lfs diff=lfs merge=lfs -text
|
||||
|
||||
# https://github.com/alexkaratarakis/gitattributes/blob/master/Common.gitattributes
|
||||
# Documents
|
||||
*.bibtex text diff=bibtex
|
||||
*.doc diff=astextplain
|
||||
*.DOC diff=astextplain
|
||||
*.docx diff=astextplain
|
||||
*.DOCX diff=astextplain
|
||||
*.dot diff=astextplain
|
||||
*.DOT diff=astextplain
|
||||
*.pdf diff=astextplain
|
||||
*.PDF diff=astextplain
|
||||
*.rtf diff=astextplain
|
||||
*.RTF diff=astextplain
|
||||
*.md text diff=markdown
|
||||
*.mdx text diff=markdown
|
||||
*.tex text diff=tex
|
||||
*.adoc text
|
||||
*.textile text
|
||||
*.mustache text
|
||||
*.csv text
|
||||
*.tab text
|
||||
*.tsv text
|
||||
*.txt text
|
||||
*.sql text
|
||||
*.epub diff=astextplain
|
||||
|
||||
# Graphics
|
||||
*.png binary
|
||||
*.jpg binary
|
||||
*.jpeg binary
|
||||
*.gif binary
|
||||
*.tif binary
|
||||
*.tiff binary
|
||||
*.ico binary
|
||||
# SVG treated as text by default.
|
||||
*.svg text
|
||||
# If you want to treat it as binary,
|
||||
# use the following line instead.
|
||||
# *.svg binary
|
||||
*.eps binary
|
||||
|
||||
# Scripts
|
||||
*.bash text eol=lf
|
||||
*.fish text eol=lf
|
||||
*.sh text eol=lf
|
||||
*.zsh text eol=lf
|
||||
# These are explicitly windows files and should use crlf
|
||||
*.bat text eol=crlf
|
||||
*.cmd text eol=crlf
|
||||
*.ps1 text eol=crlf
|
||||
|
||||
# Serialisation
|
||||
*.json text
|
||||
*.toml text
|
||||
*.xml text
|
||||
*.yaml text
|
||||
*.yml text
|
||||
|
||||
# Archives
|
||||
*.7z binary
|
||||
*.gz binary
|
||||
*.tar binary
|
||||
*.tgz binary
|
||||
*.zip binary
|
||||
|
||||
# Text files where line endings should be preserved
|
||||
*.patch -text
|
||||
|
||||
#
|
||||
# Exclude files from exporting
|
||||
#
|
||||
|
||||
.gitattributes export-ignore
|
||||
.gitignore export-ignore
|
||||
.gitkeep export-ignore
|
||||
83
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
Normal file
83
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
---
|
||||
name: Bug report
|
||||
description: >
|
||||
Use for any bug that happens after campaign generation. If the New Game wizard
|
||||
failed, use the "New Game wizard failed" template instead.
|
||||
labels: bug
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: >
|
||||
Before filing, please search the issue tracker to see if the issue has
|
||||
already been reported.
|
||||
- type: dropdown
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Affected versions
|
||||
multiple: true
|
||||
description: >
|
||||
Select all DCS Liberation versions in which you have observed this bug.
|
||||
You do not need to test all of them, but the information is useful if
|
||||
you have it.
|
||||
|
||||
|
||||
If you do not see your version listed here you are on an old release
|
||||
that is not supported, and the bug may already be fixed in a newer
|
||||
release. Check that the bug still exists in a newer release before
|
||||
filing.
|
||||
|
||||
|
||||
If the bug was found in a development build, select "Development build"
|
||||
and provide a link to the build in the field below.
|
||||
options:
|
||||
- 7.1.0
|
||||
- Development build
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Build information
|
||||
description:
|
||||
The build information from the Help -> Report an issue window.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Description
|
||||
description: >
|
||||
Describe the bug. What went wrong? What did you expect to happen
|
||||
instead? What steps should we take to reproduce the error? If an error
|
||||
dialog was shown, include the full text.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Save game and other files
|
||||
description: >
|
||||
Attach any files needed to reproduce the bug here. **A save game is
|
||||
required.** We typically cannot help without a save game (the
|
||||
`.liberation.zip` file found in `%USERPROFILE%/Saved
|
||||
Games/DCS/Liberation/Saves`), so most bugs filed without saved games
|
||||
will be closed without investigation.
|
||||
|
||||
|
||||
Other useful files to include are:
|
||||
|
||||
|
||||
The Liberation log file. The log file is located at `<Liberation install
|
||||
directory>/logs/liberation.log`. The log often includes data about
|
||||
non-fatal errors that could be the root cause of the problem.
|
||||
|
||||
|
||||
The `liberation_nextturn.miz` or a track file. This should always be
|
||||
included for bugs where the mission was generated incorrectly or where
|
||||
the in-game AI is misbehaving.
|
||||
|
||||
|
||||
The `state.json` file for the most recently completed turn, located at
|
||||
`<Liberation install directory>/state.json`. This file is essential for
|
||||
investigating any issues with end-of-turn results processing.
|
||||
|
||||
|
||||
You can attach files to the bug by dragging and dropping the file into
|
||||
this text box. GitHub will not allow uploads of all file types, so
|
||||
attach a zip of the files if needed.
|
||||
validations:
|
||||
required: true
|
||||
28
.github/ISSUE_TEMPLATE/campaign_update.md
vendored
Normal file
28
.github/ISSUE_TEMPLATE/campaign_update.md
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
name: Campaign update submission
|
||||
about: Submit an update to a campaign you maintain.
|
||||
title: 'Update for <campaign name>'
|
||||
labels: campaign-update-submission
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
This form should only be used for submitted updated miz/json files for campaigns
|
||||
distributed with Liberation. If you are _requesting_ an update to a campaign, see
|
||||
https://github.com/dcs-liberation/dcs_liberation/wiki/Campaign-maintenance. If the
|
||||
campaign has an owner, it will be updated before release. If it does not, you can
|
||||
volunteer to own it.
|
||||
|
||||
If you are not the owner of the campaign listed on
|
||||
https://github.com/dcs-liberation/dcs_liberation/wiki/Campaign-maintenance, please start
|
||||
there.
|
||||
|
||||
Otherwise, delete everything above the line below and fill out the following form. Note:
|
||||
GitHub does not accept .miz files. You can either rename the file to .miz.txt or add the
|
||||
file to a .zip file.
|
||||
|
||||
---
|
||||
|
||||
* Campaign name:
|
||||
* Files:
|
||||
* Update summary (optional):
|
||||
19
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
19
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: FAQ
|
||||
url: https://discord.gg/PXHA6AXw
|
||||
about: Check to see if your issue is in the FAQ.
|
||||
- name: Manual
|
||||
url: https://github.com/dcs-liberation/dcs_liberation/wiki/
|
||||
- name: Feature blocking DCS AI bugs
|
||||
url: https://github.com/dcs-liberation/dcs_liberation#dcs-bugs
|
||||
about: >
|
||||
A list of known DCS bugs that prevent us from improving AI behavior. Check
|
||||
the list before filing AI bugs here to see if it's something we know about
|
||||
but cannot fix.
|
||||
- name: DCS bugs
|
||||
url: https://forums.eagle.ru/forum/119-dcs-world-27/
|
||||
about: >
|
||||
DCS bugs should be reported against DCS, not here. Occasionally we can add
|
||||
workarounds for DCS bugs. Use the "Bug report" template if you can suggest
|
||||
a workaround.
|
||||
21
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
21
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
Before filing, please search the issue tracker to see if this feature has already been requested.
|
||||
|
||||
If requesting a DCS AI feature, check If reporting a DCS AI bug, check https://github.com/dcs-liberation/dcs_liberation#dcs-bugs.
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
12
.github/ISSUE_TEMPLATE/mod_support.md
vendored
Normal file
12
.github/ISSUE_TEMPLATE/mod_support.md
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
name: Mod support request
|
||||
about: Request Liberation support for new mods, or updates to existing mods
|
||||
title: Add/update <mod name>
|
||||
labels: mod support
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
* Mod name:
|
||||
* Mod URL:
|
||||
* Update or new mod?
|
||||
113
.github/ISSUE_TEMPLATE/new-game-bug.yml
vendored
Normal file
113
.github/ISSUE_TEMPLATE/new-game-bug.yml
vendored
Normal file
@@ -0,0 +1,113 @@
|
||||
---
|
||||
name: New Game wizard failed
|
||||
description: >
|
||||
Use for bugs that prevent the "New Game" wizard from completing successfully.
|
||||
If the wizard completes without issue, use the normal bug report template.
|
||||
labels: bug
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: >
|
||||
Before filing, please search the issue tracker to see if the issue has
|
||||
already been reported.
|
||||
|
||||
|
||||
If the bug is not related to campaign generation (the campaign was
|
||||
created successfully and as expected), use the normal bug report
|
||||
template instead, as this template will not include the information we
|
||||
need. We are unable to investigate incomplete bug reports, so they will
|
||||
be closed and you will be asked to refile. If you're unsure, use your
|
||||
best guess. Needing to refile is not the end of the world :)
|
||||
- type: dropdown
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Affected versions
|
||||
multiple: true
|
||||
description: >
|
||||
Select all DCS Liberation versions in which you have observed this bug.
|
||||
You do not need to test all of them, but the information is useful if
|
||||
you have it.
|
||||
|
||||
|
||||
If you do not see your version listed here you are on an old release
|
||||
that is not supported, and the bug may already be fixed in a newer
|
||||
release. Check that the bug still exists in a newer release before
|
||||
filing.
|
||||
|
||||
|
||||
If the bug was found in a development build, select "Development build"
|
||||
and provide a link to the build in the field below.
|
||||
options:
|
||||
- 7.1.0
|
||||
- Development build
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Build information
|
||||
description:
|
||||
The build information from the Help -> Report an issue window.
|
||||
- type: input
|
||||
attributes:
|
||||
label: Campaign name
|
||||
description: >
|
||||
The name of the campaign you selected. If the bug only occurs with a
|
||||
custom campaign (or modifications to a stock campaign), upload the
|
||||
campaign file as an attachment to the bug description field.
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Blue faction
|
||||
description: >
|
||||
The name of the blue faction you selected. If the bug only occurs with a
|
||||
custom faction (or modifications to a stock faction), upload the faction
|
||||
file as an attachment to the bug description field.
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Red faction
|
||||
description: >
|
||||
The name of the red faction you selected. If the bug only occurs with a
|
||||
custom faction (or modifications to a stock faction), upload the faction
|
||||
file as an attachment to the bug description field.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Modifications to default settings
|
||||
description: >
|
||||
Describe any modifications you made to the default campaign generation
|
||||
settings.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Description
|
||||
description: >
|
||||
Describe the bug. What went wrong? If an error dialog was shown, include
|
||||
the full text.
|
||||
|
||||
|
||||
Attach any relevant files such as custom campaign files or factions
|
||||
here. You can attach files to the bug by dragging and dropping the file
|
||||
into this text box. GitHub will not allow uploads of all file types, so
|
||||
attach a zip of the files if needed.
|
||||
|
||||
|
||||
If possible, also include the save game. If the bug prevented the game
|
||||
from being generated at all this will not be possible, but if the bug is
|
||||
that the wizard generated something incorrectly, the save game will help
|
||||
us see what went wrong.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Log file
|
||||
description: >
|
||||
Attach the Liberation log file. The log file is located at `<Liberation
|
||||
install directory>/logs/liberation.log`.
|
||||
|
||||
|
||||
You can attach files to the bug by dragging and dropping the file into
|
||||
this text box.
|
||||
validations:
|
||||
required: true
|
||||
22
.github/actions/build-app/action.yaml
vendored
Normal file
22
.github/actions/build-app/action.yaml
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
name: Build Liberation package
|
||||
description: Assembles the full Liberation application.
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Build client
|
||||
shell: powershell
|
||||
run: |
|
||||
cd client
|
||||
npm run build
|
||||
|
||||
- name: Build binaries
|
||||
shell: powershell
|
||||
run: |
|
||||
./venv/scripts/activate
|
||||
$env:PYTHONPATH=".;./pydcs"
|
||||
pyinstaller pyinstaller.spec
|
||||
|
||||
- name: Install changelog
|
||||
shell: powershell
|
||||
run: |
|
||||
Copy-Item .\changelog.md .\dist
|
||||
16
.github/actions/mypy/action.yaml
vendored
Normal file
16
.github/actions/mypy/action.yaml
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
name: mypy
|
||||
description: Type checks Python code.
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: mypy game
|
||||
shell: powershell
|
||||
run: |
|
||||
./venv/scripts/activate
|
||||
mypy game
|
||||
|
||||
- name: mypy tests
|
||||
shell: powershell
|
||||
run: |
|
||||
./venv/scripts/activate
|
||||
mypy tests
|
||||
17
.github/actions/setup-liberation-js/action.yaml
vendored
Normal file
17
.github/actions/setup-liberation-js/action.yaml
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
name: Liberation JS set-up
|
||||
description: Sets up the Liberation Javascript environment.
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: "16"
|
||||
cache: npm
|
||||
cache-dependency-path: client/package-lock.json
|
||||
|
||||
- name: npm ci
|
||||
shell: powershell
|
||||
run: |
|
||||
cd client
|
||||
npm ci
|
||||
21
.github/actions/setup-liberation-python/action.yaml
vendored
Normal file
21
.github/actions/setup-liberation-python/action.yaml
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
name: Liberation Python set-up
|
||||
description: Sets up the Liberation Python environment.
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: "3.11"
|
||||
cache: pip
|
||||
|
||||
- name: Install environment
|
||||
shell: powershell
|
||||
run: |
|
||||
python -m venv ./venv
|
||||
|
||||
- name: Install dependencies
|
||||
shell: powershell
|
||||
run: |
|
||||
./venv/scripts/activate
|
||||
python -m pip install -r requirements.txt
|
||||
24
.github/pull_request_template.md
vendored
Normal file
24
.github/pull_request_template.md
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
Pull requests should be made against the `develop` branch. Any backports
|
||||
necessary will be handled by the development team.
|
||||
|
||||
Pull requests should be focused on one task. Multiple bug fixes should be
|
||||
multiple PRs. We cannot merge half a PR, and combined PRs are much more
|
||||
difficult to review. PRs that do not adhere to this will have their review
|
||||
delayed.
|
||||
|
||||
Prefer rebase to merge, and squash commits as needed to preserve a readable
|
||||
commit history. This project maintains linear history in the develop branch, so
|
||||
we will either rebase or squash your PR when merging. It is much easier for us
|
||||
if your branch already has a readable commit history (ensure that your commit
|
||||
subject lines are clear enough to identify the patch in the git log). An
|
||||
exception to this is made for large PRs that are likely to require multiple
|
||||
rounds of review; in that case it's easier if you **don't** do this (GitHub
|
||||
does not preserve the history of old commits, so we cannot filter a PR for only
|
||||
new changes if a branch is force pushed) and we will squash it when merging.
|
||||
|
||||
New features and bug fixes are usually worth mentioning in the changelog.
|
||||
Exceptions are fixes for bugs that never shipped (were only present in a canary
|
||||
build), and changes with no intended user observable behavior, such as a
|
||||
refactor. If you're comfortable writing the note yourself, add it to
|
||||
`changelog.md` in the root of the project in the section for the upcoming
|
||||
release.
|
||||
41
.github/workflows/build.yml
vendored
Normal file
41
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
name: Build
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
uses: ./.github/workflows/lint.yml
|
||||
|
||||
test:
|
||||
uses: ./.github/workflows/test.yml
|
||||
|
||||
build:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Set up Python environment
|
||||
uses: ./.github/actions/setup-liberation-python
|
||||
|
||||
- name: Set up JS environment
|
||||
uses: ./.github/actions/setup-liberation-js
|
||||
|
||||
- name: Set build number
|
||||
run: |
|
||||
[IO.File]::WriteAllLines($pwd.path + "\resources\buildnumber", $env:GITHUB_RUN_NUMBER)
|
||||
[IO.File]::WriteAllLines($pwd.path + "\resources\gitsha", $env:GITHUB_SHA)
|
||||
|
||||
- name: Build app
|
||||
uses: ./.github/actions/build-app
|
||||
|
||||
- name: Create archive
|
||||
run:
|
||||
Compress-Archive -Path .\dist\dcs_liberation\ -DestinationPath
|
||||
dist\dcs_liberation.zip
|
||||
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: dcs_liberation
|
||||
path: dist/dcs_liberation.zip
|
||||
30
.github/workflows/lint.yml
vendored
Normal file
30
.github/workflows/lint.yml
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
name: Python lint
|
||||
|
||||
on: workflow_call
|
||||
|
||||
jobs:
|
||||
black:
|
||||
name: Black
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-python@v2
|
||||
- uses: psf/black@stable
|
||||
with:
|
||||
version: ~=22.12
|
||||
src: "."
|
||||
options: "--check"
|
||||
|
||||
mypy:
|
||||
name: Type checking
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Set up Python environment
|
||||
uses: ./.github/actions/setup-liberation-python
|
||||
|
||||
- name: mypy
|
||||
uses: ./.github/actions/mypy
|
||||
84
.github/workflows/release.yml
vendored
Normal file
84
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
name: Release Pipeline
|
||||
|
||||
on:
|
||||
push:
|
||||
tags: ["*"]
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
uses: ./.github/workflows/lint.yml
|
||||
|
||||
test:
|
||||
uses: ./.github/workflows/test.yml
|
||||
|
||||
build:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Set up Python environment
|
||||
uses: ./.github/actions/setup-liberation-python
|
||||
|
||||
- name: Set up JS environment
|
||||
uses: ./.github/actions/setup-liberation-js
|
||||
|
||||
- name: Finalize build
|
||||
run: |
|
||||
New-Item -ItemType file resources\final
|
||||
|
||||
- name: Build app
|
||||
uses: ./.github/actions/build-app
|
||||
with:
|
||||
release: true
|
||||
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: dcs_liberation
|
||||
path: dist/
|
||||
|
||||
release:
|
||||
needs: [build]
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: dcs_liberation
|
||||
|
||||
- name: "Get Version"
|
||||
id: version
|
||||
env:
|
||||
TAG_NAME: ${{ github.ref }}
|
||||
run: |
|
||||
Get-ChildItem -Recurse -Depth 1
|
||||
$version = ($env:TAG_NAME -split "/") | Select-Object -Last 1
|
||||
$prerelease = ("2.1.1-alpha3" -match '[^\.\d]').ToString().ToLower()
|
||||
Write-Host $version
|
||||
Write-Host $prerelease
|
||||
Write-Output "::set-output name=number::$version"
|
||||
Write-Output "::set-output name=prerelease::$prerelease"
|
||||
$changelog = Get-Content .\changelog.md
|
||||
$last_change = ($changelog | Select-String -Pattern "^#\s" | Select-Object -Skip 1 -First 1).LineNumber - 2
|
||||
($changelog | Select-Object -First $last_change) -join "`n" | Out-File .\releasenotes.md
|
||||
Compress-Archive -Path .\dcs_liberation -DestinationPath "dcs_liberation.$version.zip" -Compression Optimal
|
||||
|
||||
- uses: actions/create-release@v1
|
||||
id: create_release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: ${{ github.ref }}
|
||||
body_path: releasenotes.md
|
||||
draft: false
|
||||
prerelease: ${{ steps.version.outputs.prerelease }}
|
||||
|
||||
- uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./dcs_liberation.${{ steps.version.outputs.number }}.zip
|
||||
asset_name: dcs_liberation.${{ steps.version.outputs.number }}.zip
|
||||
asset_content_type: application/zip
|
||||
38
.github/workflows/test.yml
vendored
Normal file
38
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
name: Tests
|
||||
on: workflow_call
|
||||
jobs:
|
||||
python-tests:
|
||||
name: Python tests
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Set up Python environment
|
||||
uses: ./.github/actions/setup-liberation-python
|
||||
|
||||
- name: run tests
|
||||
run: |
|
||||
./venv/scripts/activate
|
||||
pytest --cov --cov-report=xml tests
|
||||
|
||||
- name: Upload coverage reports to Codecov
|
||||
uses: codecov/codecov-action@v3
|
||||
|
||||
ts-tests:
|
||||
name: Typescript tests
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Set up JS environment
|
||||
uses: ./.github/actions/setup-liberation-js
|
||||
|
||||
- name: run tests
|
||||
run: |
|
||||
cd client
|
||||
npm test -- --coverage
|
||||
|
||||
- name: Upload coverage reports to Codecov
|
||||
uses: codecov/codecov-action@v3
|
||||
21
.gitignore
vendored
21
.gitignore
vendored
@@ -1,13 +1,26 @@
|
||||
*.pyc
|
||||
__pycache__
|
||||
build/**
|
||||
# Sphinx
|
||||
docs/_build
|
||||
resources/payloads/*.lua
|
||||
venv
|
||||
logs.txt
|
||||
.DS_Store
|
||||
.vscode/settings.json
|
||||
dist/**
|
||||
a.py
|
||||
resources/tools/a.miz
|
||||
tests/**
|
||||
/.coverage
|
||||
/coverage.xml
|
||||
# User-specific stuff
|
||||
.idea/
|
||||
.env
|
||||
env/
|
||||
|
||||
/kneeboards
|
||||
/liberation_preferences.json
|
||||
/state.json
|
||||
/serverconfig.env
|
||||
|
||||
/logs/
|
||||
/resources/logging.yaml
|
||||
|
||||
*.psd
|
||||
|
||||
6
.pre-commit-config.yaml
Normal file
6
.pre-commit-config.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
repos:
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 22.12.0
|
||||
hooks:
|
||||
- id: black
|
||||
language_version: python3
|
||||
13
.readthedocs.yaml
Normal file
13
.readthedocs.yaml
Normal file
@@ -0,0 +1,13 @@
|
||||
version: 2
|
||||
|
||||
build:
|
||||
os: ubuntu-22.04
|
||||
tools:
|
||||
python: "3.11"
|
||||
|
||||
sphinx:
|
||||
configuration: docs/conf.py
|
||||
|
||||
python:
|
||||
install:
|
||||
- requirements: docs/requirements.txt
|
||||
67
.vscode/launch.json
vendored
Normal file
67
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Python: Main",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"program": "qt_ui\\main.py",
|
||||
"console": "integratedTerminal",
|
||||
"env": {
|
||||
"PYTHONPATH": ".;./pydcs"
|
||||
},
|
||||
"preLaunchTask": "Prepare Environment"
|
||||
},
|
||||
{
|
||||
"name": "Python: Debug",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"program": "qt_ui\\main.py",
|
||||
"console": "integratedTerminal",
|
||||
"env": {
|
||||
"PYTHONPATH": ".;./pydcs",
|
||||
"CORS_ALLOW_DEBUG_SERVER": "true"
|
||||
},
|
||||
"args": ["--dev"],
|
||||
"preLaunchTask": "Prepare Environment"
|
||||
},
|
||||
{
|
||||
"name": "Node: Development Server",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"cwd": "${workspaceRoot}\\client",
|
||||
"runtimeExecutable": "npm",
|
||||
"runtimeArgs": [
|
||||
"run", "start"
|
||||
],
|
||||
"env": {
|
||||
"BROWSER": "none"
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "Python: Make Release",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"program": "resources\\tools\\mkrelease.py",
|
||||
"console": "integratedTerminal",
|
||||
"env": {
|
||||
"PYTHONPATH": ".;./pydcs"
|
||||
},
|
||||
"preLaunchTask": "Prepare Environment"
|
||||
},
|
||||
{
|
||||
"name": "Fix Layout orientation",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"program": "resources\\tools\\fix_layout_orientation.py",
|
||||
"console": "integratedTerminal",
|
||||
"env": {
|
||||
"PYTHONPATH": ".;./pydcs"
|
||||
},
|
||||
"args": ["resources/layouts/anti_air/S-300_Site.miz"]
|
||||
},
|
||||
]
|
||||
}
|
||||
35
.vscode/tasks.json
vendored
Normal file
35
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Prepare Environment",
|
||||
"type": "shell",
|
||||
"isBackground": false,
|
||||
"problemMatcher": [],
|
||||
"command": "powershell",
|
||||
"args": [
|
||||
"-Command",
|
||||
"& {if (-not (Test-Path ${workspaceFolder}\\venv)) { python -m venv ${workspaceFolder}\\venv; . ${workspaceFolder}\\venv\\scripts\\activate.ps1; pip install -r requirements.txt; Copy-Item ${workspaceFolder}\\venv\\Lib\\site-packages\\shiboken2\\shiboken2.abi3.dll ${workspaceFolder}\\venv\\Lib\\site-packages\\PySide2 } }",
|
||||
],
|
||||
"group": "build",
|
||||
"options": {
|
||||
"env": {
|
||||
"PYTHONPATH": ".;./pydcs"
|
||||
}
|
||||
},
|
||||
"presentation": {
|
||||
"echo": true,
|
||||
"reveal": "never",
|
||||
"focus": false,
|
||||
"panel": "shared",
|
||||
"showReuseMessage": true,
|
||||
"clear": false
|
||||
},
|
||||
"runOptions": {
|
||||
"runOn": "folderOpen"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
76
CODE_OF_CONDUCT.md
Normal file
76
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||
level of experience, education, socio-economic status, nationality, personal
|
||||
appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at khopa.studio@gmail.com. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see
|
||||
https://www.contributor-covenant.org/faq
|
||||
26
CONTRIBUTING.md
Normal file
26
CONTRIBUTING.md
Normal file
@@ -0,0 +1,26 @@
|
||||
First, note that we have a code of conduct, please follow it in all your interactions with the project.
|
||||
|
||||
## Contributing as a non-developer
|
||||
|
||||
* Report bugs by opening issues here on Github.
|
||||
* Help others users on Discord by answering their questions.
|
||||
* Raise awareness about the project, by making a video and/or a tutorial.
|
||||
|
||||
Should you report a bug, please use the search bar at the top of the page to see if it has already been reported.
|
||||
Note that you may need to remove the filter for open bugs if it's something we've recently fixed.
|
||||
|
||||
## Making content for Liberation
|
||||
|
||||
You can create new campaigns : See [campaign creation wiki](https://github.com/dcs-liberation/dcs_liberation/wiki/Custom-Campaigns).
|
||||
You can also improve existing campaigns.
|
||||
|
||||
You can then submit new campaigns on the "campaigns" channel on Discord, or by making a pull request if you are comfortable with git.
|
||||
|
||||
## Develop new features
|
||||
|
||||
If you want to develop a new feature, we recommend you first open an issue describing the new feature and discuss it with us on Discord before starting development.
|
||||
However, feel free to work on any existing issue.
|
||||
|
||||
## Pull requests
|
||||
|
||||
Please submit your pull requests on the **develop** branch. We expect a description of its content, and when applicable, a reference to the issue(s) it is resolving.
|
||||
165
LICENSE
Normal file
165
LICENSE
Normal file
@@ -0,0 +1,165 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
65
README.md
65
README.md
@@ -1,12 +1,65 @@
|
||||

|
||||
[](https://shdwp.github.io/ukraine/)
|
||||
|
||||
[DCS World](https://www.digitalcombatsimulator.com/en/products/world/) single-player semi dynamic campaign.
|
||||
(Github Readme Banner and Splash screen Artwork by Andriy Dankovych, CC BY-SA 4.0)
|
||||
|
||||
DCS Liberation uses [pydcs](http://github.com/pydcs/dcs) for mission generation
|
||||
and [Mist](https://github.com/mrSkortch/MissionScriptingTools) for mission scripting
|
||||
[](https://patreon.com/khopa)
|
||||
|
||||
[](https://github.com/dcs-liberation/dcs_liberation/releases)
|
||||
|
||||
[](https://discord.gg/bKrtrkJ)
|
||||
|
||||
[](https://codecov.io/gh/dcs-liberation/dcs_liberation)
|
||||
[](https://github.com/dcs-liberation/dcs_liberation)
|
||||
[](https://github.com/dcs-liberation/dcs_liberation/issues)
|
||||

|
||||
|
||||
## About DCS Liberation
|
||||
DCS Liberation is a [DCS World](https://www.digitalcombatsimulator.com/en/products/world/) turn based single-player or co-op dynamic campaign.
|
||||
It is an external program that generates full and complex DCS missions and manage a persistent combat environment.
|
||||
|
||||
**Note that DCS Liberation does not support the stable release of DCS. We can
|
||||
only guarantee compatibility with either the open beta or the stable release,
|
||||
and more people play the open beta. DCS stable _might_ work sometimes, but it's
|
||||
untested, and we will be unable to fix any bugs unique to stable DCS.**
|
||||
|
||||

|
||||
|
||||
## Downloads
|
||||
|
||||
Latest release is available here : https://github.com/dcs-liberation/dcs_liberation/releases
|
||||
|
||||
To download preview builds of the next version of DCS Liberation, see https://github.com/dcs-liberation/dcs_liberation/wiki/Preview-builds.
|
||||
|
||||
## DCS bugs
|
||||
|
||||
These DCS bugs prevent us from improving AI behavior. Please upvote them! (But please
|
||||
_don't_ spam them with comments):
|
||||
|
||||
* [A2A and SEAD escorts don't escort](https://forums.eagle.ru/topic/251798-options-for-alternate-ai-escort-behavior/?tab=comments#comment-4668033)
|
||||
* [DEAD can't use mixed loadouts effectively](https://forums.eagle.ru/topic/271941-ai-rtbs-after-firing-decoys-despite-full-load-of-bombs/)
|
||||
|
||||
## Bugs and feature requests
|
||||
|
||||
If you need to report a bug or want to suggest a new feature, you can do this on our [bug tracker](https://github.com/dcs-liberation/dcs_liberation/issues). In either case, please use the search bar at the top of the page to see if it has already been reported. Note that you may need to remove the filter for open bugs if it's something we've recently fixed.
|
||||
|
||||
## Roadmap
|
||||
|
||||
Our plans for future releases can be found on our [Projects page](https://github.com/dcs-liberation/dcs_liberation/projects). Each planned release has a Project, and the page for that project has columns for to do, in progress, and done. Items in the Done column are in the [preview build](https://github.com/dcs-liberation/dcs_liberation/wiki/Preview-builds) for that release. Items in the To do column are planned to be added to that release.
|
||||
|
||||
## Resources
|
||||
|
||||
* [Getting Started](https://github.com/Khopa/dcs_liberation/wiki/Getting-started)
|
||||
Tutorials, contributors and developer's guides are available in the project's [Wiki](https://github.com/dcs-liberation/dcs_liberation/wiki/)
|
||||
|
||||
* [Tutorials](https://github.com/Khopa/dcs_liberation/wiki/Tutorial-01-:-UI)
|
||||
## Special Thanks
|
||||
|
||||
First, a big thanks to shdwp, for starting the original DCS Liberation project.
|
||||
|
||||
Then, DCS Liberation uses [pydcs](http://github.com/pydcs/dcs) for mission generation, and nothing would be possible without this.
|
||||
It also uses the popular [Mist](https://github.com/mrSkortch/MissionScriptingTools) lua framework for mission scripting.
|
||||
|
||||
Excellent lua scripts DCS Liberation uses as plugins:
|
||||
|
||||
* For the JTAC feature, DCS Liberation embeds Ciribob's JTAC Autolase [script](https://github.com/ciribob/DCS-JTACAutoLaze).
|
||||
* Walder's [Skynet-IADS](https://github.com/walder/Skynet-IADS) is used for Integrated Air Defense System.
|
||||
|
||||
Please also show some support to these projects !
|
||||
|
||||
64
__init__.py
64
__init__.py
@@ -1,64 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
import dcs
|
||||
|
||||
import ui.corruptedsavemenu
|
||||
import ui.mainmenu
|
||||
import ui.newgamemenu
|
||||
import ui.window
|
||||
from game.game import Game
|
||||
from userdata import persistency, logging as logging_module
|
||||
|
||||
assert len(sys.argv) >= 3, "__init__.py should be started with two mandatory arguments: %UserProfile% location and application version"
|
||||
|
||||
persistency.setup(sys.argv[1])
|
||||
dcs.planes.FlyingType.payload_dirs = [os.path.join(os.path.dirname(os.path.realpath(__file__)), "resources\\payloads")]
|
||||
|
||||
VERSION_STRING = sys.argv[2]
|
||||
logging_module.setup_version_string(VERSION_STRING)
|
||||
logging.info("Using {} as userdata folder".format(persistency.base_path()))
|
||||
|
||||
|
||||
def proceed_to_main_menu(game: Game):
|
||||
m = ui.mainmenu.MainMenu(w, None, game)
|
||||
m.display()
|
||||
|
||||
|
||||
def is_version_compatible(save_version):
|
||||
current_version_components = re.split(r"[\._]", VERSION_STRING)
|
||||
save_version_components = re.split(r"[\._]", save_version)
|
||||
|
||||
if "--ignore-save" in sys.argv:
|
||||
return False
|
||||
|
||||
if current_version_components == save_version_components:
|
||||
return True
|
||||
|
||||
if save_version in ["1.4_rc1", "1.4_rc2", "1.4_rc3", "1.4_rc4", "1.4_rc5", "1.4_rc6"]:
|
||||
return False
|
||||
|
||||
if current_version_components[:2] == save_version_components[:2]:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
w = ui.window.Window()
|
||||
|
||||
try:
|
||||
game = persistency.restore_game()
|
||||
if not game or not is_version_compatible(game.settings.version):
|
||||
ui.newgamemenu.NewGameMenu(w, w.start_new_game).display()
|
||||
else:
|
||||
game.settings.version = VERSION_STRING
|
||||
proceed_to_main_menu(game)
|
||||
except Exception as e:
|
||||
logging.exception(e)
|
||||
ui.corruptedsavemenu.CorruptedSaveMenu(w).display()
|
||||
|
||||
w.run()
|
||||
|
||||
1030
changelog.md
1030
changelog.md
File diff suppressed because it is too large
Load Diff
26
client/.gitignore
vendored
Normal file
26
client/.gitignore
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
.vscode/settings.json
|
||||
.vscode/tasks.json
|
||||
10
client/.vscode/launch.json
vendored
Normal file
10
client/.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"type": "pwa-chrome",
|
||||
"name": "http://localhost:3000",
|
||||
"request": "launch",
|
||||
"url": "http://localhost:3000"
|
||||
}
|
||||
]
|
||||
}
|
||||
76
client/README.md
Normal file
76
client/README.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# DCS Liberation Client
|
||||
|
||||
This is a React app for the front-end of DCS Liberation. It is a work in
|
||||
progress that just barely implements the map. This is not useful for players
|
||||
yet.
|
||||
|
||||
For development, set the following environment variables when launching DCS
|
||||
Liberation (the Qt UI):
|
||||
|
||||
- `CORS_ALLOW_DEBUG_SERVER=true`
|
||||
|
||||
This will allow the front-end to make requests to the server, as long as the
|
||||
front-end is running on http://localhost:3000.
|
||||
|
||||
Then, run `npm start` to start the development server. Launch the Qt UI with
|
||||
`--new-map --dev` to connect the webview to the development server, or navigate
|
||||
to http://localhost:3000 in your browser.
|
||||
|
||||
## Regenerating the API stubs
|
||||
|
||||
The backend uses FastAPI which exposes `/openapi.json`. This is consumed by
|
||||
`@rtk-query/codegen-openapi` to automatically generate the API stubs in
|
||||
`src/api/liberationApi.ts`.
|
||||
|
||||
If you make a change to the API surface the typescript API will need to be
|
||||
regenerated. To do this, first launch Liberation (to start the backend) and run
|
||||
|
||||
```powershell
|
||||
npm run regenerate-api
|
||||
```
|
||||
|
||||
See https://redux-toolkit.js.org/rtk-query/usage/code-generation for more
|
||||
information.
|
||||
|
||||
## Available Scripts
|
||||
|
||||
In the project directory, you can run:
|
||||
|
||||
### `npm start`
|
||||
|
||||
Runs the app in the development mode.<br />
|
||||
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
|
||||
|
||||
The page will reload if you make edits.<br />
|
||||
You will also see any lint errors in the console.
|
||||
|
||||
### `npm test`
|
||||
|
||||
Launches the test runner in the interactive watch mode.<br />
|
||||
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
|
||||
|
||||
### `npm run build`
|
||||
|
||||
Builds the app for production to the `build` folder.<br />
|
||||
It correctly bundles React in production mode and optimizes the build for the best performance.
|
||||
|
||||
The build is minified and the filenames include the hashes.<br />
|
||||
Your app is ready to be deployed!
|
||||
|
||||
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
|
||||
|
||||
### `npm run eject`
|
||||
|
||||
**Note: this is a one-way operation. Once you `eject`, you can’t go back!**
|
||||
|
||||
If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
|
||||
|
||||
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
|
||||
|
||||
You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
|
||||
|
||||
## Learn More
|
||||
|
||||
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
|
||||
|
||||
To learn React, check out the [React documentation](https://reactjs.org/).
|
||||
57
client/main.js
Normal file
57
client/main.js
Normal file
@@ -0,0 +1,57 @@
|
||||
const path = require("path");
|
||||
|
||||
const { app, BrowserWindow } = require("electron");
|
||||
const isDev = require("electron-is-dev");
|
||||
const windowStateKeeper = require("electron-window-state");
|
||||
|
||||
function createWindow() {
|
||||
let mainWindowState = windowStateKeeper({
|
||||
defaultWidth: 1000,
|
||||
defaultHeight: 800,
|
||||
});
|
||||
|
||||
// Create the browser window.
|
||||
const win = new BrowserWindow({
|
||||
x: mainWindowState.x,
|
||||
y: mainWindowState.y,
|
||||
width: mainWindowState.width,
|
||||
height: mainWindowState.height,
|
||||
show: false,
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
},
|
||||
});
|
||||
mainWindowState.manage(win);
|
||||
|
||||
// and load the index.html of the app.
|
||||
// win.loadFile("index.html");
|
||||
win.loadURL(
|
||||
isDev
|
||||
? "http://localhost:3000"
|
||||
: `file://${path.join(__dirname, "../build/index.html")}`
|
||||
);
|
||||
// Open the DevTools.
|
||||
if (isDev) {
|
||||
win.webContents.openDevTools({ mode: "detach" });
|
||||
}
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.whenReady().then(createWindow);
|
||||
|
||||
// Quit when all windows are closed, except on macOS. There, it's common
|
||||
// for applications and their menu bar to stay active until the user quits
|
||||
// explicitly with Cmd + Q.
|
||||
app.on("window-all-closed", () => {
|
||||
if (process.platform !== "darwin") {
|
||||
app.quit();
|
||||
}
|
||||
});
|
||||
|
||||
app.on("activate", () => {
|
||||
if (BrowserWindow.getAllWindows().length === 0) {
|
||||
createWindow();
|
||||
}
|
||||
});
|
||||
12
client/openapi-config.ts
Normal file
12
client/openapi-config.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { ConfigFile } from "@rtk-query/codegen-openapi";
|
||||
|
||||
const config: ConfigFile = {
|
||||
schemaFile: "http://[::1]:16880/openapi.json",
|
||||
apiFile: "./src/api/baseApi.ts",
|
||||
apiImport: "baseApi",
|
||||
outputFile: "./src/api/_liberationApi.ts",
|
||||
exportName: "_liberationApi",
|
||||
hooks: true,
|
||||
};
|
||||
|
||||
export default config;
|
||||
36432
client/package-lock.json
generated
Normal file
36432
client/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
86
client/package.json
Normal file
86
client/package.json
Normal file
@@ -0,0 +1,86 @@
|
||||
{
|
||||
"name": "liberation-client",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"main": "main.js",
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"homepage": ".",
|
||||
"dependencies": {
|
||||
"@reduxjs/toolkit": "^1.8.5",
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@testing-library/user-event": "^14.4.3",
|
||||
"@types/jest": "^29.1.2",
|
||||
"@types/node": "^18.8.3",
|
||||
"@types/react": "^18.0.21",
|
||||
"@types/react-dom": "^18.0.6",
|
||||
"@types/react-redux": "^7.1.24",
|
||||
"axios": "^1.1.2",
|
||||
"electron-window-state": "^5.0.3",
|
||||
"esri-leaflet": "^3.0.8",
|
||||
"leaflet": "^1.9.2",
|
||||
"leaflet-ruler": "^1.0.0",
|
||||
"milsymbol": "^2.0.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-esri-leaflet": "^2.0.1",
|
||||
"react-leaflet": "^4.1.0",
|
||||
"react-redux": "^8.0.4",
|
||||
"redux-logger": "^3.0.6",
|
||||
"typescript": "~4.8.4"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build && generate-license-file --input package.json --output build/NOTICE",
|
||||
"regenerate-api": "rtk-query-codegen-openapi ./openapi-config.ts",
|
||||
"lint": "eslint src",
|
||||
"prepare": "eslint src && license-checker --onlyAllow \"MIT;Apache-2.0;CC0-1.0;BSD-3-Clause;ISC;Custom: https://github.com/tmcw/jsonlint;BSD-2-Clause;Hippocratic-2.1;BSD*;WTFPL\" --excludePrivatePackages --production",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject",
|
||||
"electron": "wait-on tcp:3000 && electron ."
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "react-app"
|
||||
},
|
||||
"eslintIgnore": [
|
||||
"leaflet-ruler.d.ts"
|
||||
],
|
||||
"prettier": {
|
||||
"endOfLine": "auto"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rtk-query/codegen-openapi": "^1.0.0",
|
||||
"@trivago/prettier-plugin-sort-imports": "^3.3.0",
|
||||
"@types/leaflet": "^1.8.0",
|
||||
"@types/redux-logger": "^3.0.9",
|
||||
"@types/websocket": "^1.0.5",
|
||||
"electron": "^21.1.0",
|
||||
"electron-is-dev": "^2.0.0",
|
||||
"generate-license-file": "^2.0.0",
|
||||
"identity-obj-proxy": "^3.0.0",
|
||||
"license-checker": "^25.0.1",
|
||||
"react-scripts": "5.0.1",
|
||||
"ts-node": "^10.9.1",
|
||||
"wait-on": "^6.0.1"
|
||||
},
|
||||
"jest": {
|
||||
"transformIgnorePatterns": [
|
||||
"node_modules/(?!(@?react-leaflet|axios)/)"
|
||||
],
|
||||
"moduleNameMapper": {
|
||||
".+\\.(css|styl|less|sass|scss|png|jpg|ttf|woff|woff2)$": "identity-obj-proxy"
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
client/public/favicon.ico
Normal file
BIN
client/public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.5 KiB |
43
client/public/index.html
Normal file
43
client/public/index.html
Normal file
@@ -0,0 +1,43 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Web site created using create-react-app"
|
||||
/>
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>React Redux App</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
BIN
client/public/logo192.png
Normal file
BIN
client/public/logo192.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.1 KiB |
BIN
client/public/logo512.png
Normal file
BIN
client/public/logo512.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
25
client/public/manifest.json
Normal file
25
client/public/manifest.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"short_name": "React App",
|
||||
"name": "Create React App Sample",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
},
|
||||
{
|
||||
"src": "logo192.png",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
},
|
||||
{
|
||||
"src": "logo512.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
||||
3
client/public/robots.txt
Normal file
3
client/public/robots.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
# https://www.robotstxt.org/robotstxt.html
|
||||
User-agent: *
|
||||
Disallow:
|
||||
12
client/src/App.test.tsx
Normal file
12
client/src/App.test.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
import App from "./App";
|
||||
import { setupStore } from "./app/store";
|
||||
import { render } from "@testing-library/react";
|
||||
import { Provider } from "react-redux";
|
||||
|
||||
test("app renders", () => {
|
||||
render(
|
||||
<Provider store={setupStore()}>
|
||||
<App />
|
||||
</Provider>
|
||||
);
|
||||
});
|
||||
16
client/src/App.tsx
Normal file
16
client/src/App.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import LiberationMap from "./components/liberationmap";
|
||||
import useEventStream from "./hooks/useEventSteam";
|
||||
import useInitialGameState from "./hooks/useInitialGameState";
|
||||
|
||||
function App() {
|
||||
useInitialGameState();
|
||||
useEventStream();
|
||||
|
||||
return (
|
||||
<div className="App">
|
||||
<LiberationMap />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
524
client/src/api/_liberationApi.ts
Normal file
524
client/src/api/_liberationApi.ts
Normal file
@@ -0,0 +1,524 @@
|
||||
import { baseApi as api } from "./baseApi";
|
||||
|
||||
const injectedRtkApi = api.injectEndpoints({
|
||||
endpoints: (build) => ({
|
||||
listControlPoints: build.query<
|
||||
ListControlPointsApiResponse,
|
||||
ListControlPointsApiArg
|
||||
>({
|
||||
query: () => ({ url: `/control-points/` }),
|
||||
}),
|
||||
getControlPointById: build.query<
|
||||
GetControlPointByIdApiResponse,
|
||||
GetControlPointByIdApiArg
|
||||
>({
|
||||
query: (queryArg) => ({ url: `/control-points/${queryArg.cpId}` }),
|
||||
}),
|
||||
controlPointDestinationInRange: build.query<
|
||||
ControlPointDestinationInRangeApiResponse,
|
||||
ControlPointDestinationInRangeApiArg
|
||||
>({
|
||||
query: (queryArg) => ({
|
||||
url: `/control-points/${queryArg.cpId}/destination-in-range`,
|
||||
params: { lat: queryArg.lat, lng: queryArg.lng },
|
||||
}),
|
||||
}),
|
||||
setControlPointDestination: build.mutation<
|
||||
SetControlPointDestinationApiResponse,
|
||||
SetControlPointDestinationApiArg
|
||||
>({
|
||||
query: (queryArg) => ({
|
||||
url: `/control-points/${queryArg.cpId}/destination`,
|
||||
method: "PUT",
|
||||
body: queryArg.body,
|
||||
}),
|
||||
}),
|
||||
clearControlPointDestination: build.mutation<
|
||||
ClearControlPointDestinationApiResponse,
|
||||
ClearControlPointDestinationApiArg
|
||||
>({
|
||||
query: (queryArg) => ({
|
||||
url: `/control-points/${queryArg.cpId}/cancel-travel`,
|
||||
method: "PUT",
|
||||
}),
|
||||
}),
|
||||
getDebugHoldZones: build.query<
|
||||
GetDebugHoldZonesApiResponse,
|
||||
GetDebugHoldZonesApiArg
|
||||
>({
|
||||
query: (queryArg) => ({
|
||||
url: `/debug/waypoint-geometries/hold/${queryArg.flightId}`,
|
||||
}),
|
||||
}),
|
||||
getDebugIpZones: build.query<
|
||||
GetDebugIpZonesApiResponse,
|
||||
GetDebugIpZonesApiArg
|
||||
>({
|
||||
query: (queryArg) => ({
|
||||
url: `/debug/waypoint-geometries/ip/${queryArg.flightId}`,
|
||||
}),
|
||||
}),
|
||||
getDebugJoinZones: build.query<
|
||||
GetDebugJoinZonesApiResponse,
|
||||
GetDebugJoinZonesApiArg
|
||||
>({
|
||||
query: (queryArg) => ({
|
||||
url: `/debug/waypoint-geometries/join/${queryArg.flightId}`,
|
||||
}),
|
||||
}),
|
||||
listFlights: build.query<ListFlightsApiResponse, ListFlightsApiArg>({
|
||||
query: (queryArg) => ({
|
||||
url: `/flights/`,
|
||||
params: { with_waypoints: queryArg.withWaypoints },
|
||||
}),
|
||||
}),
|
||||
getFlightById: build.query<GetFlightByIdApiResponse, GetFlightByIdApiArg>({
|
||||
query: (queryArg) => ({
|
||||
url: `/flights/${queryArg.flightId}`,
|
||||
params: { with_waypoints: queryArg.withWaypoints },
|
||||
}),
|
||||
}),
|
||||
getCommitBoundaryForFlight: build.query<
|
||||
GetCommitBoundaryForFlightApiResponse,
|
||||
GetCommitBoundaryForFlightApiArg
|
||||
>({
|
||||
query: (queryArg) => ({
|
||||
url: `/flights/${queryArg.flightId}/commit-boundary`,
|
||||
}),
|
||||
}),
|
||||
listFrontLines: build.query<
|
||||
ListFrontLinesApiResponse,
|
||||
ListFrontLinesApiArg
|
||||
>({
|
||||
query: () => ({ url: `/front-lines/` }),
|
||||
}),
|
||||
getFrontLineById: build.query<
|
||||
GetFrontLineByIdApiResponse,
|
||||
GetFrontLineByIdApiArg
|
||||
>({
|
||||
query: (queryArg) => ({ url: `/front-lines/${queryArg.frontLineId}` }),
|
||||
}),
|
||||
getGameState: build.query<GetGameStateApiResponse, GetGameStateApiArg>({
|
||||
query: () => ({ url: `/game/` }),
|
||||
}),
|
||||
getTerrainZones: build.query<
|
||||
GetTerrainZonesApiResponse,
|
||||
GetTerrainZonesApiArg
|
||||
>({
|
||||
query: () => ({ url: `/map-zones/terrain` }),
|
||||
}),
|
||||
listUnculledZones: build.query<
|
||||
ListUnculledZonesApiResponse,
|
||||
ListUnculledZonesApiArg
|
||||
>({
|
||||
query: () => ({ url: `/map-zones/unculled` }),
|
||||
}),
|
||||
getThreatZones: build.query<
|
||||
GetThreatZonesApiResponse,
|
||||
GetThreatZonesApiArg
|
||||
>({
|
||||
query: () => ({ url: `/map-zones/threats` }),
|
||||
}),
|
||||
getNavmesh: build.query<GetNavmeshApiResponse, GetNavmeshApiArg>({
|
||||
query: (queryArg) => ({
|
||||
url: `/navmesh/`,
|
||||
params: { for_player: queryArg.forPlayer },
|
||||
}),
|
||||
}),
|
||||
openNewFrontLinePackageDialog: build.mutation<
|
||||
OpenNewFrontLinePackageDialogApiResponse,
|
||||
OpenNewFrontLinePackageDialogApiArg
|
||||
>({
|
||||
query: (queryArg) => ({
|
||||
url: `/qt/create-package/front-line/${queryArg.frontLineId}`,
|
||||
method: "POST",
|
||||
}),
|
||||
}),
|
||||
openNewTgoPackageDialog: build.mutation<
|
||||
OpenNewTgoPackageDialogApiResponse,
|
||||
OpenNewTgoPackageDialogApiArg
|
||||
>({
|
||||
query: (queryArg) => ({
|
||||
url: `/qt/create-package/tgo/${queryArg.tgoId}`,
|
||||
method: "POST",
|
||||
}),
|
||||
}),
|
||||
openTgoInfoDialog: build.mutation<
|
||||
OpenTgoInfoDialogApiResponse,
|
||||
OpenTgoInfoDialogApiArg
|
||||
>({
|
||||
query: (queryArg) => ({
|
||||
url: `/qt/info/tgo/${queryArg.tgoId}`,
|
||||
method: "POST",
|
||||
}),
|
||||
}),
|
||||
openNewControlPointPackageDialog: build.mutation<
|
||||
OpenNewControlPointPackageDialogApiResponse,
|
||||
OpenNewControlPointPackageDialogApiArg
|
||||
>({
|
||||
query: (queryArg) => ({
|
||||
url: `/qt/create-package/control-point/${queryArg.cpId}`,
|
||||
method: "POST",
|
||||
}),
|
||||
}),
|
||||
openControlPointInfoDialog: build.mutation<
|
||||
OpenControlPointInfoDialogApiResponse,
|
||||
OpenControlPointInfoDialogApiArg
|
||||
>({
|
||||
query: (queryArg) => ({
|
||||
url: `/qt/info/control-point/${queryArg.cpId}`,
|
||||
method: "POST",
|
||||
}),
|
||||
}),
|
||||
listSupplyRoutes: build.query<
|
||||
ListSupplyRoutesApiResponse,
|
||||
ListSupplyRoutesApiArg
|
||||
>({
|
||||
query: () => ({ url: `/supply-routes/` }),
|
||||
}),
|
||||
listTgos: build.query<ListTgosApiResponse, ListTgosApiArg>({
|
||||
query: () => ({ url: `/tgos/` }),
|
||||
}),
|
||||
getTgoById: build.query<GetTgoByIdApiResponse, GetTgoByIdApiArg>({
|
||||
query: (queryArg) => ({ url: `/tgos/${queryArg.tgoId}` }),
|
||||
}),
|
||||
listAllWaypointsForFlight: build.query<
|
||||
ListAllWaypointsForFlightApiResponse,
|
||||
ListAllWaypointsForFlightApiArg
|
||||
>({
|
||||
query: (queryArg) => ({ url: `/waypoints/${queryArg.flightId}` }),
|
||||
}),
|
||||
setWaypointPosition: build.mutation<
|
||||
SetWaypointPositionApiResponse,
|
||||
SetWaypointPositionApiArg
|
||||
>({
|
||||
query: (queryArg) => ({
|
||||
url: `/waypoints/${queryArg.flightId}/${queryArg.waypointIdx}/position`,
|
||||
method: "POST",
|
||||
body: queryArg.leafletPoint,
|
||||
}),
|
||||
}),
|
||||
getIadsNetwork: build.query<
|
||||
GetIadsNetworkApiResponse,
|
||||
GetIadsNetworkApiArg
|
||||
>({
|
||||
query: () => ({ url: `/iads-network/` }),
|
||||
}),
|
||||
getIadsConnectionsForTgo: build.query<
|
||||
GetIadsConnectionsForTgoApiResponse,
|
||||
GetIadsConnectionsForTgoApiArg
|
||||
>({
|
||||
query: (queryArg) => ({ url: `/iads-network/for-tgo/${queryArg.tgoId}` }),
|
||||
}),
|
||||
}),
|
||||
overrideExisting: false,
|
||||
});
|
||||
export { injectedRtkApi as _liberationApi };
|
||||
export type ListControlPointsApiResponse =
|
||||
/** status 200 Successful Response */ ControlPoint[];
|
||||
export type ListControlPointsApiArg = void;
|
||||
export type GetControlPointByIdApiResponse =
|
||||
/** status 200 Successful Response */ ControlPoint;
|
||||
export type GetControlPointByIdApiArg = {
|
||||
cpId: string;
|
||||
};
|
||||
export type ControlPointDestinationInRangeApiResponse =
|
||||
/** status 200 Successful Response */ boolean;
|
||||
export type ControlPointDestinationInRangeApiArg = {
|
||||
cpId: string;
|
||||
lat: number;
|
||||
lng: number;
|
||||
};
|
||||
export type SetControlPointDestinationApiResponse =
|
||||
/** status 204 Successful Response */ undefined;
|
||||
export type SetControlPointDestinationApiArg = {
|
||||
cpId: string;
|
||||
body: LatLng;
|
||||
};
|
||||
export type ClearControlPointDestinationApiResponse =
|
||||
/** status 204 Successful Response */ undefined;
|
||||
export type ClearControlPointDestinationApiArg = {
|
||||
cpId: string;
|
||||
};
|
||||
export type GetDebugHoldZonesApiResponse =
|
||||
/** status 200 Successful Response */ HoldZones;
|
||||
export type GetDebugHoldZonesApiArg = {
|
||||
flightId: string;
|
||||
};
|
||||
export type GetDebugIpZonesApiResponse =
|
||||
/** status 200 Successful Response */ IpZones;
|
||||
export type GetDebugIpZonesApiArg = {
|
||||
flightId: string;
|
||||
};
|
||||
export type GetDebugJoinZonesApiResponse =
|
||||
/** status 200 Successful Response */ JoinZones;
|
||||
export type GetDebugJoinZonesApiArg = {
|
||||
flightId: string;
|
||||
};
|
||||
export type ListFlightsApiResponse =
|
||||
/** status 200 Successful Response */ Flight[];
|
||||
export type ListFlightsApiArg = {
|
||||
withWaypoints?: boolean;
|
||||
};
|
||||
export type GetFlightByIdApiResponse =
|
||||
/** status 200 Successful Response */ Flight;
|
||||
export type GetFlightByIdApiArg = {
|
||||
flightId: string;
|
||||
withWaypoints?: boolean;
|
||||
};
|
||||
export type GetCommitBoundaryForFlightApiResponse =
|
||||
/** status 200 Successful Response */ LatLng[][];
|
||||
export type GetCommitBoundaryForFlightApiArg = {
|
||||
flightId: string;
|
||||
};
|
||||
export type ListFrontLinesApiResponse =
|
||||
/** status 200 Successful Response */ FrontLine[];
|
||||
export type ListFrontLinesApiArg = void;
|
||||
export type GetFrontLineByIdApiResponse =
|
||||
/** status 200 Successful Response */ FrontLine;
|
||||
export type GetFrontLineByIdApiArg = {
|
||||
frontLineId: string;
|
||||
};
|
||||
export type GetGameStateApiResponse =
|
||||
/** status 200 Successful Response */ Game;
|
||||
export type GetGameStateApiArg = void;
|
||||
export type GetTerrainZonesApiResponse =
|
||||
/** status 200 Successful Response */ MapZones;
|
||||
export type GetTerrainZonesApiArg = void;
|
||||
export type ListUnculledZonesApiResponse =
|
||||
/** status 200 Successful Response */ UnculledZone[];
|
||||
export type ListUnculledZonesApiArg = void;
|
||||
export type GetThreatZonesApiResponse =
|
||||
/** status 200 Successful Response */ ThreatZoneContainer;
|
||||
export type GetThreatZonesApiArg = void;
|
||||
export type GetNavmeshApiResponse =
|
||||
/** status 200 Successful Response */ NavMesh;
|
||||
export type GetNavmeshApiArg = {
|
||||
forPlayer: boolean;
|
||||
};
|
||||
export type OpenNewFrontLinePackageDialogApiResponse =
|
||||
/** status 200 Successful Response */ any;
|
||||
export type OpenNewFrontLinePackageDialogApiArg = {
|
||||
frontLineId: string;
|
||||
};
|
||||
export type OpenNewTgoPackageDialogApiResponse =
|
||||
/** status 200 Successful Response */ any;
|
||||
export type OpenNewTgoPackageDialogApiArg = {
|
||||
tgoId: string;
|
||||
};
|
||||
export type OpenTgoInfoDialogApiResponse =
|
||||
/** status 200 Successful Response */ any;
|
||||
export type OpenTgoInfoDialogApiArg = {
|
||||
tgoId: string;
|
||||
};
|
||||
export type OpenNewControlPointPackageDialogApiResponse =
|
||||
/** status 200 Successful Response */ any;
|
||||
export type OpenNewControlPointPackageDialogApiArg = {
|
||||
cpId: string;
|
||||
};
|
||||
export type OpenControlPointInfoDialogApiResponse =
|
||||
/** status 200 Successful Response */ any;
|
||||
export type OpenControlPointInfoDialogApiArg = {
|
||||
cpId: string;
|
||||
};
|
||||
export type ListSupplyRoutesApiResponse =
|
||||
/** status 200 Successful Response */ SupplyRoute[];
|
||||
export type ListSupplyRoutesApiArg = void;
|
||||
export type ListTgosApiResponse = /** status 200 Successful Response */ Tgo[];
|
||||
export type ListTgosApiArg = void;
|
||||
export type GetTgoByIdApiResponse = /** status 200 Successful Response */ Tgo;
|
||||
export type GetTgoByIdApiArg = {
|
||||
tgoId: string;
|
||||
};
|
||||
export type ListAllWaypointsForFlightApiResponse =
|
||||
/** status 200 Successful Response */ Waypoint[];
|
||||
export type ListAllWaypointsForFlightApiArg = {
|
||||
flightId: string;
|
||||
};
|
||||
export type SetWaypointPositionApiResponse =
|
||||
/** status 204 Successful Response */ undefined;
|
||||
export type SetWaypointPositionApiArg = {
|
||||
flightId: string;
|
||||
waypointIdx: number;
|
||||
leafletPoint: LatLng;
|
||||
};
|
||||
export type GetIadsNetworkApiResponse =
|
||||
/** status 200 Successful Response */ IadsNetwork;
|
||||
export type GetIadsNetworkApiArg = void;
|
||||
export type GetIadsConnectionsForTgoApiResponse =
|
||||
/** status 200 Successful Response */ IadsConnection[];
|
||||
export type GetIadsConnectionsForTgoApiArg = {
|
||||
tgoId: string;
|
||||
};
|
||||
export type LatLng = {
|
||||
lat: number;
|
||||
lng: number;
|
||||
};
|
||||
export type ControlPoint = {
|
||||
id: string;
|
||||
name: string;
|
||||
blue: boolean;
|
||||
position: LatLng;
|
||||
mobile: boolean;
|
||||
destination?: LatLng;
|
||||
sidc: string;
|
||||
};
|
||||
export type ValidationError = {
|
||||
loc: (string | number)[];
|
||||
msg: string;
|
||||
type: string;
|
||||
};
|
||||
export type HttpValidationError = {
|
||||
detail?: ValidationError[];
|
||||
};
|
||||
export type HoldZones = {
|
||||
homeBubble: LatLng[][];
|
||||
targetBubble: LatLng[][];
|
||||
joinBubble: LatLng[][];
|
||||
excludedZones: LatLng[][][];
|
||||
permissibleZones: LatLng[][][];
|
||||
preferredLines: LatLng[][];
|
||||
};
|
||||
export type IpZones = {
|
||||
homeBubble: LatLng[][];
|
||||
ipBubble: LatLng[][];
|
||||
permissibleZone: LatLng[][];
|
||||
safeZones: LatLng[][][];
|
||||
};
|
||||
export type JoinZones = {
|
||||
homeBubble: LatLng[][];
|
||||
targetBubble: LatLng[][];
|
||||
ipBubble: LatLng[][];
|
||||
excludedZones: LatLng[][][];
|
||||
permissibleZones: LatLng[][][];
|
||||
preferredLines: LatLng[][];
|
||||
};
|
||||
export type Waypoint = {
|
||||
name: string;
|
||||
position: LatLng;
|
||||
altitude_ft: number;
|
||||
altitude_reference: string;
|
||||
is_movable: boolean;
|
||||
should_mark: boolean;
|
||||
include_in_path: boolean;
|
||||
timing: string;
|
||||
};
|
||||
export type Flight = {
|
||||
id: string;
|
||||
blue: boolean;
|
||||
position?: LatLng;
|
||||
sidc: string;
|
||||
waypoints?: Waypoint[];
|
||||
};
|
||||
export type FrontLine = {
|
||||
id: string;
|
||||
extents: LatLng[];
|
||||
};
|
||||
export type Tgo = {
|
||||
id: string;
|
||||
name: string;
|
||||
control_point_name: string;
|
||||
category: string;
|
||||
blue: boolean;
|
||||
position: LatLng;
|
||||
units: string[];
|
||||
threat_ranges: number[];
|
||||
detection_ranges: number[];
|
||||
dead: boolean;
|
||||
sidc: string;
|
||||
};
|
||||
export type SupplyRoute = {
|
||||
id: string;
|
||||
points: LatLng[];
|
||||
front_active: boolean;
|
||||
is_sea: boolean;
|
||||
blue: boolean;
|
||||
active_transports: string[];
|
||||
};
|
||||
export type IadsConnection = {
|
||||
id: string;
|
||||
points: LatLng[];
|
||||
node: string;
|
||||
connected: string;
|
||||
active: boolean;
|
||||
blue: boolean;
|
||||
is_power: boolean;
|
||||
};
|
||||
export type IadsNetwork = {
|
||||
advanced: boolean;
|
||||
connections: IadsConnection[];
|
||||
};
|
||||
export type ThreatZones = {
|
||||
full: LatLng[][][];
|
||||
aircraft: LatLng[][][];
|
||||
air_defenses: LatLng[][][];
|
||||
radar_sams: LatLng[][][];
|
||||
};
|
||||
export type ThreatZoneContainer = {
|
||||
blue: ThreatZones;
|
||||
red: ThreatZones;
|
||||
};
|
||||
export type NavMeshPoly = {
|
||||
poly: LatLng[][];
|
||||
threatened: boolean;
|
||||
};
|
||||
export type NavMesh = {
|
||||
polys: NavMeshPoly[];
|
||||
};
|
||||
export type NavMeshes = {
|
||||
blue: NavMesh;
|
||||
red: NavMesh;
|
||||
};
|
||||
export type UnculledZone = {
|
||||
position: LatLng;
|
||||
radius: number;
|
||||
};
|
||||
export type Game = {
|
||||
control_points: ControlPoint[];
|
||||
tgos: Tgo[];
|
||||
supply_routes: SupplyRoute[];
|
||||
front_lines: FrontLine[];
|
||||
flights: Flight[];
|
||||
iads_network: IadsNetwork;
|
||||
threat_zones: ThreatZoneContainer;
|
||||
navmeshes: NavMeshes;
|
||||
map_center?: LatLng;
|
||||
unculled_zones: UnculledZone[];
|
||||
};
|
||||
export type MapZones = {
|
||||
inclusion: LatLng[][][];
|
||||
exclusion: LatLng[][][];
|
||||
sea: LatLng[][][];
|
||||
};
|
||||
export const {
|
||||
useListControlPointsQuery,
|
||||
useGetControlPointByIdQuery,
|
||||
useControlPointDestinationInRangeQuery,
|
||||
useSetControlPointDestinationMutation,
|
||||
useClearControlPointDestinationMutation,
|
||||
useGetDebugHoldZonesQuery,
|
||||
useGetDebugIpZonesQuery,
|
||||
useGetDebugJoinZonesQuery,
|
||||
useListFlightsQuery,
|
||||
useGetFlightByIdQuery,
|
||||
useGetCommitBoundaryForFlightQuery,
|
||||
useListFrontLinesQuery,
|
||||
useGetFrontLineByIdQuery,
|
||||
useGetGameStateQuery,
|
||||
useGetTerrainZonesQuery,
|
||||
useListUnculledZonesQuery,
|
||||
useGetThreatZonesQuery,
|
||||
useGetNavmeshQuery,
|
||||
useOpenNewFrontLinePackageDialogMutation,
|
||||
useOpenNewTgoPackageDialogMutation,
|
||||
useOpenTgoInfoDialogMutation,
|
||||
useOpenNewControlPointPackageDialogMutation,
|
||||
useOpenControlPointInfoDialogMutation,
|
||||
useListSupplyRoutesQuery,
|
||||
useListTgosQuery,
|
||||
useGetTgoByIdQuery,
|
||||
useListAllWaypointsForFlightQuery,
|
||||
useSetWaypointPositionMutation,
|
||||
useGetIadsNetworkQuery,
|
||||
useGetIadsConnectionsForTgoQuery,
|
||||
} = injectedRtkApi;
|
||||
5
client/src/api/actions.ts
Normal file
5
client/src/api/actions.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { Game } from "./liberationApi";
|
||||
import { createAction } from "@reduxjs/toolkit";
|
||||
|
||||
export const gameLoaded = createAction<Game>("game/loaded");
|
||||
export const gameUnloaded = createAction("game/unloaded");
|
||||
15
client/src/api/backend.ts
Normal file
15
client/src/api/backend.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import axios from "axios";
|
||||
|
||||
const backendAddr =
|
||||
new URL(window.location.toString()).searchParams.get("server") ??
|
||||
"[::1]:16880";
|
||||
|
||||
export const HTTP_URL = `http://${backendAddr}/`;
|
||||
|
||||
export const backend = axios.create({
|
||||
baseURL: HTTP_URL,
|
||||
});
|
||||
|
||||
export const WEBSOCKET_URL = `ws://${backendAddr}/eventstream`;
|
||||
|
||||
export default backend;
|
||||
7
client/src/api/baseApi.ts
Normal file
7
client/src/api/baseApi.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { HTTP_URL } from "./backend";
|
||||
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
|
||||
|
||||
export const baseApi = createApi({
|
||||
baseQuery: fetchBaseQuery({ baseUrl: HTTP_URL }),
|
||||
endpoints: () => ({}),
|
||||
});
|
||||
8
client/src/api/combat.ts
Normal file
8
client/src/api/combat.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { LatLng } from "leaflet";
|
||||
|
||||
export default interface Combat {
|
||||
id: string;
|
||||
flight_position: LatLng | null;
|
||||
target_positions: LatLng[] | null;
|
||||
footprint: LatLng[][] | null;
|
||||
}
|
||||
49
client/src/api/combatSlice.ts
Normal file
49
client/src/api/combatSlice.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { RootState } from "../app/store";
|
||||
import { gameLoaded, gameUnloaded } from "./actions";
|
||||
import Combat from "./combat";
|
||||
import { PayloadAction, createSlice } from "@reduxjs/toolkit";
|
||||
|
||||
interface CombatState {
|
||||
combat: { [key: string]: Combat };
|
||||
}
|
||||
|
||||
const initialState: CombatState = {
|
||||
combat: {},
|
||||
};
|
||||
|
||||
export const combatSlice = createSlice({
|
||||
name: "combat",
|
||||
initialState,
|
||||
reducers: {
|
||||
newCombats: (state, action: PayloadAction<Combat[]>) => {
|
||||
for (const combat of action.payload) {
|
||||
state.combat[combat.id] = combat;
|
||||
}
|
||||
},
|
||||
updateCombats: (state, action: PayloadAction<Combat[]>) => {
|
||||
for (const combat of action.payload) {
|
||||
state.combat[combat.id] = combat;
|
||||
}
|
||||
},
|
||||
endCombats: (state, action: PayloadAction<string[]>) => {
|
||||
for (const cID of action.payload) {
|
||||
delete state.combat[cID];
|
||||
}
|
||||
},
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
builder.addCase(gameLoaded, (state, action) => {
|
||||
state.combat = {};
|
||||
});
|
||||
builder.addCase(gameUnloaded, (state) => {
|
||||
state.combat = {};
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export const { newCombats, updateCombats, endCombats } =
|
||||
combatSlice.actions;
|
||||
|
||||
export const selectCombat = (state: RootState) => state.combat;
|
||||
|
||||
export default combatSlice.reducer;
|
||||
44
client/src/api/controlPointsSlice.ts
Normal file
44
client/src/api/controlPointsSlice.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { RootState } from "../app/store";
|
||||
import { gameLoaded, gameUnloaded } from "./actions";
|
||||
import { ControlPoint } from "./liberationApi";
|
||||
import { PayloadAction, createSlice } from "@reduxjs/toolkit";
|
||||
|
||||
interface ControlPointsState {
|
||||
controlPoints: { [key: string]: ControlPoint };
|
||||
}
|
||||
|
||||
const initialState: ControlPointsState = {
|
||||
controlPoints: {},
|
||||
};
|
||||
|
||||
export const controlPointsSlice = createSlice({
|
||||
name: "controlPoints",
|
||||
initialState,
|
||||
reducers: {
|
||||
updateControlPoint: (state, action: PayloadAction<ControlPoint[]>) => {
|
||||
for (const cp of action.payload) {
|
||||
state.controlPoints[cp.id] = cp;
|
||||
}
|
||||
},
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
builder.addCase(gameLoaded, (state, action) => {
|
||||
state.controlPoints = action.payload.control_points.reduce(
|
||||
(acc: { [key: string]: ControlPoint }, curr) => {
|
||||
acc[curr.id] = curr;
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
});
|
||||
builder.addCase(gameUnloaded, (state) => {
|
||||
state.controlPoints = {};
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export const { updateControlPoint } = controlPointsSlice.actions;
|
||||
|
||||
export const selectControlPoints = (state: RootState) => state.controlPoints;
|
||||
|
||||
export default controlPointsSlice.reducer;
|
||||
149
client/src/api/eventstream.tsx
Normal file
149
client/src/api/eventstream.tsx
Normal file
@@ -0,0 +1,149 @@
|
||||
import { AppDispatch } from "../app/store";
|
||||
import { gameUnloaded } from "./actions";
|
||||
import Combat from "./combat";
|
||||
import { endCombats, newCombats, updateCombats } from "./combatSlice";
|
||||
import { updateControlPoint } from "./controlPointsSlice";
|
||||
import {
|
||||
deselectFlight,
|
||||
registerFlights,
|
||||
selectFlight,
|
||||
unregisterFlights,
|
||||
updateFlights,
|
||||
updateFlightPositions,
|
||||
} from "./flightsSlice";
|
||||
import {
|
||||
deleteFrontLine,
|
||||
updateFrontLine,
|
||||
} from "./frontLinesSlice";
|
||||
import reloadGameState from "./gamestate";
|
||||
import {
|
||||
ControlPoint,
|
||||
Flight,
|
||||
FrontLine,
|
||||
IadsConnection,
|
||||
NavMesh,
|
||||
Tgo,
|
||||
ThreatZones,
|
||||
UnculledZone,
|
||||
} from "./liberationApi";
|
||||
import { navMeshUpdated } from "./navMeshSlice";
|
||||
import { updateTgo } from "./tgosSlice";
|
||||
import { threatZonesUpdated } from "./threatZonesSlice";
|
||||
import { unculledZonesUpdated } from "./unculledZonesSlice";
|
||||
import { LatLng } from "leaflet";
|
||||
import { updateIadsConnection, removeIadsConnection } from "./iadsNetworkSlice";
|
||||
|
||||
interface GameUpdateEvents {
|
||||
updated_flight_positions: { [id: string]: LatLng };
|
||||
new_combats: Combat[];
|
||||
updated_combats: Combat[];
|
||||
ended_combats: string[];
|
||||
navmesh_updates: {blue: boolean, mesh: NavMesh}[];
|
||||
updated_unculled_zones: UnculledZone[];
|
||||
threat_zones_updated: {blue: boolean, zones: ThreatZones}[];
|
||||
new_flights: Flight[];
|
||||
updated_flights: Flight[];
|
||||
deleted_flights: string[];
|
||||
selected_flight: string | null;
|
||||
deselected_flight: boolean;
|
||||
updated_front_lines: FrontLine[];
|
||||
deleted_front_lines: string[];
|
||||
updated_tgos: Tgo[];
|
||||
updated_control_points: ControlPoint[];
|
||||
updated_iads: IadsConnection[];
|
||||
deleted_iads: string[];
|
||||
reset_on_map_center: LatLng | null;
|
||||
game_unloaded: boolean;
|
||||
new_turn: boolean;
|
||||
}
|
||||
|
||||
export const handleStreamedEvents = (
|
||||
dispatch: AppDispatch,
|
||||
events: GameUpdateEvents
|
||||
) => {
|
||||
if (Object.keys(events.updated_flight_positions).length) {
|
||||
dispatch(
|
||||
updateFlightPositions(Object.entries(events.updated_flight_positions))
|
||||
);
|
||||
}
|
||||
|
||||
if (events.new_combats.length > 0) {
|
||||
dispatch(newCombats(events.new_combats));
|
||||
}
|
||||
|
||||
if (events.updated_combats.length > 0) {
|
||||
dispatch(updateCombats(events.updated_combats));
|
||||
}
|
||||
|
||||
if (events.ended_combats.length > 0) {
|
||||
dispatch(endCombats(events.ended_combats));
|
||||
}
|
||||
|
||||
if (Object.keys(events.navmesh_updates).length > 0) {
|
||||
dispatch(navMeshUpdated(events.navmesh_updates));
|
||||
}
|
||||
|
||||
if (events.updated_unculled_zones.length > 0) {
|
||||
dispatch(unculledZonesUpdated(events.updated_unculled_zones));
|
||||
}
|
||||
|
||||
if (Object.keys(events.threat_zones_updated).length > 0) {
|
||||
dispatch(threatZonesUpdated(events.threat_zones_updated));
|
||||
}
|
||||
|
||||
if (events.new_flights.length > 0) {
|
||||
dispatch(registerFlights(events.new_flights));
|
||||
}
|
||||
|
||||
if (events.updated_flights.length > 0) {
|
||||
dispatch(updateFlights(events.updated_flights));
|
||||
}
|
||||
|
||||
if (events.deleted_flights.length > 0) {
|
||||
dispatch(unregisterFlights(events.deleted_flights));
|
||||
}
|
||||
|
||||
if (events.deselected_flight) {
|
||||
dispatch(deselectFlight());
|
||||
}
|
||||
|
||||
if (events.selected_flight != null) {
|
||||
dispatch(selectFlight(events.selected_flight));
|
||||
}
|
||||
|
||||
if (events.updated_front_lines.length > 0) {
|
||||
dispatch(updateFrontLine(events.updated_front_lines));
|
||||
}
|
||||
|
||||
if (events.deleted_front_lines.length > 0) {
|
||||
dispatch(deleteFrontLine(events.deleted_front_lines));
|
||||
}
|
||||
|
||||
if (events.updated_tgos.length > 0) {
|
||||
dispatch(updateTgo(events.updated_tgos));
|
||||
}
|
||||
|
||||
if (events.updated_control_points.length > 0) {
|
||||
dispatch(updateControlPoint(events.updated_control_points));
|
||||
}
|
||||
|
||||
if (events.deleted_iads.length > 0) {
|
||||
dispatch(removeIadsConnection(events.deleted_iads));
|
||||
}
|
||||
|
||||
if (events.updated_iads.length > 0) {
|
||||
dispatch(updateIadsConnection(events.updated_iads));
|
||||
}
|
||||
|
||||
if (events.reset_on_map_center != null) {
|
||||
reloadGameState(dispatch);
|
||||
}
|
||||
|
||||
if (events.game_unloaded) {
|
||||
dispatch(gameUnloaded());
|
||||
}
|
||||
|
||||
if (events.new_turn) {
|
||||
reloadGameState(dispatch, true);
|
||||
}
|
||||
};
|
||||
89
client/src/api/flightsSlice.ts
Normal file
89
client/src/api/flightsSlice.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
import { RootState } from "../app/store";
|
||||
import { gameLoaded, gameUnloaded } from "./actions";
|
||||
import { Flight } from "./liberationApi";
|
||||
import { PayloadAction, createSlice } from "@reduxjs/toolkit";
|
||||
import { LatLng } from "leaflet";
|
||||
|
||||
interface FlightsState {
|
||||
flights: { [id: string]: Flight };
|
||||
selected: string | null;
|
||||
}
|
||||
|
||||
const initialState: FlightsState = {
|
||||
flights: {},
|
||||
selected: null,
|
||||
};
|
||||
|
||||
export const flightsSlice = createSlice({
|
||||
name: "flights",
|
||||
initialState,
|
||||
reducers: {
|
||||
registerFlights: (state, action: PayloadAction<Flight[]>) => {
|
||||
for (const flight of action.payload) {
|
||||
if (flight.id in state.flights) {
|
||||
console.log(`Overriding flight with ID: ${flight.id}`);
|
||||
}
|
||||
state.flights[flight.id] = flight;
|
||||
}
|
||||
},
|
||||
unregisterFlights: (state, action: PayloadAction<string[]>) => {
|
||||
for (const id of action.payload) {
|
||||
delete state.flights[id];
|
||||
}
|
||||
},
|
||||
updateFlights: (state, action: PayloadAction<Flight[]>) => {
|
||||
for (const flight of action.payload) {
|
||||
state.flights[flight.id] = flight;
|
||||
}
|
||||
},
|
||||
deselectFlight: (state) => {
|
||||
state.selected = null;
|
||||
},
|
||||
selectFlight: (state, action: PayloadAction<string>) => {
|
||||
state.selected = action.payload;
|
||||
},
|
||||
updateFlightPositions: (
|
||||
state,
|
||||
action: PayloadAction<[string, LatLng][]>
|
||||
) => {
|
||||
for (const [id, position] of action.payload) {
|
||||
state.flights[id].position = position;
|
||||
}
|
||||
},
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
builder.addCase(gameLoaded, (state, action) => {
|
||||
state.selected = null;
|
||||
state.flights = action.payload.flights.reduce(
|
||||
(acc: { [key: string]: Flight }, curr) => {
|
||||
acc[curr.id] = curr;
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
});
|
||||
builder.addCase(gameUnloaded, (state) => {
|
||||
state.selected = null;
|
||||
state.flights = {};
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export const {
|
||||
registerFlights,
|
||||
unregisterFlights,
|
||||
updateFlights,
|
||||
deselectFlight,
|
||||
selectFlight,
|
||||
updateFlightPositions,
|
||||
} = flightsSlice.actions;
|
||||
|
||||
export const selectFlights = (state: RootState) => state.flights;
|
||||
export const selectSelectedFlightId = (state: RootState) =>
|
||||
state.flights.selected;
|
||||
export const selectSelectedFlight = (state: RootState) => {
|
||||
const id = state.flights.selected;
|
||||
return id ? state.flights.flights[id] : null;
|
||||
};
|
||||
|
||||
export default flightsSlice.reducer;
|
||||
50
client/src/api/frontLinesSlice.ts
Normal file
50
client/src/api/frontLinesSlice.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import { RootState } from "../app/store";
|
||||
import { gameLoaded, gameUnloaded } from "./actions";
|
||||
import { FrontLine } from "./liberationApi";
|
||||
import { PayloadAction, createSlice } from "@reduxjs/toolkit";
|
||||
|
||||
interface FrontLinesState {
|
||||
fronts: { [key: string]: FrontLine };
|
||||
}
|
||||
|
||||
const initialState: FrontLinesState = {
|
||||
fronts: {},
|
||||
};
|
||||
|
||||
export const frontLinesSlice = createSlice({
|
||||
name: "frontLines",
|
||||
initialState,
|
||||
reducers: {
|
||||
updateFrontLine: (state, action: PayloadAction<FrontLine[]>) => {
|
||||
for (const front of action.payload) {
|
||||
state.fronts[front.id] = front;
|
||||
}
|
||||
},
|
||||
deleteFrontLine: (state, action: PayloadAction<string[]>) => {
|
||||
for (const uid of action.payload) {
|
||||
delete state.fronts[uid];
|
||||
}
|
||||
},
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
builder.addCase(gameLoaded, (state, action) => {
|
||||
state.fronts = action.payload.front_lines.reduce(
|
||||
(acc: { [key: string]: FrontLine }, curr) => {
|
||||
acc[curr.id] = curr;
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
});
|
||||
builder.addCase(gameUnloaded, (state) => {
|
||||
state.fronts = {};
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export const { updateFrontLine, deleteFrontLine } =
|
||||
frontLinesSlice.actions;
|
||||
|
||||
export const selectFrontLines = (state: RootState) => state.frontLines;
|
||||
|
||||
export default frontLinesSlice.reducer;
|
||||
24
client/src/api/gamestate.ts
Normal file
24
client/src/api/gamestate.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { AppDispatch } from "../app/store";
|
||||
import { gameLoaded, gameUnloaded } from "./actions";
|
||||
import backend from "./backend";
|
||||
import { Game } from "./liberationApi";
|
||||
|
||||
export default function reloadGameState(
|
||||
dispatch: AppDispatch,
|
||||
ignoreRecenter: boolean = false
|
||||
) {
|
||||
backend
|
||||
.get("/game")
|
||||
.catch((error) => console.log(`Error fetching game state: ${error}`))
|
||||
.then((response) => {
|
||||
if (response == null || response.data == null) {
|
||||
dispatch(gameUnloaded());
|
||||
return;
|
||||
}
|
||||
const game = response.data as Game;
|
||||
if (ignoreRecenter) {
|
||||
game.map_center = undefined;
|
||||
}
|
||||
dispatch(gameLoaded(game));
|
||||
});
|
||||
}
|
||||
49
client/src/api/iadsNetworkSlice.ts
Normal file
49
client/src/api/iadsNetworkSlice.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { RootState } from "../app/store";
|
||||
import { gameLoaded, gameUnloaded } from "./actions";
|
||||
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||
import { IadsConnection} from "./_liberationApi";
|
||||
|
||||
interface IadsNetworkState {
|
||||
connections: {[key: string]: IadsConnection}
|
||||
}
|
||||
|
||||
const initialState: IadsNetworkState = {
|
||||
connections: {},
|
||||
};
|
||||
|
||||
export const IadsNetworkSlice = createSlice({
|
||||
name: "iadsNetwork",
|
||||
initialState,
|
||||
reducers: {
|
||||
updateIadsConnection: (state, action: PayloadAction<IadsConnection[]>) => {
|
||||
for (const connection of action.payload) {
|
||||
state.connections[connection.id] = connection
|
||||
}
|
||||
},
|
||||
removeIadsConnection: (state, action: PayloadAction<string[]>) => {
|
||||
for (const cID of action.payload) {
|
||||
delete state.connections[cID];
|
||||
}
|
||||
}
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
builder.addCase(gameLoaded, (state, action) => {
|
||||
state.connections = action.payload.iads_network.connections.reduce(
|
||||
(acc: { [key: string]: IadsConnection }, curr) => {
|
||||
acc[curr.id] = curr;
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
});
|
||||
builder.addCase(gameUnloaded, (state) => {
|
||||
state.connections = {};
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export const { updateIadsConnection, removeIadsConnection } = IadsNetworkSlice.actions;
|
||||
|
||||
export const selectIadsNetwork = (state: RootState) => state.iadsNetwork;
|
||||
|
||||
export default IadsNetworkSlice.reducer;
|
||||
71
client/src/api/liberationApi.ts
Normal file
71
client/src/api/liberationApi.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { _liberationApi } from "./_liberationApi";
|
||||
|
||||
// See https://redux-toolkit.js.org/rtk-query/usage/automated-refetching for an
|
||||
// explanation of tag behavior.
|
||||
|
||||
export enum Tags {
|
||||
FLIGHT_PLAN = "FlightPlan",
|
||||
}
|
||||
|
||||
const LIST_ID = "LIST";
|
||||
|
||||
function providesList<R extends { id: string | number }[], T extends string>(
|
||||
resultsWithIds: R | undefined,
|
||||
tagType: T
|
||||
) {
|
||||
return resultsWithIds
|
||||
? [
|
||||
{ type: tagType, id: LIST_ID },
|
||||
...resultsWithIds.map(({ id }) => ({ type: tagType, id })),
|
||||
]
|
||||
: [{ type: tagType, id: LIST_ID }];
|
||||
}
|
||||
|
||||
export const liberationApi = _liberationApi.enhanceEndpoints({
|
||||
addTagTypes: Object.values(Tags),
|
||||
endpoints: {
|
||||
// /debug/waypoint-geometries
|
||||
getDebugHoldZones: {
|
||||
providesTags: (result, error, arg) => [
|
||||
{ type: Tags.FLIGHT_PLAN, id: arg.flightId },
|
||||
],
|
||||
},
|
||||
getDebugIpZones: {
|
||||
providesTags: (result, error, arg) => [
|
||||
{ type: Tags.FLIGHT_PLAN, id: arg.flightId },
|
||||
],
|
||||
},
|
||||
getDebugJoinZones: {
|
||||
providesTags: (result, error, arg) => [
|
||||
{ type: Tags.FLIGHT_PLAN, id: arg.flightId },
|
||||
],
|
||||
},
|
||||
// /flights/
|
||||
getCommitBoundaryForFlight: {
|
||||
providesTags: (result, error, arg) => [
|
||||
{ type: Tags.FLIGHT_PLAN, id: arg.flightId },
|
||||
],
|
||||
},
|
||||
getFlightById: {
|
||||
providesTags: (result, error, arg) => [
|
||||
{ type: Tags.FLIGHT_PLAN, id: arg.flightId },
|
||||
],
|
||||
},
|
||||
listFlights: {
|
||||
providesTags: (result) => providesList(result, Tags.FLIGHT_PLAN),
|
||||
},
|
||||
// /waypoints/
|
||||
listAllWaypointsForFlight: {
|
||||
providesTags: (result, error, arg) => [
|
||||
{ type: Tags.FLIGHT_PLAN, id: arg.flightId },
|
||||
],
|
||||
},
|
||||
setWaypointPosition: {
|
||||
invalidatesTags: (result, error, arg) => [
|
||||
{ type: Tags.FLIGHT_PLAN, id: arg.flightId },
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export * from "./_liberationApi";
|
||||
32
client/src/api/mapSlice.ts
Normal file
32
client/src/api/mapSlice.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { RootState } from "../app/store";
|
||||
import { gameLoaded, gameUnloaded } from "./actions";
|
||||
import { createSlice } from "@reduxjs/toolkit";
|
||||
import { LatLngLiteral } from "leaflet";
|
||||
|
||||
interface MapState {
|
||||
center: LatLngLiteral;
|
||||
}
|
||||
|
||||
const initialState: MapState = {
|
||||
center: { lat: 0, lng: 0 },
|
||||
};
|
||||
|
||||
const mapSlice = createSlice({
|
||||
name: "map",
|
||||
initialState: initialState,
|
||||
reducers: {},
|
||||
extraReducers: (builder) => {
|
||||
builder.addCase(gameLoaded, (state, action) => {
|
||||
if (action.payload.map_center != null) {
|
||||
state.center = action.payload.map_center;
|
||||
}
|
||||
});
|
||||
builder.addCase(gameUnloaded, (state) => {
|
||||
state.center = { lat: 0, lng: 0 };
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export const selectMapCenter = (state: RootState) => state.map.center;
|
||||
|
||||
export default mapSlice.reducer;
|
||||
53
client/src/api/navMeshSlice.ts
Normal file
53
client/src/api/navMeshSlice.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { RootState } from "../app/store";
|
||||
import { gameLoaded, gameUnloaded } from "./actions";
|
||||
import { NavMesh, NavMeshPoly } from "./liberationApi";
|
||||
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||
|
||||
interface NavMeshState {
|
||||
blue: NavMeshPoly[];
|
||||
red: NavMeshPoly[];
|
||||
}
|
||||
|
||||
const initialState: NavMeshState = {
|
||||
blue: [],
|
||||
red: [],
|
||||
};
|
||||
|
||||
export interface INavMeshUpdate {
|
||||
blue: boolean;
|
||||
mesh: NavMesh;
|
||||
}
|
||||
|
||||
const navMeshSlice = createSlice({
|
||||
name: "navmesh",
|
||||
initialState: initialState,
|
||||
reducers: {
|
||||
updated: (state, action: PayloadAction<INavMeshUpdate[]>) => {
|
||||
for (const [blue, navmesh] of Object.entries(action.payload)) {
|
||||
const data = {blue: (blue === "true"), mesh: navmesh} as unknown as INavMeshUpdate
|
||||
const polys = data.mesh.polys;
|
||||
if (data.blue) {
|
||||
state.blue = polys;
|
||||
} else {
|
||||
state.red = polys;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
builder.addCase(gameLoaded, (state, action) => {
|
||||
state.blue = action.payload.navmeshes.blue.polys;
|
||||
state.red = action.payload.navmeshes.red.polys;
|
||||
});
|
||||
builder.addCase(gameUnloaded, (state) => {
|
||||
state.blue = [];
|
||||
state.red = [];
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export const { updated: navMeshUpdated } = navMeshSlice.actions;
|
||||
|
||||
export const selectNavMeshes = (state: RootState) => state.navmeshes;
|
||||
|
||||
export default navMeshSlice.reducer;
|
||||
30
client/src/api/supplyRoutesSlice.ts
Normal file
30
client/src/api/supplyRoutesSlice.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { RootState } from "../app/store";
|
||||
import { gameLoaded, gameUnloaded } from "./actions";
|
||||
import { SupplyRoute } from "./liberationApi";
|
||||
import { createSlice } from "@reduxjs/toolkit";
|
||||
|
||||
interface SupplyRoutesState {
|
||||
routes: SupplyRoute[];
|
||||
}
|
||||
|
||||
const initialState: SupplyRoutesState = {
|
||||
routes: [],
|
||||
};
|
||||
|
||||
export const supplyRoutesSlice = createSlice({
|
||||
name: "supplyRoutes",
|
||||
initialState,
|
||||
reducers: {},
|
||||
extraReducers: (builder) => {
|
||||
builder.addCase(gameLoaded, (state, action) => {
|
||||
state.routes = action.payload.supply_routes;
|
||||
});
|
||||
builder.addCase(gameUnloaded, (state) => {
|
||||
state.routes = [];
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export const selectSupplyRoutes = (state: RootState) => state.supplyRoutes;
|
||||
|
||||
export default supplyRoutesSlice.reducer;
|
||||
44
client/src/api/tgosSlice.ts
Normal file
44
client/src/api/tgosSlice.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { RootState } from "../app/store";
|
||||
import { gameLoaded, gameUnloaded } from "./actions";
|
||||
import { Tgo } from "./liberationApi";
|
||||
import { PayloadAction, createSlice } from "@reduxjs/toolkit";
|
||||
|
||||
interface TgosState {
|
||||
tgos: { [key: string]: Tgo };
|
||||
}
|
||||
|
||||
const initialState: TgosState = {
|
||||
tgos: {},
|
||||
};
|
||||
|
||||
export const tgosSlice = createSlice({
|
||||
name: "tgos",
|
||||
initialState,
|
||||
reducers: {
|
||||
updateTgo: (state, action: PayloadAction<Tgo[]>) => {
|
||||
for (const tgo of action.payload) {
|
||||
state.tgos[tgo.id] = tgo;
|
||||
}
|
||||
},
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
builder.addCase(gameLoaded, (state, action) => {
|
||||
state.tgos = action.payload.tgos.reduce(
|
||||
(acc: { [key: string]: Tgo }, curr) => {
|
||||
acc[curr.id] = curr;
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
});
|
||||
builder.addCase(gameUnloaded, (state) => {
|
||||
state.tgos = {};
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export const { updateTgo } = tgosSlice.actions;
|
||||
|
||||
export const selectTgos = (state: RootState) => state.tgos;
|
||||
|
||||
export default tgosSlice.reducer;
|
||||
61
client/src/api/threatZonesSlice.ts
Normal file
61
client/src/api/threatZonesSlice.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import { RootState } from "../app/store";
|
||||
import { gameLoaded, gameUnloaded } from "./actions";
|
||||
import { ThreatZoneContainer, ThreatZones } from "./liberationApi";
|
||||
import { PayloadAction, createSlice } from "@reduxjs/toolkit";
|
||||
|
||||
interface ThreatZonesState {
|
||||
zones: ThreatZoneContainer;
|
||||
}
|
||||
|
||||
const initialState: ThreatZonesState = {
|
||||
zones: {
|
||||
blue: {
|
||||
full: [],
|
||||
aircraft: [],
|
||||
air_defenses: [],
|
||||
radar_sams: [],
|
||||
},
|
||||
red: {
|
||||
full: [],
|
||||
aircraft: [],
|
||||
air_defenses: [],
|
||||
radar_sams: [],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export interface IThreatZoneUpdate {
|
||||
blue: boolean;
|
||||
zones: ThreatZones;
|
||||
}
|
||||
|
||||
export const threatZonesSlice = createSlice({
|
||||
name: "threatZonesState",
|
||||
initialState,
|
||||
reducers: {
|
||||
updated: (state, action: PayloadAction<IThreatZoneUpdate[]>) => {
|
||||
for (const [blue, zones] of Object.entries(action.payload)) {
|
||||
const data = {blue: (blue === "true"), zones: zones} as unknown as IThreatZoneUpdate
|
||||
if (data.blue) {
|
||||
state.zones.blue = data.zones;
|
||||
} else {
|
||||
state.zones.red = data.zones;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
builder.addCase(gameLoaded, (state, action) => {
|
||||
state.zones = action.payload.threat_zones;
|
||||
});
|
||||
builder.addCase(gameUnloaded, (state) => {
|
||||
state.zones = initialState.zones;
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export const { updated: threatZonesUpdated } = threatZonesSlice.actions;
|
||||
|
||||
export const selectThreatZones = (state: RootState) => state.threatZones;
|
||||
|
||||
export default threatZonesSlice.reducer;
|
||||
36
client/src/api/unculledZonesSlice.ts
Normal file
36
client/src/api/unculledZonesSlice.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { RootState } from "../app/store";
|
||||
import { gameLoaded, gameUnloaded } from "./actions";
|
||||
import { UnculledZone } from "./liberationApi";
|
||||
import { PayloadAction, createSlice } from "@reduxjs/toolkit";
|
||||
|
||||
interface UnculledZonesState {
|
||||
zones: UnculledZone[];
|
||||
}
|
||||
|
||||
const initialState: UnculledZonesState = {
|
||||
zones: [],
|
||||
};
|
||||
|
||||
export const unculledZonesSlice = createSlice({
|
||||
name: "unculledZonesState",
|
||||
initialState,
|
||||
reducers: {
|
||||
updated: (state, action: PayloadAction<UnculledZone[]>) => {
|
||||
state.zones = action.payload;
|
||||
},
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
builder.addCase(gameLoaded, (state, action) => {
|
||||
state.zones = action.payload.unculled_zones;
|
||||
});
|
||||
builder.addCase(gameUnloaded, (state) => {
|
||||
state.zones = initialState.zones;
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export const { updated: unculledZonesUpdated } = unculledZonesSlice.actions;
|
||||
|
||||
export const selectUnculledZones = (state: RootState) => state.unculledZones;
|
||||
|
||||
export default unculledZonesSlice.reducer;
|
||||
6
client/src/app/hooks.ts
Normal file
6
client/src/app/hooks.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import type { RootState, AppDispatch } from "./store";
|
||||
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
|
||||
|
||||
// Use throughout your app instead of plain `useDispatch` and `useSelector`
|
||||
export const useAppDispatch = () => useDispatch<AppDispatch>();
|
||||
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
|
||||
53
client/src/app/store.ts
Normal file
53
client/src/app/store.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { baseApi } from "../api/baseApi";
|
||||
import combatReducer from "../api/combatSlice";
|
||||
import controlPointsReducer from "../api/controlPointsSlice";
|
||||
import flightsReducer from "../api/flightsSlice";
|
||||
import frontLinesReducer from "../api/frontLinesSlice";
|
||||
import iadsNetworkReducer from "../api/iadsNetworkSlice";
|
||||
import mapReducer from "../api/mapSlice";
|
||||
import navMeshReducer from "../api/navMeshSlice";
|
||||
import supplyRoutesReducer from "../api/supplyRoutesSlice";
|
||||
import tgosReducer from "../api/tgosSlice";
|
||||
import threatZonesReducer from "../api/threatZonesSlice";
|
||||
import unculledZonesReducer from "../api/unculledZonesSlice";
|
||||
import {
|
||||
Action,
|
||||
PreloadedState,
|
||||
ThunkAction,
|
||||
combineReducers,
|
||||
configureStore,
|
||||
} from "@reduxjs/toolkit";
|
||||
|
||||
const rootReducer = combineReducers({
|
||||
combat: combatReducer,
|
||||
controlPoints: controlPointsReducer,
|
||||
flights: flightsReducer,
|
||||
frontLines: frontLinesReducer,
|
||||
map: mapReducer,
|
||||
navmeshes: navMeshReducer,
|
||||
supplyRoutes: supplyRoutesReducer,
|
||||
iadsNetwork: iadsNetworkReducer,
|
||||
tgos: tgosReducer,
|
||||
threatZones: threatZonesReducer,
|
||||
[baseApi.reducerPath]: baseApi.reducer,
|
||||
unculledZones: unculledZonesReducer,
|
||||
});
|
||||
|
||||
export function setupStore(preloadedState?: PreloadedState<RootState>) {
|
||||
return configureStore({
|
||||
reducer: rootReducer,
|
||||
middleware: (getDefaultMiddleware) =>
|
||||
getDefaultMiddleware().concat(baseApi.middleware),
|
||||
preloadedState: preloadedState,
|
||||
});
|
||||
}
|
||||
|
||||
export type AppStore = ReturnType<typeof setupStore>;
|
||||
export type AppDispatch = AppStore["dispatch"];
|
||||
export type RootState = ReturnType<typeof rootReducer>;
|
||||
export type AppThunk<ReturnType = void> = ThunkAction<
|
||||
ReturnType,
|
||||
RootState,
|
||||
unknown,
|
||||
Action<string>
|
||||
>;
|
||||
53
client/src/components/aircraft/Aircraft.test.tsx
Normal file
53
client/src/components/aircraft/Aircraft.test.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import Aircraft from "./Aircraft";
|
||||
import { render } from "@testing-library/react";
|
||||
import { Icon } from "leaflet";
|
||||
|
||||
const mockMarker = jest.fn();
|
||||
jest.mock("react-leaflet", () => ({
|
||||
Marker: (props: any) => {
|
||||
mockMarker(props);
|
||||
},
|
||||
}));
|
||||
|
||||
test("grounded aircraft do not render", async () => {
|
||||
const { container } = render(
|
||||
<Aircraft
|
||||
flight={{
|
||||
id: "",
|
||||
blue: true,
|
||||
position: undefined,
|
||||
sidc: "",
|
||||
waypoints: [],
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(container).toBeEmptyDOMElement();
|
||||
});
|
||||
|
||||
test("in-flight aircraft render", async () => {
|
||||
render(
|
||||
<Aircraft
|
||||
flight={{
|
||||
id: "",
|
||||
blue: true,
|
||||
position: {
|
||||
lat: 10,
|
||||
lng: 20,
|
||||
},
|
||||
sidc: "foobar",
|
||||
waypoints: [],
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(mockMarker).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
position: {
|
||||
lat: 10,
|
||||
lng: 20,
|
||||
},
|
||||
icon: expect.any(Icon),
|
||||
})
|
||||
);
|
||||
});
|
||||
32
client/src/components/aircraft/Aircraft.tsx
Normal file
32
client/src/components/aircraft/Aircraft.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import { Flight } from "../../api/liberationApi";
|
||||
import { Icon, Point } from "leaflet";
|
||||
import { Symbol } from "milsymbol";
|
||||
import { Marker } from "react-leaflet";
|
||||
|
||||
function iconForFlight(flight: Flight) {
|
||||
const symbol = new Symbol(flight.sidc, {
|
||||
size: 20,
|
||||
});
|
||||
|
||||
return new Icon({
|
||||
iconUrl: symbol.toDataURL(),
|
||||
iconAnchor: new Point(symbol.getAnchor().x, symbol.getAnchor().y),
|
||||
});
|
||||
}
|
||||
|
||||
interface AircraftProps {
|
||||
flight: Flight;
|
||||
}
|
||||
|
||||
export default function Aircraft(props: AircraftProps) {
|
||||
if (!props.flight.position) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Marker
|
||||
position={props.flight.position}
|
||||
icon={iconForFlight(props.flight)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
1
client/src/components/aircraft/index.ts
Normal file
1
client/src/components/aircraft/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from "./Aircraft";
|
||||
53
client/src/components/aircraftlayer/AircraftLayer.test.tsx
Normal file
53
client/src/components/aircraftlayer/AircraftLayer.test.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import { renderWithProviders } from "../../testutils";
|
||||
import AircraftLayer from "./AircraftLayer";
|
||||
import { PropsWithChildren } from "react";
|
||||
|
||||
const mockLayerGroup = jest.fn();
|
||||
const mockMarker = jest.fn();
|
||||
jest.mock("react-leaflet", () => ({
|
||||
LayerGroup: (props: PropsWithChildren<any>) => {
|
||||
mockLayerGroup(props);
|
||||
return <>{props.children}</>;
|
||||
},
|
||||
Marker: (props: any) => {
|
||||
mockMarker(props);
|
||||
},
|
||||
}));
|
||||
|
||||
test("layer is empty by default", async () => {
|
||||
renderWithProviders(<AircraftLayer />);
|
||||
expect(mockLayerGroup).toHaveBeenCalledTimes(1);
|
||||
expect(mockMarker).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("layer has aircraft if non-empty", async () => {
|
||||
renderWithProviders(<AircraftLayer />, {
|
||||
preloadedState: {
|
||||
flights: {
|
||||
flights: {
|
||||
foo: {
|
||||
id: "foo",
|
||||
blue: true,
|
||||
sidc: "",
|
||||
position: {
|
||||
lat: 0,
|
||||
lng: 0,
|
||||
},
|
||||
},
|
||||
bar: {
|
||||
id: "bar",
|
||||
blue: false,
|
||||
sidc: "",
|
||||
position: {
|
||||
lat: 0,
|
||||
lng: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
selected: null,
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(mockLayerGroup).toHaveBeenCalledTimes(1);
|
||||
expect(mockMarker).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
15
client/src/components/aircraftlayer/AircraftLayer.tsx
Normal file
15
client/src/components/aircraftlayer/AircraftLayer.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import { selectFlights } from "../../api/flightsSlice";
|
||||
import { useAppSelector } from "../../app/hooks";
|
||||
import Aircraft from "../aircraft";
|
||||
import { LayerGroup } from "react-leaflet";
|
||||
|
||||
export default function AircraftLayer() {
|
||||
const flights = useAppSelector(selectFlights).flights;
|
||||
return (
|
||||
<LayerGroup>
|
||||
{Object.values(flights).map((flight) => {
|
||||
return <Aircraft key={flight.id} flight={flight} />;
|
||||
})}
|
||||
</LayerGroup>
|
||||
);
|
||||
}
|
||||
1
client/src/components/aircraftlayer/index.ts
Normal file
1
client/src/components/aircraftlayer/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from "./AircraftLayer";
|
||||
@@ -0,0 +1,146 @@
|
||||
import { renderWithProviders } from "../../testutils";
|
||||
import AirDefenseRangeLayer, { colorFor } from "./AirDefenseRangeLayer";
|
||||
import { PropsWithChildren } from "react";
|
||||
|
||||
const mockLayerGroup = jest.fn();
|
||||
const mockCircle = jest.fn();
|
||||
jest.mock("react-leaflet", () => ({
|
||||
LayerGroup: (props: PropsWithChildren<any>) => {
|
||||
mockLayerGroup(props);
|
||||
return <>{props.children}</>;
|
||||
},
|
||||
Circle: (props: any) => {
|
||||
mockCircle(props);
|
||||
},
|
||||
}));
|
||||
|
||||
describe("colorFor", () => {
|
||||
it("has a unique color for each configuration", () => {
|
||||
const params = [
|
||||
[false, false],
|
||||
[false, true],
|
||||
[true, false],
|
||||
[true, true],
|
||||
];
|
||||
var colors = new Set<string>();
|
||||
for (const [blue, detection] of params) {
|
||||
colors.add(colorFor(blue, detection));
|
||||
}
|
||||
expect(colors.size).toEqual(4);
|
||||
});
|
||||
});
|
||||
|
||||
describe("AirDefenseRangeLayer", () => {
|
||||
it("draws nothing when there are no TGOs", () => {
|
||||
renderWithProviders(<AirDefenseRangeLayer blue={true} />);
|
||||
expect(mockLayerGroup).toHaveBeenCalledTimes(1);
|
||||
expect(mockCircle).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does not draw wrong range types", () => {
|
||||
renderWithProviders(<AirDefenseRangeLayer blue={true} />, {
|
||||
preloadedState: {
|
||||
tgos: {
|
||||
tgos: {
|
||||
foo: {
|
||||
id: "foo",
|
||||
name: "Foo",
|
||||
control_point_name: "Bar",
|
||||
category: "AA",
|
||||
blue: false,
|
||||
position: {
|
||||
lat: 0,
|
||||
lng: 0,
|
||||
},
|
||||
units: [],
|
||||
threat_ranges: [],
|
||||
detection_ranges: [20],
|
||||
dead: false,
|
||||
sidc: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(mockLayerGroup).toHaveBeenCalledTimes(1);
|
||||
expect(mockCircle).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("draws threat ranges", () => {
|
||||
renderWithProviders(<AirDefenseRangeLayer blue={true} />, {
|
||||
preloadedState: {
|
||||
tgos: {
|
||||
tgos: {
|
||||
foo: {
|
||||
id: "foo",
|
||||
name: "Foo",
|
||||
control_point_name: "Bar",
|
||||
category: "AA",
|
||||
blue: true,
|
||||
position: {
|
||||
lat: 10,
|
||||
lng: 20,
|
||||
},
|
||||
units: [],
|
||||
threat_ranges: [10],
|
||||
detection_ranges: [20],
|
||||
dead: false,
|
||||
sidc: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(mockLayerGroup).toHaveBeenCalledTimes(1);
|
||||
expect(mockCircle).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
center: {
|
||||
lat: 10,
|
||||
lng: 20,
|
||||
},
|
||||
radius: 10,
|
||||
color: colorFor(true, false),
|
||||
interactive: false,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("draws detection ranges", () => {
|
||||
renderWithProviders(<AirDefenseRangeLayer blue={true} detection />, {
|
||||
preloadedState: {
|
||||
tgos: {
|
||||
tgos: {
|
||||
foo: {
|
||||
id: "foo",
|
||||
name: "Foo",
|
||||
control_point_name: "Bar",
|
||||
category: "AA",
|
||||
blue: true,
|
||||
position: {
|
||||
lat: 10,
|
||||
lng: 20,
|
||||
},
|
||||
units: [],
|
||||
threat_ranges: [10],
|
||||
detection_ranges: [20],
|
||||
dead: false,
|
||||
sidc: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(mockLayerGroup).toHaveBeenCalledTimes(1);
|
||||
expect(mockCircle).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
center: {
|
||||
lat: 10,
|
||||
lng: 20,
|
||||
},
|
||||
radius: 20,
|
||||
color: colorFor(true, true),
|
||||
interactive: false,
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,65 @@
|
||||
import { Tgo } from "../../api/liberationApi";
|
||||
import { selectTgos } from "../../api/tgosSlice";
|
||||
import { useAppSelector } from "../../app/hooks";
|
||||
import { Circle, LayerGroup } from "react-leaflet";
|
||||
|
||||
interface TgoRangeCirclesProps {
|
||||
tgo: Tgo;
|
||||
blue: boolean;
|
||||
detection?: boolean;
|
||||
}
|
||||
|
||||
export function colorFor(blue: boolean, detection: boolean) {
|
||||
if (blue) {
|
||||
return detection ? "#bb89ff" : "#0084ff";
|
||||
}
|
||||
return detection ? "#eee17b" : "#c85050";
|
||||
}
|
||||
|
||||
const TgoRangeCircles = (props: TgoRangeCirclesProps) => {
|
||||
const radii = props.detection
|
||||
? props.tgo.detection_ranges
|
||||
: props.tgo.threat_ranges;
|
||||
const color = colorFor(props.blue, props.detection === true);
|
||||
const weight = props.detection ? 1 : 2;
|
||||
|
||||
return (
|
||||
<>
|
||||
{radii.map((radius, idx) => {
|
||||
return (
|
||||
<Circle
|
||||
key={idx}
|
||||
center={props.tgo.position}
|
||||
radius={radius}
|
||||
color={color}
|
||||
fill={false}
|
||||
weight={weight}
|
||||
interactive={false}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
interface AirDefenseRangeLayerProps {
|
||||
blue: boolean;
|
||||
detection?: boolean;
|
||||
}
|
||||
|
||||
export const AirDefenseRangeLayer = (props: AirDefenseRangeLayerProps) => {
|
||||
const tgos = Object.values(useAppSelector(selectTgos).tgos);
|
||||
var tgosForSide = tgos.filter((tgo) => tgo.blue === props.blue);
|
||||
|
||||
return (
|
||||
<LayerGroup>
|
||||
{tgosForSide.map((tgo) => {
|
||||
return (
|
||||
<TgoRangeCircles key={tgo.id} tgo={tgo} {...props}></TgoRangeCircles>
|
||||
);
|
||||
})}
|
||||
</LayerGroup>
|
||||
);
|
||||
};
|
||||
|
||||
export default AirDefenseRangeLayer;
|
||||
1
client/src/components/airdefenserangelayer/index.ts
Normal file
1
client/src/components/airdefenserangelayer/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from "./AirDefenseRangeLayer";
|
||||
132
client/src/components/combat/Combat.test.tsx
Normal file
132
client/src/components/combat/Combat.test.tsx
Normal file
@@ -0,0 +1,132 @@
|
||||
import { renderWithProviders } from "../../testutils";
|
||||
import Combat from "./Combat";
|
||||
import { LatLng } from "leaflet";
|
||||
|
||||
const mockPolyline = jest.fn();
|
||||
const mockPolygon = jest.fn();
|
||||
jest.mock("react-leaflet", () => ({
|
||||
Polyline: (props: any) => {
|
||||
mockPolyline(props);
|
||||
},
|
||||
Polygon: (props: any) => {
|
||||
mockPolygon(props);
|
||||
},
|
||||
}));
|
||||
|
||||
describe("Combat", () => {
|
||||
describe("footprint", () => {
|
||||
it("is not interactive", () => {
|
||||
renderWithProviders(
|
||||
<Combat
|
||||
combat={{
|
||||
id: "foo",
|
||||
flight_position: null,
|
||||
target_positions: null,
|
||||
footprint: [[new LatLng(0, 0), new LatLng(0, 1), new LatLng(1, 0)]],
|
||||
}}
|
||||
/>
|
||||
);
|
||||
expect(mockPolygon).toBeCalledWith(
|
||||
expect.objectContaining({ interactive: false })
|
||||
);
|
||||
});
|
||||
|
||||
// Fails because we don't handle multi-poly combat footprints correctly.
|
||||
it.skip("renders single polygons", () => {
|
||||
const boundary = [new LatLng(0, 0), new LatLng(0, 1), new LatLng(1, 0)];
|
||||
renderWithProviders(
|
||||
<Combat
|
||||
combat={{
|
||||
id: "foo",
|
||||
flight_position: null,
|
||||
target_positions: null,
|
||||
footprint: [boundary],
|
||||
}}
|
||||
/>
|
||||
);
|
||||
expect(mockPolygon).toBeCalledWith(
|
||||
expect.objectContaining({ positions: boundary })
|
||||
);
|
||||
});
|
||||
|
||||
// Fails because we don't handle multi-poly combat footprints correctly.
|
||||
it.skip("renders multiple polygons", () => {
|
||||
const boundary = [new LatLng(0, 0), new LatLng(0, 1), new LatLng(1, 0)];
|
||||
renderWithProviders(
|
||||
<Combat
|
||||
combat={{
|
||||
id: "foo",
|
||||
flight_position: null,
|
||||
target_positions: null,
|
||||
footprint: [boundary, boundary],
|
||||
}}
|
||||
/>
|
||||
);
|
||||
expect(mockPolygon).toBeCalledTimes(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe("lines", () => {
|
||||
it("is not interactive", () => {
|
||||
renderWithProviders(
|
||||
<Combat
|
||||
combat={{
|
||||
id: "foo",
|
||||
flight_position: new LatLng(0, 0),
|
||||
target_positions: [new LatLng(1, 0)],
|
||||
footprint: null,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
expect(mockPolyline).toBeCalledWith(
|
||||
expect.objectContaining({ interactive: false })
|
||||
);
|
||||
});
|
||||
|
||||
it("renders single line", () => {
|
||||
renderWithProviders(
|
||||
<Combat
|
||||
combat={{
|
||||
id: "foo",
|
||||
flight_position: new LatLng(0, 0),
|
||||
target_positions: [new LatLng(0, 1)],
|
||||
footprint: null,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
expect(mockPolyline).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
positions: [new LatLng(0, 0), new LatLng(0, 1)],
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("renders multiple lines", () => {
|
||||
renderWithProviders(
|
||||
<Combat
|
||||
combat={{
|
||||
id: "foo",
|
||||
flight_position: new LatLng(0, 0),
|
||||
target_positions: [new LatLng(0, 1), new LatLng(1, 0)],
|
||||
footprint: null,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
expect(mockPolyline).toBeCalledTimes(2);
|
||||
});
|
||||
});
|
||||
|
||||
it("renders nothing if no footprint or targets", () => {
|
||||
const { container } = renderWithProviders(
|
||||
<Combat
|
||||
combat={{
|
||||
id: "foo",
|
||||
flight_position: new LatLng(0, 0),
|
||||
target_positions: null,
|
||||
footprint: null,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
expect(container).toBeEmptyDOMElement();
|
||||
});
|
||||
});
|
||||
53
client/src/components/combat/Combat.tsx
Normal file
53
client/src/components/combat/Combat.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import CombatModel from "../../api/combat";
|
||||
import { LatLng } from "leaflet";
|
||||
import { Polygon, Polyline } from "react-leaflet";
|
||||
|
||||
interface CombatProps {
|
||||
combat: CombatModel;
|
||||
}
|
||||
|
||||
function CombatFootprint(props: CombatProps) {
|
||||
if (!props.combat.footprint) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Polygon
|
||||
positions={props.combat.footprint}
|
||||
color="#c85050"
|
||||
interactive={false}
|
||||
fillOpacity={0.2}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function CombatLines(props: CombatProps) {
|
||||
if (!props.combat.flight_position || !props.combat.target_positions) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
const flightPosition: LatLng = props.combat.flight_position;
|
||||
return (
|
||||
<>
|
||||
{props.combat.target_positions.map((position, idx) => {
|
||||
return (
|
||||
<Polyline
|
||||
key={idx}
|
||||
positions={[flightPosition, position]}
|
||||
color="#c85050"
|
||||
interactive={false}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default function Combat(props: CombatProps) {
|
||||
return (
|
||||
<>
|
||||
<CombatFootprint {...props} />
|
||||
<CombatLines {...props} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
1
client/src/components/combat/index.ts
Normal file
1
client/src/components/combat/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from "./Combat";
|
||||
48
client/src/components/combatlayer/CombatLayer.test.tsx
Normal file
48
client/src/components/combatlayer/CombatLayer.test.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import { renderWithProviders } from "../../testutils";
|
||||
import CombatLayer from "./CombatLayer";
|
||||
import { LatLng } from "leaflet";
|
||||
import { PropsWithChildren } from "react";
|
||||
|
||||
const mockPolyline = jest.fn();
|
||||
const mockLayerGroup = jest.fn();
|
||||
jest.mock("react-leaflet", () => ({
|
||||
LayerGroup: (props: PropsWithChildren<any>) => {
|
||||
mockLayerGroup(props);
|
||||
return <>{props.children}</>;
|
||||
},
|
||||
Polyline: (props: any) => {
|
||||
mockPolyline(props);
|
||||
},
|
||||
}));
|
||||
|
||||
describe("CombatLayer", () => {
|
||||
it("renders each combat", () => {
|
||||
renderWithProviders(<CombatLayer />, {
|
||||
preloadedState: {
|
||||
combat: {
|
||||
combat: {
|
||||
foo: {
|
||||
id: "foo",
|
||||
flight_position: new LatLng(0, 0),
|
||||
target_positions: [new LatLng(0, 1)],
|
||||
footprint: null,
|
||||
},
|
||||
bar: {
|
||||
id: "foo",
|
||||
flight_position: new LatLng(0, 0),
|
||||
target_positions: [new LatLng(0, 1)],
|
||||
footprint: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(mockPolyline).toBeCalledTimes(2);
|
||||
});
|
||||
|
||||
it("renders LayerGroup but no contents if no combat", () => {
|
||||
renderWithProviders(<CombatLayer />);
|
||||
expect(mockLayerGroup).toBeCalledTimes(1);
|
||||
expect(mockPolyline).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
16
client/src/components/combatlayer/CombatLayer.tsx
Normal file
16
client/src/components/combatlayer/CombatLayer.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import { selectCombat } from "../../api/combatSlice";
|
||||
import { useAppSelector } from "../../app/hooks";
|
||||
import Combat from "../combat/Combat";
|
||||
import { LayerGroup } from "react-leaflet";
|
||||
|
||||
export default function CombatLayer() {
|
||||
const combats = useAppSelector(selectCombat);
|
||||
return (
|
||||
<LayerGroup>
|
||||
{Object.values(combats.combat).map((combat) => {
|
||||
return <Combat key={combat.id} combat={combat} />;
|
||||
})}
|
||||
(
|
||||
</LayerGroup>
|
||||
);
|
||||
}
|
||||
1
client/src/components/combatlayer/index.ts
Normal file
1
client/src/components/combatlayer/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from "./CombatLayer";
|
||||
15
client/src/components/controlpoints/ControlPoint.tsx
Normal file
15
client/src/components/controlpoints/ControlPoint.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import { ControlPoint as ControlPointModel } from "../../api/liberationApi";
|
||||
import { MobileControlPoint } from "./MobileControlPoint";
|
||||
import { StaticControlPoint } from "./StaticControlPoint";
|
||||
|
||||
interface ControlPointProps {
|
||||
controlPoint: ControlPointModel;
|
||||
}
|
||||
|
||||
export default function ControlPoint(props: ControlPointProps) {
|
||||
if (props.controlPoint.mobile) {
|
||||
return <MobileControlPoint {...props} />;
|
||||
} else {
|
||||
return <StaticControlPoint {...props} />;
|
||||
}
|
||||
}
|
||||
22
client/src/components/controlpoints/EventHandlers.ts
Normal file
22
client/src/components/controlpoints/EventHandlers.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { ControlPoint } from "../../api/_liberationApi";
|
||||
import backend from "../../api/backend";
|
||||
|
||||
function openInfoDialog(controlPoint: ControlPoint) {
|
||||
backend.post(`/qt/info/control-point/${controlPoint.id}`);
|
||||
}
|
||||
|
||||
function openNewPackageDialog(controlPoint: ControlPoint) {
|
||||
backend.post(`/qt/create-package/control-point/${controlPoint.id}`);
|
||||
}
|
||||
|
||||
export const makeLocationMarkerEventHandlers = (controlPoint: ControlPoint) => {
|
||||
return {
|
||||
click: () => {
|
||||
openInfoDialog(controlPoint);
|
||||
},
|
||||
|
||||
contextmenu: () => {
|
||||
openNewPackageDialog(controlPoint);
|
||||
},
|
||||
};
|
||||
};
|
||||
15
client/src/components/controlpoints/Icons.tsx
Normal file
15
client/src/components/controlpoints/Icons.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import { ControlPoint } from "../../api/_liberationApi";
|
||||
import { Icon, Point } from "leaflet";
|
||||
import { Symbol } from "milsymbol";
|
||||
|
||||
export const iconForControlPoint = (cp: ControlPoint) => {
|
||||
const symbol = new Symbol(cp.sidc, {
|
||||
size: 24,
|
||||
colorMode: "Dark",
|
||||
});
|
||||
|
||||
return new Icon({
|
||||
iconUrl: symbol.toDataURL(),
|
||||
iconAnchor: new Point(symbol.getAnchor().x, symbol.getAnchor().y),
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
interface LocationTooltipTextProps {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export const LocationTooltipText = (props: LocationTooltipTextProps) => {
|
||||
return <h3 style={{ margin: 0 }}>{props.name}</h3>;
|
||||
};
|
||||
|
||||
export default LocationTooltipText;
|
||||
228
client/src/components/controlpoints/MobileControlPoint.tsx
Normal file
228
client/src/components/controlpoints/MobileControlPoint.tsx
Normal file
@@ -0,0 +1,228 @@
|
||||
import { ControlPoint } from "../../api/_liberationApi";
|
||||
import backend from "../../api/backend";
|
||||
import {
|
||||
useClearControlPointDestinationMutation,
|
||||
useSetControlPointDestinationMutation,
|
||||
} from "../../api/liberationApi";
|
||||
import { makeLocationMarkerEventHandlers } from "./EventHandlers";
|
||||
import { iconForControlPoint } from "./Icons";
|
||||
import LocationTooltipText from "./LocationTooltipText";
|
||||
import { MovementPath, MovementPathHandle } from "./MovementPath";
|
||||
import { StaticControlPoint } from "./StaticControlPoint";
|
||||
import { LatLng, Marker as LMarker, LatLngLiteral } from "leaflet";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import ReactDOMServer from "react-dom/server";
|
||||
import { Marker, Tooltip } from "react-leaflet";
|
||||
|
||||
function metersToNauticalMiles(meters: number) {
|
||||
return meters * 0.000539957;
|
||||
}
|
||||
|
||||
function formatLatLng(latLng: LatLng) {
|
||||
const lat = latLng.lat.toFixed(2);
|
||||
const lng = latLng.lng.toFixed(2);
|
||||
const ns = latLng.lat >= 0 ? "N" : "S";
|
||||
const ew = latLng.lng >= 0 ? "E" : "W";
|
||||
return `${lat}°${ns} ${lng}°${ew}`;
|
||||
}
|
||||
|
||||
function destinationTooltipText(
|
||||
cp: ControlPoint,
|
||||
destinationish: LatLngLiteral,
|
||||
inRange: boolean
|
||||
) {
|
||||
const destination = new LatLng(destinationish.lat, destinationish.lng);
|
||||
const distance = metersToNauticalMiles(
|
||||
destination.distanceTo(cp.position)
|
||||
).toFixed(1);
|
||||
if (!inRange) {
|
||||
return `Out of range (${distance}nm away)`;
|
||||
}
|
||||
const dest = formatLatLng(destination);
|
||||
return `${cp.name} moving ${distance}nm to ${dest} next turn`;
|
||||
}
|
||||
|
||||
interface PrimaryMarkerProps {
|
||||
controlPoint: ControlPoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* The primary control point marker. For non-mobile control points, this has
|
||||
* fairly simple behavior: it's a marker in a fixed location that can manage
|
||||
* units and can have missions planned against it.
|
||||
*
|
||||
* For mobile control points, this is a draggable marker. If the control point
|
||||
* has a destination (either because it was dragged after render, or because it
|
||||
* had a destination in the game that was loaded), the unit management and
|
||||
* mission planning behaviors are delegated to SecondaryMarker, and the primary
|
||||
* marker becomes only a destination marker. It can be dragged to change the
|
||||
* destination, and can be right clicked to cancel movement.
|
||||
*/
|
||||
function PrimaryMarker(props: PrimaryMarkerProps) {
|
||||
// We can't use normal state to update the marker tooltip or the line points
|
||||
// because if we set any state in the drag event it will re-render this
|
||||
// component and all children, interrupting dragging. Instead, keep refs to
|
||||
// the objects and mutate them directly.
|
||||
//
|
||||
// For the same reason, the path is owned by this component, because updating
|
||||
// sibling state would be messy. Lifting the state into the parent would still
|
||||
// cause this component to redraw.
|
||||
const markerRef = useRef<LMarker | null>(null);
|
||||
const pathRef = useRef<MovementPathHandle | null>(null);
|
||||
|
||||
const [hasDestination, setHasDestination] = useState<boolean>(
|
||||
props.controlPoint.destination != null
|
||||
);
|
||||
const [position, setPosition] = useState<LatLngLiteral>(
|
||||
props.controlPoint.destination
|
||||
? props.controlPoint.destination
|
||||
: props.controlPoint.position
|
||||
);
|
||||
|
||||
const setDestination = useCallback((destination: LatLng) => {
|
||||
setPosition(destination);
|
||||
setHasDestination(true);
|
||||
}, []);
|
||||
|
||||
const resetDestination = useCallback(() => {
|
||||
setPosition(props.controlPoint.position);
|
||||
setHasDestination(false);
|
||||
}, [props]);
|
||||
|
||||
const [putDestination, { isLoading }] =
|
||||
useSetControlPointDestinationMutation();
|
||||
const [cancelTravel] = useClearControlPointDestinationMutation();
|
||||
|
||||
useEffect(() => {
|
||||
markerRef.current?.setTooltipContent(
|
||||
props.controlPoint.destination
|
||||
? destinationTooltipText(
|
||||
props.controlPoint,
|
||||
props.controlPoint.destination,
|
||||
true
|
||||
)
|
||||
: ReactDOMServer.renderToString(
|
||||
<LocationTooltipText name={props.controlPoint.name} />
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
const locationClickHandlers = makeLocationMarkerEventHandlers(
|
||||
props.controlPoint
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Marker
|
||||
position={position}
|
||||
icon={iconForControlPoint(props.controlPoint)}
|
||||
draggable={!isLoading}
|
||||
autoPan
|
||||
// We might draw other markers on top of the CP. The tooltips from the
|
||||
// other markers are helpful so we want to keep them, but make sure the CP
|
||||
// is always the clickable thing.
|
||||
zIndexOffset={1000}
|
||||
opacity={props.controlPoint.destination ? 0.5 : 1}
|
||||
ref={(ref) => {
|
||||
if (ref != null) {
|
||||
markerRef.current = ref;
|
||||
}
|
||||
}}
|
||||
eventHandlers={{
|
||||
click: () => {
|
||||
if (!hasDestination) {
|
||||
locationClickHandlers.click();
|
||||
}
|
||||
},
|
||||
contextmenu: () => {
|
||||
if (props.controlPoint.destination) {
|
||||
cancelTravel({ cpId: props.controlPoint.id }).then(() => {
|
||||
resetDestination();
|
||||
});
|
||||
} else {
|
||||
locationClickHandlers.contextmenu();
|
||||
}
|
||||
},
|
||||
drag: (event) => {
|
||||
const destination = event.target.getLatLng();
|
||||
backend
|
||||
.get(
|
||||
`/control-points/${props.controlPoint.id}/destination-in-range?lat=${destination.lat}&lng=${destination.lng}`
|
||||
)
|
||||
.then((inRange) => {
|
||||
markerRef.current?.setTooltipContent(
|
||||
destinationTooltipText(
|
||||
props.controlPoint,
|
||||
destination,
|
||||
inRange.data
|
||||
)
|
||||
);
|
||||
});
|
||||
pathRef.current?.setDestination(destination);
|
||||
},
|
||||
dragend: async (event) => {
|
||||
const currentPosition = new LatLng(position.lat, position.lng);
|
||||
const destination = event.target.getLatLng();
|
||||
setDestination(destination);
|
||||
try {
|
||||
await putDestination({
|
||||
cpId: props.controlPoint.id,
|
||||
body: { lat: destination.lat, lng: destination.lng },
|
||||
}).unwrap();
|
||||
} catch (error) {
|
||||
console.error("setDestination failed", error);
|
||||
setDestination(currentPosition);
|
||||
}
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Tooltip />
|
||||
</Marker>
|
||||
<MovementPath
|
||||
source={props.controlPoint.position}
|
||||
destination={position}
|
||||
ref={pathRef}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
interface SecondaryMarkerProps {
|
||||
controlPoint: ControlPoint;
|
||||
destination: LatLngLiteral | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* The secondary marker for a control point. The secondary marker will only be
|
||||
* shown when the control point has a destination set. For mobile control
|
||||
* points, the primary marker is draggable, and the secondary marker will be
|
||||
* shown at the current location iff the control point has been dragged. The
|
||||
* secondary marker is also the marker that has the normal control point
|
||||
* interaction options (mission planning and unit management).
|
||||
*/
|
||||
function SecondaryMarker(props: SecondaryMarkerProps) {
|
||||
if (!props.destination) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return <StaticControlPoint controlPoint={props.controlPoint} />;
|
||||
}
|
||||
|
||||
interface MobileControlPointProps {
|
||||
controlPoint: ControlPoint;
|
||||
}
|
||||
|
||||
export const MobileControlPoint = (props: MobileControlPointProps) => {
|
||||
return (
|
||||
<>
|
||||
<PrimaryMarker
|
||||
controlPoint={props.controlPoint}
|
||||
key={props.controlPoint.destination ? 0 : 1}
|
||||
/>
|
||||
<SecondaryMarker
|
||||
controlPoint={props.controlPoint}
|
||||
destination={props.controlPoint.destination}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
35
client/src/components/controlpoints/MovementPath.tsx
Normal file
35
client/src/components/controlpoints/MovementPath.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import { LatLngLiteral, Polyline as LPolyline } from "leaflet";
|
||||
import { forwardRef, useImperativeHandle, useRef } from "react";
|
||||
import { Polyline } from "react-leaflet";
|
||||
|
||||
interface MovementPathProps {
|
||||
source: LatLngLiteral;
|
||||
destination: LatLngLiteral;
|
||||
}
|
||||
|
||||
export interface MovementPathHandle {
|
||||
setDestination: (destination: LatLngLiteral) => void;
|
||||
}
|
||||
|
||||
export const MovementPath = forwardRef<MovementPathHandle, MovementPathProps>(
|
||||
(props: MovementPathProps, ref) => {
|
||||
const lineRef = useRef<LPolyline | null>(null);
|
||||
useImperativeHandle(
|
||||
ref,
|
||||
() => ({
|
||||
setDestination: (destination: LatLngLiteral) => {
|
||||
lineRef.current?.setLatLngs([props.source, destination]);
|
||||
},
|
||||
}),
|
||||
[props]
|
||||
);
|
||||
return (
|
||||
<Polyline
|
||||
positions={[props.source, props.destination]}
|
||||
weight={1}
|
||||
color="#80BA80"
|
||||
ref={lineRef}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
||||
27
client/src/components/controlpoints/StaticControlPoint.tsx
Normal file
27
client/src/components/controlpoints/StaticControlPoint.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import { ControlPoint } from "../../api/_liberationApi";
|
||||
import { makeLocationMarkerEventHandlers } from "./EventHandlers";
|
||||
import { iconForControlPoint } from "./Icons";
|
||||
import LocationTooltipText from "./LocationTooltipText";
|
||||
import { Marker, Tooltip } from "react-leaflet";
|
||||
|
||||
interface StaticControlPointProps {
|
||||
controlPoint: ControlPoint;
|
||||
}
|
||||
|
||||
export const StaticControlPoint = (props: StaticControlPointProps) => {
|
||||
return (
|
||||
<Marker
|
||||
position={props.controlPoint.position}
|
||||
icon={iconForControlPoint(props.controlPoint)}
|
||||
// We might draw other markers on top of the CP. The tooltips from the
|
||||
// other markers are helpful so we want to keep them, but make sure the CP
|
||||
// is always the clickable thing.
|
||||
zIndexOffset={1000}
|
||||
eventHandlers={makeLocationMarkerEventHandlers(props.controlPoint)}
|
||||
>
|
||||
<Tooltip>
|
||||
<LocationTooltipText name={props.controlPoint.name} />
|
||||
</Tooltip>
|
||||
</Marker>
|
||||
);
|
||||
};
|
||||
1
client/src/components/controlpoints/index.ts
Normal file
1
client/src/components/controlpoints/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from "./ControlPoint";
|
||||
@@ -0,0 +1,52 @@
|
||||
import { renderWithProviders } from "../../testutils";
|
||||
import ControlPointsLayer from "./ControlPointsLayer";
|
||||
import { LatLng } from "leaflet";
|
||||
import { PropsWithChildren } from "react";
|
||||
|
||||
const mockMarker = jest.fn();
|
||||
const mockLayerGroup = jest.fn();
|
||||
jest.mock("react-leaflet", () => ({
|
||||
LayerGroup: (props: PropsWithChildren<any>) => {
|
||||
mockLayerGroup(props);
|
||||
return <>{props.children}</>;
|
||||
},
|
||||
Marker: (props: any) => {
|
||||
mockMarker(props);
|
||||
},
|
||||
}));
|
||||
|
||||
describe("ControlPointsLayer", () => {
|
||||
it("renders each control point", () => {
|
||||
renderWithProviders(<ControlPointsLayer />, {
|
||||
preloadedState: {
|
||||
controlPoints: {
|
||||
controlPoints: {
|
||||
foo: {
|
||||
id: "foo",
|
||||
name: "Foo",
|
||||
blue: true,
|
||||
position: new LatLng(0, 0),
|
||||
mobile: false,
|
||||
sidc: "",
|
||||
},
|
||||
bar: {
|
||||
id: "bar",
|
||||
name: "Bar",
|
||||
blue: false,
|
||||
position: new LatLng(1, 0),
|
||||
mobile: false,
|
||||
sidc: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(mockMarker).toBeCalledTimes(2);
|
||||
});
|
||||
|
||||
it("renders LayerGroup but no contents if no combat", () => {
|
||||
renderWithProviders(<ControlPointsLayer />);
|
||||
expect(mockLayerGroup).toBeCalledTimes(1);
|
||||
expect(mockMarker).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
import { selectControlPoints } from "../../api/controlPointsSlice";
|
||||
import { useAppSelector } from "../../app/hooks";
|
||||
import ControlPoint from "../controlpoints";
|
||||
import { LayerGroup } from "react-leaflet";
|
||||
|
||||
export default function ControlPointsLayer() {
|
||||
const controlPoints = useAppSelector(selectControlPoints);
|
||||
return (
|
||||
<LayerGroup>
|
||||
{Object.values(controlPoints.controlPoints).map((controlPoint) => {
|
||||
return (
|
||||
<ControlPoint key={controlPoint.id} controlPoint={controlPoint} />
|
||||
);
|
||||
})}
|
||||
</LayerGroup>
|
||||
);
|
||||
}
|
||||
1
client/src/components/controlpointslayer/index.ts
Normal file
1
client/src/components/controlpointslayer/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from "./ControlPointsLayer";
|
||||
@@ -0,0 +1,78 @@
|
||||
import { renderWithProviders } from "../../testutils";
|
||||
import CullingExclusionZones from "./CullingExclusionZones";
|
||||
import { PropsWithChildren } from "react";
|
||||
|
||||
const mockCircle = jest.fn();
|
||||
const mockLayerGroup = jest.fn();
|
||||
const mockLayerControlOverlay = jest.fn();
|
||||
jest.mock("react-leaflet", () => ({
|
||||
LayerGroup: (props: PropsWithChildren<any>) => {
|
||||
mockLayerGroup(props);
|
||||
return <>{props.children}</>;
|
||||
},
|
||||
LayersControl: {
|
||||
Overlay: (props: PropsWithChildren<any>) => {
|
||||
mockLayerControlOverlay(props);
|
||||
return <>{props.children}</>;
|
||||
},
|
||||
},
|
||||
Circle: (props: any) => {
|
||||
mockCircle(props);
|
||||
},
|
||||
}));
|
||||
|
||||
describe("CullingExclusionZones", () => {
|
||||
it("is empty there are no exclusion zones", () => {
|
||||
renderWithProviders(<CullingExclusionZones />);
|
||||
expect(mockCircle).not.toHaveBeenCalled();
|
||||
expect(mockLayerGroup).toHaveBeenCalledTimes(1);
|
||||
expect(mockLayerControlOverlay).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
describe("zone circles", () => {
|
||||
it("are drawn in the correct locations", () => {
|
||||
renderWithProviders(<CullingExclusionZones />, {
|
||||
preloadedState: {
|
||||
unculledZones: {
|
||||
zones: [
|
||||
{
|
||||
position: {
|
||||
lat: 0,
|
||||
lng: 0,
|
||||
},
|
||||
radius: 10,
|
||||
},
|
||||
{
|
||||
position: {
|
||||
lat: 1,
|
||||
lng: 1,
|
||||
},
|
||||
radius: 2,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(mockCircle).toHaveBeenCalledTimes(2);
|
||||
expect(mockCircle).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
center: {
|
||||
lat: 0,
|
||||
lng: 0,
|
||||
},
|
||||
radius: 10,
|
||||
})
|
||||
);
|
||||
expect(mockCircle).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
center: {
|
||||
lat: 1,
|
||||
lng: 1,
|
||||
},
|
||||
radius: 2,
|
||||
})
|
||||
);
|
||||
});
|
||||
it("are not interactive", () => {});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,39 @@
|
||||
import { UnculledZone } from "../../api/liberationApi";
|
||||
import { selectUnculledZones } from "../../api/unculledZonesSlice";
|
||||
import { useAppSelector } from "../../app/hooks";
|
||||
import { LayerGroup, LayersControl, Circle } from "react-leaflet";
|
||||
|
||||
interface CullingExclusionCirclesProps {
|
||||
zones: UnculledZone[];
|
||||
}
|
||||
|
||||
const CullingExclusionCircles = (props: CullingExclusionCirclesProps) => {
|
||||
return (
|
||||
<>
|
||||
<LayerGroup>
|
||||
{props.zones.map((zone, idx) => {
|
||||
return (
|
||||
<Circle
|
||||
key={idx}
|
||||
center={zone.position}
|
||||
radius={zone.radius}
|
||||
color="#b4ff8c"
|
||||
fill={false}
|
||||
interactive={false}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</LayerGroup>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default function CullingExclusionZones() {
|
||||
const data = useAppSelector(selectUnculledZones).zones;
|
||||
|
||||
return (
|
||||
<LayersControl.Overlay name="Culling exclusion zones">
|
||||
<CullingExclusionCircles zones={data}></CullingExclusionCircles>
|
||||
</LayersControl.Overlay>
|
||||
);
|
||||
}
|
||||
1
client/src/components/cullingexclusionzones/index.ts
Normal file
1
client/src/components/cullingexclusionzones/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from "./CullingExclusionZones";
|
||||
120
client/src/components/flightplan/FlightPlan.tsx
Normal file
120
client/src/components/flightplan/FlightPlan.tsx
Normal file
@@ -0,0 +1,120 @@
|
||||
import { Flight } from "../../api/liberationApi";
|
||||
import { useGetCommitBoundaryForFlightQuery } from "../../api/liberationApi";
|
||||
import WaypointMarker from "../waypointmarker";
|
||||
import { ReactElement } from "react";
|
||||
import { Polyline } from "react-leaflet";
|
||||
|
||||
const BLUE_PATH = "#0084ff";
|
||||
const RED_PATH = "#c85050";
|
||||
const SELECTED_PATH = "#ffff00";
|
||||
|
||||
interface FlightPlanProps {
|
||||
flight: Flight;
|
||||
selected: boolean;
|
||||
highlight?: boolean;
|
||||
}
|
||||
|
||||
const pathColor = (props: FlightPlanProps) => {
|
||||
if (props.selected && props.highlight) {
|
||||
return SELECTED_PATH;
|
||||
} else if (props.flight.blue) {
|
||||
return BLUE_PATH;
|
||||
} else {
|
||||
return RED_PATH;
|
||||
}
|
||||
};
|
||||
|
||||
function FlightPlanPath(props: FlightPlanProps) {
|
||||
const color = pathColor(props);
|
||||
const waypoints = props.flight.waypoints;
|
||||
if (waypoints == null) {
|
||||
return <></>;
|
||||
}
|
||||
const points = waypoints
|
||||
.filter((waypoint) => waypoint.include_in_path)
|
||||
.map((waypoint) => waypoint.position);
|
||||
return (
|
||||
<Polyline
|
||||
positions={points}
|
||||
pathOptions={{ color: color, interactive: false }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const WaypointMarkers = (props: FlightPlanProps) => {
|
||||
if (!props.selected || props.flight.waypoints == null) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
var markers: ReactElement[] = [];
|
||||
props.flight.waypoints?.forEach((p, idx) => {
|
||||
if (p.should_mark) {
|
||||
markers.push(
|
||||
<WaypointMarker
|
||||
key={idx}
|
||||
number={idx}
|
||||
waypoint={p}
|
||||
flight={props.flight}
|
||||
/>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return <>{markers}</>;
|
||||
};
|
||||
|
||||
interface CommitBoundaryProps {
|
||||
flightId: string;
|
||||
selected: boolean;
|
||||
}
|
||||
|
||||
function CommitBoundary(props: CommitBoundaryProps) {
|
||||
const { data, error, isLoading } = useGetCommitBoundaryForFlightQuery(
|
||||
{
|
||||
flightId: props.flightId,
|
||||
},
|
||||
// RTK Query doesn't seem to allow us to invalidate the cache from anything
|
||||
// but a mutation, but this data can be invalidated by events from the
|
||||
// websocket. Just disable the cache for this.
|
||||
//
|
||||
// This isn't perfect. It won't redraw until the component remounts. There
|
||||
// doesn't appear to be a better way.
|
||||
{ refetchOnMountOrArgChange: true }
|
||||
);
|
||||
if (isLoading) {
|
||||
return <></>;
|
||||
}
|
||||
if (error) {
|
||||
console.error(`Error loading commit boundary for ${props.flightId}`, error);
|
||||
return <></>;
|
||||
}
|
||||
if (!data) {
|
||||
console.log(
|
||||
`Null response data when loading commit boundary for ${props.flightId}`
|
||||
);
|
||||
return <></>;
|
||||
}
|
||||
return (
|
||||
<Polyline positions={data} color="#ffff00" weight={1} interactive={false} />
|
||||
);
|
||||
}
|
||||
|
||||
function CommitBoundaryIfSelected(props: CommitBoundaryProps) {
|
||||
if (!props.selected) {
|
||||
return <></>;
|
||||
}
|
||||
return <CommitBoundary {...props} />;
|
||||
}
|
||||
|
||||
export default function FlightPlan(props: FlightPlanProps) {
|
||||
return (
|
||||
<>
|
||||
<FlightPlanPath {...props} />
|
||||
<WaypointMarkers {...props} />
|
||||
<CommitBoundaryIfSelected
|
||||
flightId={props.flight.id}
|
||||
selected={props.selected}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
1
client/src/components/flightplan/index.ts
Normal file
1
client/src/components/flightplan/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from "./FlightPlan";
|
||||
405
client/src/components/flightplanslayer/FlightPlansLayer.test.tsx
Normal file
405
client/src/components/flightplanslayer/FlightPlansLayer.test.tsx
Normal file
@@ -0,0 +1,405 @@
|
||||
import { renderWithProviders } from "../../testutils";
|
||||
import FlightPlansLayer from "./FlightPlansLayer";
|
||||
import { PropsWithChildren } from "react";
|
||||
|
||||
const mockPolyline = jest.fn();
|
||||
const mockLayerGroup = jest.fn();
|
||||
jest.mock("react-leaflet", () => ({
|
||||
LayerGroup: (props: PropsWithChildren<any>) => {
|
||||
mockLayerGroup(props);
|
||||
return <>{props.children}</>;
|
||||
},
|
||||
Polyline: (props: any) => {
|
||||
mockPolyline(props);
|
||||
},
|
||||
}));
|
||||
|
||||
// The waypoints in test data below should all use `should_make: false`. Markers
|
||||
// need useMap() to check the zoom level to decide if they should be drawn or
|
||||
// not, and we don't have good options here for mocking that behavior.
|
||||
describe("FlightPlansLayer", () => {
|
||||
describe("unselected flights", () => {
|
||||
it("are drawn", () => {
|
||||
renderWithProviders(<FlightPlansLayer blue={true} />, {
|
||||
preloadedState: {
|
||||
flights: {
|
||||
flights: {
|
||||
foo: {
|
||||
id: "foo",
|
||||
blue: true,
|
||||
sidc: "",
|
||||
waypoints: [
|
||||
{
|
||||
name: "",
|
||||
position: {
|
||||
lat: 0,
|
||||
lng: 0,
|
||||
},
|
||||
altitude_ft: 0,
|
||||
altitude_reference: "MSL",
|
||||
is_movable: true,
|
||||
should_mark: false,
|
||||
include_in_path: true,
|
||||
timing: "",
|
||||
},
|
||||
{
|
||||
name: "",
|
||||
position: {
|
||||
lat: 1,
|
||||
lng: 1,
|
||||
},
|
||||
altitude_ft: 0,
|
||||
altitude_reference: "MSL",
|
||||
is_movable: true,
|
||||
should_mark: false,
|
||||
include_in_path: true,
|
||||
timing: "",
|
||||
},
|
||||
],
|
||||
},
|
||||
bar: {
|
||||
id: "bar",
|
||||
blue: true,
|
||||
sidc: "",
|
||||
waypoints: [
|
||||
{
|
||||
name: "",
|
||||
position: {
|
||||
lat: 0,
|
||||
lng: 0,
|
||||
},
|
||||
altitude_ft: 0,
|
||||
altitude_reference: "MSL",
|
||||
is_movable: true,
|
||||
should_mark: false,
|
||||
include_in_path: true,
|
||||
timing: "",
|
||||
},
|
||||
{
|
||||
name: "",
|
||||
position: {
|
||||
lat: 1,
|
||||
lng: 1,
|
||||
},
|
||||
altitude_ft: 0,
|
||||
altitude_reference: "MSL",
|
||||
is_movable: true,
|
||||
should_mark: false,
|
||||
include_in_path: true,
|
||||
timing: "",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
selected: null,
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(mockPolyline).toHaveBeenCalledTimes(2);
|
||||
expect(mockLayerGroup).toBeCalledTimes(1);
|
||||
});
|
||||
it("are not drawn if wrong coalition", () => {
|
||||
renderWithProviders(<FlightPlansLayer blue={true} />, {
|
||||
preloadedState: {
|
||||
flights: {
|
||||
flights: {
|
||||
foo: {
|
||||
id: "foo",
|
||||
blue: true,
|
||||
sidc: "",
|
||||
waypoints: [
|
||||
{
|
||||
name: "",
|
||||
position: {
|
||||
lat: 0,
|
||||
lng: 0,
|
||||
},
|
||||
altitude_ft: 0,
|
||||
altitude_reference: "MSL",
|
||||
is_movable: true,
|
||||
should_mark: false,
|
||||
include_in_path: true,
|
||||
timing: "",
|
||||
},
|
||||
{
|
||||
name: "",
|
||||
position: {
|
||||
lat: 1,
|
||||
lng: 1,
|
||||
},
|
||||
altitude_ft: 0,
|
||||
altitude_reference: "MSL",
|
||||
is_movable: true,
|
||||
should_mark: false,
|
||||
include_in_path: true,
|
||||
timing: "",
|
||||
},
|
||||
],
|
||||
},
|
||||
bar: {
|
||||
id: "bar",
|
||||
blue: false,
|
||||
sidc: "",
|
||||
waypoints: [
|
||||
{
|
||||
name: "",
|
||||
position: {
|
||||
lat: 0,
|
||||
lng: 0,
|
||||
},
|
||||
altitude_ft: 0,
|
||||
altitude_reference: "MSL",
|
||||
is_movable: true,
|
||||
should_mark: false,
|
||||
include_in_path: true,
|
||||
timing: "",
|
||||
},
|
||||
{
|
||||
name: "",
|
||||
position: {
|
||||
lat: 1,
|
||||
lng: 1,
|
||||
},
|
||||
altitude_ft: 0,
|
||||
altitude_reference: "MSL",
|
||||
is_movable: true,
|
||||
should_mark: false,
|
||||
include_in_path: true,
|
||||
timing: "",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
selected: null,
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(mockPolyline).toHaveBeenCalledTimes(1);
|
||||
expect(mockLayerGroup).toBeCalledTimes(1);
|
||||
});
|
||||
it("are not drawn when only selected flights are to be drawn", () => {
|
||||
renderWithProviders(<FlightPlansLayer blue={true} selectedOnly />, {
|
||||
preloadedState: {
|
||||
flights: {
|
||||
flights: {
|
||||
foo: {
|
||||
id: "foo",
|
||||
blue: true,
|
||||
sidc: "",
|
||||
waypoints: [
|
||||
{
|
||||
name: "",
|
||||
position: {
|
||||
lat: 0,
|
||||
lng: 0,
|
||||
},
|
||||
altitude_ft: 0,
|
||||
altitude_reference: "MSL",
|
||||
is_movable: true,
|
||||
should_mark: false,
|
||||
include_in_path: true,
|
||||
timing: "",
|
||||
},
|
||||
{
|
||||
name: "",
|
||||
position: {
|
||||
lat: 1,
|
||||
lng: 1,
|
||||
},
|
||||
altitude_ft: 0,
|
||||
altitude_reference: "MSL",
|
||||
is_movable: true,
|
||||
should_mark: false,
|
||||
include_in_path: true,
|
||||
timing: "",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
selected: null,
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(mockPolyline).not.toHaveBeenCalled();
|
||||
expect(mockLayerGroup).toBeCalledTimes(1);
|
||||
});
|
||||
});
|
||||
describe("selected flights", () => {
|
||||
it("are drawn", () => {
|
||||
renderWithProviders(<FlightPlansLayer blue={true} />, {
|
||||
preloadedState: {
|
||||
flights: {
|
||||
flights: {
|
||||
foo: {
|
||||
id: "foo",
|
||||
blue: true,
|
||||
sidc: "",
|
||||
waypoints: [
|
||||
{
|
||||
name: "",
|
||||
position: {
|
||||
lat: 0,
|
||||
lng: 0,
|
||||
},
|
||||
altitude_ft: 0,
|
||||
altitude_reference: "MSL",
|
||||
is_movable: true,
|
||||
should_mark: false,
|
||||
include_in_path: true,
|
||||
timing: "",
|
||||
},
|
||||
{
|
||||
name: "",
|
||||
position: {
|
||||
lat: 1,
|
||||
lng: 1,
|
||||
},
|
||||
altitude_ft: 0,
|
||||
altitude_reference: "MSL",
|
||||
is_movable: true,
|
||||
should_mark: false,
|
||||
include_in_path: true,
|
||||
timing: "",
|
||||
},
|
||||
],
|
||||
},
|
||||
bar: {
|
||||
id: "bar",
|
||||
blue: true,
|
||||
sidc: "",
|
||||
waypoints: [
|
||||
{
|
||||
name: "",
|
||||
position: {
|
||||
lat: 0,
|
||||
lng: 0,
|
||||
},
|
||||
altitude_ft: 0,
|
||||
altitude_reference: "MSL",
|
||||
is_movable: true,
|
||||
should_mark: false,
|
||||
include_in_path: true,
|
||||
timing: "",
|
||||
},
|
||||
{
|
||||
name: "",
|
||||
position: {
|
||||
lat: 1,
|
||||
lng: 1,
|
||||
},
|
||||
altitude_ft: 0,
|
||||
altitude_reference: "MSL",
|
||||
is_movable: true,
|
||||
should_mark: false,
|
||||
include_in_path: true,
|
||||
timing: "",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
selected: "foo",
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(mockPolyline).toHaveBeenCalledTimes(2);
|
||||
expect(mockLayerGroup).toBeCalledTimes(1);
|
||||
});
|
||||
it("are not drawn twice", () => {
|
||||
renderWithProviders(<FlightPlansLayer blue={true} />, {
|
||||
preloadedState: {
|
||||
flights: {
|
||||
flights: {
|
||||
foo: {
|
||||
id: "foo",
|
||||
blue: true,
|
||||
sidc: "",
|
||||
waypoints: [
|
||||
{
|
||||
name: "",
|
||||
position: {
|
||||
lat: 0,
|
||||
lng: 0,
|
||||
},
|
||||
altitude_ft: 0,
|
||||
altitude_reference: "MSL",
|
||||
is_movable: true,
|
||||
should_mark: false,
|
||||
include_in_path: true,
|
||||
timing: "",
|
||||
},
|
||||
{
|
||||
name: "",
|
||||
position: {
|
||||
lat: 1,
|
||||
lng: 1,
|
||||
},
|
||||
altitude_ft: 0,
|
||||
altitude_reference: "MSL",
|
||||
is_movable: true,
|
||||
should_mark: false,
|
||||
include_in_path: true,
|
||||
timing: "",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
selected: "foo",
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(mockPolyline).toHaveBeenCalledTimes(1);
|
||||
expect(mockLayerGroup).toBeCalledTimes(1);
|
||||
});
|
||||
it("are not drawn if red", () => {
|
||||
renderWithProviders(<FlightPlansLayer blue={false} selectedOnly />, {
|
||||
preloadedState: {
|
||||
flights: {
|
||||
flights: {
|
||||
foo: {
|
||||
id: "foo",
|
||||
blue: false,
|
||||
sidc: "",
|
||||
waypoints: [
|
||||
{
|
||||
name: "",
|
||||
position: {
|
||||
lat: 0,
|
||||
lng: 0,
|
||||
},
|
||||
altitude_ft: 0,
|
||||
altitude_reference: "MSL",
|
||||
is_movable: true,
|
||||
should_mark: false,
|
||||
include_in_path: true,
|
||||
timing: "",
|
||||
},
|
||||
{
|
||||
name: "",
|
||||
position: {
|
||||
lat: 1,
|
||||
lng: 1,
|
||||
},
|
||||
altitude_ft: 0,
|
||||
altitude_reference: "MSL",
|
||||
is_movable: true,
|
||||
should_mark: false,
|
||||
include_in_path: true,
|
||||
timing: "",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
selected: "foo",
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(mockPolyline).not.toHaveBeenCalled();
|
||||
expect(mockLayerGroup).toBeCalledTimes(1);
|
||||
});
|
||||
});
|
||||
it("are not drawn if there are no flights", () => {
|
||||
renderWithProviders(<FlightPlansLayer blue={true} />);
|
||||
expect(mockPolyline).not.toHaveBeenCalled();
|
||||
expect(mockLayerGroup).toBeCalledTimes(1);
|
||||
});
|
||||
});
|
||||
67
client/src/components/flightplanslayer/FlightPlansLayer.tsx
Normal file
67
client/src/components/flightplanslayer/FlightPlansLayer.tsx
Normal file
@@ -0,0 +1,67 @@
|
||||
import { selectFlights, selectSelectedFlight } from "../../api/flightsSlice";
|
||||
import { Flight } from "../../api/liberationApi";
|
||||
import { useAppSelector } from "../../app/hooks";
|
||||
import FlightPlan from "../flightplan";
|
||||
import { LayerGroup } from "react-leaflet";
|
||||
|
||||
interface FlightPlansLayerProps {
|
||||
blue: boolean;
|
||||
selectedOnly?: true;
|
||||
}
|
||||
|
||||
function SelectedFlightPlan(props: FlightPlansLayerProps) {
|
||||
const flight = useAppSelector(selectSelectedFlight);
|
||||
if (!flight) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
if (!props.blue) {
|
||||
// We don't currently support playing as red, so nothing to draw.
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return (
|
||||
<FlightPlan
|
||||
key={flight.id}
|
||||
flight={flight}
|
||||
selected={true}
|
||||
highlight={!props.selectedOnly}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function UnselectedFlightPlans(props: FlightPlansLayerProps) {
|
||||
const flightData = useAppSelector(selectFlights);
|
||||
const isNotSelected = (flight: Flight) => {
|
||||
if (flightData.selected == null) {
|
||||
return true;
|
||||
}
|
||||
return flightData.selected !== flight.id;
|
||||
};
|
||||
|
||||
if (props.selectedOnly) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{Object.values(flightData.flights)
|
||||
.filter(isNotSelected)
|
||||
.filter((flight) => props.blue === flight.blue)
|
||||
.map((flight) => {
|
||||
return (
|
||||
<FlightPlan key={flight.id} flight={flight} selected={false} />
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default function FlightPlansLayer(props: FlightPlansLayerProps) {
|
||||
return (
|
||||
<LayerGroup>
|
||||
<UnselectedFlightPlans {...props} />
|
||||
<SelectedFlightPlan {...props} />
|
||||
</LayerGroup>
|
||||
);
|
||||
}
|
||||
1
client/src/components/flightplanslayer/index.ts
Normal file
1
client/src/components/flightplanslayer/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from "./FlightPlansLayer";
|
||||
32
client/src/components/frontline/FrontLine.test.tsx
Normal file
32
client/src/components/frontline/FrontLine.test.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import { renderWithProviders } from "../../testutils";
|
||||
import FrontLine from "./FrontLine";
|
||||
import { PolylineProps } from "react-leaflet";
|
||||
|
||||
const mockPolyline = jest.fn();
|
||||
jest.mock("react-leaflet", () => ({
|
||||
Polyline: (props: PolylineProps) => {
|
||||
mockPolyline(props);
|
||||
},
|
||||
}));
|
||||
|
||||
describe("FrontLine", () => {
|
||||
it("is drawn in the correct location", () => {
|
||||
const extents = [
|
||||
{ lat: 0, lng: 0 },
|
||||
{ lat: 1, lng: 0 },
|
||||
];
|
||||
renderWithProviders(
|
||||
<FrontLine
|
||||
front={{
|
||||
id: "",
|
||||
extents: extents,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
expect(mockPolyline).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
positions: extents,
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user