Compare commits
548 Commits
2018-04-16
...
2022-01-17
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ead1143f2e | ||
|
|
2fc85e0c08 | ||
|
|
fcafcb340a | ||
|
|
ae9b8b8f34 | ||
|
|
994704d353 | ||
|
|
d61c604bf4 | ||
|
|
baaf22d0c4 | ||
|
|
368ff1793f | ||
|
|
3253ad64fd | ||
|
|
1e70989f38 | ||
|
|
f6634de18d | ||
|
|
7903cd520a | ||
|
|
26d1fcc944 | ||
|
|
59d4e64a8d | ||
|
|
1347d88ddb | ||
|
|
6981cca2ae | ||
|
|
4d6c9ede8c | ||
|
|
e845c95816 | ||
|
|
a9f2fc427b | ||
|
|
07e6aadbbe | ||
|
|
86881bbbc3 | ||
|
|
1f15445c69 | ||
|
|
811ee54c76 | ||
|
|
d1a40fd36e | ||
|
|
a3d3aaaca8 | ||
|
|
c5aaa0bc2e | ||
|
|
6dc9f004ce | ||
|
|
6ce346af4a | ||
|
|
37879c4255 | ||
|
|
0683d1aced | ||
|
|
73c5956ece | ||
|
|
7c27e955d5 | ||
|
|
6ef394000b | ||
|
|
755a09bd83 | ||
|
|
8b1daa21ef | ||
|
|
691bcb9338 | ||
|
|
911a303326 | ||
|
|
5652b56b45 | ||
|
|
26acfd5102 | ||
|
|
f789e02096 | ||
|
|
43eee6b32e | ||
|
|
45d86e7ab7 | ||
|
|
b0845837c2 | ||
|
|
a10226d096 | ||
|
|
5e3a524401 | ||
|
|
b095d9b82f | ||
|
|
4cb7240f9a | ||
|
|
a87c66885c | ||
|
|
ac300b0b6d | ||
|
|
013bb8269f | ||
|
|
7712862036 | ||
|
|
656e3230de | ||
|
|
915c14f6cf | ||
|
|
ed32e72dc1 | ||
|
|
d684a9c5fc | ||
|
|
6f360374cc | ||
|
|
bbbf3e2a65 | ||
|
|
b1ef8220ee | ||
|
|
ebebb9c4bb | ||
|
|
586f23cfa9 | ||
|
|
f75ff2a7c8 | ||
|
|
e9ba195d7d | ||
|
|
19333c53f6 | ||
|
|
36e5a399d5 | ||
|
|
dde0f568d9 | ||
|
|
3bc90003b3 | ||
|
|
689f65b38a | ||
|
|
154e1084ba | ||
|
|
6df5cece04 | ||
|
|
c8bb8b0aae | ||
|
|
1e995cd97c | ||
|
|
c25bf491e4 | ||
|
|
affc288144 | ||
|
|
051be37419 | ||
|
|
c0bd49cf13 | ||
|
|
2fe572c398 | ||
|
|
904e740460 | ||
|
|
00add2a527 | ||
|
|
bbe43d4246 | ||
|
|
0280fea3e6 | ||
|
|
a65ce8694c | ||
|
|
c9ddd042fc | ||
|
|
da9222929b | ||
|
|
b858e36183 | ||
|
|
ebe2c494aa | ||
|
|
fac7bfaa92 | ||
|
|
0d05f9097d | ||
|
|
8db9475804 | ||
|
|
294229622d | ||
|
|
5cf9023a21 | ||
|
|
66d24f086e | ||
|
|
19a7c4092c | ||
|
|
b27cb58727 | ||
|
|
472f401590 | ||
|
|
744099277a | ||
|
|
dff25a175b | ||
|
|
61f1141fe8 | ||
|
|
0a73162bcf | ||
|
|
5f32892e75 | ||
|
|
ae7437750b | ||
|
|
046a3649ed | ||
|
|
890810f5b9 | ||
|
|
8fb561b4c4 | ||
|
|
b9c4b496e4 | ||
|
|
ff6f28390a | ||
|
|
e034de9083 | ||
|
|
ecf57b4226 | ||
|
|
63fe34437a | ||
|
|
1062894397 | ||
|
|
5969656429 | ||
|
|
ad0f313c9d | ||
|
|
6c004155ff | ||
|
|
88a8ee09bd | ||
|
|
7d1f082b27 | ||
|
|
1c48656623 | ||
|
|
b940e17fe7 | ||
|
|
8e954b10e6 | ||
|
|
1b4543aa11 | ||
|
|
406c0b17ae | ||
|
|
09de56ac87 | ||
|
|
07ea2d4334 | ||
|
|
c5fac2ee35 | ||
|
|
a5b8245227 | ||
|
|
073349fd05 | ||
|
|
00ed5c370c | ||
|
|
8e1d7d12e0 | ||
|
|
1811f7305e | ||
|
|
06bfc0291a | ||
|
|
b722864caf | ||
|
|
18e27ef932 | ||
|
|
7e3a669af0 | ||
|
|
aa6a0313e9 | ||
|
|
9bbe2f36bc | ||
|
|
0c7830b53c | ||
|
|
2b0a9975be | ||
|
|
df0b867d9e | ||
|
|
d27f108cbd | ||
|
|
fd0076e920 | ||
|
|
db5f6e01c9 | ||
|
|
f3255180ec | ||
|
|
039d058770 | ||
|
|
401fdcaf7a | ||
|
|
1bfcca91be | ||
|
|
3edb862561 | ||
|
|
00c0162da3 | ||
|
|
6fa5f4f9a5 | ||
|
|
db528c6762 | ||
|
|
0c54cdf6bc | ||
|
|
b63145c0a1 | ||
|
|
1ddc9cc929 | ||
|
|
a0deb73df6 | ||
|
|
0457e65751 | ||
|
|
d5b36e8b8a | ||
|
|
f595a61d50 | ||
|
|
dbf6cd745e | ||
|
|
0ce813b826 | ||
|
|
77be6a120c | ||
|
|
37053cc909 | ||
|
|
34e951298f | ||
|
|
8845a23d5d | ||
|
|
38606bdb87 | ||
|
|
56a51c7834 | ||
|
|
402c09e028 | ||
|
|
9e702ec358 | ||
|
|
c047a8ae3c | ||
|
|
b4740ad395 | ||
|
|
99b0abe7fe | ||
|
|
b0239c11ab | ||
|
|
46cf50d468 | ||
|
|
9f9581c2be | ||
|
|
f3cf1f0dde | ||
|
|
2f62671d8a | ||
|
|
589fbcdcd5 | ||
|
|
51b24bb92c | ||
|
|
6e00db4ef6 | ||
|
|
0d842b5a35 | ||
|
|
8441cb7ba9 | ||
|
|
4aaedf64d2 | ||
|
|
45d838a0b3 | ||
|
|
ca5f1dd434 | ||
|
|
d07bf1211a | ||
|
|
0966a8e90f | ||
|
|
68074b0f74 | ||
|
|
f11f072e0a | ||
|
|
3064621a7e | ||
|
|
0405c82cb2 | ||
|
|
8e9d4e3a67 | ||
|
|
ef78fdf342 | ||
|
|
a49c4865bb | ||
|
|
1a94261490 | ||
|
|
e10446f5b8 | ||
|
|
2081639970 | ||
|
|
8cbc4c91f6 | ||
|
|
b8cd3af21f | ||
|
|
1e8464c1d4 | ||
|
|
5df069ab19 | ||
|
|
752ba7d905 | ||
|
|
9cf7621102 | ||
|
|
9330774632 | ||
|
|
e33f802ae8 | ||
|
|
48c6458766 | ||
|
|
35fe6f624c | ||
|
|
e2251fe06b | ||
|
|
a5511190a3 | ||
|
|
eba9c097f6 | ||
|
|
58d024d067 | ||
|
|
be7b172e55 | ||
|
|
a8b79fd020 | ||
|
|
14fcb2e5d7 | ||
|
|
02935be14f | ||
|
|
bec02b4952 | ||
|
|
79f590c99a | ||
|
|
ade3e81682 | ||
|
|
45cbdad5fb | ||
|
|
3536fa8a75 | ||
|
|
b0c7b9078d | ||
|
|
964207d04f | ||
|
|
feee9cc328 | ||
|
|
4a563a131b | ||
|
|
69f035f017 | ||
|
|
daa89a9fb4 | ||
|
|
44297dcd1c | ||
|
|
80f613a77a | ||
|
|
776aa5c0ff | ||
|
|
446f9be24d | ||
|
|
5d9d91262b | ||
|
|
fe63dfa762 | ||
|
|
a76a3e5db6 | ||
|
|
faecfd65fe | ||
|
|
0063493066 | ||
|
|
2a7268c088 | ||
|
|
7fa1936d0f | ||
|
|
0f0e0193c1 | ||
|
|
0337f58088 | ||
|
|
53c6440cac | ||
|
|
28f3229a0e | ||
|
|
3dff63069e | ||
|
|
1eea8e9a37 | ||
|
|
2de863f645 | ||
|
|
dc8603596d | ||
|
|
fd0620445c | ||
|
|
04274d2497 | ||
|
|
c69e77f330 | ||
|
|
1988e4558f | ||
|
|
9246c190fa | ||
|
|
d30691559a | ||
|
|
46fe0cd725 | ||
|
|
45d62b6880 | ||
|
|
db85ec48c7 | ||
|
|
1976c4caed | ||
|
|
2c3eab9b0c | ||
|
|
27b7ebe208 | ||
|
|
a135ad064a | ||
|
|
e8d5715f7a | ||
|
|
9cec0852bb | ||
|
|
ca618c6cc1 | ||
|
|
c5e0b08800 | ||
|
|
568a4973fa | ||
|
|
5508699e92 | ||
|
|
71dd6b8a30 | ||
|
|
18a07274d4 | ||
|
|
1eb766b9d8 | ||
|
|
e84409c0cf | ||
|
|
91dc8b3b08 | ||
|
|
63b4f9b2f0 | ||
|
|
7285f24a29 | ||
|
|
17efe8c003 | ||
|
|
1815094249 | ||
|
|
a80c756dcb | ||
|
|
361833e023 | ||
|
|
632e44b0b7 | ||
|
|
8dd1e39ea9 | ||
|
|
0f18fa9546 | ||
|
|
7bfefee073 | ||
|
|
0ff7472ce5 | ||
|
|
8fb0baa449 | ||
|
|
57c02dcd5a | ||
|
|
b072b540a2 | ||
|
|
cd431594e2 | ||
|
|
e4c98e2ab8 | ||
|
|
32b557b862 | ||
|
|
e690b45f27 | ||
|
|
eadcdc6f7c | ||
|
|
b187fb52e0 | ||
|
|
7e89933552 | ||
|
|
096a472ed0 | ||
|
|
365b0a31ed | ||
|
|
e8fd2ce2aa | ||
|
|
a3fc9b6ee5 | ||
|
|
9eebc590c1 | ||
|
|
f840dcbd66 | ||
|
|
8879fc2e39 | ||
|
|
cd29e2f252 | ||
|
|
bcf505c98b | ||
|
|
933f3e1392 | ||
|
|
ba0462b24f | ||
|
|
7e8a63cd62 | ||
|
|
03e109ef12 | ||
|
|
257f2eb34c | ||
|
|
53728598fe | ||
|
|
3b98eb77f5 | ||
|
|
b6df5a4ac3 | ||
|
|
f54165025e | ||
|
|
013137c418 | ||
|
|
faf02100a5 | ||
|
|
a8b34d51a5 | ||
|
|
2ef3e6fc93 | ||
|
|
a3a1e20074 | ||
|
|
ce54aa6813 | ||
|
|
5139039402 | ||
|
|
f4adf79ad9 | ||
|
|
f10f9ada3a | ||
|
|
965a6cdde7 | ||
|
|
62c0825874 | ||
|
|
4e918f0f5d | ||
|
|
3e3154a58c | ||
|
|
22e2e442f5 | ||
|
|
c0c4a6df50 | ||
|
|
36ba9c2d94 | ||
|
|
1288795de9 | ||
|
|
0380de9571 | ||
|
|
6ac3852995 | ||
|
|
1d8fb79e11 | ||
|
|
e084bd18a9 | ||
|
|
7c3cc527f6 | ||
|
|
218aec07f8 | ||
|
|
ada13f6578 | ||
|
|
8682367b52 | ||
|
|
a80f3b77da | ||
|
|
fd8bf66acd | ||
|
|
115ed78059 | ||
|
|
00ca69fced | ||
|
|
6c21855f98 | ||
|
|
3830c85ce6 | ||
|
|
0dc4cc7e03 | ||
|
|
8f5bfd1f87 | ||
|
|
95d6efcdbf | ||
|
|
63cf0ae764 | ||
|
|
f1e79707e8 | ||
|
|
9073cb53a8 | ||
|
|
d018070891 | ||
|
|
837924b819 | ||
|
|
00cfb1347a | ||
|
|
0ce2e61db9 | ||
|
|
1854e3440b | ||
|
|
a0260eb0b2 | ||
|
|
c874f201c3 | ||
|
|
7072f24103 | ||
|
|
eb4914d36f | ||
|
|
6d27631764 | ||
|
|
a522255baf | ||
|
|
7eb2e36740 | ||
|
|
2d8f01b2e9 | ||
|
|
6f95556632 | ||
|
|
0326f0d4c9 | ||
|
|
389f7fdc25 | ||
|
|
11b2942d09 | ||
|
|
52cc725de4 | ||
|
|
a304d4235d | ||
|
|
4d7024e066 | ||
|
|
4ce928eb41 | ||
|
|
b172172be1 | ||
|
|
5fd86954d6 | ||
|
|
55c4207182 | ||
|
|
440311c939 | ||
|
|
81059fa3d8 | ||
|
|
9f856acba0 | ||
|
|
69474bad22 | ||
|
|
fafdb65453 | ||
|
|
477faafb84 | ||
|
|
abe4f5ca60 | ||
|
|
a86aeb00c5 | ||
|
|
754b9bdc5a | ||
|
|
5a3f2fcff7 | ||
|
|
9411396b97 | ||
|
|
18ad3cf4a5 | ||
|
|
a7f19f7848 | ||
|
|
63839eb464 | ||
|
|
eb60fec8e2 | ||
|
|
4427ad1451 | ||
|
|
e77e439c4b | ||
|
|
45b16ba78d | ||
|
|
e68305d7bf | ||
|
|
c2150fd9bd | ||
|
|
ea8201af5c | ||
|
|
9d27b36704 | ||
|
|
d519f992d2 | ||
|
|
1b3da22dd7 | ||
|
|
317f29b321 | ||
|
|
c32f2190bd | ||
|
|
d49ec0088f | ||
|
|
5b197c072e | ||
|
|
294b4332cc | ||
|
|
6d6b7b4537 | ||
|
|
b3fd5857f8 | ||
|
|
78c18b7db6 | ||
|
|
099d524a42 | ||
|
|
612edae5f6 | ||
|
|
b065ada633 | ||
|
|
e4bac025dd | ||
|
|
923b4acdbc | ||
|
|
2703db75d0 | ||
|
|
d932581f4a | ||
|
|
6e3fd30fcb | ||
|
|
46b34d6515 | ||
|
|
073c531855 | ||
|
|
9118871afb | ||
|
|
a7d485d6f5 | ||
|
|
a719e3b306 | ||
|
|
2bf444e4b7 | ||
|
|
c1d25bf58b | ||
|
|
544c66edde | ||
|
|
5a4f020196 | ||
|
|
ec081b8b03 | ||
|
|
9a8c81cf5e | ||
|
|
5e38214675 | ||
|
|
ba985f97e5 | ||
|
|
c9c0fb28ee | ||
|
|
6e1b0a7590 | ||
|
|
3562a48984 | ||
|
|
bb4ca4db4a | ||
|
|
c51377ca2a | ||
|
|
c3d94a51ec | ||
|
|
7cd9b9c0c8 | ||
|
|
0dcf97d29d | ||
|
|
ebc881fa1c | ||
|
|
37f9f62f03 | ||
|
|
1ace060e34 | ||
|
|
495e232c22 | ||
|
|
ed70099e36 | ||
|
|
19180243aa | ||
|
|
a08cf9379b | ||
|
|
1c6b43c545 | ||
|
|
41bfbf2e83 | ||
|
|
273d5d89b7 | ||
|
|
b0e643ecc0 | ||
|
|
4eda7cda9e | ||
|
|
463ef13fe0 | ||
|
|
ce140041e3 | ||
|
|
84f6da103f | ||
|
|
2621cc09ac | ||
|
|
40f787be14 | ||
|
|
59300e61fc | ||
|
|
ab1c4cb1d7 | ||
|
|
4616dd47bc | ||
|
|
94fe64c6c1 | ||
|
|
65f41e520e | ||
|
|
cee69705d8 | ||
|
|
3a05bcfb29 | ||
|
|
e921cf88f9 | ||
|
|
952f13dec4 | ||
|
|
ed2a3f92e0 | ||
|
|
f1563c5604 | ||
|
|
06081bd940 | ||
|
|
843b9df939 | ||
|
|
c7b8f3e923 | ||
|
|
964bb0974c | ||
|
|
3f40a51cdb | ||
|
|
d9b11858fb | ||
|
|
ec61d1f012 | ||
|
|
af2bce7141 | ||
|
|
6b6c6c71ca | ||
|
|
57f15a9e98 | ||
|
|
72ed98e404 | ||
|
|
f70699d3de | ||
|
|
638ee1af4a | ||
|
|
8028bad7b1 | ||
|
|
e1e9caf0ef | ||
|
|
8b7a287b44 | ||
|
|
0cd671c022 | ||
|
|
051fcff284 | ||
|
|
4e3d499162 | ||
|
|
7e6c4d8a6c | ||
|
|
986e405ca7 | ||
|
|
75203cf385 | ||
|
|
bc2cb59c50 | ||
|
|
cf9fdcd09e | ||
|
|
eb4b1e74f1 | ||
|
|
70005a6f1d | ||
|
|
1415a4dafa | ||
|
|
8b567427c2 | ||
|
|
3605a2ca0e | ||
|
|
c58abadff1 | ||
|
|
135c02ea41 | ||
|
|
ed01752cb4 | ||
|
|
e341337ce0 | ||
|
|
e09ea1e683 | ||
|
|
cbd430555f | ||
|
|
8239539946 | ||
|
|
e467698d72 | ||
|
|
846b426a7c | ||
|
|
f1eab4e26a | ||
|
|
283ce23111 | ||
|
|
451e1874a7 | ||
|
|
69c103ede3 | ||
|
|
ac0f92bac6 | ||
|
|
aa308717de | ||
|
|
2d0d29fb5c | ||
|
|
cba1ca8d5b | ||
|
|
c12c3c071f | ||
|
|
d37d73017f | ||
|
|
a766aa7d8a | ||
|
|
b61c07a205 | ||
|
|
c01aad6767 | ||
|
|
d0d662f73a | ||
|
|
3ae363cf4b | ||
|
|
4796110afb | ||
|
|
dc727cb620 | ||
|
|
87eb132af1 | ||
|
|
2354a6f89d | ||
|
|
1618e89c41 | ||
|
|
e223f07462 | ||
|
|
f70e87c5a0 | ||
|
|
0b3e86e307 | ||
|
|
a5ea4f7da2 | ||
|
|
4e75a74bfd | ||
|
|
c7072cd543 | ||
|
|
5e0bfc3193 | ||
|
|
b316a5d541 | ||
|
|
e147bd6cea | ||
|
|
d36f86c523 | ||
|
|
81803e2612 | ||
|
|
91b3c7343d | ||
|
|
1276c8dd46 | ||
|
|
89ab7f1f67 | ||
|
|
664d717e17 | ||
|
|
494dfc2f4b | ||
|
|
3a05d063af | ||
|
|
f97959febc | ||
|
|
b3df71ae41 | ||
|
|
ec4a99e47b | ||
|
|
ad66a48b8e | ||
|
|
7a2558d834 | ||
|
|
3b3f5a639c | ||
|
|
9acf606e93 | ||
|
|
4cdd17945d | ||
|
|
c06fc562a1 | ||
|
|
73fb9ee03e | ||
|
|
4e10ce7473 | ||
|
|
a7f4aace9c | ||
|
|
9727699e26 | ||
|
|
2520d07ef2 | ||
|
|
661e00f563 | ||
|
|
2780270911 | ||
|
|
8a5baa4637 | ||
|
|
f5eb05b682 | ||
|
|
f309ddf28a | ||
|
|
67a2a8cf08 |
133
.appveyor.yml
@@ -1,133 +0,0 @@
|
||||
version: 2.5.1-branch-{branch}-build-{build}
|
||||
|
||||
# Skipping commits affecting specific files (GitHub only). More details here: https://www.appveyor.com/docs/appveyor-yml
|
||||
skip_commits:
|
||||
files:
|
||||
- .github/*
|
||||
- .travis/*
|
||||
- .tx/*
|
||||
- webclient/*
|
||||
- .*ignore
|
||||
- .codacy.yml
|
||||
- .gitlab-ci.yml
|
||||
- .travis.yml
|
||||
- '*.md'
|
||||
- Dockerfile
|
||||
- LICENSE
|
||||
|
||||
image: Visual Studio 2017
|
||||
|
||||
cache:
|
||||
- c:\openssl-release
|
||||
- c:\protobuf-release
|
||||
- c:\zlib-release
|
||||
|
||||
environment:
|
||||
matrix:
|
||||
- qt_ver: 5.9\msvc2017_64
|
||||
openssl_ver: 1.0.2n-x64_86-win64
|
||||
protobuf_ver: 3.5.1
|
||||
zlib_ver: 1.2.11
|
||||
cmake_generator: Visual Studio 15 2017 Win64
|
||||
cmake_toolset: v141,host=x64
|
||||
target_arch: win64
|
||||
vc_arch: amd64
|
||||
- qt_ver: 5.9\msvc2015 # Qt doesn't provide a msvc2017_32
|
||||
openssl_ver: 1.0.2n-i386-win32
|
||||
protobuf_ver: 3.5.1
|
||||
zlib_ver: 1.2.11
|
||||
cmake_generator: Visual Studio 15 2017
|
||||
cmake_toolset: v141
|
||||
target_arch: win32
|
||||
vc_arch: amd64_x86
|
||||
|
||||
install:
|
||||
- ps: |
|
||||
if (Test-Path c:\openssl-release) {
|
||||
echo "using openssl from cache"
|
||||
} else {
|
||||
Invoke-WebRequest "https://indy.fulgan.com/SSL/openssl-$env:openssl_ver.zip" -OutFile c:\openssl-$env:openssl_ver.zip
|
||||
Expand-Archive -Path c:\openssl-$env:openssl_ver.zip -DestinationPath c:\openssl-release
|
||||
Set-Location -Path C:\openssl-release
|
||||
}
|
||||
if (Test-Path c:\protobuf-release) {
|
||||
echo "using protobuf from cache"
|
||||
} else {
|
||||
Invoke-WebRequest "https://github.com/google/protobuf/releases/download/v$env:protobuf_ver/protobuf-cpp-$env:protobuf_ver.zip" -OutFile c:\protobuf-cpp-$env:protobuf_ver.zip
|
||||
Expand-Archive -Path c:\protobuf-cpp-$env:protobuf_ver.zip -DestinationPath c:\
|
||||
Set-Location -Path C:\protobuf-$env:protobuf_ver\cmake
|
||||
cmake . -G "$env:cmake_generator" -T "$env:cmake_toolset" -Dprotobuf_BUILD_TESTS=0 -Dprotobuf_MSVC_STATIC_RUNTIME=0 -DCMAKE_INSTALL_PREFIX=c:/protobuf-release
|
||||
msbuild INSTALL.vcxproj /p:Configuration=Release
|
||||
}
|
||||
if (Test-Path c:\zlib-release) {
|
||||
echo "using zlib from cache"
|
||||
} else {
|
||||
Invoke-WebRequest "https://github.com/madler/zlib/archive/v$env:zlib_ver.zip" -OutFile c:\zlib-$env:zlib_ver.zip
|
||||
Expand-Archive -Path c:\zlib-$env:zlib_ver.zip -DestinationPath c:\
|
||||
Set-Location -Path C:\zlib-$env:zlib_ver
|
||||
cmake . -G "$env:cmake_generator" -T "$env:cmake_toolset" -DCMAKE_INSTALL_PREFIX=c:/zlib-release
|
||||
msbuild INSTALL.vcxproj /p:Configuration=Release
|
||||
}
|
||||
|
||||
services:
|
||||
- mysql
|
||||
|
||||
build_script:
|
||||
- ps: |
|
||||
New-Item -ItemType directory -Path $env:APPVEYOR_BUILD_FOLDER\build
|
||||
Set-Location -Path $env:APPVEYOR_BUILD_FOLDER\build
|
||||
$zlibdir = "c:\zlib-release"
|
||||
$openssldir = "C:\openssl-release"
|
||||
$protodir = "c:\protobuf-release"
|
||||
$protoc = "c:\protobuf-release\bin\protoc.exe"
|
||||
$mysqldll = "c:\Program Files\MySQL\MySQL Server 5.7\lib\libmysql.dll"
|
||||
cmake .. -G "$env:cmake_generator" -T "$env:cmake_toolset" "-DCMAKE_PREFIX_PATH=c:/Qt/$env:qt_ver;$protodir;$zlibdir;$openssldir" "-DWITH_SERVER=1" "-DPROTOBUF_PROTOC_EXECUTABLE=$protoc" "-DMYSQLCLIENT_LIBRARIES=$mysqldll"
|
||||
- msbuild PACKAGE.vcxproj /p:Configuration=Release
|
||||
- ps: |
|
||||
$exe = dir -name *.exe
|
||||
$new_name = $exe.Replace(".exe", "-${env:target_arch}.exe")
|
||||
Push-AppveyorArtifact $exe -FileName $new_name
|
||||
$cmake_name = $exe.Replace(".exe", "-${env:target_arch}.cmake.txt")
|
||||
Push-AppveyorArtifact CMakeCache.txt -FileName $cmake_name
|
||||
$json = New-Object PSObject
|
||||
(New-Object PSObject | Add-Member -PassThru NoteProperty bin $new_name | Add-Member -PassThru NoteProperty cmake $cmake_name | Add-Member -PassThru NoteProperty commit $env:APPVEYOR_REPO_COMMIT) | ConvertTo-JSON | Out-File -FilePath "latest-$env:target_arch" -Encoding ASCII
|
||||
Push-AppveyorArtifact "latest-$env:target_arch"
|
||||
$version = $matches['content']
|
||||
|
||||
test: off
|
||||
|
||||
|
||||
# Builds for pull requests skip the deployment step altogether
|
||||
deploy:
|
||||
# Deploy configuration for "beta" releases
|
||||
- provider: GitHub
|
||||
auth_token:
|
||||
secure: p+7wPVry2XEa6TBm9XH8IaQZbBmXQ/J2ldbGmcIxUZD3NkkPrSRRlmE7Of1CBBIO
|
||||
tag: "$(APPVEYOR_REPO_TAG_NAME)"
|
||||
release: "Cockatrice $(APPVEYOR_REPO_TAG_NAME)"
|
||||
description: "Beta release of Cockatrice"
|
||||
artifact: /.*\.exe/
|
||||
force_update: true
|
||||
draft: false
|
||||
prerelease: true
|
||||
on:
|
||||
APPVEYOR_REPO_TAG: true
|
||||
APPVEYOR_REPO_TAG_NAME: /([0-9]|[1-9][0-9])(\.([0-9]|[1-9][0-9])){2}-beta(\.([2-9]|[1-9][0-9]))?$/ # regex to match semver naming convention for beta pre-releases
|
||||
|
||||
# Deploy configuration for "stable" releases
|
||||
- provider: GitHub
|
||||
auth_token:
|
||||
secure: p+7wPVry2XEa6TBm9XH8IaQZbBmXQ/J2ldbGmcIxUZD3NkkPrSRRlmE7Of1CBBIO
|
||||
tag: "$(APPVEYOR_REPO_TAG_NAME)"
|
||||
release: "Cockatrice $(APPVEYOR_REPO_TAG_NAME)"
|
||||
artifact: /.*\.exe/
|
||||
force_update: true
|
||||
draft: false
|
||||
prerelease: false
|
||||
on:
|
||||
APPVEYOR_REPO_TAG: true
|
||||
APPVEYOR_REPO_TAG_NAME: /([0-9]|[1-9][0-9])(\.([0-9]|[1-9][0-9])){2}$/ # regex to match semver naming convention for stable full releases
|
||||
|
||||
|
||||
# official validator for ".appveyor.yml" config file: https://ci.appveyor.com/tools/validate-yaml
|
||||
# appveyor config documentation: https://www.appveyor.com/docs/build-configuration/
|
||||
16
.ci/ArchLinux/Dockerfile
Normal file
@@ -0,0 +1,16 @@
|
||||
from archlinux:latest
|
||||
|
||||
RUN pacman --sync --refresh --sysupgrade --needed --noconfirm \
|
||||
base-devel \
|
||||
ccache \
|
||||
cmake \
|
||||
git \
|
||||
gtest \
|
||||
mariadb-libs \
|
||||
protobuf \
|
||||
qt5-base \
|
||||
qt5-multimedia \
|
||||
qt5-svg \
|
||||
qt5-tools \
|
||||
qt5-websockets \
|
||||
&& pacman --sync --clean --clean --noconfirm
|
||||
24
.ci/Debian10/Dockerfile
Normal file
@@ -0,0 +1,24 @@
|
||||
FROM debian:10
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
ccache \
|
||||
clang-format \
|
||||
cmake \
|
||||
file \
|
||||
g++ \
|
||||
git \
|
||||
liblzma-dev \
|
||||
libmariadb-dev-compat \
|
||||
libprotobuf-dev \
|
||||
libqt5multimedia5-plugins \
|
||||
libqt5sql5-mysql \
|
||||
libqt5svg5-dev \
|
||||
libqt5websockets5-dev \
|
||||
protobuf-compiler \
|
||||
qt5-default \
|
||||
qtbase5-dev \
|
||||
qtmultimedia5-dev \
|
||||
qttools5-dev-tools \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
24
.ci/Debian11/Dockerfile
Normal file
@@ -0,0 +1,24 @@
|
||||
FROM debian:11
|
||||
|
||||
RUN apt-get update && \
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
ccache \
|
||||
clang-format \
|
||||
cmake \
|
||||
file \
|
||||
g++ \
|
||||
git \
|
||||
liblzma-dev \
|
||||
libmariadb-dev-compat \
|
||||
libprotobuf-dev \
|
||||
libqt5multimedia5-plugins \
|
||||
libqt5sql5-mysql \
|
||||
libqt5svg5-dev \
|
||||
libqt5websockets5-dev \
|
||||
protobuf-compiler \
|
||||
qtmultimedia5-dev \
|
||||
qttools5-dev \
|
||||
qttools5-dev-tools \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
21
.ci/Fedora34/Dockerfile
Normal file
@@ -0,0 +1,21 @@
|
||||
FROM fedora:34
|
||||
|
||||
RUN dnf install -y \
|
||||
@development-tools \
|
||||
ccache \
|
||||
cmake \
|
||||
desktop-file-utils \
|
||||
file \
|
||||
gcc-c++ \
|
||||
git \
|
||||
hicolor-icon-theme \
|
||||
libappstream-glib \
|
||||
mariadb-devel \
|
||||
protobuf-devel \
|
||||
qt5-{qttools,qtsvg,qtmultimedia,qtwebsockets}-devel \
|
||||
rpm-build \
|
||||
sqlite-devel \
|
||||
wget \
|
||||
xz-devel \
|
||||
zlib-devel \
|
||||
&& dnf clean all
|
||||
21
.ci/Fedora35/Dockerfile
Normal file
@@ -0,0 +1,21 @@
|
||||
FROM fedora:35
|
||||
|
||||
RUN dnf install -y \
|
||||
@development-tools \
|
||||
ccache \
|
||||
cmake \
|
||||
desktop-file-utils \
|
||||
file \
|
||||
gcc-c++ \
|
||||
git \
|
||||
hicolor-icon-theme \
|
||||
libappstream-glib \
|
||||
mariadb-devel \
|
||||
protobuf-devel \
|
||||
qt5-{qttools,qtsvg,qtmultimedia,qtwebsockets}-devel \
|
||||
rpm-build \
|
||||
sqlite-devel \
|
||||
wget \
|
||||
xz-devel \
|
||||
zlib-devel \
|
||||
&& dnf clean all
|
||||
24
.ci/UbuntuBionic/Dockerfile
Normal file
@@ -0,0 +1,24 @@
|
||||
FROM ubuntu:bionic
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
ccache \
|
||||
clang-format \
|
||||
cmake \
|
||||
file \
|
||||
g++ \
|
||||
git \
|
||||
liblzma-dev \
|
||||
libmariadb-dev-compat \
|
||||
libprotobuf-dev \
|
||||
libqt5multimedia5-plugins \
|
||||
libqt5sql5-mysql \
|
||||
libqt5svg5-dev \
|
||||
libqt5websockets5-dev \
|
||||
protobuf-compiler \
|
||||
qt5-default \
|
||||
qtmultimedia5-dev \
|
||||
qttools5-dev \
|
||||
qttools5-dev-tools \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
25
.ci/UbuntuFocal/Dockerfile
Normal file
@@ -0,0 +1,25 @@
|
||||
FROM ubuntu:focal
|
||||
|
||||
RUN apt-get update && \
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
ccache \
|
||||
clang-format \
|
||||
cmake \
|
||||
file \
|
||||
g++ \
|
||||
git \
|
||||
liblzma-dev \
|
||||
libmariadb-dev-compat \
|
||||
libprotobuf-dev \
|
||||
libqt5multimedia5-plugins \
|
||||
libqt5sql5-mysql \
|
||||
libqt5svg5-dev \
|
||||
libqt5websockets5-dev \
|
||||
protobuf-compiler \
|
||||
qt5-default \
|
||||
qtmultimedia5-dev \
|
||||
qttools5-dev \
|
||||
qttools5-dev-tools \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
24
.ci/UbuntuHirsute/Dockerfile
Normal file
@@ -0,0 +1,24 @@
|
||||
FROM ubuntu:hirsute
|
||||
|
||||
RUN apt-get update && \
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
ccache \
|
||||
clang-format \
|
||||
cmake \
|
||||
file \
|
||||
g++ \
|
||||
git \
|
||||
liblzma-dev \
|
||||
libmariadb-dev-compat \
|
||||
libprotobuf-dev \
|
||||
libqt5multimedia5-plugins \
|
||||
libqt5sql5-mysql \
|
||||
libqt5svg5-dev \
|
||||
libqt5websockets5-dev \
|
||||
protobuf-compiler \
|
||||
qtmultimedia5-dev \
|
||||
qttools5-dev \
|
||||
qttools5-dev-tools \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
24
.ci/UbuntuImpish/Dockerfile
Normal file
@@ -0,0 +1,24 @@
|
||||
FROM ubuntu:impish
|
||||
|
||||
RUN apt-get update && \
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
ccache \
|
||||
clang-format \
|
||||
cmake \
|
||||
file \
|
||||
g++ \
|
||||
git \
|
||||
liblzma-dev \
|
||||
libmariadb-dev-compat \
|
||||
libprotobuf-dev \
|
||||
libqt5multimedia5-plugins \
|
||||
libqt5sql5-mysql \
|
||||
libqt5svg5-dev \
|
||||
libqt5websockets5-dev \
|
||||
protobuf-compiler \
|
||||
qtmultimedia5-dev \
|
||||
qttools5-dev \
|
||||
qttools5-dev-tools \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
152
.ci/compile.sh
Executable file
@@ -0,0 +1,152 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This script is to be used by the ci environment from the project root directory, do not use it from somewhere else.
|
||||
|
||||
# Read arguments
|
||||
while [[ "$@" ]]; do
|
||||
case "$1" in
|
||||
'--')
|
||||
shift
|
||||
;;
|
||||
'--format')
|
||||
CHECK_FORMAT=1
|
||||
shift
|
||||
;;
|
||||
'--install')
|
||||
MAKE_INSTALL=1
|
||||
shift
|
||||
;;
|
||||
'--package')
|
||||
MAKE_PACKAGE=1
|
||||
shift
|
||||
if [[ $# != 0 && $1 != -* ]]; then
|
||||
PACKAGE_TYPE="$1"
|
||||
shift
|
||||
fi
|
||||
;;
|
||||
'--suffix')
|
||||
shift
|
||||
if [[ $# == 0 ]]; then
|
||||
echo "::error file=$0::--suffix expects an argument"
|
||||
exit 1
|
||||
fi
|
||||
PACKAGE_SUFFIX="$1"
|
||||
shift
|
||||
;;
|
||||
'--server')
|
||||
MAKE_SERVER=1
|
||||
shift
|
||||
;;
|
||||
'--test')
|
||||
MAKE_TEST=1
|
||||
shift
|
||||
;;
|
||||
'--debug')
|
||||
BUILDTYPE="Debug"
|
||||
shift
|
||||
;;
|
||||
'--release')
|
||||
BUILDTYPE="Release"
|
||||
shift
|
||||
;;
|
||||
'--ccache')
|
||||
USE_CCACHE=1
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
if [[ $1 == -* ]]; then
|
||||
echo "::error file=$0::unrecognized option: $1"
|
||||
exit 3
|
||||
fi
|
||||
BUILDTYPE="$1"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Check formatting using clang-format
|
||||
if [[ $CHECK_FORMAT ]]; then
|
||||
echo "::group::Run linter"
|
||||
source ./.ci/lint.sh
|
||||
echo "::endgroup::"
|
||||
fi
|
||||
|
||||
set -e
|
||||
|
||||
# Setup
|
||||
./servatrice/check_schema_version.sh
|
||||
mkdir -p build
|
||||
cd build
|
||||
|
||||
if [[ ! $CMAKE_BUILD_PARALLEL_LEVEL ]]; then
|
||||
CMAKE_BUILD_PARALLEL_LEVEL=2 # default machines have 2 cores
|
||||
fi
|
||||
|
||||
# Add cmake flags
|
||||
if [[ $MAKE_SERVER ]]; then
|
||||
flags+=" -DWITH_SERVER=1"
|
||||
fi
|
||||
if [[ $MAKE_TEST ]]; then
|
||||
flags+=" -DTEST=1"
|
||||
fi
|
||||
if [[ $BUILDTYPE ]]; then
|
||||
flags+=" -DCMAKE_BUILD_TYPE=$BUILDTYPE"
|
||||
fi
|
||||
if [[ $PACKAGE_TYPE ]]; then
|
||||
flags+=" -DCPACK_GENERATOR=$PACKAGE_TYPE"
|
||||
fi
|
||||
|
||||
if [[ $(uname) == "Darwin" ]]; then
|
||||
if [[ $USE_CCACHE ]]; then
|
||||
# prepend ccache compiler binaries to path
|
||||
PATH="/usr/local/opt/ccache/libexec:$PATH"
|
||||
fi
|
||||
# Add qt install location when using homebrew
|
||||
flags+=" -DCMAKE_PREFIX_PATH=/usr/local/opt/qt5/"
|
||||
fi
|
||||
|
||||
# Compile
|
||||
if [[ $USE_CCACHE ]]; then
|
||||
echo "::group::Show ccache stats"
|
||||
ccache --show-stats
|
||||
echo "::endgroup::"
|
||||
fi
|
||||
|
||||
echo "::group::Configure cmake"
|
||||
cmake --version
|
||||
cmake .. $flags
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Build project"
|
||||
cmake --build .
|
||||
echo "::endgroup::"
|
||||
|
||||
if [[ $USE_CCACHE ]]; then
|
||||
echo "::group::Show ccache stats again"
|
||||
ccache --show-stats
|
||||
echo "::endgroup::"
|
||||
fi
|
||||
|
||||
if [[ $MAKE_TEST ]]; then
|
||||
echo "::group::Run tests"
|
||||
cmake --build . --target test
|
||||
echo "::endgroup::"
|
||||
fi
|
||||
|
||||
if [[ $MAKE_INSTALL ]]; then
|
||||
echo "::group::Install"
|
||||
cmake --build . --target install
|
||||
echo "::endgroup::"
|
||||
fi
|
||||
|
||||
if [[ $MAKE_PACKAGE ]]; then
|
||||
echo "::group::Create package"
|
||||
cmake --build . --target package
|
||||
echo "::endgroup::"
|
||||
|
||||
if [[ $PACKAGE_SUFFIX ]]; then
|
||||
echo "::group::Update package name"
|
||||
../.ci/name_build.sh "$PACKAGE_SUFFIX"
|
||||
echo "::endgroup::"
|
||||
fi
|
||||
fi
|
||||
146
.ci/docker.sh
Normal file
@@ -0,0 +1,146 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This script is to be used by the ci environment from the project root directory, do not use it from somewhere else.
|
||||
|
||||
# Creates or loads docker images to use in compilation, creates RUN function to start compilation on the docker image.
|
||||
# --get loads the image from a previously saved image cache, will build if no image is found
|
||||
# --build builds the image from the Dockerfile in .ci/$NAME
|
||||
# --save stores the image, if an image was loaded it will not be stored
|
||||
# requires: docker
|
||||
# uses env: NAME CACHE BUILD GET SAVE (correspond to args: <name> --set-cache <cache> --build --get --save)
|
||||
# sets env: RUN CCACHE_DIR IMAGE_NAME RUN_ARGS RUN_OPTS BUILD_SCRIPT
|
||||
# exitcode: 1 for failure, 2 for missing dockerfile, 3 for invalid arguments
|
||||
export BUILD_SCRIPT=".ci/compile.sh"
|
||||
|
||||
project_name="cockatrice"
|
||||
save_extension=".tar.gz"
|
||||
image_cache="image"
|
||||
ccache_cache=".ccache"
|
||||
|
||||
# Read arguments
|
||||
while [[ "$@" ]]; do
|
||||
case "$1" in
|
||||
'--build')
|
||||
BUILD=1
|
||||
shift
|
||||
;;
|
||||
'--get')
|
||||
GET=1
|
||||
shift
|
||||
;;
|
||||
'--save')
|
||||
SAVE=1
|
||||
shift
|
||||
;;
|
||||
'--set-cache')
|
||||
CACHE=$2
|
||||
if ! [[ -d $CACHE ]]; then
|
||||
echo "could not find cache path: $CACHE" >&2
|
||||
exit 3
|
||||
fi
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
if [[ $1 == -* ]]; then
|
||||
echo "unrecognized option: $1"
|
||||
return 3
|
||||
fi
|
||||
NAME="$1"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Setup
|
||||
if ! [[ $NAME ]]; then
|
||||
echo "no build name given" >&2
|
||||
return 3
|
||||
fi
|
||||
|
||||
export IMAGE_NAME="${project_name,,}_${NAME,,}" # lower case
|
||||
|
||||
docker_dir=".ci/$NAME"
|
||||
if ! [[ -r $docker_dir/Dockerfile ]]; then
|
||||
echo "could not find Dockerfile in $docker_dir" >&2
|
||||
return 2 # even if the image is cached, we do not want to run if there is no way to build this image
|
||||
fi
|
||||
|
||||
if ! [[ $CACHE ]]; then
|
||||
echo "cache dir is not set!" >&2
|
||||
else
|
||||
if ! [[ -d $CACHE ]]; then
|
||||
echo "could not find cache dir: $CACHE" >&2
|
||||
mkdir -p $CACHE
|
||||
unset GET # the dir is empty
|
||||
fi
|
||||
if [[ $GET || $SAVE ]]; then
|
||||
img_dir="$CACHE/$image_cache"
|
||||
img_save="$img_dir/$IMAGE_NAME$save_extension"
|
||||
if ! [[ -d $img_dir ]]; then
|
||||
echo "could not find image dir: $img_dir" >&2
|
||||
mkdir -p "$img_dir"
|
||||
fi
|
||||
fi
|
||||
export CCACHE_DIR="$CACHE/$ccache_cache"
|
||||
if ! [[ -d $CCACHE_DIR ]]; then
|
||||
echo "could not find ccache dir: $CCACHE_DIR" >&2
|
||||
mkdir -p "$CCACHE_DIR"
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
# Get the docker image from previously stored save
|
||||
if [[ $GET ]]; then
|
||||
if [[ $img_save ]] && docker load --input "$img_save"; then
|
||||
echo "loaded image"
|
||||
docker images
|
||||
unset BUILD # do not overwrite the loaded image with build
|
||||
unset SAVE # do not overwrite the stored image with the same image
|
||||
if [[ $(find "$CCACHE_DIR" -type f -print -quit) ]]; then # check contents of ccache
|
||||
echo "setting ccache to readonly"
|
||||
export RUN_ARGS="$RUN_ARGS -e CCACHE_READONLY=1 -e CCACHE_NOSTATS=1" # do not overwrite ccache
|
||||
else
|
||||
echo "ccache is empty: $(find "$CCACHE_DIR")" >&2
|
||||
fi
|
||||
else
|
||||
echo "could not load cached image, building instead" >&2
|
||||
BUILD=1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Build the docker image from dockerfile
|
||||
if [[ $BUILD ]]; then
|
||||
if docker build --tag "$IMAGE_NAME" "$docker_dir"; then
|
||||
echo "built image"
|
||||
docker images
|
||||
else
|
||||
echo "could not build image $IMAGE_NAME" >&2
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Save docker image to cache (compressed)
|
||||
if [[ $SAVE ]]; then
|
||||
if [[ $img_save ]] && docker save --output "$img_save" "$IMAGE_NAME"; then
|
||||
echo "saved image to: $img_save"
|
||||
else
|
||||
echo "could not save image $IMAGE_NAME" >&2
|
||||
fi
|
||||
fi
|
||||
|
||||
# Set compile function, runs the compile script on the image, passes arguments to the script
|
||||
function RUN ()
|
||||
{
|
||||
echo "running image:"
|
||||
if docker images | grep "$IMAGE_NAME"; then
|
||||
args="--mount type=bind,source=$PWD,target=/src -w=/src"
|
||||
if [[ $CCACHE_DIR ]]; then
|
||||
args+=" --mount type=bind,source=$CCACHE_DIR,target=/.ccache -e CCACHE_DIR=/.ccache"
|
||||
fi
|
||||
docker run $args $RUN_ARGS "$IMAGE_NAME" bash "$BUILD_SCRIPT" $RUN_OPTS $@
|
||||
return $?
|
||||
else
|
||||
echo "could not find docker image: $IMAGE_NAME" >&2
|
||||
return 3
|
||||
fi
|
||||
}
|
||||
69
.ci/lint_cpp.sh
Executable file
@@ -0,0 +1,69 @@
|
||||
#!/bin/bash
|
||||
|
||||
# fetch master branch
|
||||
git fetch origin master
|
||||
|
||||
# unshallow if needed
|
||||
echo "Finding merge base"
|
||||
if ! git merge-base origin/master HEAD; then
|
||||
echo "Could not find merge base, unshallowing repo"
|
||||
git fetch --unshallow
|
||||
fi
|
||||
|
||||
# Check formatting using clangify
|
||||
echo "Checking your code using clang-format..."
|
||||
|
||||
diff="$(./clangify.sh --diff --cf-version --branch origin/master)"
|
||||
err=$?
|
||||
|
||||
case $err in
|
||||
1)
|
||||
cat <<EOM
|
||||
|
||||
***********************************************************
|
||||
*** ***
|
||||
*** Your code does not comply with our style guide. ***
|
||||
*** ***
|
||||
*** Please correct it or run the "clangify.sh" script. ***
|
||||
*** Then commit and push those changes to this branch. ***
|
||||
*** Check our CONTRIBUTING.md file for more details. ***
|
||||
*** ***
|
||||
*** Thank you ❤️ ***
|
||||
*** ***
|
||||
***********************************************************
|
||||
|
||||
Used clang-format version:
|
||||
${diff%%
|
||||
*}
|
||||
|
||||
The following changes should be made:
|
||||
${diff#*
|
||||
}
|
||||
|
||||
Exiting...
|
||||
EOM
|
||||
exit 2
|
||||
;;
|
||||
|
||||
0)
|
||||
cat <<EOM
|
||||
|
||||
***********************************************************
|
||||
*** ***
|
||||
*** Your code complies with our style guide! ***
|
||||
*** ***
|
||||
*** Awesome 👍 ***
|
||||
*** ***
|
||||
***********************************************************
|
||||
|
||||
Exiting...
|
||||
EOM
|
||||
exit 0
|
||||
;;
|
||||
|
||||
*)
|
||||
echo ""
|
||||
echo "Something went wrong in our formatting checks: clangify returned $err" >&2
|
||||
echo ""
|
||||
;;
|
||||
esac
|
||||
50
.ci/name_build.sh
Executable file
@@ -0,0 +1,50 @@
|
||||
#!/bin/bash
|
||||
# used by the ci to rename build artifacts
|
||||
# renames the file to [original name][SUFFIX].[original extension]
|
||||
# where SUFFIX is either available in the environment or as the first arg
|
||||
# if MAKE_ZIP is set instead a zip is made
|
||||
# expected to be run in the build directory
|
||||
builddir="."
|
||||
findrx="Cockatrice-*.*"
|
||||
|
||||
if [[ $1 ]]; then
|
||||
SUFFIX="$1"
|
||||
fi
|
||||
|
||||
# check env
|
||||
if [[ ! $SUFFIX ]]; then
|
||||
echo "::error file=$0::SUFFIX is missing"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
set -e
|
||||
|
||||
# find file
|
||||
found="$(find "$builddir" -maxdepth 1 -type f -name "$findrx" -print -quit)"
|
||||
path="${found%/*}" # remove all after last /
|
||||
file="${found##*/}" # remove all before last /
|
||||
if [[ ! $file ]]; then
|
||||
echo "::error file=$0::could not find package"
|
||||
exit 1
|
||||
fi
|
||||
if ! cd "$path"; then
|
||||
echo "::error file=$0::could not get file path"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# set filename
|
||||
name="${file%.*}" # remove all after last .
|
||||
new_name="$name$SUFFIX."
|
||||
if [[ $MAKE_ZIP ]]; then
|
||||
filename="${new_name}zip"
|
||||
echo "creating zip '$filename' from '$file'"
|
||||
zip "$filename" "$file"
|
||||
else
|
||||
extension="${file##*.}" # remove all before last .
|
||||
filename="$new_name$extension"
|
||||
echo "renaming '$file' to '$filename'"
|
||||
mv "$file" "$filename"
|
||||
fi
|
||||
ls -l "$PWD/$filename"
|
||||
echo "::set-output name=path::$PWD/$filename"
|
||||
echo "::set-output name=name::$filename"
|
||||
112
.ci/prep_release.sh
Executable file
@@ -0,0 +1,112 @@
|
||||
#!/bin/bash
|
||||
# sets the properties of ci releases
|
||||
# this doesn't have to be 100% foolproof
|
||||
# the releases are first made as drafts and will be vetted by a human
|
||||
# it just has to provide a template
|
||||
# this requires the repo to be unshallowed
|
||||
template_path=".ci/release_template.md"
|
||||
body_path="/tmp/release.md"
|
||||
beta_regex='beta'
|
||||
name_regex='set\(GIT_TAG_RELEASENAME "([[:print:]]+)")'
|
||||
whitespace='^\s*$'
|
||||
|
||||
if [[ $1 ]]; then
|
||||
TAG="$1"
|
||||
fi
|
||||
|
||||
# check env
|
||||
if [[ ! $TAG ]]; then
|
||||
echo "::error file=$0::TAG is missing"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# create title
|
||||
if [[ $TAG =~ $beta_regex ]]; then
|
||||
echo "::set-output name=is_beta::yes"
|
||||
title="$TAG"
|
||||
echo "creating beta release '$title'"
|
||||
elif [[ ! $(cat CMakeLists.txt) =~ $name_regex ]]; then
|
||||
echo "::error file=$0::could not find releasename in CMakeLists.txt"
|
||||
exit 1
|
||||
else
|
||||
echo "::set-output name=is_beta::no"
|
||||
name="${BASH_REMATCH[1]}"
|
||||
version="${TAG##*-}"
|
||||
title="Cockatrice $version: $name"
|
||||
no_beta=1
|
||||
echo "::set-output name=friendly_name::$name"
|
||||
echo "creating full release '$title'"
|
||||
fi
|
||||
echo "::set-output name=title::$title"
|
||||
|
||||
# add release notes template
|
||||
if [[ $no_beta ]]; then
|
||||
body="$(cat "$template_path")"
|
||||
if [[ ! $body ]]; then
|
||||
echo "::warning file=$0::could not find release template"
|
||||
fi
|
||||
body="${body//--REPLACE-WITH-RELEASE-TITLE--/$title}"
|
||||
else
|
||||
body="--REPLACE-WITH-COMMIT-COUNT-- commits have been included over the previous --REPLACE-WITH-PREVIOUS-RELEASE-TYPE--
|
||||
|
||||
<details>
|
||||
<summary><b>show changes</b></summary>
|
||||
|
||||
--REPLACE-WITH-GENERATED-LIST--
|
||||
</details>"
|
||||
fi
|
||||
|
||||
# add git log to release notes
|
||||
all_tags="
|
||||
$(git tag)" # tags are ordered alphabetically
|
||||
before="${all_tags%%
|
||||
$TAG*}" # strip line with current tag an all lines after it
|
||||
# note the extra newlines are needed to always have a last line
|
||||
if [[ $all_tags == $before ]]; then
|
||||
echo "::warning file=$0::could not find current tag"
|
||||
else
|
||||
while
|
||||
previous="${before##*
|
||||
}" # get the last line
|
||||
# skip this tag if this is a full release and it's a beta or if empty
|
||||
[[ $no_beta && $previous =~ $beta_regex || ! $previous ]]
|
||||
do
|
||||
beta_list+=" $previous" # add to list of skipped betas
|
||||
next_before="${before%
|
||||
*}" # strip the last line
|
||||
if [[ $next_before == $before ]]; then
|
||||
unset previous
|
||||
break
|
||||
fi
|
||||
before="$next_before"
|
||||
done
|
||||
if [[ $previous ]]; then
|
||||
if generated_list="$(git log "$previous..$TAG" --pretty="- %s")"; then
|
||||
count="$(git rev-list --count "$previous..$TAG")"
|
||||
[[ $previous =~ $beta_regex ]] && previousreleasetype="beta release" || previousreleasetype="full release"
|
||||
echo "adding list of commits to release notes:"
|
||||
echo "'$previous' to '$TAG' ($count commits)"
|
||||
# --> is the markdown comment escape sequence, emojis are way better
|
||||
generated_list="${generated_list//-->/→}"
|
||||
body="${body//--REPLACE-WITH-GENERATED-LIST--/$generated_list}"
|
||||
body="${body//--REPLACE-WITH-COMMIT-COUNT--/$count}"
|
||||
body="${body//--REPLACE-WITH-PREVIOUS-RELEASE-TAG--/$previous}"
|
||||
body="${body//--REPLACE-WITH-PREVIOUS-RELEASE-TYPE--/$previousreleasetype}"
|
||||
if [[ $beta_list =~ $whitespace ]]; then
|
||||
beta_list="-n there are no betas to delete!"
|
||||
else
|
||||
echo "the following betas should be deleted after publishing:"
|
||||
echo "$beta_list"
|
||||
fi
|
||||
body="${body//--REPLACE-WITH-BETA-LIST--/$beta_list}"
|
||||
else
|
||||
echo "::warning file=$0::failed to produce git log"
|
||||
fi
|
||||
else
|
||||
echo "::warning file=$0::could not find previous tag"
|
||||
fi
|
||||
fi
|
||||
|
||||
# write to file
|
||||
echo "::set-output name=body_path::$body_path"
|
||||
echo "$body" >"$body_path"
|
||||
94
.ci/release_template.md
Normal file
@@ -0,0 +1,94 @@
|
||||
<!-- this template comes from .ci/release_template.md -->
|
||||
|
||||
<!-- Don't forget to delete the previous betas after publishing this!
|
||||
git push -d origin --REPLACE-WITH-BETA-LIST--
|
||||
-->
|
||||
|
||||
<!-- This list of binaries should be updated every time the ci is changed to
|
||||
include different targets -->
|
||||
<pre>
|
||||
<b>Pre-compiled binaries we serve:</b>
|
||||
- <kbd>Windows 7/8/10 (32-bit)</kbd></i>
|
||||
- <kbd>Windows 7/8/10 (64-bit)</kbd></i>
|
||||
- <kbd>macOS 10.14</kbd> ("Mojave")</i>
|
||||
- <kbd>macOS 10.15</kbd> ("Catalina")</i>
|
||||
- <kbd>macOS 11.0</kbd> ("Big Sur")</i>
|
||||
- <kbd>Ubuntu 18.04</kbd> ("Bionic Beaver")</i>
|
||||
- <kbd>Ubuntu 20.04</kbd> ("Focal Fossa")</i>
|
||||
- <kbd>Ubuntu 20.10</kbd> ("Groovy Gorilla")</i>
|
||||
- <kbd>Ubuntu 21.04</kbd> ("Hirsute Hippo")</i>
|
||||
- <kbd>Debian 10</kbd> ("Buster")</i>
|
||||
- <kbd>Fedora 33</kbd></i>
|
||||
- <kbd>Fedora 34</kbd></i>
|
||||
<kbd>We are also packaged in Arch Linux's official community repository, courtesy of @FFY00</kbd></i>
|
||||
<kbd>General linux support is available via a flatpak package (Flathub)</kbd></i>
|
||||
</pre>
|
||||
|
||||
|
||||
## General Notes
|
||||
|
||||
<!-- --REPLACE-WITH-RELEASE-TITLE-- should be placed here by the ci -->
|
||||
We're pleased to announce the newest official release: <kbd>--REPLACE-WITH-RELEASE-TITLE--</kbd>
|
||||
|
||||
We hope you enjoy the changes made and we have listed all changes, with their corresponding tickets, since the last version of Cockatrice was released for your convenience.
|
||||
|
||||
If you ever encounter a bug, have a suggestion or idea, or feel a need for a developer to look into something, please feel free to [open a ticket](https://github.com/Cockatrice/Cockatrice/issues). ([How to create a GitHub Ticket for Cockatrice](https://github.com/Cockatrice/Cockatrice/wiki/How-to-Create-a-GitHub-Ticket-Regarding-Cockatrice))
|
||||
|
||||
For any information relating to Cockatrice, please take a look at our official site: **https://cockatrice.github.io**
|
||||
|
||||
If you'd like to help contribute to Cockatrice in any way, check out our [README](https://github.com/Cockatrice/Cockatrice#get-involved-). We're always available to answer questions you may have on how the program works and how you can provide a meaningful contribution.
|
||||
|
||||
|
||||
## Upgrading Cockatrice
|
||||
<!-- this optional section puts a warning banner for problems with updating
|
||||
> ⚠️ **With this release, we no longer provide a ready-to-install binary for:**
|
||||
> --DEPRECATED-OS-HERE--
|
||||
-->
|
||||
|
||||
- Run the internal software updater: <kbd>Help → Check for Client Updates</kbd>
|
||||
|
||||
Don't forget to update your card database right after! (<kbd>Help → Check for Card Updates...</kbd>)
|
||||
|
||||
|
||||
## Changelog
|
||||
<!--
|
||||
This list is generated and should be moved to their respective header and
|
||||
possibly edited a little.
|
||||
Append PR numbers of fixups to their main PR to keep the list coherent.
|
||||
Put the quantity of remaining PR's below the highlights section.
|
||||
Remove empty headers when done.
|
||||
|
||||
--REPLACE-WITH-GENERATED-LIST--
|
||||
-->
|
||||
|
||||
<!-- Highlights of the release -->
|
||||
### ⚠️ Important:
|
||||
### ✨ New Features:
|
||||
### 🐛 Fixed Bugs / Resolved issues:
|
||||
|
||||
<!-- Complete list of changes (foldable) -->
|
||||
<details>
|
||||
<summary>
|
||||
📘 <b>Show all changes</b> (--REPLACE-WITH-COMMIT-COUNT-- commits)
|
||||
</summary>
|
||||
|
||||
### User Interface
|
||||
### Under the Hood
|
||||
### Oracle
|
||||
### Servatrice
|
||||
### Webatrice
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
## Translations
|
||||
- **Thanks for over 300 people contributing to 20+ different languages up to now!**
|
||||
- Without the help of the community we couldn't offer that great language support... keep up the good work!
|
||||
- It's actually very easy to join and help for yourself - find out more here:
|
||||
- [Help us Translate Cockatrice into your native language!](https://github.com/Cockatrice/Cockatrice/wiki/Translation-FAQ)
|
||||
|
||||
|
||||
## Special Thanks
|
||||
<!-- Personalise this a bit! -->
|
||||
We continue to find it amazing that so many people contribute their time, knowledge, code, testing and more to the project. We'd like to thank the entire Cockatrice community for their efforts.
|
||||
<!-- We'd like to especially recognize @ZeldaZach, --ADD-CONTRIBUTORS-HERE-- for their help in preparing so many amazing new features for the user base. -->
|
||||
@@ -1,52 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
./servatrice/check_schema_version.sh
|
||||
|
||||
mkdir -p build
|
||||
cd build
|
||||
prefix=""
|
||||
|
||||
if [[ $TRAVIS_OS_NAME == "osx" ]]; then
|
||||
export PATH="/usr/local/opt/ccache/bin:$PATH"
|
||||
prefix="-DCMAKE_PREFIX_PATH=$(echo /usr/local/opt/qt*/)"
|
||||
fi
|
||||
if [[ $TRAVIS_OS_NAME == "linux" ]]; then
|
||||
prefix="-DCMAKE_PREFIX_PATH=$(echo /opt/qt5*/lib/cmake/)"
|
||||
fi
|
||||
|
||||
if [[ $BUILDTYPE == "Debug" ]]; then
|
||||
cmake .. -DWITH_SERVER=1 -DCMAKE_BUILD_TYPE=$BUILDTYPE $prefix -DTEST=1
|
||||
make -j2
|
||||
make test
|
||||
|
||||
if [[ $TRAVIS_OS_NAME == "linux" ]]; then
|
||||
cd ..
|
||||
clang-format -i \
|
||||
common/*.h \
|
||||
common/*.cpp \
|
||||
cockatrice/src/*.h \
|
||||
cockatrice/src/*.cpp \
|
||||
oracle/src/*.h \
|
||||
oracle/src/*.cpp \
|
||||
servatrice/src/*.h \
|
||||
servatrice/src/*.cpp
|
||||
|
||||
git clean -f
|
||||
git diff --quiet || (
|
||||
echo "*****************************************************";
|
||||
echo "*** This PR is not clean against our code style ***";
|
||||
echo "*** Run clang-format and fix up any differences ***";
|
||||
echo "*** Check our CONTRIBUTING.md file for details! ***";
|
||||
echo "*** Thank you ♥ ***";
|
||||
echo "*****************************************************";
|
||||
)
|
||||
git diff --exit-code
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ $BUILDTYPE == "Release" ]]; then
|
||||
cmake .. -DWITH_SERVER=1 -DCMAKE_BUILD_TYPE=$BUILDTYPE $prefix
|
||||
make package -j2
|
||||
fi
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [[ $TRAVIS_OS_NAME == "osx" ]] ; then
|
||||
brew update
|
||||
brew install ccache clang-format protobuf qt@5.5
|
||||
fi
|
||||
if [[ $TRAVIS_OS_NAME == "linux" ]] ; then
|
||||
echo Skipping... packages are installed with the Travis apt addon for sudo disabled container builds
|
||||
fi
|
||||
@@ -22,4 +22,10 @@ AllowShortFunctionsOnASingleLine: None
|
||||
BinPackParameters: false
|
||||
AllowAllParametersOfDeclarationOnNextLine: false
|
||||
IndentCaseLabels: true
|
||||
PointerAlignment: Right
|
||||
PointerAlignment: Right
|
||||
SortIncludes: true
|
||||
IncludeBlocks: Regroup
|
||||
---
|
||||
Language: Proto
|
||||
AllowShortFunctionsOnASingleLine: None
|
||||
SpacesInContainerLiterals: false
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
.git/
|
||||
build/
|
||||
.github/
|
||||
.travis/
|
||||
.tx/
|
||||
cockatrice/
|
||||
doc/
|
||||
|
||||
362
.github/CONTRIBUTING.md
vendored
@@ -1,4 +1,6 @@
|
||||
[Introduction](#contributing-to-cockatrice) | [Code Style Guide](#code-style-guide) | [Translations](#translations) | [Release Management](#release-management)
|
||||
[Introduction](#contributing-to-cockatrice) | [Code Style Guide](
|
||||
#code-style-guide) | [Translations](#translations) | [Release Management](
|
||||
#release-management)
|
||||
|
||||
----
|
||||
|
||||
@@ -7,28 +9,91 @@
|
||||
# Contributing to Cockatrice #
|
||||
First off, thanks for taking the time to contribute to our project! 🎉 ❤ ️✨
|
||||
|
||||
The following is a set of guidelines for contributing to Cockatrice. These are mostly guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request.
|
||||
The following is a set of guidelines for contributing to Cockatrice. These are
|
||||
mostly guidelines, not rules. Use your best judgment, and feel free to propose
|
||||
changes to this document in a pull request.
|
||||
|
||||
|
||||
# Recommended Setups #
|
||||
|
||||
For those developers who like the Linux or MacOS environment, many of our developers like working with a nifty program called [CLion](https://www.jetbrains.com/clion/). The program's a great asset and one of the best tools you'll find on these systems, but you're welcomed to use any IDE you most enjoy.
|
||||
For those developers who like the Linux or MacOS environment, many of our
|
||||
developers like working with a nifty program called [CLion](
|
||||
https://www.jetbrains.com/clion/). The program's a great asset and one of the
|
||||
best tools you'll find on these systems, but you're welcomed to use any IDE
|
||||
you most enjoy.
|
||||
|
||||
Developers who like Windows development tend to find [Visual Studio](https://www.visualstudio.com/) the best tool for the job.
|
||||
Developers who like Windows development tend to find [Visual Studio](
|
||||
https://www.visualstudio.com/) the best tool for the job.
|
||||
|
||||
If you have any questions on IDEs, feel free to chat with us on [Gitter](https://gitter.im/Cockatrice/Cockatrice) and we would love to help answer your questions!
|
||||
[](https://discord.gg/ZASRzKu)
|
||||
[](https://gitter.im/Cockatrice/Cockatrice)
|
||||
|
||||
If you'd like to ask questions, get advice, or just want to say hi,
|
||||
the Cockatrice Development Team uses [Discord](https://discord.gg/ZASRzKu)
|
||||
for communications in the #dev channel. If you're not into Discord, we also
|
||||
have a [Gitter](https://gitter.im/Cockatrice/Cockatrice) channel available,
|
||||
albeit slightly less active.
|
||||
|
||||
|
||||
# Code Style Guide #
|
||||
|
||||
### Formatting and continuous integration (CI) ###
|
||||
|
||||
We use a separate job on the CI to check your code for formatting issues. If
|
||||
your pull request failed the test, you can check the output on the checks tab.
|
||||
It's the first job called "linter", you can click the "Run clangify" step to
|
||||
see the output of the test.
|
||||
|
||||
The message will look like this:
|
||||
```
|
||||
***********************************************************
|
||||
*** ***
|
||||
*** Your code does not comply with our style guide. ***
|
||||
*** ***
|
||||
*** Please correct it or run the "clangify.sh" script. ***
|
||||
*** Then commit and push those changes to this branch. ***
|
||||
*** Check our CONTRIBUTING.md file for more details. ***
|
||||
*** ***
|
||||
*** Thank you ❤️ ***
|
||||
*** ***
|
||||
***********************************************************
|
||||
```
|
||||
The CONTRIBUTING.md file mentioned in that message is the file you are
|
||||
currently reading. Please see [this section](#formatting) below for full
|
||||
information on our formatting guidelines.
|
||||
|
||||
### Compatibility ###
|
||||
|
||||
Cockatrice is currently compiled on all platforms using <kbd>C++11</kbd>. You'll notice <kbd>C++03</kbd> code throughout the codebase. Please feel free to help convert it over!
|
||||
Cockatrice is currently compiled on all platforms using <kbd>C++11</kbd>.
|
||||
You'll notice <kbd>C++03</kbd> code throughout the codebase. Please feel free
|
||||
to help convert it over!
|
||||
|
||||
For consistency, we use Qt data structures where possible. For example, `QString` over
|
||||
`std::string` and `QList` over `std::vector`.
|
||||
For consistency, we use Qt data structures where possible. For example,
|
||||
`QString` over `std::string` and `QList` over `std::vector`.
|
||||
|
||||
### Header files ###
|
||||
Do not use old C style casts in new code, instead use a [`static_cast<>`](
|
||||
https://en.cppreference.com/w/cpp/language/static_cast)
|
||||
or other appropriate conversion.
|
||||
|
||||
### Formatting ###
|
||||
|
||||
The handy tool `clang-format` can format your code for you, it is available for
|
||||
almost any environment. A special `.clang-format` configuration file is
|
||||
included in the project and is used to format your code.
|
||||
|
||||
We've also included a bash script, `clangify.sh`, that will use clang-format to
|
||||
format all files in your pr in one go. Use `./clangify.sh --help` to show a
|
||||
full help page.
|
||||
|
||||
To run clang-format on a single source file simply use the command
|
||||
`clang-format -i <filename>` to format it in place. (Some systems install
|
||||
clang-format with a specific version number appended,
|
||||
`find /usr/bin -name clang-format*` should find it for you)
|
||||
|
||||
See [the clang-format documentation](
|
||||
https://clang.llvm.org/docs/ClangFormat.html) for more information about the tool.
|
||||
|
||||
#### Header files ####
|
||||
|
||||
Use header files with the extension `.h` and source files with the extension
|
||||
`.cpp`.
|
||||
@@ -38,41 +103,48 @@ Use header guards in the form of `FILE_NAME_H`.
|
||||
Simple functions, such as getters, may be written inline in the header file,
|
||||
but other functions should be written in the source file.
|
||||
|
||||
Keep library includes and project includes grouped together. So this is okay:
|
||||
Group project includes first, followed by library includes. All in alphabetic order.
|
||||
Like this:
|
||||
```c++
|
||||
// Good
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
#include "card.h"
|
||||
#include "deck.h"
|
||||
|
||||
// Good
|
||||
#include "card.h"
|
||||
#include "deck.h"
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
|
||||
// Bad:
|
||||
// Bad
|
||||
#include <QList>
|
||||
#include "card.h"
|
||||
#include <QString>
|
||||
#include "deck.h"
|
||||
|
||||
// Bad
|
||||
#include "card.h"
|
||||
#include "deck.h"
|
||||
#include <QString>
|
||||
#include <QList>
|
||||
```
|
||||
|
||||
### Naming ###
|
||||
#### Naming ####
|
||||
|
||||
Use `UpperCamelCase` for classes, structs, enums, etc. and `lowerCamelCase` for
|
||||
function and variable names.
|
||||
|
||||
Member variables aren't decorated in any way. Don't prefix or suffix with
|
||||
Don't use [Hungarian Notation](
|
||||
https://en.wikipedia.org/wiki/Hungarian_notation).
|
||||
|
||||
Member variables aren't decorated in any way. Don't prefix or suffix them with
|
||||
underscores, etc.
|
||||
|
||||
Use a separate line for each declaration, don't use a single line like this
|
||||
`int one = 1, two = 2` and instead split them into two lines.
|
||||
|
||||
For arguments to constructors which have the same names as member variables,
|
||||
prefix those arguments with underscores:
|
||||
```c++
|
||||
MyClass::MyClass(int _myData) : myData(_myData)
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
```
|
||||
Pointers and references should be denoted with the `*` or `&` going with the
|
||||
@@ -89,43 +161,49 @@ Bar& bar2 = *bar1;
|
||||
Use `nullptr` instead of `NULL` (or `0`) for null pointers.
|
||||
If you find any usage of the old keywords, we encourage you to fix it.
|
||||
|
||||
### Braces ###
|
||||
#### Braces ####
|
||||
|
||||
Braces should almost always go on their own line:
|
||||
Braces should go on their own line except for control statements, the use of
|
||||
braces around single line statements is preferred.
|
||||
See the following example:
|
||||
```c++
|
||||
int main()
|
||||
{
|
||||
if (someCondition)
|
||||
{
|
||||
doSomething();
|
||||
}
|
||||
else if (someOtherCondition1)
|
||||
{
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
{ // function or class: own line
|
||||
if (someCondition) { // control statement: same line
|
||||
doSomething(); // single line statement, braces preferred
|
||||
} else if (someOtherCondition1) { // else goes on the same line as a closing brace
|
||||
for (int i = 0; i < 100; i++) {
|
||||
doSomethingElse();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (someOtherCondition2)
|
||||
{
|
||||
} else {
|
||||
while (someOtherCondition2) {
|
||||
doSomethingElse();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
Braces should never be omitted for single-statement. Keeping the code legibile is a high priority of ours and we hope you share a similar belief :)
|
||||
|
||||
### Tabs vs Spaces ###
|
||||
#### Indentation and Spacing ####
|
||||
|
||||
We _highly_ encourate the use of spaces. If you use tabs, please readjust them to 4 spaces per tab before submitting.
|
||||
Always indent using 4 spaces, do not use tabs. Opening and closing braces
|
||||
should be on the same indentation layer, member access specifiers in classes or
|
||||
structs should not be indented.
|
||||
|
||||
### Lines ###
|
||||
All operators and braces should be separated by spaces, do not add a space next
|
||||
to the inside of a brace.
|
||||
|
||||
Do not have trailing whitespace in your lines, if possible. Most IDEs check for this nowadays and clean it up for you.
|
||||
If multiple lines of code that follow eachother have single line comments
|
||||
behind them, place all of them on the same indentation level. This indentation
|
||||
level should be equal to the longest line of code for each of these comments,
|
||||
without added spacing.
|
||||
|
||||
Lines should be 120 characters or less, but you can exceed this if you find it necessary.
|
||||
#### Lines ####
|
||||
|
||||
Do not leave trailing whitespace on any line. Most IDEs check for this
|
||||
nowadays and clean it up for you.
|
||||
|
||||
Lines should be 120 characters or less. Please break up lines that are too long
|
||||
into smaller parts, for example at spaces or after opening a brace.
|
||||
|
||||
### Memory Management ###
|
||||
|
||||
@@ -139,7 +217,7 @@ int main()
|
||||
Card card;
|
||||
showCard(card);
|
||||
}
|
||||
|
||||
|
||||
// Bad: relies on manual memory management and doesn't give us much
|
||||
// null-safety.
|
||||
void showCard(const Card *card);
|
||||
@@ -161,51 +239,99 @@ as `QScopedPointer`, or, less preferably, `QSharedPointer`.
|
||||
The servatrice database's schema can be found at `servatrice/servatrice.sql`.
|
||||
Everytime the schema gets modified, some other steps are due:
|
||||
1. Increment the value of `cockatrice_schema_version` in `servatrice.sql`;
|
||||
2. Increment the value of `DATABASE_SCHEMA_VERSION` in `servatrice_database_interface.h` accordingly;
|
||||
3. Create a new migration file inside the `servatrice/migrations` directory named after the new schema version.
|
||||
4. Run the `servatrice/check_schema_version.sh` script to ensure everything is fine.
|
||||
|
||||
The migration file should include the sql statements needed to migrate the database schema and data from the previous to the new version, and an additional statement that updates `cockatrice_schema_version` to the correct value.
|
||||
2. Increment the value of `DATABASE_SCHEMA_VERSION` in
|
||||
`servatrice_database_interface.h` accordingly;
|
||||
3. Create a new migration file inside the `servatrice/migrations` directory
|
||||
named after the new schema version.
|
||||
4. Run the `servatrice/check_schema_version.sh` script to ensure everything is
|
||||
fine.
|
||||
|
||||
The migration file should include the sql statements needed to migrate the
|
||||
database schema and data from the previous to the new version, and an
|
||||
additional statement that updates `cockatrice_schema_version` to the correct
|
||||
value.
|
||||
|
||||
Ensure that the migration produces the expected effects; e.g. if you add a
|
||||
new column, make sure the migration places it in the same order as
|
||||
servatrice.sql.
|
||||
|
||||
### Protocol buffer ###
|
||||
|
||||
Cockatrice and Servatrice exchange data using binary messages. The syntax of these messages is defined in the `proto` files in the `common/pb` folder. These files defines the way data contained in each message is serialized using Google's [protocol buffer](https://developers.google.com/protocol-buffers/).
|
||||
Any change to the `proto` file should be taken with caution and tested intensively before being merged, becaus a change to the protocol could make new clients incompatible to the old server and vice versa.
|
||||
Cockatrice and Servatrice exchange data using binary messages. The syntax of
|
||||
these messages is defined in the `proto` files in the `common/pb` folder. These
|
||||
files define the way data contained in each message is serialized using
|
||||
Google's [protocol buffers](https://developers.google.com/protocol-buffers/).
|
||||
Any change to the `proto` files should be taken with caution and tested
|
||||
intensively before being merged, because a change to the protocol could make
|
||||
new clients incompatible to the old server and vice versa.
|
||||
|
||||
You can find more information on how we use Protobuf on [our wiki!](https://github.com/Cockatrice/Cockatrice/wiki/Client-server-protocol)
|
||||
You can find more information on how we use Protobuf on [our wiki!](
|
||||
https://github.com/Cockatrice/Cockatrice/wiki/Client-server-protocol)
|
||||
|
||||
# Reviewing Pull Requests #
|
||||
|
||||
After you have finished your changes to the project you should put them on a
|
||||
separate branch of your fork on GitHub and open a [pull request](
|
||||
https://docs.github.com/en/free-pro-team@latest/desktop/contributing-and-collaborating-using-github-desktop/creating-an-issue-or-pull-request
|
||||
).
|
||||
Your code will then be automatically compiled by GitHub actions for Linux and
|
||||
macOS, and by Appveyor for Windows. Additionally GitHub will perform a [Linting
|
||||
check](#formatting-and-continuous-integration-ci). If any issues come up you
|
||||
can check their status at the bottom of the pull request page, click on details
|
||||
to go to the CI website and see the different build logs.
|
||||
|
||||
If your pull request passes our tests and has no merge conflicts, it will be
|
||||
reviewed by our team members. You can then address any requested changes. When
|
||||
all changes have been approved your pull request will be squashed into a single
|
||||
commit and merged into the master branch by a team member. Your change will then
|
||||
be included in the next release 👍
|
||||
|
||||
# Translations #
|
||||
|
||||
**Basic workflow for translations:**
|
||||
Basic workflow for translations:
|
||||
1. Developer adds a `tr("foo")` string in the code;
|
||||
2. Every few days, a maintainer updates the `*_en.ts files` with the new strings;
|
||||
3. Transifex picks up the new files from github every 24 hours;
|
||||
4. Translators translate the new untraslated strings on Transifex;
|
||||
3. Transifex picks up the new files from GitHub every 24 hours;
|
||||
4. Translators translate the new untranslated strings on Transifex;
|
||||
5. Before a release, a maintainer fetches the updated translations from Transifex.
|
||||
|
||||
### Translations (for developers) ###
|
||||
### Using Translations (for developers) ###
|
||||
|
||||
**Step 1: Adding translatable strings to the code (`tr("foo")`)**
|
||||
All the user-interface strings inside Cockatrice's source code must be written
|
||||
in English(US).
|
||||
Translations to other languages are managed using [Transifex](
|
||||
https://www.transifex.com/projects/p/cockatrice/).
|
||||
|
||||
All the user-interface strings inside Cockatrice's source code must be written in
|
||||
english language.<br>
|
||||
Translations to other languages are managed using [Transifex](https://www.transifex.com/projects/p/cockatrice/).
|
||||
Adding a new string to translate is as easy as adding the string in the
|
||||
'tr("")' function, the string will be picked up as translatable automatically
|
||||
and translated as needed.
|
||||
For example, setting the text of a label in the way that the string
|
||||
`"My name is:"` can be translated:
|
||||
```c++
|
||||
nameLabel.setText(tr("My name is:"));
|
||||
```
|
||||
|
||||
If you're about to propose a change that adds or modifies any translatable string
|
||||
in the code, you don't need to take care of adding the new strings to the
|
||||
translation files. Every few days, or when a lot of new strings have been added,
|
||||
someone from the development team will take care of extracing all the new strings,
|
||||
adding them to the english translation files and making them available to
|
||||
translators on Transifex.
|
||||
To translate a string that would have plural forms you can add the amount to
|
||||
the tr call, also you can add an extra string as a hint for translators:
|
||||
```c++
|
||||
QString message = tr("Everyone draws %n cards", "pop up message", amount);
|
||||
```
|
||||
See [QT's wiki on translations](
|
||||
https://doc.qt.io/qt-5/i18n-source-translation.html#handling-plurals)
|
||||
|
||||
### Translations (for maintainers) ###
|
||||
If you're about to propose a change that adds or modifies any translatable
|
||||
string in the code, you don't need to take care of adding the new strings to
|
||||
the translation files.
|
||||
Every few days, or when a lot of new strings have been added, someone from the
|
||||
development team will take care of extracting all the new strings and adding
|
||||
them to the english translation files and making them available to translators
|
||||
on Transifex.
|
||||
|
||||
**Step 2: Updating `*_en.ts` files with new strings**
|
||||
### Maintaining Translations (for maintainers) ###
|
||||
|
||||
When new translatable strings have been added to the code, it would be nice to
|
||||
When new translatable strings have been added to the code, a maintainer should
|
||||
make them available to translators on Transifex. Every few days, or when a lot
|
||||
of new strings have been added, a maintainer should take care of extracing all
|
||||
of new strings have been added, a maintainer should take care of extracting all
|
||||
the new strings and add them to the english translation files.
|
||||
|
||||
To update the english translation files, re-run cmake enabling the appropriate
|
||||
@@ -227,79 +353,111 @@ You should then notice that the following files have uncommitted changes:
|
||||
cockatrice/translations/cockatrice_en.ts
|
||||
oracle/translations/oracle_en.ts
|
||||
|
||||
It's now suggested to disable the parameter using:
|
||||
It is recommended to disable the parameter afterwards using:
|
||||
```sh
|
||||
cmake .. -DUPDATE_TRANSLATIONS=OFF
|
||||
```
|
||||
Now you are ready to propose your change.
|
||||
|
||||
**Step 3: Automatic pushing to Transifex**
|
||||
Once your change gets merged, Transifex will pick up the modified files
|
||||
automatically (checked every 24 hours) and update the interface where
|
||||
translators will be able to translate the new strings.
|
||||
|
||||
Once your change gets merged, Transifex will pick up the modified files automatically (checks every 24 hours)
|
||||
and update the interface where translators will be able to translate the new strings.
|
||||
### Releasing Translations (for maintainers) ###
|
||||
|
||||
**Step 5: Fetching new translations from Transifex**
|
||||
|
||||
Before rushing out a new release, it would be nice to fetch the most up to date
|
||||
Before rushing out a new release, a maintainer should fetch the most up to date
|
||||
translations from Transifex and commit them into the Cockatrice source code.
|
||||
This can be done manually from the Transifex web interface, but it's quite time
|
||||
consuming.
|
||||
|
||||
As an alternative, you can install the Transifex CLI:
|
||||
|
||||
As an alternative, you can install the Transifex CLI:
|
||||
|
||||
http://docs.transifex.com/developer/client/
|
||||
|
||||
You'll then be able to use a git-like cli command to push and pull translations
|
||||
from Transifex to the source code and vice versa.
|
||||
|
||||
### Translations (for translators) ###
|
||||
### Adding Translations (for translators) ###
|
||||
|
||||
**Step 4: Editing translations at Transifex**
|
||||
|
||||
Please have a look at the specific [FAQ for translators](https://github.com/Cockatrice/Cockatrice/wiki/Translation-FAQ).
|
||||
As a translator you can help translate the new strings on [Transifex](
|
||||
https://www.transifex.com/projects/p/cockatrice/).
|
||||
Please have a look at the specific [FAQ for translators](
|
||||
https://github.com/Cockatrice/Cockatrice/wiki/Translation-FAQ).
|
||||
|
||||
|
||||
# Release Management #
|
||||
|
||||
### Publishing A New Beta Release ###
|
||||
### Publishing A New Release ###
|
||||
|
||||
Travis and AppVeyor have been configured to upload files to GitHub Releases whenever a <kbd>tag</kbd> is pushed.<br>
|
||||
Usually, tags are created through publishing a (pre-)release, but there's a way around that.
|
||||
We use [GitHub Releases](https://github.com/Cockatrice/Cockatrice/releases) to
|
||||
publish new stable versions and betas.
|
||||
Whenever a git tag is pushed to the repository github will create a draft
|
||||
release and upload binaries automatically.
|
||||
|
||||
To trigger Travis and AppVeyor, simply do the following:
|
||||
To create a tag, simply do the following:
|
||||
```bash
|
||||
cd $COCKATRICE_REPO
|
||||
git checkout master
|
||||
git remote update -p
|
||||
git pull
|
||||
git tag $TAG_NAME
|
||||
git push upstream $TAG_NAME
|
||||
git push $UPSTREAM $TAG_NAME
|
||||
```
|
||||
|
||||
You should define the variables as such:
|
||||
```
|
||||
upstream - git@github.com:Cockatrice/Cockatrice.git
|
||||
$COCKATRICE_REPO - /Location/of/repository/cockatrice.git
|
||||
`$TAG_NAME` should be:
|
||||
`$UPSTREAM` - the remote for git@github.com:Cockatrice/Cockatrice.git
|
||||
`$TAG_NAME` should be formatted as:
|
||||
- `YYYY-MM-DD-Release-MAJ.MIN.PATCH` for **stable releases**
|
||||
- `YYYY-MM-DD-Development-MAJ.MIN.PATCH-beta.X` for **beta releases**<br>
|
||||
With *MAJ.MIN.PATCH* being the NEXT release version!
|
||||
```
|
||||
|
||||
This will cause a tagged release to be established on the GitHub repository, which will then lead to the upload of binaries. If you use this method, the tags (releases) that you create will be marked as a "Pre-release". The `/latest` URL will not be impacted (for stable release downloads) so that's good.
|
||||
This will cause a tagged release to be established on the GitHub repository,
|
||||
with the binaries being added to the release whenever they are ready.
|
||||
The release is initially a draft, where the path notes can be edited and after
|
||||
all is good and ready it can be published on GitHub.
|
||||
If you use a SemVer tag including "beta", the release that will be created at
|
||||
GitHub will be marked as a "Pre-release" automatically.
|
||||
The target of the `.../latest` URL will not be changed in that case, it always
|
||||
points to the latest stable release and not pre-releases/betas.
|
||||
|
||||
If you accidentally push a tag incorrectly (the tag is outdated, you didn't pull in the latest branch accidentally, you named the tag wrong, etc.) you can revoke the tag by doing the following:
|
||||
If you accidentally push a tag incorrectly (the tag is outdated, you didn't
|
||||
pull in the latest branch accidentally, you named the tag wrong, etc.) you can
|
||||
revoke the tag by doing the following:
|
||||
```bash
|
||||
git push --delete upstream $TAG_NAME
|
||||
git tag -d $TAG_NAME
|
||||
```
|
||||
You can also do this on GitHub, you'll also want to delete the new release.
|
||||
|
||||
**NOTE:** Unfortunately, due to the method of how Travis and AppVeyor work, to publish a stable release you will need to make a copy of the release notes locally and then paste them into the GitHub GUI once the binaries have been uploaded by them. These CI services will automatically overwrite the name of the release (to "Cockatrice $TAG_NAME"), the status of the release (to "Pre-release"), and the release body (to "Beta build of Cockatrice").
|
||||
In the first lines of [CMakeLists.txt](
|
||||
https://github.com/Cockatrice/Cockatrice/blob/master/CMakeLists.txt)
|
||||
there's an hardcoded version number and pretty name used when compiling from
|
||||
master or custom (not tagged) versions.
|
||||
While on tagged versions these numbers are overridden by the version numbers
|
||||
coming from the tag title, it's good practice to increment the ones at CMake
|
||||
after every full release to stress that master is ahead of the last stable
|
||||
release.
|
||||
The preferred flow of operation is:
|
||||
* Just before a release, make sure the version number in CMakeLists.txt is set
|
||||
to the same release version you are about to tag.
|
||||
* This is also the time to change the pretty name in CMakeLists.txt called
|
||||
GIT_TAG_RELEASENAME and commit and push these changes.
|
||||
* Tag the release following the previously described syntax in order to get it
|
||||
correctly built and deployed by CI.
|
||||
* Wait for the configure step to create the release and update the patch
|
||||
notes.
|
||||
* Check on the github actions page for build progress which should be the top
|
||||
listed [here](
|
||||
https://github.com/Cockatrice/Cockatrice/actions?query=event%3Apush+-branch%3Amaster
|
||||
).
|
||||
* When the build has been completed you can verify all uploaded files on the
|
||||
release are in order and hit the publish button.
|
||||
* After the release is complete, update the CMake version number again to the
|
||||
next targeted beta version, typically increasing `PROJECT_VERSION_PATCH` by
|
||||
one.
|
||||
|
||||
**NOTE 2:** In the first lines of https://github.com/Cockatrice/Cockatrice/blob/master/CMakeLists.txt there's an hardcoded version number used when compiling custom (not tagged) versions. While on tagged versions these numbers are overriden by the version numbers coming from the tag title, it's a good practice to keep them aligned with the real ones.
|
||||
The preferred flow of operations is:
|
||||
* just before a release, update the version number in CMakeLists.txt to "next release version";
|
||||
* tag the release following the previously described syntax in order to get it built by CI;
|
||||
* wait for CI to upload the binaries, double check if everything is in order
|
||||
* after the release is complete, update the version number again to "next targeted beta version", typically increasing `PROJECT_VERSION_PATCH` by one.
|
||||
|
||||
**NOTE 3:** When releasing a new stable version, all the previous beta versions should be deleted. This is needed for Cockatrice to pick up the stable release also for users that chose the "beta" release channel.
|
||||
When releasing a new stable version, all previous beta releases (and tags)
|
||||
should be deleted. This is needed for Cockatrice to update users of the "Beta"
|
||||
release channel correctly to the latest stable version as well.
|
||||
This can be done the same way as revoking tags, mentioned above.
|
||||
|
||||
12
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: [ZeldaZach]
|
||||
patreon: mtgjson
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
custom: ["paypal.me/zachhalpern"]
|
||||
14
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,14 +0,0 @@
|
||||
<b>System Information:</b>
|
||||
<!-- Go to "Help → View Debug Log" and copy the top few lines here! -->
|
||||
|
||||
<!-- If you can't install Cockatrice to access that information, make
|
||||
sure to include your OS and the app version from the setup file here -->
|
||||
________________________________________________________________________________________
|
||||
|
||||
<!-- Explain your issue/request/suggestion in detail here! -->
|
||||
|
||||
<!-- This repository is ONLY about development of the Cockatrice app.
|
||||
If you have any problems with a server (e.g. registering, connecting, ban...)
|
||||
you have to contact that server's owner/admin.
|
||||
Check this list of public servers with webpage links and contact details:
|
||||
https://github.com/Cockatrice/Cockatrice/wiki/Public-Servers -->
|
||||
33
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
---
|
||||
name: "🐛 Bug Report"
|
||||
about: Report an issue encountered while using Cockatrice
|
||||
title: ''
|
||||
labels: 'Bug'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!-- READ THIS BEFORE POSTING
|
||||
Go to "Help → View Debug Log" in Cockatrice and copy all information at the
|
||||
top (above the separation line) below "System Information" in this ticket!
|
||||
If you can't start Cockatrice to access these details, make
|
||||
sure to post your OS and the file name of the setup binary instead. -->
|
||||
|
||||
**System Information:**
|
||||
|
||||
|
||||
_______________________________________________________________________________________
|
||||
|
||||
<!-- Explain your issue in detail here! Please attach screenshots if possible. -->
|
||||
|
||||
|
||||
|
||||
|
||||
_______________________________________________________________________________________
|
||||
|
||||
<!-- Describe the sequence of actions needed to experience the bug -->
|
||||
|
||||
**Steps to reproduce:**
|
||||
- Do A
|
||||
- Do B
|
||||
- Do C ...
|
||||
9
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: 💬 Discord Community (Get help with server issues, e.g. Login)
|
||||
url: https://discord.gg/3Z9yzmA
|
||||
about: Need help with using the client? Want to find some games? Try the Discord server!
|
||||
- name: 🌐 Translations (Help improve the localization of the app)
|
||||
url: https://www.transifex.com/cockatrice/cockatrice/
|
||||
# it is not possible to add a link to the wiki to this description
|
||||
about: For more information and guidance check our Translation FAQ on our wiki!
|
||||
23
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
---
|
||||
name: "💡 Feature Request"
|
||||
about: Request a new feature
|
||||
title: ''
|
||||
labels: 'Feature Request'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
Please search the issue tracker for similar issues before posting!
|
||||
If your request is related to another request (but not the same!) list it here
|
||||
-->
|
||||
**Similar Requests**
|
||||
|
||||
|
||||
<!-- Describe your feature idea here in detail -->
|
||||
**Description of New Feature**
|
||||
|
||||
|
||||
<!-- If your feature requires some context, provide it here -->
|
||||
**Additional Context**
|
||||
|
||||
398
.github/workflows/desktop-build.yml
vendored
Normal file
@@ -0,0 +1,398 @@
|
||||
name: Build Desktop
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- 'webclient/**'
|
||||
- '.github/workflows/web-*.yml'
|
||||
tags:
|
||||
- '*'
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- 'webclient/**'
|
||||
- '.github/workflows/web-*.yml'
|
||||
|
||||
jobs:
|
||||
configure:
|
||||
name: Configure
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
outputs:
|
||||
tag: ${{steps.configure.outputs.tag}}
|
||||
sha: ${{steps.configure.outputs.sha}}
|
||||
upload_url: ${{steps.create_release.outputs.upload_url}}
|
||||
|
||||
steps:
|
||||
- name: Cancel previous runs
|
||||
uses: styfle/cancel-workflow-action@0.6.0
|
||||
with:
|
||||
access_token: ${{github.token}} # needs other token https://github.com/styfle/cancel-workflow-action/issues/7
|
||||
|
||||
- name: Configure
|
||||
id: configure
|
||||
shell: bash
|
||||
run: |
|
||||
tag_regex='^refs/tags/'
|
||||
if [[ $GITHUB_EVENT_NAME == pull-request ]]; then # pull request
|
||||
sha="${{github.event.pull_request.head.sha}}"
|
||||
elif [[ $GITHUB_REF =~ $tag_regex ]]; then # release
|
||||
sha="$GITHUB_SHA"
|
||||
tag="${GITHUB_REF/refs\/tags\//}"
|
||||
echo "::set-output name=tag::$tag"
|
||||
else # push to branch
|
||||
sha="$GITHUB_SHA"
|
||||
fi
|
||||
echo "::set-output name=sha::$sha"
|
||||
|
||||
- name: Checkout
|
||||
if: steps.configure.outputs.tag != null
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Prepare release paramaters
|
||||
id: prepare
|
||||
if: steps.configure.outputs.tag != null
|
||||
shell: bash
|
||||
env:
|
||||
TAG: ${{steps.configure.outputs.tag}}
|
||||
run: .ci/prep_release.sh
|
||||
|
||||
- name: Create release
|
||||
if: steps.configure.outputs.tag != null
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{github.token}}
|
||||
with:
|
||||
tag_name: ${{github.ref}}
|
||||
release_name: ${{steps.prepare.outputs.title}}
|
||||
body_path: ${{steps.prepare.outputs.body_path}}
|
||||
draft: true
|
||||
prerelease: ${{steps.prepare.outputs.is_beta == 'yes'}}
|
||||
|
||||
build-linux:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# these names correspond to the files in .ci/$distro
|
||||
include:
|
||||
- distro: UbuntuImpish
|
||||
package: DEB
|
||||
|
||||
- distro: UbuntuHirsute
|
||||
package: DEB
|
||||
test: skip
|
||||
|
||||
- distro: UbuntuFocal
|
||||
package: DEB
|
||||
test: skip # UbuntuFocal has a broken qt for debug builds
|
||||
|
||||
- distro: UbuntuBionic
|
||||
package: DEB
|
||||
|
||||
- distro: ArchLinux
|
||||
package: skip # we are packaged in arch already
|
||||
allow-failure: yes
|
||||
|
||||
- distro: Debian10
|
||||
package: DEB
|
||||
|
||||
- distro: Debian11
|
||||
package: DEB
|
||||
|
||||
- distro: Fedora34
|
||||
package: RPM
|
||||
test: skip # gtest does not compile for some reason
|
||||
|
||||
- distro: Fedora35
|
||||
package: RPM
|
||||
|
||||
|
||||
name: ${{matrix.distro}}
|
||||
|
||||
needs: configure
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
continue-on-error: ${{matrix.allow-failure == 'yes'}}
|
||||
|
||||
env:
|
||||
NAME: ${{matrix.distro}}
|
||||
CACHE: /tmp/${{matrix.distro}}-cache # ${{runner.temp}} does not work?
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Get cache timestamp
|
||||
id: cache_timestamp
|
||||
shell: bash
|
||||
run: echo "::set-output name=timestamp::$(date -u '+%Y%m%d%H%M%S')"
|
||||
|
||||
- name: Restore cache
|
||||
uses: actions/cache@v2
|
||||
env:
|
||||
timestamp: ${{steps.cache_timestamp.outputs.timestamp}}
|
||||
with:
|
||||
path: ${{env.CACHE}}
|
||||
key: docker-${{matrix.distro}}-cache-${{env.timestamp}}
|
||||
restore-keys: |
|
||||
docker-${{matrix.distro}}-cache-
|
||||
|
||||
- name: Build ${{matrix.distro}} Docker image
|
||||
shell: bash
|
||||
run: source .ci/docker.sh --build
|
||||
|
||||
- name: Build debug and test
|
||||
if: matrix.test != 'skip'
|
||||
shell: bash
|
||||
run: |
|
||||
source .ci/docker.sh
|
||||
RUN --server --debug --test --ccache
|
||||
|
||||
- name: Build release package
|
||||
id: package
|
||||
if: matrix.package != 'skip'
|
||||
shell: bash
|
||||
env:
|
||||
suffix: '-${{matrix.distro}}'
|
||||
type: '${{matrix.package}}'
|
||||
run: |
|
||||
source .ci/docker.sh
|
||||
RUN --server --release --package "$type" --suffix "$suffix"
|
||||
|
||||
- name: Upload artifact
|
||||
if: matrix.package != 'skip'
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ${{matrix.distro}}-package
|
||||
path: ./build/${{steps.package.outputs.name}}
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload to release
|
||||
if: matrix.package != 'skip' && needs.configure.outputs.tag != null
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{github.token}}
|
||||
with:
|
||||
upload_url: ${{needs.configure.outputs.upload_url}}
|
||||
asset_path: ./build/${{steps.package.outputs.name}}
|
||||
asset_name: ${{steps.package.outputs.name}}
|
||||
asset_content_type: application/octet-stream
|
||||
|
||||
build-macos:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
target:
|
||||
- Debug
|
||||
- 10.14_Mojave
|
||||
- 10.15_Catalina
|
||||
- 11_Big_Sur
|
||||
include:
|
||||
- target: Debug # tests only
|
||||
os: macos-latest
|
||||
xcode: 12.5.1
|
||||
type: Debug
|
||||
do_tests: 0 # tests do not work yet on mac
|
||||
make_package: false
|
||||
|
||||
- target: 10.14_Mojave
|
||||
os: macos-10.15 # runs on Catalina
|
||||
xcode: 10.3 # allows compatibility with macOS 10.14
|
||||
type: Release
|
||||
do_tests: 0
|
||||
make_package: true
|
||||
|
||||
- target: 10.15_Catalina
|
||||
os: macos-10.15
|
||||
xcode: 12.1
|
||||
type: Release
|
||||
do_tests: 0
|
||||
make_package: true
|
||||
|
||||
- target: 11_Big_Sur
|
||||
os: macos-11
|
||||
xcode: 12.5.1
|
||||
type: Release
|
||||
do_tests: 0
|
||||
make_package: true
|
||||
|
||||
name: macOS ${{matrix.target}}
|
||||
|
||||
needs: configure
|
||||
|
||||
runs-on: ${{matrix.os}}
|
||||
|
||||
continue-on-error: ${{matrix.allow-failure == 'yes'}}
|
||||
|
||||
env:
|
||||
DEVELOPER_DIR:
|
||||
/Applications/Xcode_${{matrix.xcode}}.app/Contents/Developer
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install dependencies using homebrew
|
||||
shell: bash
|
||||
# cmake cannot find the mysql connector
|
||||
# neither of these works: mariadb-connector-c mysql-connector-c++
|
||||
run: brew update && brew install protobuf
|
||||
|
||||
- name: Install QT using homebrew
|
||||
id: brew_install_qt
|
||||
continue-on-error: true
|
||||
shell: bash
|
||||
run: brew install qt@5 --force-bottle
|
||||
|
||||
- name: Install QT using actions
|
||||
if: steps.brew_install_qt.outcome != 'success'
|
||||
uses: jurplel/install-qt-action@v2
|
||||
|
||||
- name: Build on Xcode ${{matrix.xcode}}
|
||||
shell: bash
|
||||
env:
|
||||
CMAKE_BUILD_PARALLEL_LEVEL: 3 # mac machines actually have 3 cores
|
||||
run: .ci/compile.sh ${{matrix.type}} --server
|
||||
|
||||
- name: Test
|
||||
if: matrix.do_tests == 1
|
||||
shell: bash
|
||||
working-directory: build
|
||||
run: cmake --build . --target test
|
||||
|
||||
- name: Package for ${{matrix.target}}
|
||||
id: package
|
||||
if: matrix.make_package
|
||||
shell: bash
|
||||
working-directory: build
|
||||
run: |
|
||||
cmake --build . --target package
|
||||
../.ci/name_build.sh "-macOS-${{matrix.target}}"
|
||||
|
||||
- name: Upload artifact
|
||||
if: matrix.make_package
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: macOS-${{matrix.target}}-xcode-${{matrix.xcode}}-dmg
|
||||
path: ${{steps.package.outputs.path}}
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload to release
|
||||
if: matrix.make_package && needs.configure.outputs.tag != null
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{github.token}}
|
||||
with:
|
||||
upload_url: ${{needs.configure.outputs.upload_url}}
|
||||
asset_path: ${{steps.package.outputs.path}}
|
||||
asset_name: ${{steps.package.outputs.name}}
|
||||
asset_content_type: application/octet-stream
|
||||
|
||||
build-windows:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
arch:
|
||||
- 64
|
||||
- 32
|
||||
include:
|
||||
- arch: 64
|
||||
triplet: x64
|
||||
cmake: x64
|
||||
append: _64
|
||||
|
||||
- arch: 32
|
||||
triplet: x86
|
||||
cmake: Win32
|
||||
|
||||
name: Windows ${{matrix.arch}}
|
||||
|
||||
needs: configure
|
||||
|
||||
runs-on: windows-latest
|
||||
|
||||
env:
|
||||
QT_VERSION: '5.15.2'
|
||||
QT_ARCH: msvc2019${{matrix.append}}
|
||||
CMAKE_GENERATOR: 'Visual Studio 16 2019'
|
||||
|
||||
steps:
|
||||
- name: Add msbuild to PATH
|
||||
id: add-msbuild
|
||||
uses: microsoft/setup-msbuild@v1.0.2
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Restore Qt ${{env.QT_VERSION}} ${{matrix.arch}}-bit from cache
|
||||
id: cache-qt
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
key: ${{runner.os}}-QtCache-${{env.QT_VERSION}}-${{matrix.arch}}
|
||||
path: ${{runner.workspace}}/Qt
|
||||
|
||||
- name: Install ${{matrix.arch}}-bit Qt
|
||||
uses: jurplel/install-qt-action@v2
|
||||
with:
|
||||
cached: ${{steps.cache-qt.outputs.cache-hit}}
|
||||
version: ${{env.QT_VERSION}}
|
||||
arch: win${{matrix.arch}}_${{env.QT_ARCH}}
|
||||
|
||||
- name: Restore or setup vcpkg
|
||||
uses: lukka/run-vcpkg@v6
|
||||
with:
|
||||
vcpkgArguments: '@${{github.workspace}}/vcpkg.txt'
|
||||
vcpkgDirectory: ${{github.workspace}}/vcpkg
|
||||
appendedCacheKey: ${{hashFiles('**/vcpkg.txt')}}
|
||||
vcpkgTriplet: ${{matrix.triplet}}-windows
|
||||
|
||||
- name: Configure Cockatrice ${{matrix.arch}}-bit
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p build
|
||||
cd build
|
||||
export QTDIR="${{runner.workspace}}/Qt/${{env.QT_VERSION}}/${{env.QT_ARCH}}"
|
||||
cmake .. -G "${{env.CMAKE_GENERATOR}}" -A "${{matrix.cmake}}" -DCMAKE_BUILD_TYPE="Release" -DWITH_SERVER=1 -DTEST=1
|
||||
|
||||
- name: Build Cockatrice ${{matrix.arch}}-bit
|
||||
id: package
|
||||
shell: bash
|
||||
working-directory: build
|
||||
run: |
|
||||
cmake --build . --target package --config Release
|
||||
../.ci/name_build.sh "-win${{matrix.arch}}"
|
||||
|
||||
- name: Run tests
|
||||
shell: bash
|
||||
working-directory: build
|
||||
run: ctest -T Test -C Release
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: Windows-${{matrix.arch}}bit-installer
|
||||
path: ./build/${{steps.package.outputs.name}}
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload to release
|
||||
if: needs.configure.outputs.tag != null
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{github.token}}
|
||||
with:
|
||||
upload_url: ${{needs.configure.outputs.upload_url}}
|
||||
asset_path: ./build/${{steps.package.outputs.name}}
|
||||
asset_name: ${{steps.package.outputs.name}}
|
||||
asset_content_type: application/octet-stream
|
||||
30
.github/workflows/desktop-lint.yml
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
name: Code Style (C++)
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- 'webclient/**'
|
||||
- '.github/workflows/web-*.yml'
|
||||
|
||||
jobs:
|
||||
clang-format:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 20 # should be enough to find merge base
|
||||
|
||||
- name: Install clang-format
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends clang-format
|
||||
|
||||
- name: Run clangify
|
||||
shell: bash
|
||||
run: ./.ci/lint_cpp.sh
|
||||
55
.github/workflows/web-build.yml
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
name: Build Web
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- '.github/workflows/web-*.yml'
|
||||
- 'webclient/**'
|
||||
- '!**.md'
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- '.github/workflows/web-*.yml'
|
||||
- 'webclient/**'
|
||||
- '!**.md'
|
||||
|
||||
jobs:
|
||||
build-web:
|
||||
name: React (Node ${{matrix.node_version}})
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: webclient
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
node_version:
|
||||
- 12
|
||||
- lts/*
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{matrix.node_version}}
|
||||
cache: 'npm'
|
||||
cache-dependency-path: 'webclient/package-lock.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm clean-install
|
||||
|
||||
- name: Build app
|
||||
run: npm run build
|
||||
|
||||
- name: Test app
|
||||
run: npm run test
|
||||
|
||||
34
.github/workflows/web-lint.yml
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
name: Code Style (TypeScript)
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- '.github/workflows/web-*.yml'
|
||||
- 'webclient/**'
|
||||
- '!**.md'
|
||||
|
||||
jobs:
|
||||
ESLint:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: webclient
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
cache: 'npm'
|
||||
cache-dependency-path: 'webclient/package-lock.json'
|
||||
|
||||
- name: Install ESLint
|
||||
run: npm clean-install --ignore-scripts
|
||||
|
||||
- name: Run ESLint
|
||||
run: npm run lint
|
||||
4
.gitignore
vendored
@@ -7,3 +7,7 @@ mysql.cnf
|
||||
.idea/
|
||||
*.aps
|
||||
cmake-build-debug/
|
||||
preferences
|
||||
compile_commands.json
|
||||
.vs/
|
||||
.vscode/
|
||||
|
||||
139
.gitlab-ci.yml
@@ -1,139 +0,0 @@
|
||||
---
|
||||
stages:
|
||||
- build
|
||||
|
||||
.artifacts: &artifacts
|
||||
artifacts:
|
||||
paths:
|
||||
- build/
|
||||
|
||||
.cache: &cache
|
||||
cache:
|
||||
key: "$CI_BUILD_NAME"
|
||||
paths:
|
||||
- cache/
|
||||
|
||||
.branches: &branches
|
||||
only:
|
||||
- master
|
||||
|
||||
.tags: &tags
|
||||
tags:
|
||||
- linux
|
||||
- docker
|
||||
|
||||
|
||||
#================================ DEBIAN-BASED ================================
|
||||
|
||||
.build_rc_package_deb: &build_rc_package_deb
|
||||
stage: build
|
||||
script:
|
||||
- mkdir -p build
|
||||
- cd build
|
||||
- cmake .. -DWITH_SERVER=1 -DCMAKE_BUILD_TYPE=Release -DCPACK_GENERATOR=DEB
|
||||
- make package -j2
|
||||
|
||||
.build_debug_package_deb: &build_debug_package_deb
|
||||
stage: build
|
||||
script:
|
||||
- mkdir -p build
|
||||
- cd build
|
||||
- cmake .. -DWITH_SERVER=1 -DCMAKE_BUILD_TYPE=Debug -DCPACK_GENERATOR=DEB
|
||||
- make package -j2
|
||||
|
||||
.deb-artifacts: &artifacts_deb
|
||||
artifacts:
|
||||
paths:
|
||||
- build/*.deb
|
||||
- build/CMakeFiles/*.log
|
||||
when: always
|
||||
|
||||
#----------------------------------- UBUNTU -----------------------------------
|
||||
|
||||
.requirements_16xx: &install_requirements_16xx
|
||||
before_script:
|
||||
- apt-get -o dir::cache::archives="cache" update -qq
|
||||
- apt-get -o dir::cache::archives="cache" install -y build-essential g++ cmake git
|
||||
- apt-get -o dir::cache::archives="cache" install -y libprotobuf-dev protobuf-compiler
|
||||
- apt-get -o dir::cache::archives="cache" install -y qt5-default qttools5-dev qttools5-dev-tools
|
||||
- apt-get -o dir::cache::archives="cache" install -y qtmultimedia5-dev libqt5multimedia5-plugins
|
||||
- apt-get -o dir::cache::archives="cache" install -y libqt5svg5-dev libqt5sql5-mysql
|
||||
- apt-get -o dir::cache::archives="cache" install -y libqt5websockets5-dev
|
||||
|
||||
.requirements_17xx: &install_requirements_17xx
|
||||
before_script:
|
||||
- apt-get -o dir::cache::archives="cache" update -qq
|
||||
- apt-get -o dir::cache::archives="cache" install -y build-essential g++ cmake git
|
||||
- apt-get -o dir::cache::archives="cache" install -y libprotobuf-dev protobuf-compiler
|
||||
- apt-get -o dir::cache::archives="cache" install -y qt5-default qttools5-dev qttools5-dev-tools
|
||||
- apt-get -o dir::cache::archives="cache" install -y qtmultimedia5-dev libqt5multimedia5-plugins
|
||||
- apt-get -o dir::cache::archives="cache" install -y libqt5svg5-dev libqt5sql5-mysql
|
||||
- apt-get -o dir::cache::archives="cache" install -y libqt5websockets5-dev
|
||||
|
||||
.build_1604: &1604
|
||||
image: ubuntu:16.04
|
||||
<<: *tags
|
||||
<<: *branches
|
||||
<<: *install_requirements_16xx
|
||||
<<: *artifacts_deb
|
||||
<<: *cache
|
||||
|
||||
.build_1710: &1710
|
||||
image: ubuntu:17.10
|
||||
<<: *tags
|
||||
<<: *branches
|
||||
<<: *install_requirements_17xx
|
||||
<<: *artifacts_deb
|
||||
<<: *cache
|
||||
|
||||
build_rc_1604:
|
||||
<<: *1604
|
||||
<<: *build_rc_package_deb
|
||||
when: always
|
||||
|
||||
build_debug_1604:
|
||||
<<: *1604
|
||||
<<: *build_debug_package_deb
|
||||
when: always
|
||||
|
||||
build_rc_1710:
|
||||
<<: *1710
|
||||
<<: *build_rc_package_deb
|
||||
when: always
|
||||
|
||||
build_debug_1710:
|
||||
<<: *1710
|
||||
<<: *build_debug_package_deb
|
||||
when: always
|
||||
allow_failure: true
|
||||
|
||||
#----------------------------------- DEBIAN -----------------------------------
|
||||
|
||||
.requirements_stretch: &install_requirements_stretch
|
||||
before_script:
|
||||
- apt-get -o dir::cache::archives="cache" update -qq
|
||||
- apt-get -o dir::cache::archives="cache" install -y build-essential g++ cmake git
|
||||
- apt-get -o dir::cache::archives="cache" install -y qt5-default qtbase5-dev-tools
|
||||
- apt-get -o dir::cache::archives="cache" install -y qttools5-dev-tools qtmultimedia5-dev
|
||||
- apt-get -o dir::cache::archives="cache" install -y libqt5svg5-dev libqt5websockets5-dev
|
||||
- apt-get -o dir::cache::archives="cache" install -y libprotobuf-dev protobuf-compiler
|
||||
|
||||
.build_stretch: &stretch
|
||||
image: debian:stretch
|
||||
<<: *tags
|
||||
<<: *branches
|
||||
<<: *install_requirements_stretch
|
||||
<<: *artifacts_deb
|
||||
<<: *cache
|
||||
|
||||
build_rc_stretch:
|
||||
<<: *stretch
|
||||
<<: *build_rc_package_deb
|
||||
when: always
|
||||
|
||||
build_debug_stretch:
|
||||
<<: *stretch
|
||||
<<: *build_debug_package_deb
|
||||
when: always
|
||||
allow_failure: true
|
||||
|
||||
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "vcpkg"]
|
||||
path = vcpkg
|
||||
url = https://github.com/microsoft/vcpkg.git
|
||||
98
.travis.yml
@@ -1,98 +0,0 @@
|
||||
language: cpp
|
||||
|
||||
cache: ccache
|
||||
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
include:
|
||||
- os: linux
|
||||
dist: trusty
|
||||
group: stable
|
||||
env: BUILDTYPE=Debug
|
||||
- os: linux
|
||||
dist: trusty
|
||||
group: stable
|
||||
env: BUILDTYPE=Release
|
||||
if: (branch = master AND NOT type = pull_request) OR tag IS present
|
||||
- os: osx
|
||||
osx_image: xcode8
|
||||
env: BUILDTYPE=Debug
|
||||
- os: osx
|
||||
osx_image: xcode8
|
||||
env: BUILDTYPE=Release
|
||||
if: (branch = master AND NOT type = pull_request) OR tag IS present
|
||||
|
||||
#install dependencies for container-based "linux" builds
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- sourceline: 'deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-5.0 main'
|
||||
key_url: 'http://llvm.org/apt/llvm-snapshot.gpg.key'
|
||||
packages:
|
||||
- bc
|
||||
- clang-format-5.0
|
||||
- cmake
|
||||
- libprotobuf-dev
|
||||
- protobuf-compiler
|
||||
- qt5-default
|
||||
- qttools5-dev
|
||||
- qttools5-dev-tools
|
||||
- qtmultimedia5-dev
|
||||
- libqt5multimedia5-plugins
|
||||
- libqt5svg5-dev
|
||||
- libqt5sql5-mysql
|
||||
|
||||
before_install: bash ./.ci/travis-dependencies.sh
|
||||
|
||||
script: bash ./.ci/travis-compile.sh
|
||||
|
||||
|
||||
# Builds for pull requests skip the deployment step altogether
|
||||
deploy:
|
||||
# Deploy configuration for "beta" releases
|
||||
- provider: releases
|
||||
api_key:
|
||||
secure: mLMF41q7xgOR1sjczsilEy7HQis2PkZCzhfOGbn/8FoOQnmmPOZjrsdhn06ZSl3SFsbfCLuClDYXAbFscQmdgjcGN5AmHV+JYfW650QEuQa/f4/lQFsVRtEqUA1O3FQ0OuRxdpCfJubZBdFVH8SbZ93GLC5zXJbkWQNq+xCX1fU=
|
||||
skip_cleanup: true
|
||||
name: "Cockatrice $TRAVIS_TAG"
|
||||
body: "Beta release of Cockatrice"
|
||||
file_glob: true
|
||||
file: "build/Cockatrice-*"
|
||||
overwrite: true
|
||||
draft: false
|
||||
prerelease: true
|
||||
on:
|
||||
tags: true
|
||||
repo: Cockatrice/Cockatrice
|
||||
condition: $BUILDTYPE = Release && $TRAVIS_TAG =~ ([0-9]|[1-9][0-9])(\.([0-9]|[1-9][0-9])){2}-beta(\.([2-9]|[1-9][0-9]))?$ # regex to match semver naming convention for beta pre-releases
|
||||
|
||||
# Deploy configuration for "stable" releases
|
||||
- provider: releases
|
||||
api_key:
|
||||
secure: mLMF41q7xgOR1sjczsilEy7HQis2PkZCzhfOGbn/8FoOQnmmPOZjrsdhn06ZSl3SFsbfCLuClDYXAbFscQmdgjcGN5AmHV+JYfW650QEuQa/f4/lQFsVRtEqUA1O3FQ0OuRxdpCfJubZBdFVH8SbZ93GLC5zXJbkWQNq+xCX1fU=
|
||||
skip_cleanup: true
|
||||
file_glob: true
|
||||
file: "build/Cockatrice-*"
|
||||
overwrite: true
|
||||
draft: false
|
||||
prerelease: false
|
||||
on:
|
||||
tags: true
|
||||
repo: Cockatrice/Cockatrice
|
||||
condition: $BUILDTYPE = Release && $TRAVIS_TAG =~ ([0-9]|[1-9][0-9])(\.([0-9]|[1-9][0-9])){2}$ # regex to match semver naming convention for stable full releases
|
||||
|
||||
|
||||
notifications:
|
||||
webhooks:
|
||||
urls:
|
||||
- https://webhooks.gitter.im/e/d94969c3b01b22cbdcb7
|
||||
on_success: change
|
||||
on_failure: change
|
||||
on_start: never
|
||||
on_cancel: change
|
||||
on_error: change
|
||||
|
||||
|
||||
# official validator for ".travis.yml" config file: https://yaml.travis-ci.org
|
||||
# travis config documentation: https://docs.travis-ci.com/user/customizing-the-build
|
||||
@@ -10,3 +10,4 @@ source_lang = en
|
||||
file_filter = oracle/translations/oracle_<lang>.ts
|
||||
source_file = oracle/translations/oracle_en.ts
|
||||
source_lang = en
|
||||
|
||||
|
||||
146
CMakeLists.txt
@@ -6,15 +6,7 @@
|
||||
# like the installation path, compilation flags etc..
|
||||
|
||||
# Cmake 3.1 is required to enable C++11 support correctly
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
|
||||
if(POLICY CMP0064)
|
||||
cmake_policy(SET CMP0064 NEW)
|
||||
endif()
|
||||
|
||||
if(POLICY CMP0071)
|
||||
cmake_policy(SET CMP0071 NEW)
|
||||
endif()
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
# Default to "Release" build type
|
||||
# User-provided value for CMAKE_BUILD_TYPE must be checked before the PROJECT() call
|
||||
@@ -25,16 +17,36 @@ ELSE()
|
||||
ENDIF()
|
||||
|
||||
# Early detect ccache
|
||||
find_program(CCACHE_PROGRAM ccache)
|
||||
if(CCACHE_PROGRAM)
|
||||
# Support Unix Makefiles and Ninja
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}")
|
||||
MESSAGE(STATUS "Found CCache ${CCACHE_PROGRAM}")
|
||||
OPTION(USE_CCACHE "Cache the build results with ccache" ON)
|
||||
if(USE_CCACHE)
|
||||
find_program(CCACHE_PROGRAM ccache)
|
||||
if(CCACHE_PROGRAM)
|
||||
# Support Unix Makefiles and Ninja
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}")
|
||||
MESSAGE(STATUS "Found CCache ${CCACHE_PROGRAM}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
# Use vcpkg toolchain on Windows
|
||||
set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_SOURCE_DIR}/vcpkg/scripts/buildsystems/vcpkg.cmake
|
||||
CACHE STRING "Vcpkg toolchain file")
|
||||
# Qt path set by user or env var
|
||||
if (QTDIR OR DEFINED ENV{QTDIR} OR DEFINED ENV{QTDIR32} OR DEFINED ENV{QTDIR64})
|
||||
else()
|
||||
set(QTDIR "" CACHE PATH "Path to Qt (e.g. C:/Qt/5.7/msvc2015_64)")
|
||||
message(WARNING "QTDIR variable is missing. Please set this variable to specify path to Qt (e.g. C:/Qt/5.7/msvc2015_64)")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# A project name is needed for CPack
|
||||
# Version can be overriden by git tags, see cmake/getversion.cmake
|
||||
PROJECT("Cockatrice" VERSION 2.5.0)
|
||||
PROJECT("Cockatrice" VERSION 2.8.1)
|
||||
|
||||
# Set release name if not provided via env/cmake var
|
||||
if(NOT DEFINED GIT_TAG_RELEASENAME)
|
||||
set(GIT_TAG_RELEASENAME "Prismatic Bridge")
|
||||
endif()
|
||||
|
||||
# Use c++11 for all targets
|
||||
set(CMAKE_CXX_STANDARD 11 CACHE STRING "C++ ISO Standard")
|
||||
@@ -46,7 +58,6 @@ set(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS true)
|
||||
# Search path for cmake modules
|
||||
SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
|
||||
|
||||
# Retrieve git version hash
|
||||
include(getversion)
|
||||
|
||||
# Create a header and a cpp file containing the version hash
|
||||
@@ -89,14 +100,16 @@ if(UNIX)
|
||||
endif()
|
||||
endif()
|
||||
elseif(WIN32)
|
||||
set(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/release)
|
||||
set(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/rundir/${CMAKE_BUILD_TYPE})
|
||||
endif()
|
||||
|
||||
# Treat warnings as errors (Debug builds only)
|
||||
option(WARNING_AS_ERROR "Treat warnings as errors in debug builds" ON)
|
||||
|
||||
# Define proper compilation flags
|
||||
IF(MSVC)
|
||||
# Visual Studio:
|
||||
# Maximum optimization
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "/Ox /MD")
|
||||
# Visual Studio: Maximum optimization, disable warning C4251
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "/Ox /MD /wd4251 ")
|
||||
# Generate complete debugging information
|
||||
#set(CMAKE_CXX_FLAGS_DEBUG "/Zi")
|
||||
ELSEIF (CMAKE_COMPILER_IS_GNUCXX)
|
||||
@@ -104,7 +117,11 @@ ELSEIF (CMAKE_COMPILER_IS_GNUCXX)
|
||||
include(CheckCXXCompilerFlag)
|
||||
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-s -O2")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "-ggdb -O0 -Wall -Wextra -Werror")
|
||||
if(WARNING_AS_ERROR)
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "-ggdb -O0 -Wall -Wextra -Werror")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "-ggdb -O0 -Wall -Wextra")
|
||||
endif()
|
||||
|
||||
set(ADDITIONAL_DEBUG_FLAGS -Wcast-align -Wmissing-declarations -Wno-long-long -Wno-error=extra -Wno-error=delete-non-virtual-dtor -Wno-error=sign-compare -Wno-error=missing-declarations)
|
||||
|
||||
@@ -117,7 +134,11 @@ ELSEIF (CMAKE_COMPILER_IS_GNUCXX)
|
||||
ELSE()
|
||||
# other: osx/llvm, bsd/llvm
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-O2")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "-g -O0")
|
||||
if(WARNING_AS_ERROR)
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "-g -O0 -Wall -Wextra -Werror")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "-g -O0 -Wall -Wextra")
|
||||
endif()
|
||||
ENDIF()
|
||||
|
||||
# GNU systems need to define the Mersenne exponent for the RNG to compile w/o warning
|
||||
@@ -125,51 +146,70 @@ IF(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
|
||||
ADD_DEFINITIONS("-DSFMT_MEXP=19937")
|
||||
ENDIF()
|
||||
|
||||
FIND_PACKAGE(Threads REQUIRED)
|
||||
|
||||
# Find Qt5
|
||||
OPTION(UPDATE_TRANSLATIONS "Update translations on compile" OFF)
|
||||
MESSAGE(STATUS "UPDATE TRANSLATIONS: ${UPDATE_TRANSLATIONS}")
|
||||
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
set(_lib_suffix 64)
|
||||
else()
|
||||
set(_lib_suffix 32)
|
||||
endif()
|
||||
|
||||
IF(WIN32)
|
||||
FIND_PACKAGE(Qt5Widgets 5.4.0 REQUIRED) # For QSysInfo::buildAbi()
|
||||
ELSE()
|
||||
FIND_PACKAGE(Qt5Widgets 5.0.3 REQUIRED)
|
||||
ENDIF()
|
||||
if(DEFINED QTDIR${_lib_suffix})
|
||||
list(APPEND CMAKE_PREFIX_PATH "${QTDIR${_lib_suffix}}")
|
||||
elseif(DEFINED QTDIR)
|
||||
list(APPEND CMAKE_PREFIX_PATH "${QTDIR}")
|
||||
elseif(DEFINED ENV{QTDIR${_lib_suffix}})
|
||||
list(APPEND CMAKE_PREFIX_PATH "$ENV{QTDIR${_lib_suffix}}")
|
||||
elseif(DEFINED ENV{QTDIR})
|
||||
list(APPEND CMAKE_PREFIX_PATH "$ENV{QTDIR}")
|
||||
endif()
|
||||
|
||||
IF(Qt5Widgets_FOUND)
|
||||
MESSAGE(STATUS "Found Qt ${Qt5Widgets_VERSION_STRING}")
|
||||
FIND_PACKAGE(Qt5Core 5.5.0 REQUIRED)
|
||||
|
||||
IF(Qt5Core_FOUND)
|
||||
MESSAGE(STATUS "Found Qt ${Qt5Core_VERSION_STRING}")
|
||||
|
||||
# FIX: Qt was built with -reduce-relocations
|
||||
if (Qt5_POSITION_INDEPENDENT_CODE)
|
||||
SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
endif()
|
||||
|
||||
FIND_PACKAGE(Qt5LinguistTools)
|
||||
IF(UPDATE_TRANSLATIONS)
|
||||
IF(NOT Qt5_LUPDATE_EXECUTABLE)
|
||||
MESSAGE(WARNING "Qt's lupdate not found.")
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
|
||||
IF(NOT Qt5_LRELEASE_EXECUTABLE)
|
||||
MESSAGE(WARNING "Qt's lrelease not found.")
|
||||
ENDIF()
|
||||
# guess plugins and libraries directory
|
||||
set(QT_PLUGINS_DIR "${Qt5Core_DIR}/../../../plugins")
|
||||
get_target_property(QT_LIBRARY_DIR Qt5::Core LOCATION)
|
||||
get_filename_component(QT_LIBRARY_DIR ${QT_LIBRARY_DIR} PATH)
|
||||
|
||||
ELSE()
|
||||
MESSAGE(FATAL_ERROR "No Qt5 found!")
|
||||
ENDIF()
|
||||
|
||||
# Check for translation updates
|
||||
OPTION(UPDATE_TRANSLATIONS "Update translations on compile" OFF)
|
||||
MESSAGE(STATUS "UPDATE TRANSLATIONS: ${UPDATE_TRANSLATIONS}")
|
||||
|
||||
set(CMAKE_AUTOMOC TRUE)
|
||||
|
||||
# Find other needed libraries
|
||||
FIND_PACKAGE(Protobuf REQUIRED)
|
||||
IF(NOT EXISTS "${Protobuf_PROTOC_EXECUTABLE}")
|
||||
MESSAGE(FATAL_ERROR "No protoc command found!")
|
||||
ELSE()
|
||||
MESSAGE(STATUS "Protoc version ${Protobuf_VERSION} found!")
|
||||
ENDIF()
|
||||
|
||||
#Find OpenSSL
|
||||
IF(WIN32)
|
||||
FIND_PACKAGE(Win32SslRuntime)
|
||||
ENDIF()
|
||||
|
||||
#Find VCredist
|
||||
IF(MSVC)
|
||||
FIND_PACKAGE(VCredistRuntime)
|
||||
ENDIF()
|
||||
|
||||
# Package builder
|
||||
set(CPACK_PACKAGE_CONTACT "Gavin Bisesi <Daenyth+github@gmail.com>")
|
||||
set(CPACK_PACKAGE_CONTACT "Zach Halpern <zahalpern+github@gmail.com>")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY ${PROJECT_NAME})
|
||||
set(CPACK_PACKAGE_VENDOR "Cockatrice Development Team")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_FILE "${PROJECT_SOURCE_DIR}/README.md")
|
||||
@@ -204,6 +244,11 @@ if(UNIX)
|
||||
endif()
|
||||
elseif(WIN32)
|
||||
set(CPACK_GENERATOR NSIS ${CPACK_GENERATOR})
|
||||
if("${CMAKE_GENERATOR_PLATFORM}" MATCHES "(x64)")
|
||||
set(TRICE_IS_64_BIT 1)
|
||||
else()
|
||||
set(TRICE_IS_64_BIT 0)
|
||||
endif()
|
||||
|
||||
# Configure file with custom definitions for NSIS.
|
||||
configure_file(
|
||||
@@ -212,13 +257,9 @@ elseif(WIN32)
|
||||
)
|
||||
|
||||
# include vcredist into the package; NSIS will take care of running it
|
||||
IF(MSVC)
|
||||
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
INSTALL(FILES "C:/Program Files (x86)/Microsoft Visual Studio/2017/Community/VC/redist/MSVC/14.13.26020/vc_redist.x64.exe" DESTINATION ./)
|
||||
else()
|
||||
INSTALL(FILES "C:/Program Files (x86)/Microsoft Visual Studio/2017/Community/VC/redist/MSVC/14.13.26020/vc_redist.x86.exe" DESTINATION ./)
|
||||
endif()
|
||||
ENDIF()
|
||||
if(VCREDISTRUNTIME_FOUND)
|
||||
INSTALL(FILES "${VCREDISTRUNTIME_FILE}" DESTINATION ./)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include(CPack)
|
||||
@@ -245,6 +286,13 @@ if(WITH_ORACLE)
|
||||
SET(CPACK_INSTALL_CMAKE_PROJECTS "Oracle;Oracle;ALL;/" ${CPACK_INSTALL_CMAKE_PROJECTS})
|
||||
endif()
|
||||
|
||||
# Compile dbconverter (default on)
|
||||
option(WITH_DBCONVERTER "build dbconverter" ON)
|
||||
if(WITH_DBCONVERTER)
|
||||
add_subdirectory(dbconverter)
|
||||
SET(CPACK_INSTALL_CMAKE_PROJECTS "Dbconverter;Dbconverter;ALL;/" ${CPACK_INSTALL_CMAKE_PROJECTS})
|
||||
endif()
|
||||
|
||||
# Compile tests (default off)
|
||||
option(TEST "build tests" OFF)
|
||||
if(TEST)
|
||||
|
||||
31
Dockerfile
@@ -1,33 +1,25 @@
|
||||
FROM ubuntu:trusty
|
||||
MAINTAINER Gavin Bisesi <Daenyth@gmail.com>
|
||||
FROM ubuntu:bionic
|
||||
MAINTAINER Zach Halpern <zahalpern+github@gmail.com>
|
||||
|
||||
RUN apt-get update && apt-get install -y software-properties-common
|
||||
RUN apt-add-repository ppa:ubuntu-sdk-team/ppa
|
||||
RUN add-apt-repository -y ppa:smspillaz/cmake-master
|
||||
RUN apt-get update && apt-get install -y\
|
||||
build-essential g++\
|
||||
build-essential\
|
||||
cmake\
|
||||
git\
|
||||
libprotobuf-dev\
|
||||
libqt5sql5-mysql\
|
||||
libmysqlclient-dev\
|
||||
libqt5websockets5-dev\
|
||||
protobuf-compiler\
|
||||
qt5-default\
|
||||
qtbase5-dev\
|
||||
qttools5-dev-tools\
|
||||
qttools5-dev\
|
||||
libqt5sql5-mysql
|
||||
qttools5-dev
|
||||
|
||||
ENV dir /home/servatrice/code
|
||||
WORKDIR $dir
|
||||
RUN mkdir oracle
|
||||
COPY LICENSE LICENSE
|
||||
COPY CMakeLists.txt CMakeLists.txt
|
||||
COPY cmake/ cmake/
|
||||
COPY common/ common/
|
||||
COPY servatrice/ servatrice/
|
||||
COPY README.md README.md
|
||||
COPY . /home/servatrice/code/
|
||||
WORKDIR /home/servatrice/code
|
||||
|
||||
WORKDIR build
|
||||
RUN cmake .. -DWITH_SERVER=1 -DWITH_CLIENT=0 -DWITH_ORACLE=0 &&\
|
||||
RUN cmake .. -DWITH_SERVER=1 -DWITH_CLIENT=0 -DWITH_ORACLE=0 -DWITH_DBCONVERTER=0 &&\
|
||||
make &&\
|
||||
make install
|
||||
|
||||
@@ -35,4 +27,5 @@ WORKDIR /home/servatrice
|
||||
|
||||
EXPOSE 4747
|
||||
|
||||
ENTRYPOINT [ "servatrice" ]
|
||||
ENTRYPOINT [ "servatrice", "--log-to-console" ]
|
||||
|
||||
|
||||
81
README.md
@@ -2,7 +2,16 @@
|
||||
|
||||
---
|
||||
|
||||
**Table of Contents** [Cockatrice](#cockatrice) | [Downloads](#downloads-) | [Get Involved](#get-involved-) | [Community](#community-resources) | [Translation](#translation-status-) | [Building](#building--) | [Running](#running) | [License](#license-)
|
||||
<p align='center'>
|
||||
<a href="#cockatrice"><b>Cockatrice</b></a> <b>|</b>
|
||||
<a href="#download-">Download</a> <b>|</b>
|
||||
<a href="#get-involved--">Get Involved</a> <b>|</b>
|
||||
<a href="#community-resources">Community</a> <b>|</b>
|
||||
<a href="#translations-">Translations</a> <b>|</b>
|
||||
<a href="#build--">Build</a> <b>|</b>
|
||||
<a href="#run">Run</a> <b>|</b>
|
||||
<a href="#license-">License</a>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
@@ -18,23 +27,23 @@
|
||||
Cockatrice is an open-source, multiplatform program for playing tabletop card games over a network. The program's server design prevents users from manipulating the game for unfair advantage. The client also provides a single-player mode, which allows users to brew while offline. This project uses C++ and the Qt5 libraries.<br>
|
||||
|
||||
|
||||
# Downloads [](https://tooomm.github.io/github-release-stats/?username=Cockatrice&repository=Cockatrice)
|
||||
# Download [](https://tooomm.github.io/github-release-stats/?username=Cockatrice&repository=Cockatrice)
|
||||
|
||||
Downloads are available for full releases and the current beta version in development.<br>
|
||||
Full releases are checkpoints featuring major feature or UI enhancements - we recommend to use those. There is no strict schedule for new full releases.
|
||||
Downloads are available for full releases and the current beta version in development. There is no strict release schedule for either of them.
|
||||
|
||||
The beta release contains the most recently added features and bugfixes, but can be unstable. They are released as we feel need.
|
||||
- Latest `stable` release: [](https://github.com/cockatrice/cockatrice/releases/latest) [](https://tooomm.github.io/github-release-stats/?username=Cockatrice&repository=Cockatrice)<br>
|
||||
- Stable versions are checkpoints featuring major feature and UI enhancements.
|
||||
- **Recommended for most users!**
|
||||
|
||||
- Latest full release (**recommended**): [](https://github.com/cockatrice/cockatrice/releases/latest) [](https://tooomm.github.io/github-release-stats/?username=Cockatrice&repository=Cockatrice)<br>
|
||||
|
||||
- Latest beta release: [](https://github.com/cockatrice/cockatrice/releases)
|
||||
- Beta versions may be unstable and contain bugs.
|
||||
- Latest `beta` release: [](https://github.com/cockatrice/cockatrice/releases) [](https://tooomm.github.io/github-release-stats/?username=Cockatrice&repository=Cockatrice)
|
||||
- Beta versions include the most recently added features and bugfixes, but can be unstable.
|
||||
- To be a Cockatrice Beta Tester, use this version. Find more information [here](https://github.com/Cockatrice/Cockatrice/wiki/Release-Channels)!
|
||||
|
||||
|
||||
# Get Involved [](https://discord.gg/3Z9yzmA) [](https://gitter.im/Cockatrice/Cockatrice)
|
||||
|
||||
# Get Involved [](https://gitter.im/Cockatrice/Cockatrice)
|
||||
|
||||
[Chat](https://gitter.im/Cockatrice/Cockatrice) with the Cockatrice developers on Gitter. Come here to talk about the application, features, or just to hang out. For support regarding specific servers, please contact that server's admin or forum for support rather than asking here.<br>
|
||||
Join our [Discord community](https://discord.gg/3Z9yzmA) to connect with the project or fellow users of the app. The Cockatrice developers are also available on [Gitter](https://gitter.im/Cockatrice/Cockatrice). Come here to talk about the application, features, or just to hang out.<br>
|
||||
For support regarding specific servers, please contact that server's admin or forum for support rather than asking here.<br>
|
||||
|
||||
To contribute code to the project, please review [the guidelines](https://github.com/Cockatrice/Cockatrice/blob/master/.github/CONTRIBUTING.md).
|
||||
We maintain two tags for contributors to find issues to work on:
|
||||
@@ -54,10 +63,11 @@ Cockatrice uses the [Google Developer Documentation Style Guide](https://develop
|
||||
|
||||
- [Cockatrice Official Site](https://cockatrice.github.io)
|
||||
- [Cockatrice Official Wiki](https://github.com/Cockatrice/Cockatrice/wiki)
|
||||
- [Cockatrice Official Discord](https://discord.gg/3Z9yzmA)
|
||||
- [reddit r/Cockatrice](https://reddit.com/r/cockatrice)
|
||||
|
||||
|
||||
# Translation Status [](https://www.transifex.com/projects/p/cockatrice/)
|
||||
# Translations [](https://www.transifex.com/projects/p/cockatrice/)
|
||||
|
||||
Cockatrice uses Transifex for translations. You can help us bring Cockatrice and Oracle to your language or just edit single wordings right from within your browser by visiting our [Transifex project page](https://www.transifex.com/projects/p/cockatrice/).<br>
|
||||
|
||||
@@ -68,16 +78,17 @@ Cockatrice uses Transifex for translations. You can help us bring Cockatrice and
|
||||
Check out our [Translator FAQ](https://github.com/Cockatrice/Cockatrice/wiki/Translation-FAQ) for more information about contributing!<br>
|
||||
|
||||
|
||||
# Building [](https://travis-ci.org/Cockatrice/Cockatrice) [](https://ci.appveyor.com/project/Daenyth/cockatrice/branch/master)
|
||||
# Build [](https://github.com/Cockatrice/Cockatrice/actions/workflows/desktop-build.yml?query=branch%3Amaster+event%3Apush) [](https://github.com/Cockatrice/Cockatrice/actions/workflows/web-build.yml?query=branch%3Amaster+event%3Apush)
|
||||
|
||||
**Detailed compiling instructions are on the Cockatrice wiki under [Compiling Cockatrice](https://github.com/Cockatrice/Cockatrice/wiki/Compiling-Cockatrice)**
|
||||
**Detailed compiling instructions can be found on the Cockatrice wiki under [Compiling Cockatrice](https://github.com/Cockatrice/Cockatrice/wiki/Compiling-Cockatrice)**
|
||||
|
||||
Dependencies:
|
||||
- [Qt](https://www.qt.io/developers/)
|
||||
- [protobuf](https://github.com/google/protobuf)
|
||||
Dependencies: *(for minimum requirements search our [CMake file](https://github.com/Cockatrice/Cockatrice/blob/master/CMakeLists.txt))*
|
||||
- [Qt](https://www.qt.io/developers/)
|
||||
- [protobuf](https://github.com/protocolbuffers/protobuf)
|
||||
- [CMake](https://www.cmake.org/)
|
||||
|
||||
Oracle can optionally use zlib to load zipped files:
|
||||
Oracle can optionally use zlib and xz to load compressed files:
|
||||
- [xz](https://tukaani.org/xz/)
|
||||
- [zlib](https://www.zlib.net/)
|
||||
|
||||
To compile:
|
||||
@@ -86,7 +97,7 @@ To compile:
|
||||
cd build
|
||||
cmake ..
|
||||
make
|
||||
|
||||
|
||||
You can then run
|
||||
|
||||
make install
|
||||
@@ -103,18 +114,18 @@ The following flags can be passed to `cmake`:
|
||||
- `-DWITH_CLIENT=0` Whether to build the client (default 1 = yes).
|
||||
- `-DWITH_ORACLE=0` Whether to build oracle (default 1 = yes).
|
||||
- `-DCMAKE_BUILD_TYPE=Debug` Compile in debug mode. Enables extra logging output, debug symbols, and much more verbose compiler warnings (default `Release`).
|
||||
- `-DWARNING_AS_ERROR=0` Whether to treat compilation warnings as errors in debug mode (default 1 = yes).
|
||||
- `-DUPDATE_TRANSLATIONS=1` Configure `make` to update the translation .ts files for new strings in the source code. Note: Running `make clean` will remove the .ts files (default 0 = no).
|
||||
- `-DTEST=1` Enable regression tests (default 0 = no). Note: needs googletest, will be downloaded on the fly if unavailable. To run tests: ```make test```.
|
||||
|
||||
|
||||
# Running
|
||||
# Run
|
||||
|
||||
`Cockatrice` is the game client
|
||||
`Oracle` fetches card data
|
||||
`Cockatrice` is the game client<br>
|
||||
`Oracle` fetches card data<br>
|
||||
`Servatrice` is the server<br>
|
||||
|
||||
|
||||
#### Servatrice Docker container
|
||||
**Servatrice Docker container**
|
||||
|
||||
You can run an instance of Servatrice (the Cockatrice server) using [Docker](https://www.docker.com/what-docker) and the Cockatrice Dockerfile.<br>
|
||||
|
||||
@@ -129,6 +140,26 @@ to permit connections to the server.
|
||||
|
||||
Find more information on how to use Servatrice with Docker in our [wiki](https://github.com/Cockatrice/Cockatrice/wiki/Setting-up-Servatrice#using-docker).
|
||||
|
||||
**Docker compose**
|
||||
|
||||
There is also a docker-compose file available which will configure and run both a MySQL server and Servatrice. The docker-compose setup scripts can be found in the `servatrice/docker` folder and vary only slightly from the default sql and server .ini files. The setup scripts can either be modified in place, or docker-compose can mount alternative files into the images, as you prefer.
|
||||
|
||||
To run Servatrice via docker-compose, first install docker-compose following the [install instructions](https://docs.docker.com/compose/install/). Once installed, run the following from the root of the repository:
|
||||
```bash
|
||||
docker-compose build # Build the Servatrice image using the same Dockerfile as above.
|
||||
docker-compose up # Setup and run both the MySQL server and Servatrice.
|
||||
```
|
||||
|
||||
>Note: Similar to the above Docker setup, this will expose TCP ports 4747 and 4748.
|
||||
|
||||
>Note: The first time running the docker-compose setup, the MySQL server will take a little time to run the initial setup scripts. Due to this, the Servatrice instance may fail the first few attempts to connect to the database. Servatrice is set to `restart: always` in the docker-compose.yml, which will allow it to continue attempting to start up. Once the MySQL scripts have completed, Servatrice should then connect automatically on the next attempt.
|
||||
|
||||
**Docker compose in Windows**
|
||||
A out of box working docker-compose file has been added to help setup in Windows.
|
||||
|
||||
Docker in Windows requires additional steps in form of using Docker Desktop to allow resource sharing from the drive the volumes are mapped from, as well as potential workarounds needed to get file sharing working in Windows. This [StackOverflow discussion sheds some light on it](https://stackoverflow.com/questions/42203488/settings-to-windows-firewall-to-allow-docker-for-windows-to-share-drive)
|
||||
|
||||
|
||||
# License [](https://github.com/Cockatrice/Cockatrice/blob/master/LICENSE)
|
||||
|
||||
Cockatrice is free software, licensed under the [GPLv2](https://github.com/Cockatrice/Cockatrice/blob/master/LICENSE).
|
||||
|
||||
218
clangify.sh
@@ -1,22 +1,210 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This script will run clang-format on all modified, non-3rd-party C++/Header files.
|
||||
# Never, ever, should this recieve a path with a newline in it. Don't bother proofing it for that.
|
||||
|
||||
set -e
|
||||
|
||||
if hash clang-format 2>/dev/null && hash git 2>/dev/null; then
|
||||
files_to_clean=($(git diff --name-only $(git merge-base origin/master HEAD)))
|
||||
# go to the project root directory, this file should be located in the project root directory
|
||||
cd "${BASH_SOURCE%/*}/" || exit 2 # could not find path, this could happen with special links etc.
|
||||
|
||||
printf "%s\n" ${files_to_clean[@]} | \
|
||||
xargs -I{} find '{}' \( -name "*.cpp" -o -name "*.h" \) \
|
||||
-not -path "./cockatrice/src/qt-json/*" \
|
||||
-not -path "./servatrice/src/smtp/*" \
|
||||
-not -path "./common/sfmt/*" \
|
||||
-not -path "./oracle/src/zip/*" \
|
||||
-not -path "./build*/*" \
|
||||
-exec clang-format -style=file -i {} \;
|
||||
echo "Successfully formatted following files:"
|
||||
printf "%s\n" ${files_to_clean[@]}
|
||||
else
|
||||
echo "Please install clang-format and git to use this program"
|
||||
# defaults
|
||||
include=("common" \
|
||||
"cockatrice/src" \
|
||||
"oracle/src" \
|
||||
"servatrice/src" \
|
||||
"tests")
|
||||
exclude=("servatrice/src/smtp" \
|
||||
"common/sfmt" \
|
||||
"common/lib" \
|
||||
"oracle/src/zip" \
|
||||
"oracle/src/lzma" \
|
||||
"oracle/src/qt-json")
|
||||
exts=("cpp" "h" "proto")
|
||||
cf_cmd="clang-format"
|
||||
branch="origin/master"
|
||||
|
||||
# parse options
|
||||
while [[ $@ ]]; do
|
||||
case "$1" in
|
||||
'-b'|'--branch')
|
||||
branch=$2
|
||||
set_branch=1
|
||||
shift 2
|
||||
;;
|
||||
'-c'|'--color-diff')
|
||||
color=" --color=always"
|
||||
mode=diff
|
||||
shift
|
||||
;;
|
||||
'-d'|'--diff')
|
||||
mode=diff
|
||||
shift
|
||||
;;
|
||||
'-h'|'--help')
|
||||
cat <<EOM
|
||||
A bash script to automatically format your code using clang-format.
|
||||
|
||||
If no options are given, all dirty source files are edited in place.
|
||||
If <dir>s are given, all source files in those directories of the project root
|
||||
path are formatted. To only format changed files in these directories use the
|
||||
--branch option in combination. <dir> has to be a path relative to the project
|
||||
root path or a full path inside $PWD.
|
||||
. can not be specified as a dir, if you really want to format everything use */.
|
||||
|
||||
USAGE: $0 [option] [--branch <git branch or object>] [<dir> ...]
|
||||
|
||||
DEFAULTS:
|
||||
Current includes are:
|
||||
${include[@]/%/
|
||||
}
|
||||
Default excludes are:
|
||||
${exclude[@]/%/
|
||||
}
|
||||
OPTIONS:
|
||||
-b, --branch <branch>
|
||||
Compare to this git branch and format only files that differ.
|
||||
If unspecified it defaults to origin/master.
|
||||
To not compare to a branch this has to be explicitly set to "".
|
||||
When not comparing to a branch, git will not be used at all and every
|
||||
source file in the entire project will be parsed.
|
||||
|
||||
-c, --color-diff
|
||||
Display a colored diff. Implies --diff.
|
||||
Only available on systems which support 'diff --color'.
|
||||
|
||||
-d, --diff
|
||||
Display a diff. Implies --test.
|
||||
|
||||
-h, --help
|
||||
Display this message and exit.
|
||||
|
||||
-n, --names
|
||||
Display a list of filenames that require formatting. Implies --test.
|
||||
|
||||
-t, --test
|
||||
Do not edit files in place. Set exit code to 1 if changes are required.
|
||||
|
||||
--cf-version
|
||||
Print the version of clang-format being used before continuing.
|
||||
|
||||
EXIT CODES:
|
||||
0 on a successful format or if no files require formatting.
|
||||
1 if a file requires formatting.
|
||||
2 if given incorrect arguments.
|
||||
3 if clang-format could not be found.
|
||||
|
||||
EXAMPLES:
|
||||
$0 --test \$PWD || echo "code requires formatting"
|
||||
Tests if the source files in the current directory are correctly
|
||||
formatted and prints an error message if formatting is required.
|
||||
|
||||
$0 --branch $USER/patch-2 ${include[0]}
|
||||
Formats all changed files compared to the git branch "$USER/patch-2"
|
||||
in the directory ${include[0]}.
|
||||
EOM
|
||||
exit 0
|
||||
;;
|
||||
'-n'|'--names')
|
||||
mode=name
|
||||
shift
|
||||
;;
|
||||
'-t'|'--test')
|
||||
mode=code
|
||||
shift
|
||||
;;
|
||||
'--cf-version')
|
||||
print_version=1
|
||||
shift
|
||||
;;
|
||||
'--')
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
if next_dir=$(cd "$1" && pwd); then
|
||||
if [[ ${next_dir#$PWD/} == /* ]]; then
|
||||
echo "error in parsing arguments of $0: $next_dir is not in $PWD" >&2
|
||||
exit 2 # input error
|
||||
elif ! [[ $set_include ]]; then
|
||||
include=() # remove default includes
|
||||
set_include=1
|
||||
fi
|
||||
include+=("${next_dir#$PWD/}")
|
||||
else
|
||||
echo "error in parsing arguments of $0: $PWD/$1 is not a directory" >&2
|
||||
exit 2 # input error
|
||||
fi
|
||||
if ! [[ $set_branch ]]; then
|
||||
unset branch # unset branch if not set explicitly
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# check availability of clang-format
|
||||
if ! hash $cf_cmd 2>/dev/null; then
|
||||
echo "could not find $cf_cmd" >&2
|
||||
# find any clang-format-x.x in /usr/bin
|
||||
cf_cmd=$(find /usr/bin -regex '.*/clang-format-[0-9]+\.[0-9]+' -print -quit)
|
||||
if [[ $cf_cmd ]]; then
|
||||
echo "found $cf_cmd instead" >&2
|
||||
else
|
||||
exit 3 # special exit code for missing dependency
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ $branch ]]; then
|
||||
# get all dirty files through git
|
||||
if ! base=$(git merge-base ${branch} HEAD); then
|
||||
echo "could not find git merge base" >&2
|
||||
exit 2 # input error
|
||||
fi
|
||||
declare -a reg
|
||||
for ex in ${exts[@]}; do
|
||||
reg+=(${include[@]/%/.*\\.$ex\$})
|
||||
done
|
||||
names=$(git diff --diff-filter=d --name-only $base | grep ${reg[@]/#/-e ^})
|
||||
else
|
||||
names=$(find ${include[@]} -type f -false ${exts[@]/#/-o -name *\\.})
|
||||
fi
|
||||
|
||||
# filter excludes
|
||||
names=$(<<<"$names" grep -v ${exclude[@]/#/-e ^})
|
||||
|
||||
if ! [[ $names ]]; then
|
||||
exit 0 # nothing to format means format is successful!
|
||||
fi
|
||||
|
||||
# optionally print version
|
||||
[[ $print_version ]] && $cf_cmd -version
|
||||
|
||||
# format
|
||||
case $mode in
|
||||
diff)
|
||||
declare -i code=0
|
||||
for name in ${names[@]}; do
|
||||
if ! $cf_cmd "$name" | diff "$name" - -p $color; then
|
||||
code=1
|
||||
fi
|
||||
done
|
||||
exit $code
|
||||
;;
|
||||
name)
|
||||
declare -i code=0
|
||||
for name in ${names[@]}; do
|
||||
if ! $cf_cmd "$name" | diff "$name" - -q >/dev/null; then
|
||||
echo "$name"
|
||||
code=1
|
||||
fi
|
||||
done
|
||||
exit $code
|
||||
;;
|
||||
code)
|
||||
for name in ${names[@]}; do
|
||||
$cf_cmd "$name" | diff "$name" - -q >/dev/null || exit 1
|
||||
done
|
||||
;;
|
||||
*)
|
||||
$cf_cmd -i $names
|
||||
;;
|
||||
esac
|
||||
|
||||
18
cmake/FindLibexecinfo.cmake
Normal file
@@ -0,0 +1,18 @@
|
||||
# Find the LibExecinfo library - FreeBSD only
|
||||
|
||||
FIND_PATH(LIBEXECINFO_INCLUDE_DIR execinfo.h)
|
||||
FIND_LIBRARY(LIBEXECINFO_LIBRARY NAMES execinfo)
|
||||
|
||||
IF(LIBEXECINFO_INCLUDE_DIR AND LIBEXECINFO_LIBRARY)
|
||||
SET(LIBEXECINFO_FOUND TRUE)
|
||||
ENDIF()
|
||||
|
||||
IF(LIBEXECINFO_FOUND)
|
||||
IF(NOT LIBEXECINFO_FIND_QUIETLY)
|
||||
MESSAGE(STATUS "Found LibExecinfo: ${EXECINFO_LIBRARY}")
|
||||
ENDIF()
|
||||
ELSE()
|
||||
IF(LIBEXECINFO_FIND_REQUIRED)
|
||||
MESSAGE(FATAL_ERROR "Could not find LibExecinfo")
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
36
cmake/FindVCredistRuntime.cmake
Normal file
@@ -0,0 +1,36 @@
|
||||
# Find the MS Visual Studio VC redistributable package
|
||||
|
||||
if (WIN32)
|
||||
set(VCREDISTRUNTIME_FOUND "NO")
|
||||
|
||||
if(CMAKE_SIZEOF_VOID_P EQUAL 8) # 64-bit
|
||||
set(REDIST_ARCH x64)
|
||||
else()
|
||||
set(REDIST_ARCH x86)
|
||||
endif()
|
||||
|
||||
set(REDIST_FILE vcredist_${REDIST_ARCH}.exe)
|
||||
|
||||
set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP TRUE)
|
||||
include(InstallRequiredSystemLibraries)
|
||||
|
||||
# Check if the list contains minimum one element, to get the path from
|
||||
list(LENGTH CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS libsCount)
|
||||
if (libsCount GREATER 0)
|
||||
list(GET CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS 0 _path)
|
||||
|
||||
get_filename_component(_path ${_path} DIRECTORY)
|
||||
get_filename_component(_path ${_path}/../../ ABSOLUTE)
|
||||
|
||||
if (EXISTS "${_path}/${REDIST_FILE}") # VS 2017
|
||||
set(VCREDISTRUNTIME_FOUND "YES")
|
||||
set(VCREDISTRUNTIME_FILE ${_path}/${REDIST_FILE})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(VCREDISTRUNTIME_FOUND)
|
||||
message(STATUS "Found VCredist ${VCREDISTRUNTIME_FILE}")
|
||||
else()
|
||||
message(WARNING "Could not find VCredist package. It's not required for compiling, but needs to be available at runtime.")
|
||||
endif()
|
||||
endif()
|
||||
@@ -1,69 +1,77 @@
|
||||
# Find the OpenSSL runtime libraries (.dll) for Windows that
|
||||
# will be needed by Qt in order to access https urls.
|
||||
|
||||
if (WIN32)
|
||||
# Get standard installation paths for OpenSSL under Windows
|
||||
|
||||
# http://www.slproweb.com/products/Win32OpenSSL.html
|
||||
|
||||
if( CMAKE_SIZEOF_VOID_P EQUAL 8 )
|
||||
# target win64
|
||||
set(_OPENSSL_ROOT_HINTS
|
||||
${OPENSSL_ROOT_DIR}
|
||||
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (64-bit)_is1;Inno Setup: App Path]"
|
||||
ENV OPENSSL_ROOT_DIR
|
||||
)
|
||||
file(TO_CMAKE_PATH "$ENV{PROGRAMFILES}" _programfiles)
|
||||
set(_OPENSSL_ROOT_PATHS
|
||||
"${_programfiles}/OpenSSL-Win64"
|
||||
"C:/OpenSSL-Win64/"
|
||||
)
|
||||
unset(_programfiles)
|
||||
else( CMAKE_SIZEOF_VOID_P EQUAL 8 )
|
||||
# target win32
|
||||
set(_OPENSSL_ROOT_HINTS
|
||||
${OPENSSL_ROOT_DIR}
|
||||
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (32-bit)_is1;Inno Setup: App Path]"
|
||||
ENV OPENSSL_ROOT_DIR
|
||||
)
|
||||
file(TO_CMAKE_PATH "$ENV{PROGRAMFILES}" _programfiles)
|
||||
set(_OPENSSL_ROOT_PATHS
|
||||
"${_programfiles}/OpenSSL"
|
||||
"${_programfiles}/OpenSSL-Win32"
|
||||
"C:/OpenSSL/"
|
||||
"C:/OpenSSL-Win32/"
|
||||
)
|
||||
unset(_programfiles)
|
||||
endif( CMAKE_SIZEOF_VOID_P EQUAL 8 )
|
||||
|
||||
else ()
|
||||
set(_OPENSSL_ROOT_HINTS
|
||||
${OPENSSL_ROOT_DIR}
|
||||
ENV OPENSSL_ROOT_DIR
|
||||
)
|
||||
endif ()
|
||||
|
||||
set(_OPENSSL_ROOT_HINTS_AND_PATHS
|
||||
HINTS ${_OPENSSL_ROOT_HINTS}
|
||||
PATHS ${_OPENSSL_ROOT_PATHS}
|
||||
)
|
||||
|
||||
# For OpenSSL < 1.1, they are named libeay32 and ssleay32 and even if the dll is 64bit, it's still suffixed as *32.dll
|
||||
# For OpenSSL >= 1.1, they are named libcrypto and libssl with no suffix
|
||||
FIND_FILE(WIN32SSLRUNTIME_LIBEAY NAMES libeay32.dll libcrypto.dll ${_OPENSSL_ROOT_HINTS_AND_PATHS})
|
||||
FIND_FILE(WIN32SSLRUNTIME_SSLEAY NAMES ssleay32.dll libssl.dll ${_OPENSSL_ROOT_HINTS_AND_PATHS})
|
||||
|
||||
|
||||
IF(WIN32SSLRUNTIME_LIBEAY AND WIN32SSLRUNTIME_SSLEAY)
|
||||
SET(WIN32SSLRUNTIME_LIBRARIES "${WIN32SSLRUNTIME_LIBEAY}" "${WIN32SSLRUNTIME_SSLEAY}")
|
||||
SET(WIN32SSLRUNTIME_FOUND "YES")
|
||||
message(STATUS "Found OpenSSL ${WIN32SSLRUNTIME_LIBRARIES}")
|
||||
ELSE()
|
||||
SET(WIN32SSLRUNTIME_FOUND "NO")
|
||||
message(WARNING "Could not find OpenSSL runtime libraries. They are not required for compiling, but needs to be available at runtime.")
|
||||
ENDIF()
|
||||
|
||||
MARK_AS_ADVANCED(
|
||||
WIN32SSLRUNTIME_LIBEAY
|
||||
WIN32SSLRUNTIME_SSLEAY
|
||||
)
|
||||
# Find the OpenSSL runtime libraries (.dll) for Windows that
|
||||
# will be needed by Qt in order to access https urls.
|
||||
|
||||
if (WIN32)
|
||||
# Get standard installation paths for OpenSSL under Windows
|
||||
|
||||
# http://www.slproweb.com/products/Win32OpenSSL.html
|
||||
|
||||
if( CMAKE_SIZEOF_VOID_P EQUAL 8 )
|
||||
# target win64
|
||||
set(_OPENSSL_ROOT_HINTS
|
||||
${OPENSSL_ROOT_DIR}
|
||||
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (64-bit)_is1;Inno Setup: App Path]"
|
||||
ENV OPENSSL_ROOT_DIR
|
||||
)
|
||||
file(TO_CMAKE_PATH "$ENV{PROGRAMFILES}" _programfiles)
|
||||
set(_OPENSSL_ROOT_PATHS
|
||||
"C:/Tools/vcpkg/installed/x64-windows/bin"
|
||||
"${_programfiles}/OpenSSL-Win64"
|
||||
"C:/OpenSSL-Win64/"
|
||||
)
|
||||
unset(_programfiles)
|
||||
else( CMAKE_SIZEOF_VOID_P EQUAL 8 )
|
||||
# target win32
|
||||
set(_OPENSSL_ROOT_HINTS
|
||||
${OPENSSL_ROOT_DIR}
|
||||
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (32-bit)_is1;Inno Setup: App Path]"
|
||||
ENV OPENSSL_ROOT_DIR
|
||||
)
|
||||
file(TO_CMAKE_PATH "$ENV{PROGRAMFILES}" _programfiles)
|
||||
set(_OPENSSL_ROOT_PATHS
|
||||
"C:/Tools/vcpkg/installed/x86-windows/bin"
|
||||
"${_programfiles}/OpenSSL"
|
||||
"${_programfiles}/OpenSSL-Win32"
|
||||
"C:/OpenSSL/"
|
||||
"C:/OpenSSL-Win32/"
|
||||
)
|
||||
unset(_programfiles)
|
||||
endif( CMAKE_SIZEOF_VOID_P EQUAL 8 )
|
||||
|
||||
else ()
|
||||
set(_OPENSSL_ROOT_HINTS
|
||||
${OPENSSL_ROOT_DIR}
|
||||
ENV OPENSSL_ROOT_DIR
|
||||
)
|
||||
endif ()
|
||||
|
||||
set(_OPENSSL_ROOT_HINTS_AND_PATHS
|
||||
HINTS ${_OPENSSL_ROOT_HINTS}
|
||||
PATHS ${_OPENSSL_ROOT_PATHS}
|
||||
)
|
||||
|
||||
# For OpenSSL < 1.1, they are named libeay32 and ssleay32 and even if the dll is 64bit, it's still suffixed as *32.dll
|
||||
# For OpenSSL >= 1.1, they are named libcrypto and libssl with no suffix
|
||||
if( CMAKE_SIZEOF_VOID_P EQUAL 8 )
|
||||
# target win64
|
||||
FIND_FILE(WIN32SSLRUNTIME_LIBEAY NAMES libcrypto-1_1-x64.dll libcrypto.dll libeay32.dll ${_OPENSSL_ROOT_HINTS_AND_PATHS})
|
||||
FIND_FILE(WIN32SSLRUNTIME_SSLEAY NAMES libssl-1_1-x64.dll libssl.dll ssleay32.dll ${_OPENSSL_ROOT_HINTS_AND_PATHS})
|
||||
else( CMAKE_SIZEOF_VOID_P EQUAL 8 )
|
||||
# target win32
|
||||
FIND_FILE(WIN32SSLRUNTIME_LIBEAY NAMES libcrypto-1_1.dll libcrypto.dll libeay32.dll ${_OPENSSL_ROOT_HINTS_AND_PATHS})
|
||||
FIND_FILE(WIN32SSLRUNTIME_SSLEAY NAMES libssl-1_1.dll libssl.dll ssleay32.dll ${_OPENSSL_ROOT_HINTS_AND_PATHS})
|
||||
endif( CMAKE_SIZEOF_VOID_P EQUAL 8 )
|
||||
|
||||
IF(WIN32SSLRUNTIME_LIBEAY AND WIN32SSLRUNTIME_SSLEAY)
|
||||
SET(WIN32SSLRUNTIME_LIBRARIES "${WIN32SSLRUNTIME_LIBEAY}" "${WIN32SSLRUNTIME_SSLEAY}")
|
||||
SET(WIN32SSLRUNTIME_FOUND "YES")
|
||||
message(STATUS "Found OpenSSL ${WIN32SSLRUNTIME_LIBRARIES}")
|
||||
ELSE()
|
||||
SET(WIN32SSLRUNTIME_FOUND "NO")
|
||||
message(WARNING "Could not find OpenSSL runtime libraries. They are not required for compiling, but needs to be available at runtime.")
|
||||
ENDIF()
|
||||
|
||||
MARK_AS_ADVANCED(
|
||||
WIN32SSLRUNTIME_LIBEAY
|
||||
WIN32SSLRUNTIME_SSLEAY
|
||||
)
|
||||
|
||||
@@ -35,4 +35,4 @@
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
</plist>
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
!define NSIS_SOURCE_PATH "@PROJECT_SOURCE_DIR@"
|
||||
!define NSIS_SOURCE_PATH "@PROJECT_SOURCE_DIR@"
|
||||
!define NSIS_IS_64_BIT @TRICE_IS_64_BIT@
|
||||
@@ -3,9 +3,6 @@ Name "@CPACK_PACKAGE_NAME@"
|
||||
BrandingText "@CPACK_PACKAGE_FILE_NAME@"
|
||||
OutFile "@CPACK_TOPLEVEL_DIRECTORY@/@CPACK_OUTPUT_FILE_NAME@"
|
||||
|
||||
!define UNINSTKEY "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice"
|
||||
!define DEFAULTNORMALDESTINATON "$ProgramFiles\Cockatrice"
|
||||
!define DEFAULTPORTABLEDESTINATON "$Desktop\CockatricePortable"
|
||||
!define INST_DIR "@CPACK_TEMPORARY_DIRECTORY@"
|
||||
|
||||
RequestExecutionlevel highest
|
||||
@@ -18,6 +15,7 @@ Var PortableMode
|
||||
!include LogicLib.nsh
|
||||
!include FileFunc.nsh
|
||||
!include MUI2.nsh
|
||||
!include x64.nsh
|
||||
|
||||
!define MUI_ABORTWARNING
|
||||
!define MUI_WELCOMEFINISHPAGE_BITMAP "${NSIS_SOURCE_PATH}\cmake\leftimage.bmp"
|
||||
@@ -46,8 +44,25 @@ Page Custom PortableModePageCreate PortableModePageLeave
|
||||
!insertmacro MUI_LANGUAGE English
|
||||
|
||||
Function .onInit
|
||||
StrCpy $NormalDestDir "${DEFAULTNORMALDESTINATON}"
|
||||
StrCpy $PortableDestDir "${DEFAULTPORTABLEDESTINATON}"
|
||||
|
||||
${If} ${NSIS_IS_64_BIT} == 1 #NSIS 64bit
|
||||
${IfNot} ${RunningX64}
|
||||
MessageBox MB_OK|MB_ICONSTOP "This version of Cockatrice requires a 64-bit Windows system."
|
||||
Abort
|
||||
${EndIf}
|
||||
StrCpy $NormalDestDir "$ProgramFiles64\Cockatrice"
|
||||
SetRegView 64
|
||||
${Else} #NSIS 32bit
|
||||
${If} ${RunningX64}
|
||||
MessageBox MB_OK|MB_ICONEXCLAMATION \
|
||||
"You are about to install a 32-bit version of Cockatrice on a 64-bit Windows system.$\n\
|
||||
We advise you to use the correct 64-bit installer instead to get around potential issues.$\n$\n\
|
||||
Download from our webpage: https://cockatrice.github.io"
|
||||
${EndIf}
|
||||
StrCpy $NormalDestDir "$ProgramFiles\Cockatrice"
|
||||
${EndIf}
|
||||
|
||||
StrCpy $PortableDestDir "$Desktop\CockatricePortable"
|
||||
|
||||
${GetParameters} $9
|
||||
|
||||
@@ -75,7 +90,7 @@ ${Else}
|
||||
${EndIf}
|
||||
|
||||
${If} $InstDir == ""
|
||||
; User did not use /D to specify a directory,
|
||||
; User did not use /D to specify a directory,
|
||||
; we need to set a default based on the install mode
|
||||
StrCpy $InstDir $0
|
||||
${EndIf}
|
||||
@@ -83,6 +98,13 @@ Call SetModeDestinationFromInstdir
|
||||
|
||||
FunctionEnd
|
||||
|
||||
Function un.onInit
|
||||
|
||||
${If} ${NSIS_IS_64_BIT} == 1
|
||||
SetRegView 64
|
||||
${EndIf}
|
||||
|
||||
FunctionEnd
|
||||
|
||||
Function RequireAdmin
|
||||
UserInfo::GetAccountType
|
||||
@@ -138,17 +160,39 @@ FunctionEnd
|
||||
Function componentsPagePre
|
||||
${If} $PortableMode = 0
|
||||
SetShellVarContext all
|
||||
ReadRegStr $R0 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "UninstallString"
|
||||
StrCmp $R0 "" done
|
||||
|
||||
MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION "A previous version of Cockatrice must be uninstalled before installing the new one." IDOK uninst
|
||||
# uninstall 32bit version
|
||||
SetRegView 32
|
||||
|
||||
ReadRegStr $R0 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "UninstallString"
|
||||
StrCmp $R0 "" done32
|
||||
|
||||
MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION "A previous version of Cockatrice must be uninstalled before installing the new one." IDOK uninst32
|
||||
Abort
|
||||
|
||||
uninst:
|
||||
uninst32:
|
||||
ClearErrors
|
||||
ExecWait "$R0"
|
||||
|
||||
done:
|
||||
done32:
|
||||
|
||||
# uninstall 64bit version
|
||||
${If} ${NSIS_IS_64_BIT} == 1
|
||||
SetRegView 64
|
||||
|
||||
ReadRegStr $R0 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "UninstallString"
|
||||
StrCmp $R0 "" done64
|
||||
|
||||
MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION "A previous version of Cockatrice must be uninstalled before installing the new one." IDOK uninst64
|
||||
Abort
|
||||
|
||||
uninst64:
|
||||
ClearErrors
|
||||
ExecWait "$R0"
|
||||
|
||||
done64:
|
||||
${EndIf}
|
||||
|
||||
${Else}
|
||||
Abort
|
||||
${EndIf}
|
||||
@@ -169,6 +213,7 @@ ${AndIf} ${FileExists} "$INSTDIR\portable.dat"
|
||||
Delete "$INSTDIR\uninstall.exe"
|
||||
Delete "$INSTDIR\cockatrice.exe"
|
||||
Delete "$INSTDIR\oracle.exe"
|
||||
Delete "$INSTDIR\dbconverter.exe"
|
||||
Delete "$INSTDIR\servatrice.exe"
|
||||
Delete "$INSTDIR\Qt*.dll"
|
||||
Delete "$INSTDIR\libmysql.dll"
|
||||
@@ -190,21 +235,33 @@ ${If} $PortableMode = 0
|
||||
WriteUninstaller "$INSTDIR\uninstall.exe"
|
||||
${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2
|
||||
IntFmt $0 "0x%08X" $0
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "DisplayName" "Cockatrice"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "UninstallString" "$\"$INSTDIR\uninstall.exe$\""
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "QuietUninstallString" "$\"$INSTDIR\uninstall.exe$\" /S"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "InstallLocation" "$INSTDIR"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "DisplayIcon" "$INSTDIR\cockatrice.exe"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "DisplayName" "Cockatrice"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "DisplayVersion" "@CPACK_PACKAGE_VERSION_MAJOR@.@CPACK_PACKAGE_VERSION_MINOR@.@CPACK_PACKAGE_VERSION_PATCH@"
|
||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "EstimatedSize" "$0"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "InstallLocation" "$INSTDIR"
|
||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "NoModify" "1"
|
||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "NoRepair" "1"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "Publisher" "Cockatrice team"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "QuietUninstallString" "$\"$INSTDIR\uninstall.exe$\" /S"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "UninstallString" "$\"$INSTDIR\uninstall.exe$\""
|
||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "VersionMajor" "@CPACK_PACKAGE_VERSION_MAJOR@"
|
||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "VersionMinor" "@CPACK_PACKAGE_VERSION_MINOR@"
|
||||
|
||||
IfFileExists "$INSTDIR\vc_redist.x86.exe" VcRedist86Exists PastVcRedist86Check
|
||||
IfFileExists "$INSTDIR\vcredist_x86.exe" VcRedist86Exists PastVcRedist86Check
|
||||
VcRedist86Exists:
|
||||
ExecWait '"$INSTDIR\vc_redist.x86.exe" /passive /norestart'
|
||||
ExecWait '"$INSTDIR\vcredist_x86.exe" /passive /norestart'
|
||||
DetailPrint "Wait to ensure unlock of vc_redist file after installation..."
|
||||
Sleep 3000
|
||||
Delete "$INSTDIR\vcredist_x86.exe"
|
||||
PastVcRedist86Check:
|
||||
|
||||
IfFileExists "$INSTDIR\vc_redist.x64.exe" VcRedist64Exists PastVcRedist64Check
|
||||
IfFileExists "$INSTDIR\vcredist_x64.exe" VcRedist64Exists PastVcRedist64Check
|
||||
VcRedist64Exists:
|
||||
ExecWait '"$INSTDIR\vc_redist.x64.exe" /passive /norestart'
|
||||
ExecWait '"$INSTDIR\vcredist_x64.exe" /passive /norestart'
|
||||
DetailPrint "Sleep to ensure unlock of vc_redist file after installation..."
|
||||
Sleep 3000
|
||||
Delete "$INSTDIR\vcredist_x64.exe"
|
||||
PastVcRedist64Check:
|
||||
|
||||
${Else}
|
||||
@@ -264,4 +321,4 @@ LangString DESC_UnSecConfiguration ${LANG_ENGLISH} "Configurations, decks, card
|
||||
!insertmacro MUI_UNFUNCTION_DESCRIPTION_BEGIN
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${UnSecApplication} $(DESC_UnSecApplication)
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${UnSecConfiguration} $(DESC_UnSecConfiguration)
|
||||
!insertmacro MUI_UNFUNCTION_DESCRIPTION_END
|
||||
!insertmacro MUI_UNFUNCTION_DESCRIPTION_END
|
||||
|
||||
@@ -38,25 +38,6 @@ function(get_commit_date)
|
||||
set(GIT_COMMIT_DATE "${GIT_COM_DATE}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(clean_release_name name)
|
||||
# "name": "Cockatrice: Thopter Pie Network, Revision 2"
|
||||
|
||||
# Remove all double quotes
|
||||
STRING(REPLACE "\"" "" name "${name}")
|
||||
# Remove json prefix "name: "
|
||||
STRING(REPLACE " name: " "" name "${name}")
|
||||
# Remove "cockatrice" name
|
||||
STRING(REPLACE "Cockatrice" "" name "${name}")
|
||||
# Remove all unwanted chars
|
||||
STRING(REGEX REPLACE "[^A-Za-z0-9_ ]" "" name "${name}")
|
||||
# Strip (trim) whitespaces
|
||||
STRING(STRIP "${name}" name)
|
||||
# Replace all spaces with underscores
|
||||
STRING(REPLACE " " "_" name "${name}")
|
||||
|
||||
set(GIT_TAG_RELEASENAME "${name}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(get_tag_name commit)
|
||||
if(${commit} STREQUAL "unknown")
|
||||
return()
|
||||
@@ -165,23 +146,8 @@ function(get_tag_name commit)
|
||||
set(PROJECT_VERSION_LABEL ${GIT_TAG_LABEL} PARENT_SCOPE)
|
||||
elseif(${GIT_TAG_TYPE} STREQUAL "Release")
|
||||
set(PROJECT_VERSION_LABEL "" PARENT_SCOPE)
|
||||
|
||||
# get version name from github
|
||||
set(GIT_TAG_TEMP_FILE "${PROJECT_BINARY_DIR}/tag_informations.txt")
|
||||
set(GIT_TAG_TEMP_URL "https://api.github.com/repos/Cockatrice/Cockatrice/releases/tags/${GIT_TAG}")
|
||||
message(STATUS "Fetching tag informations from ${GIT_TAG_TEMP_URL}")
|
||||
file(REMOVE "${GIT_TAG_TEMP_FILE}")
|
||||
file(DOWNLOAD "${GIT_TAG_TEMP_URL}" "${GIT_TAG_TEMP_FILE}" STATUS status LOG log INACTIVITY_TIMEOUT 30 TIMEOUT 300 SHOW_PROGRESS)
|
||||
list(GET status 0 err)
|
||||
list(GET status 1 msg)
|
||||
if(err)
|
||||
message(WARNING "Download failed with error ${msg}: ${log}")
|
||||
return()
|
||||
endif()
|
||||
file(STRINGS "${GIT_TAG_TEMP_FILE}" GIT_TAG_RAW_RELEASENAME REGEX "\"name\": \"" LIMIT_COUNT 1)
|
||||
|
||||
clean_release_name("${GIT_TAG_RAW_RELEASENAME}")
|
||||
set(PROJECT_VERSION_RELEASENAME "${GIT_TAG_RELEASENAME}" PARENT_SCOPE)
|
||||
# set release name from env var
|
||||
set(PROJECT_VERSION_RELEASENAME "${GIT_TAG_RELEASENAME}" PARENT_SCOPE)
|
||||
endif()
|
||||
|
||||
endfunction()
|
||||
@@ -190,9 +156,9 @@ endfunction()
|
||||
|
||||
# fallback defaults
|
||||
set(GIT_COMMIT_ID "unknown")
|
||||
set(GIT_COMMIT_DATE "unknown")
|
||||
set(GIT_COMMIT_DATE_FRIENDLY "unknown")
|
||||
set(PROJECT_VERSION_LABEL "custom(unknown)")
|
||||
set(GIT_COMMIT_DATE "")
|
||||
set(GIT_COMMIT_DATE_FRIENDLY "")
|
||||
set(PROJECT_VERSION_LABEL "")
|
||||
set(PROJECT_VERSION_RELEASENAME "")
|
||||
|
||||
find_package(Git)
|
||||
|
||||
@@ -4,12 +4,12 @@ project(gtest-download LANGUAGES NONE)
|
||||
|
||||
include(ExternalProject)
|
||||
ExternalProject_Add(googletest
|
||||
URL https://github.com/google/googletest/archive/release-1.7.0.zip
|
||||
URL_HASH SHA1=f89bc9f55477df2fde082481e2d709bfafdb057b
|
||||
URL https://github.com/google/googletest/archive/release-1.11.0.zip
|
||||
URL_HASH SHA1=9ffb7b5923f4a8fcdabf2f42c6540cce299f44c0
|
||||
SOURCE_DIR "${CMAKE_BINARY_DIR}/gtest-src"
|
||||
BINARY_DIR "${CMAKE_BINARY_DIR}/gtest-build"
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
TEST_COMMAND ""
|
||||
)
|
||||
)
|
||||
|
||||
@@ -2,126 +2,132 @@
|
||||
#
|
||||
# provides the cockatrice binary
|
||||
|
||||
PROJECT(Cockatrice)
|
||||
PROJECT(Cockatrice VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
|
||||
|
||||
SET(cockatrice_SOURCES
|
||||
src/abstractcounter.cpp
|
||||
src/abstractcarddragitem.cpp
|
||||
src/abstractcarditem.cpp
|
||||
src/abstractclient.cpp
|
||||
src/abstractcounter.cpp
|
||||
src/abstractgraphicsitem.cpp
|
||||
src/arrowitem.cpp
|
||||
src/arrowtarget.cpp
|
||||
src/carddatabase.cpp
|
||||
src/carddatabasemodel.cpp
|
||||
src/carddbparser/carddatabaseparser.cpp
|
||||
src/carddbparser/cockatricexml3.cpp
|
||||
src/carddbparser/cockatricexml4.cpp
|
||||
src/carddragitem.cpp
|
||||
src/cardfilter.cpp
|
||||
src/cardframe.cpp
|
||||
src/cardinfopicture.cpp
|
||||
src/cardinfotext.cpp
|
||||
src/cardinfowidget.cpp
|
||||
src/carditem.cpp
|
||||
src/cardlist.cpp
|
||||
src/cardzone.cpp
|
||||
src/chatview/chatview.cpp
|
||||
src/counter_general.cpp
|
||||
src/customlineedit.cpp
|
||||
src/deck_loader.cpp
|
||||
src/decklistmodel.cpp
|
||||
src/deckstats_interface.cpp
|
||||
src/deckview.cpp
|
||||
src/dlg_connect.cpp
|
||||
src/dlg_create_token.cpp
|
||||
src/dlg_creategame.cpp
|
||||
src/dlg_filter_games.cpp
|
||||
src/dlg_connect.cpp
|
||||
src/dlg_create_token.cpp
|
||||
src/dlg_edit_avatar.cpp
|
||||
src/dlg_edit_password.cpp
|
||||
src/dlg_edit_tokens.cpp
|
||||
src/dlg_edit_user.cpp
|
||||
src/dlg_filter_games.cpp
|
||||
src/dlg_forgotpasswordchallenge.cpp
|
||||
src/dlg_forgotpasswordrequest.cpp
|
||||
src/dlg_forgotpasswordreset.cpp
|
||||
src/dlg_forgotpasswordchallenge.cpp
|
||||
src/dlg_register.cpp
|
||||
src/dlg_load_deck_from_clipboard.cpp
|
||||
src/dlg_load_remote_deck.cpp
|
||||
src/dlg_manage_sets.cpp
|
||||
src/dlg_register.cpp
|
||||
src/dlg_settings.cpp
|
||||
src/dlg_tip_of_the_day.cpp
|
||||
src/tip_of_the_day.cpp
|
||||
src/dlg_update.cpp
|
||||
src/dlg_viewlog.cpp
|
||||
src/abstractclient.cpp
|
||||
src/remoteclient.cpp
|
||||
src/main.cpp
|
||||
src/window_main.cpp
|
||||
src/gamesmodel.cpp
|
||||
src/player.cpp
|
||||
src/playertarget.cpp
|
||||
src/cardzone.cpp
|
||||
src/selectzone.cpp
|
||||
src/cardlist.cpp
|
||||
src/abstractcarditem.cpp
|
||||
src/carditem.cpp
|
||||
src/tablezone.cpp
|
||||
src/handzone.cpp
|
||||
src/handcounter.cpp
|
||||
src/carddatabase.cpp
|
||||
src/keysignals.cpp
|
||||
src/gameview.cpp
|
||||
src/gameselector.cpp
|
||||
src/decklistmodel.cpp
|
||||
src/deck_loader.cpp
|
||||
src/dlg_load_deck_from_clipboard.cpp
|
||||
src/dlg_load_remote_deck.cpp
|
||||
src/cardinfowidget.cpp
|
||||
src/cardframe.cpp
|
||||
src/cardinfopicture.cpp
|
||||
src/cardinfotext.cpp
|
||||
src/filter_string.cpp
|
||||
src/filterbuilder.cpp
|
||||
src/cardfilter.cpp
|
||||
src/filtertreemodel.cpp
|
||||
src/filtertree.cpp
|
||||
src/messagelogwidget.cpp
|
||||
src/zoneviewzone.cpp
|
||||
src/zoneviewwidget.cpp
|
||||
src/pilezone.cpp
|
||||
src/stackzone.cpp
|
||||
src/carddragitem.cpp
|
||||
src/carddatabasemodel.cpp
|
||||
src/setsmodel.cpp
|
||||
src/window_sets.cpp
|
||||
src/abstractgraphicsitem.cpp
|
||||
src/abstractcarddragitem.cpp
|
||||
src/dlg_settings.cpp
|
||||
src/phasestoolbar.cpp
|
||||
src/gamescene.cpp
|
||||
src/arrowitem.cpp
|
||||
src/arrowtarget.cpp
|
||||
src/tab.cpp
|
||||
src/tab_server.cpp
|
||||
src/tab_room.cpp
|
||||
src/tab_message.cpp
|
||||
src/tab_game.cpp
|
||||
src/tab_deck_storage.cpp
|
||||
src/tab_replays.cpp
|
||||
src/tab_supervisor.cpp
|
||||
src/tab_admin.cpp
|
||||
src/tab_userlists.cpp
|
||||
src/tab_deck_editor.cpp
|
||||
src/tab_logs.cpp
|
||||
src/replay_timeline_widget.cpp
|
||||
src/deckstats_interface.cpp
|
||||
src/tappedout_interface.cpp
|
||||
src/chatview/chatview.cpp
|
||||
src/chatview/userlistProxy.h
|
||||
src/userlist.cpp
|
||||
src/userinfobox.cpp
|
||||
src/user_context_menu.cpp
|
||||
src/remotedecklist_treewidget.cpp
|
||||
src/remotereplaylist_treewidget.cpp
|
||||
src/deckview.cpp
|
||||
src/playerlistwidget.cpp
|
||||
src/pixmapgenerator.cpp
|
||||
src/settingscache.cpp
|
||||
src/thememanager.cpp
|
||||
src/localserver.cpp
|
||||
src/localserverinterface.cpp
|
||||
src/localclient.cpp
|
||||
src/qt-json/json.cpp
|
||||
src/soundengine.cpp
|
||||
src/pending_command.cpp
|
||||
src/pictureloader.cpp
|
||||
src/shortcutssettings.cpp
|
||||
src/sequenceEdit/sequenceedit.cpp
|
||||
src/sequenceEdit/shortcutstab.cpp
|
||||
src/filtertreemodel.cpp
|
||||
src/gamescene.cpp
|
||||
src/gameselector.cpp
|
||||
src/gamesmodel.cpp
|
||||
src/gameview.cpp
|
||||
src/gettextwithmax.cpp
|
||||
src/handcounter.cpp
|
||||
src/handle_public_servers.cpp
|
||||
src/handzone.cpp
|
||||
src/keysignals.cpp
|
||||
src/lineeditcompleter.cpp
|
||||
src/settings/settingsmanager.cpp
|
||||
src/localclient.cpp
|
||||
src/localserver.cpp
|
||||
src/localserverinterface.cpp
|
||||
src/logger.cpp
|
||||
src/main.cpp
|
||||
src/messagelogwidget.cpp
|
||||
src/pending_command.cpp
|
||||
src/phase.cpp
|
||||
src/phasestoolbar.cpp
|
||||
src/pictureloader.cpp
|
||||
src/pilezone.cpp
|
||||
src/pixmapgenerator.cpp
|
||||
src/player.cpp
|
||||
src/playerlistwidget.cpp
|
||||
src/playertarget.cpp
|
||||
src/releasechannel.cpp
|
||||
src/remoteclient.cpp
|
||||
src/remotedecklist_treewidget.cpp
|
||||
src/remotereplaylist_treewidget.cpp
|
||||
src/replay_timeline_widget.cpp
|
||||
src/selectzone.cpp
|
||||
src/sequenceEdit/sequenceedit.cpp
|
||||
src/setsmodel.cpp
|
||||
src/settings/carddatabasesettings.cpp
|
||||
src/settings/serverssettings.cpp
|
||||
src/settings/messagesettings.cpp
|
||||
src/settings/downloadsettings.cpp
|
||||
src/settings/gamefilterssettings.cpp
|
||||
src/settings/layoutssettings.cpp
|
||||
src/update_downloader.cpp
|
||||
src/logger.cpp
|
||||
src/releasechannel.cpp
|
||||
src/userconnection_information.cpp
|
||||
src/settings/messagesettings.cpp
|
||||
src/settings/serverssettings.cpp
|
||||
src/settings/settingsmanager.cpp
|
||||
src/settingscache.cpp
|
||||
src/shortcutssettings.cpp
|
||||
src/soundengine.cpp
|
||||
src/spoilerbackgroundupdater.cpp
|
||||
src/handle_public_servers.cpp
|
||||
src/stackzone.cpp
|
||||
src/tab.cpp
|
||||
src/tab_account.cpp
|
||||
src/tab_admin.cpp
|
||||
src/tab_deck_editor.cpp
|
||||
src/tab_deck_storage.cpp
|
||||
src/tab_game.cpp
|
||||
src/tab_logs.cpp
|
||||
src/tab_message.cpp
|
||||
src/tab_replays.cpp
|
||||
src/tab_room.cpp
|
||||
src/tab_server.cpp
|
||||
src/tab_supervisor.cpp
|
||||
src/tablezone.cpp
|
||||
src/tappedout_interface.cpp
|
||||
src/thememanager.cpp
|
||||
src/tip_of_the_day.cpp
|
||||
src/translatecountername.cpp
|
||||
src/update_downloader.cpp
|
||||
src/user_context_menu.cpp
|
||||
src/userconnection_information.cpp
|
||||
src/userinfobox.cpp
|
||||
src/userlist.cpp
|
||||
src/window_main.cpp
|
||||
src/zoneviewwidget.cpp
|
||||
src/zoneviewzone.cpp
|
||||
${VERSION_STRING_CPP}
|
||||
)
|
||||
)
|
||||
|
||||
add_subdirectory(sounds)
|
||||
add_subdirectory(themes)
|
||||
@@ -147,71 +153,31 @@ if(APPLE)
|
||||
set(cockatrice_SOURCES ${cockatrice_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/resources/appicon.icns)
|
||||
ENDIF(APPLE)
|
||||
|
||||
set(COCKATRICE_LIBS)
|
||||
# Qt5
|
||||
find_package(Qt5 COMPONENTS Concurrent Multimedia Network PrintSupport Svg WebSockets Widgets REQUIRED)
|
||||
set(COCKATRICE_QT_MODULES Qt5::Concurrent Qt5::Multimedia Qt5::Network Qt5::PrintSupport Qt5::Svg Qt5::Widgets Qt5::WebSockets)
|
||||
|
||||
# qt5 stuff
|
||||
if(Qt5Widgets_FOUND)
|
||||
include_directories(${Qt5Widgets_INCLUDE_DIRS})
|
||||
list(APPEND COCKATRICE_LIBS Widgets)
|
||||
# Qt5LinguistTools
|
||||
find_package(Qt5LinguistTools)
|
||||
if(Qt5LinguistTools_FOUND)
|
||||
list(APPEND COCKATRICE_LIBS Qt5::LinguistTools)
|
||||
|
||||
# QtConcurrent
|
||||
find_package(Qt5Concurrent)
|
||||
if(Qt5Concurrent_FOUND)
|
||||
include_directories(${Qt5Concurrent_INCLUDE_DIRS})
|
||||
list(APPEND ORACLE_LIBS Concurrent)
|
||||
if(NOT Qt5_LRELEASE_EXECUTABLE)
|
||||
MESSAGE(WARNING "Qt's lrelease not found.")
|
||||
endif()
|
||||
|
||||
# QtNetwork
|
||||
find_package(Qt5Network)
|
||||
if(Qt5Network_FOUND)
|
||||
include_directories(${Qt5Network_INCLUDE_DIRS})
|
||||
list(APPEND COCKATRICE_LIBS Network)
|
||||
endif()
|
||||
|
||||
# QtMultimedia
|
||||
find_package(Qt5Multimedia)
|
||||
if(Qt5Multimedia_FOUND)
|
||||
include_directories(${Qt5Multimedia_INCLUDE_DIRS})
|
||||
list(APPEND COCKATRICE_LIBS Multimedia)
|
||||
endif()
|
||||
|
||||
# QtPrinter
|
||||
find_package(Qt5PrintSupport)
|
||||
if(Qt5PrintSupport_FOUND)
|
||||
include_directories(${Qt5PrintSupport_INCLUDE_DIRS})
|
||||
list(APPEND COCKATRICE_LIBS PrintSupport)
|
||||
endif()
|
||||
|
||||
# QtSvg
|
||||
find_package(Qt5Svg)
|
||||
if(Qt5Svg_FOUND)
|
||||
include_directories(${Qt5Svg_INCLUDE_DIRS})
|
||||
list(APPEND COCKATRICE_LIBS Svg)
|
||||
endif()
|
||||
|
||||
# Qt5LinguistTools
|
||||
find_package(Qt5LinguistTools)
|
||||
if(Qt5LinguistTools_FOUND)
|
||||
include_directories(${Qt5LinguistTools_INCLUDE_DIRS})
|
||||
list(APPEND COCKATRICE_LIBS LinguistTools)
|
||||
endif()
|
||||
|
||||
# Let cmake chew Qt5's translations and resource files
|
||||
# Note: header files are MOC-ed automatically by cmake
|
||||
IF(UPDATE_TRANSLATIONS)
|
||||
if(UPDATE_TRANSLATIONS)
|
||||
if(NOT Qt5_LUPDATE_EXECUTABLE)
|
||||
MESSAGE(WARNING "Qt's lupdate not found.")
|
||||
endif()
|
||||
QT5_CREATE_TRANSLATION(cockatrice_QM ${translate_SRCS} ${cockatrice_TS})
|
||||
ELSE()
|
||||
else()
|
||||
QT5_ADD_TRANSLATION(cockatrice_QM ${cockatrice_TS})
|
||||
ENDIF()
|
||||
|
||||
QT5_ADD_RESOURCES(cockatrice_RESOURCES_RCC ${cockatrice_RESOURCES})
|
||||
|
||||
# guess plugins and libraries directory
|
||||
set(QT_PLUGINS_DIR "${Qt5Widgets_DIR}/../../../plugins")
|
||||
get_target_property(QT_LIBRARY_DIR Qt5::Core LOCATION)
|
||||
get_filename_component(QT_LIBRARY_DIR ${QT_LIBRARY_DIR} PATH)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
QT5_ADD_RESOURCES(cockatrice_RESOURCES_RCC ${cockatrice_RESOURCES})
|
||||
|
||||
# Declare path variables
|
||||
set(ICONDIR share/icons CACHE STRING "icon dir")
|
||||
set(DESKTOPDIR share/applications CACHE STRING "desktop file destination")
|
||||
@@ -225,8 +191,7 @@ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
|
||||
# Build cockatrice binary and link it
|
||||
ADD_EXECUTABLE(cockatrice WIN32 MACOSX_BUNDLE ${cockatrice_SOURCES} ${cockatrice_QM} ${cockatrice_RESOURCES_RCC} ${cockatrice_MOC_SRCS})
|
||||
|
||||
TARGET_LINK_LIBRARIES(cockatrice cockatrice_common)
|
||||
qt5_use_modules(cockatrice ${COCKATRICE_LIBS})
|
||||
TARGET_LINK_LIBRARIES(cockatrice cockatrice_common ${COCKATRICE_QT_MODULES})
|
||||
|
||||
if(UNIX)
|
||||
if(APPLE)
|
||||
@@ -236,10 +201,11 @@ if(UNIX)
|
||||
set(MACOSX_BUNDLE_BUNDLE_NAME "${PROJECT_NAME}")
|
||||
set(MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION})
|
||||
set(MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION})
|
||||
|
||||
set_target_properties(cockatrice PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/cmake/Info.plist)
|
||||
|
||||
INSTALL(TARGETS cockatrice BUNDLE DESTINATION ./)
|
||||
INSTALL(FILES ${cockatrice_QM} DESTINATION ./Cockatrice.app/Contents/Resources/translations)
|
||||
INSTALL(FILES ${cockatrice_QM} DESTINATION ./cockatrice.app/Contents/Resources/translations)
|
||||
else()
|
||||
# Assume linux
|
||||
INSTALL(TARGETS cockatrice RUNTIME DESTINATION bin/)
|
||||
@@ -255,14 +221,22 @@ endif()
|
||||
|
||||
if(APPLE)
|
||||
# these needs to be relative to CMAKE_INSTALL_PREFIX
|
||||
set(plugin_dest_dir Cockatrice.app/Contents/Plugins)
|
||||
set(qtconf_dest_dir Cockatrice.app/Contents/Resources)
|
||||
set(plugin_dest_dir cockatrice.app/Contents/Plugins)
|
||||
set(qtconf_dest_dir cockatrice.app/Contents/Resources)
|
||||
get_filename_component(QT_LIBRARY_DIR "${QT_LIBRARY_DIR}/.." ABSOLUTE)
|
||||
|
||||
# qt5 plugins: audio, iconengines, imageformats, platforms, printsupport
|
||||
install(DIRECTORY "${QT_PLUGINS_DIR}/" DESTINATION ${plugin_dest_dir} COMPONENT Runtime
|
||||
FILES_MATCHING REGEX "(audio|iconengines|imageformats|platforms|printsupport)/.*\\.dylib"
|
||||
REGEX ".*_debug\\.dylib" EXCLUDE)
|
||||
FILES_MATCHING
|
||||
PATTERN "*.dSYM" EXCLUDE
|
||||
PATTERN "*_debug.dylib" EXCLUDE
|
||||
PATTERN "audio/*.dylib"
|
||||
PATTERN "iconengines/*.dylib"
|
||||
PATTERN "imageformats/*.dylib"
|
||||
PATTERN "platforms/*.dylib"
|
||||
PATTERN "printsupport/*.dylib"
|
||||
PATTERN "styles/*.dylib"
|
||||
)
|
||||
|
||||
install(CODE "
|
||||
file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${qtconf_dest_dir}/qt.conf\" \"[Paths]
|
||||
@@ -276,7 +250,7 @@ Data = Resources\")
|
||||
\"\${CMAKE_INSTALL_PREFIX}/${plugin_dest_dir}/*.dylib\")
|
||||
set(BU_CHMOD_BUNDLE_ITEMS ON)
|
||||
include(BundleUtilities)
|
||||
fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/Cockatrice.app\" \"\${QTPLUGINS}\" \"${QT_LIBRARY_DIR}\")
|
||||
fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/cockatrice.app\" \"\${QTPLUGINS}\" \"${QT_LIBRARY_DIR}\")
|
||||
" COMPONENT Runtime)
|
||||
endif()
|
||||
|
||||
@@ -285,9 +259,11 @@ if(WIN32)
|
||||
set(plugin_dest_dir Plugins)
|
||||
set(qtconf_dest_dir .)
|
||||
|
||||
install(DIRECTORY "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/${CMAKE_BUILD_TYPE}/" DESTINATION ./ FILES_MATCHING PATTERN "*.dll")
|
||||
|
||||
# qt5 plugins: audio, iconengines, imageformats, platforms, printsupport
|
||||
install(DIRECTORY "${QT_PLUGINS_DIR}/" DESTINATION ${plugin_dest_dir} COMPONENT Runtime
|
||||
FILES_MATCHING REGEX "(audio|iconengines|imageformats|platforms|printsupport)/.*[^d]\\.dll")
|
||||
FILES_MATCHING REGEX "(audio|iconengines|imageformats|platforms|printsupport|styles)/.*[^d]\\.dll")
|
||||
|
||||
install(CODE "
|
||||
file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${qtconf_dest_dir}/qt.conf\" \"[Paths]
|
||||
|
||||
@@ -16,16 +16,20 @@
|
||||
<file>resources/icons/conceded.svg</file>
|
||||
<file>resources/icons/decrement.svg</file>
|
||||
<file>resources/icons/delete.svg</file>
|
||||
<file>resources/icons/forgot_password.svg</file>
|
||||
<file>resources/icons/increment.svg</file>
|
||||
<file>resources/icons/info.svg</file>
|
||||
<file>resources/icons/lock.svg</file>
|
||||
<file>resources/icons/not_ready_start.svg</file>
|
||||
<file>resources/icons/pencil.svg</file>
|
||||
<file>resources/icons/player.svg</file>
|
||||
<file>resources/icons/ready_start.svg</file>
|
||||
<file>resources/icons/remove_row.svg</file>
|
||||
<file>resources/icons/scales.svg</file>
|
||||
<file>resources/icons/search.svg</file>
|
||||
<file>resources/icons/settings.svg</file>
|
||||
<file>resources/icons/spectator.svg</file>
|
||||
<file>resources/icons/sync.svg</file>
|
||||
<file>resources/icons/tab_changed.svg</file>
|
||||
<file>resources/icons/update.png</file>
|
||||
<file>resources/icons/view.svg</file>
|
||||
@@ -303,10 +307,6 @@
|
||||
<file>resources/countries/zm.svg</file>
|
||||
<file>resources/countries/zw.svg</file>
|
||||
|
||||
<file>resources/genders/male.svg</file>
|
||||
<file>resources/genders/female.svg</file>
|
||||
<file>resources/genders/unknown.svg</file>
|
||||
|
||||
<file>resources/phases/untap.svg</file>
|
||||
<file>resources/phases/upkeep.svg</file>
|
||||
<file>resources/phases/draw.svg</file>
|
||||
@@ -343,18 +343,22 @@
|
||||
<file>resources/userlevels/admin_vip_buddy.svg</file>
|
||||
|
||||
<!-- ADD TIP OF THE DAY IMAGES HERE -->
|
||||
<file>resources/tips/tips_of_the_day.xml</file>
|
||||
<file>resources/tips/images/accounts_tab.png</file>
|
||||
<file>resources/tips/images/arrows.png</file>
|
||||
<file>resources/tips/images/cockatrice_register.png</file>
|
||||
<file>resources/tips/images/cockatrice_wiki.png</file>
|
||||
<file>resources/tips/images/coin_flip.png</file>
|
||||
<file>resources/tips/images/counter_expression.png</file>
|
||||
<file>resources/tips/images/face_down.png</file>
|
||||
<file>resources/tips/images/filter_games.png</file>
|
||||
<file>resources/tips/images/github_logo.png</file>
|
||||
<file>resources/tips/images/gitter.png</file>
|
||||
<file>resources/tips/images/setpt.png</file>
|
||||
<file>resources/tips/images/shortcuts.png</file>
|
||||
<file>resources/tips/images/themes.png</file>
|
||||
<file>resources/tips/images/tip_of_the_day.png</file>
|
||||
<file>resources/tips/tips_of_the_day.xml</file>
|
||||
|
||||
<file>resources/help/search.md</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
version="1.0"
|
||||
width="75"
|
||||
height="75"
|
||||
id="svg34864">
|
||||
<defs
|
||||
id="defs34866" />
|
||||
<g
|
||||
transform="translate(-348.7552,-478.0905)"
|
||||
id="layer1">
|
||||
<g
|
||||
transform="matrix(1.071197,0,0,1.075147,-13.30677,-36.99488)"
|
||||
id="g3773">
|
||||
<path
|
||||
d="M 176 33 A 11 11 0 1 1 154,33 A 11 11 0 1 1 176 33 z"
|
||||
transform="matrix(1.540096,0,0,1.5384,118.8893,454.0543)"
|
||||
style="color:black;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:black;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
|
||||
id="path3939" />
|
||||
<path
|
||||
d="M 373.00525,521.74399 L 373.00525,543.28159"
|
||||
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:black;stroke-width:4.61774349;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path3941" />
|
||||
<path
|
||||
d="M 363.76467,534.05119 L 382.24582,534.05119"
|
||||
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:black;stroke-width:4.61774349;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4816" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
@@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.0" width="75" height="75" id="svg34864">
|
||||
<defs id="defs34866"/>
|
||||
<g transform="translate(-348.755, -478.091)" id="layer1">
|
||||
<g transform="matrix(1.94812, 0, 0, 1.93731, -342.43, -460.01)" id="g1872">
|
||||
<path d="M 387.95009,489.60348 L 378.66214,498.89143" style="opacity: 1; color: black; fill: none; fill-opacity: 0.75; fill-rule: evenodd; stroke: black; stroke-width: 3; stroke-linecap: butt; stroke-linejoin: miter; marker: none; stroke-miterlimit: 4; stroke-dasharray: none; stroke-dashoffset: 0pt; stroke-opacity: 1; visibility: visible; display: inline; overflow: visible;" id="path26867"/>
|
||||
<path d="M 49.396475 36.70454 A 15.623922 16.319134 0 1 1 18.14863,36.70454 A 15.623922 16.319134 0 1 1 49.396475 36.70454 z" transform="matrix(0.48802, 0.48802, -0.467594, 0.467594, 371.609, 473.136)" style="opacity: 1; color: black; fill: none; fill-opacity: 1; fill-rule: evenodd; stroke: black; stroke-width: 4.44072; stroke-linecap: butt; stroke-linejoin: miter; marker: none; stroke-miterlimit: 4; stroke-dasharray: none; stroke-dashoffset: 0pt; stroke-opacity: 1; visibility: visible; display: inline; overflow: visible;" id="path26871"/>
|
||||
<path d="M 379.92823,489.70212 C 387.842,489.70212 387.842,489.70212 387.842,489.70212 L 387.842,497.61589" style="fill: none; fill-rule: evenodd; stroke: black; stroke-width: 3; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 4; stroke-dasharray: none; stroke-opacity: 1;" id="path27759"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.7 KiB |
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Intersexual/Transgendered symbol by Gregory Maxwell. Copyright 2005. GFDL-1.2 only -->
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
version="1.1"
|
||||
width="210"
|
||||
height="280">
|
||||
<path d="M 188,38 v 20 l 14,-8 v -42 h -42 l -8,14 h 20 l -37,37 a 79,79 0 1,0 -59,141 v 22 h -27 v 23 h 27 v 27 h 23 v -27 h 27 v -23 h -27 v -22 a 79,79 0 0,0 52,-125 zm -100,27 a 57,57 0 1,1 0,114 a 57,57 0 1,1 0,-114 z"/>
|
||||
<!-- 88,65 // -63,-10 -->
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 500 B |
61
cockatrice/resources/help/search.md
Normal file
@@ -0,0 +1,61 @@
|
||||
## Syntax Help
|
||||
-----
|
||||
The search bar recognizes a set of special commands similar to some other card databases. Here is a list with examples. Each entry can be clicked to test the query and has a small explanation. Note that all searches are case insensitive.
|
||||
<dl>
|
||||
<dt>Name:</dt>
|
||||
<dd>[birds of paradise](#birds of paradise) <small>(Any card name containing the words birds, of, and paradise)</small></dd>
|
||||
<dd>["birds of paradise"](#%22birds of paradise%22) <small>(Any card name containing the exact phrase "birds of paradise")</small></dd>
|
||||
|
||||
<dt>Rules Text (<u>O</u>racle):</dt>
|
||||
<dd>[o:flying](#o:flying) <small>(Any card text that has the word flying)</small></dd>
|
||||
<dd>[o:"first strike"](#o:%22first strike%22) <small>(Any card text that has the exact phrase "first strike")</small></dd>
|
||||
<dd>[o:"{T}" o:"add one mana of any color"](#o:%22{T}%22 o:%22add one mana of any color%22) <small>(Any card text that has a tap symbol and the phrase "add one mana of any color")</small></dd>
|
||||
|
||||
<dt><u>T</u>ypes:</dt>
|
||||
<dd>[t:angel](#t:angel) <small>(Any card with the type angel)</small></dd>
|
||||
<dd>[t:angel t:legendary](#t:angel t:legendary) <small>(Any angel that's also legendary)</small></dd>
|
||||
<dd>[t:basic](#t:basic) <small>(Any card with the type basic)</small></dd>
|
||||
<dd>[t:arcane t:instant](#t:arcane t:instant) <small>(Any card with the types arcane and instant)</small></dd>
|
||||
|
||||
<dt><u>C</u>olors:</dt>
|
||||
<dd>[c:w](#c:w) <small>(Any card that is white)</small></dd>
|
||||
<dd>[c:wu](#c:wu) <small>(Any card that is white or blue)</small></dd>
|
||||
<dd>[c:wum](#c:wum) <small>(Any card that is white or blue, and multicolored)</small></dd>
|
||||
<!--
|
||||
<dd>[c!w](#c!w) <small>(Cards that are only white)</small></dd>
|
||||
<dd>[c!wu](#c!wu) <small>(Cards that are only white or blue, or both)</small></dd>
|
||||
<dd>[c!wum](#c!wum) <small>(Cards that are only white and blue, and multicolored)</small></dd>
|
||||
<dd>[c=wubrg](#c%3Dwubrg) <small>(Cards that are all five colors)</small></dd>
|
||||
-->
|
||||
<dd>[c:c](#c:c) <small>(Any colorless card)</small></dd>
|
||||
<dd>[ci:w](#ci:w) <small>(Any card that has white in it's color identity)</small></dd>
|
||||
|
||||
<dt><u>Pow</u>er, <u>Tou</u>ghness, <u>M</u>ana <u>V</u>alue:</dt>
|
||||
<dd>[tou:1](#tou:1) <small>(Any card with a toughness of 1)</small></dd>
|
||||
<dd>[pow>=8](#pow>=8) <small>(Any card with a power greater than or equal to 8)</small></dd>
|
||||
<dd>[mv=7](#mv=7) <small>(Any card with a mana value equal to 7)</small></dd>
|
||||
|
||||
<dt><u>R</u>arity:</dt>
|
||||
<dd>[r:mythic](#r:mythic) <small>(Any card that has the mythic-rare rarity)</small></dd>
|
||||
|
||||
<dt><u>F</u>ormat:</dt>
|
||||
<dd>[f:standard](#f:standard) <small>(Any card that can be played in standard)</small></dd>
|
||||
<dd>[banned:modern](#banned:modern) <small>(Any card that is banned in modern)</small></dd>
|
||||
<dd>[restricted:vintage](#restricted:vintage) <small>(Any card that is restricted in vintage)</small></dd>
|
||||
<dd>[legal:pauper](#legal:pauper) <small>(Any card that is legal in pauper)</small></dd>
|
||||
|
||||
<dt><u>E</u>dition:</dt>
|
||||
<dd>[set:lea](#set:lea) <small>(Cards that appear in Alpha, which has the set code LEA)</small></dd>
|
||||
<dd>[e:lea,leb](#e:lea,leb) <small>(Cards that appear in Alpha or Beta)</small></dd>
|
||||
<dd><a href="#e:lea,leb -(e:lea e:leb)">e:lea,leb -(e:lea e:leb)</a> <small>(Cards that appear in Alpha or Beta but not in both editions)</small></dd>
|
||||
|
||||
<dt>Negate:</dt>
|
||||
<dd>[c:wu -c:m](#c:wu -c:m) <small>(Any card that is white or blue, but not multicolored)</small></dd>
|
||||
|
||||
<dt>Branching:</dt>
|
||||
<dd>[t:sliver or o:changeling](#t:sliver or o:changeling) <small>(Any card that is either a sliver or has changeling)</small></dd>
|
||||
|
||||
<dt>Grouping:</dt>
|
||||
<dd><a href="#t:angel -(angel or c:w)">t:angel -(angel or c:w)</a> <small>(Any angel that doesn't have angel in its name and isn't white)</small></dd>
|
||||
|
||||
</dl>
|
||||
6
cockatrice/resources/icons/forgot_password.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="48pt" height="48pt" viewBox="0 0 48 48" version="1.1">
|
||||
<g id="surface1">
|
||||
<path style=" stroke:none;fill-rule:nonzero;fill:#000000;fill-opacity:1;" d="M 24 2 C 17.398438 2 12 7.398438 12 14 L 12 16 C 9.789063 16 8 17.789063 8 20 L 8 40 C 8 42.210938 9.789063 44 12 44 L 36 44 C 38.210938 44 40 42.210938 40 40 L 40 20 C 40 17.789063 38.210938 16 36 16 L 36 14 C 36 7.398438 30.601563 2 24 2 Z M 24 6 C 28.4375 6 32 9.5625 32 14 L 32 16 L 16 16 L 16 14 C 16 9.5625 19.554688 6 24 6 Z M 23.9375 19.9375 C 29.929688 19.9375 30 24.398438 30 25.25 C 30 29.71875 25.125 29.96875 25.375 33.875 L 21.875 33.875 C 21.578125 28.632813 26.0625 28.398438 26.0625 25.4375 C 26.0625 23.3125 24.632813 23.125 23.9375 23.125 C 21.859375 23.125 21.757813 25.09375 21.75 25.5 L 17.8125 25.5 C 17.820313 24.554688 18.015625 19.9375 23.9375 19.9375 Z M 23.6875 35.9375 C 25.671875 35.9375 25.875 37.765625 25.875 38.0625 C 25.875 38.351563 25.671875 40.125 23.6875 40.125 C 21.632813 40.125 21.5 38.351563 21.5 38.0625 C 21.5 37.765625 21.671875 35.9375 23.6875 35.9375 Z "/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
43
cockatrice/resources/icons/info.svg
Normal file
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="100"
|
||||
height="100"
|
||||
id="svg2858"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.5 r10040"
|
||||
sodipodi:docname="info.svg">
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer2"
|
||||
inkscape:label="Triangle"
|
||||
style="display:inline">
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
style="fill:#aaaaaa;fill-opacity:1;fill-rule:nonzero;stroke:none;display:inline"
|
||||
id="path3758-1"
|
||||
sodipodi:cx="53.900002"
|
||||
sodipodi:cy="78.5"
|
||||
sodipodi:rx="46.5"
|
||||
sodipodi:ry="46.5"
|
||||
d="M 100.4,78.5 A 46.5,46.5 0 1 1 7.4000015,78.5 46.5,46.5 0 1 1 100.4,78.5 z"
|
||||
transform="matrix(1.05866,0,0,1.05866,-7.0617752,-32.704809)" />
|
||||
<g
|
||||
style="font-size:133.49534607px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1.06498075;stroke-opacity:1;font-family:'Magic:the Gathering';-inkscape-font-specification:'Magic:the Gathering'"
|
||||
id="text3759">
|
||||
<path
|
||||
d="m 46.854005,16.085692 c 0.977733,-2.411704 1.683885,-4.193378 2.118456,-5.345028 0.369355,-1.1514901 0.771318,-1.9988718 1.205891,-2.5421482 0.456264,-0.4344718 1.347101,-0.825571 2.672514,-1.1732989 1.23846,-0.2823765 2.705082,-0.4670623 4.399871,-0.5540578 0.369346,8.46e-5 0.727854,8.46e-5 1.075524,0 1.694737,8.46e-5 2.672485,0.5758696 2.933248,1.7273567 -3e-5,0.1956326 -3e-5,0.3803183 0,0.5540579 -3e-5,1.3254743 -0.662726,2.9224633 -1.98809,4.7909703 -1.520969,2.238035 -3.43301,4.24785 -5.736128,6.029453 -2.32489,1.760018 -4.551983,2.781222 -6.681286,3.063614 -1.260224,-0.543125 -1.977239,-1.075454 -2.151048,-1.59699 -1.3e-5,-0.08684 -1.3e-5,-0.184616 0,-0.293325 -1.3e-5,-0.434484 0.228128,-1.010269 0.684424,-1.727357 0.521453,-0.890765 1.010327,-1.868513 1.466624,-2.933247 z M 31.372977,37.856906 c 2.650785,-0.716962 6.159814,-1.651255 10.527099,-2.802881 4.280354,-1.173242 7.398284,-1.803346 9.3538,-1.890315 0.08689,5.8e-5 0.173803,5.8e-5 0.260733,0 1.06464,5.8e-5 1.82511,0.358566 2.281415,1.075524 0.260711,0.630161 0.391077,1.520998 0.391099,2.672514 -2.2e-5,0.260787 -2.2e-5,0.532384 0,0.814791 -0.173844,1.586178 -0.260755,2.781204 -0.260733,3.58508 l 0,39.533656 c 1.238459,1e-5 2.976678,-0.673549 5.214662,-2.020681 1.673009,-0.977736 2.868034,-1.46661 3.58508,-1.466624 0.173791,1.4e-5 0.304158,0.04347 0.3911,0.130367 0.543162,0.369385 0.814759,0.771348 0.814791,1.20589 -3.2e-5,1.151583 -1.868617,2.66166 -5.605762,4.530238 -1.238507,0.717023 -3.063637,1.922912 -5.475395,3.617671 -2.49871,1.781679 -4.769259,3.204846 -6.811652,4.269505 -0.347658,0.347644 -0.923443,0.662696 -1.727357,0.945157 -0.260745,0.08691 -0.532342,0.130366 -0.814791,0.130367 -0.521477,-10e-7 -1.010351,-0.271598 -1.466623,-0.814791 C 41.856611,91.111641 41.7697,81.594892 41.76971,62.8221 c -1e-5,-1.955468 -1e-5,-4.008739 0,-6.15982 -1e-5,-2.129283 -0.08692,-4.128235 -0.260734,-5.996861 -0.260742,-1.781634 -0.706161,-3.204801 -1.336257,-4.269504 -0.717023,-1.347075 -1.60786,-2.368279 -2.672514,-3.063614 -1.151576,-0.630057 -2.357465,-1.260161 -3.617672,-1.890315 -1.325394,-0.521416 -2.161911,-1.053746 -2.509556,-1.59699 -0.195549,-0.173771 -0.293324,-0.434504 -0.293324,-0.7822 -0.08691,-0.369319 0.01086,-0.771282 0.293324,-1.20589 z"
|
||||
style=""
|
||||
id="path2993" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
6
cockatrice/resources/icons/scales.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="52pt" height="52pt" viewBox="0 0 52 52" version="1.1">
|
||||
<g id="surface1">
|
||||
<path style=" stroke:none;fill-rule:nonzero;fill:#000000;fill-opacity:1;" d="M 25.605469 0.125 C 25.605469 0.125 23.265625 3.140625 23.265625 4.433594 C 23.265625 5.316406 23.765625 6.070313 24.5 6.464844 C 23.898438 6.65625 23.375 6.992188 22.960938 7.449219 C 22.011719 6.640625 20.628906 5.910156 18.710938 5.910156 C 16.640625 5.910156 14.863281 6.753906 13.355469 7.507813 C 12.601563 7.886719 11.925781 8.25 11.324219 8.496094 C 10.964844 8.09375 10.425781 7.878906 9.847656 7.878906 C 8.75 7.878906 7.878906 8.75 7.878906 9.847656 C 7.878906 10.949219 8.75 11.816406 9.847656 11.816406 C 10.757813 11.816406 11.519531 11.234375 11.757813 10.402344 C 12.578125 10.078125 13.386719 9.648438 14.21875 9.234375 C 15.671875 8.507813 17.179688 7.878906 18.710938 7.878906 C 20.496094 7.878906 21.519531 8.679688 22.097656 9.355469 C 22.074219 9.519531 22.035156 9.6875 22.035156 9.847656 C 22.035156 11.816406 23.636719 13.417969 25.605469 13.417969 C 27.574219 13.417969 29.175781 11.816406 29.175781 9.847656 C 29.175781 9.6875 29.136719 9.519531 29.113281 9.355469 C 29.691406 8.679688 30.714844 7.878906 32.5 7.878906 C 34.03125 7.878906 35.539063 8.507813 36.992188 9.234375 C 37.824219 9.648438 38.632813 10.078125 39.457031 10.402344 C 39.695313 11.234375 40.457031 11.816406 41.363281 11.816406 C 42.464844 11.816406 43.332031 10.949219 43.332031 9.847656 C 43.332031 8.75 42.464844 7.878906 41.363281 7.878906 C 40.785156 7.878906 40.246094 8.09375 39.886719 8.496094 C 39.285156 8.25 38.609375 7.886719 37.855469 7.507813 C 36.347656 6.753906 34.570313 5.910156 32.5 5.910156 C 30.585938 5.910156 29.199219 6.640625 28.253906 7.449219 C 27.835938 6.992188 27.3125 6.65625 26.714844 6.464844 C 27.445313 6.070313 27.945313 5.316406 27.945313 4.433594 C 27.945313 3.140625 25.605469 0.125 25.605469 0.125 Z M 9.539063 13.234375 C 9.25 13.332031 9.023438 13.558594 8.925781 13.847656 L 2.277344 31.515625 L 0 31.515625 C 1.445313 35.578125 5.285156 38.53125 9.847656 38.53125 C 14.410156 38.53125 18.25 35.578125 19.695313 31.515625 L 17.417969 31.515625 L 10.773438 13.847656 C 10.601563 13.402344 10.132813 13.140625 9.664063 13.234375 C 9.625 13.234375 9.578125 13.234375 9.539063 13.234375 Z M 41.054688 13.234375 C 40.761719 13.332031 40.539063 13.558594 40.441406 13.847656 L 33.792969 31.515625 L 31.515625 31.515625 C 32.960938 35.578125 36.800781 38.53125 41.363281 38.53125 C 45.925781 38.53125 49.765625 35.578125 51.210938 31.515625 L 48.933594 31.515625 L 42.285156 13.847656 C 42.117188 13.402344 41.648438 13.140625 41.179688 13.234375 C 41.140625 13.234375 41.09375 13.234375 41.054688 13.234375 Z M 21.914063 15.757813 C 21.914063 17.082031 22.589844 18.242188 23.636719 18.898438 L 23.636719 41.980469 C 21.429688 42.773438 19.796875 44.816406 19.695313 47.273438 L 15.757813 47.273438 C 14.671875 47.273438 13.789063 48.15625 13.789063 49.242188 L 13.789063 51.210938 L 37.425781 51.210938 L 37.425781 49.242188 C 37.425781 48.15625 36.539063 47.273438 35.453125 47.273438 L 31.515625 47.273438 C 31.414063 44.816406 29.785156 42.773438 27.574219 41.980469 L 27.574219 18.898438 C 28.621094 18.242188 29.300781 17.082031 29.300781 15.757813 Z M 9.847656 17.050781 L 15.328125 31.515625 L 4.371094 31.515625 Z M 41.363281 17.050781 L 46.84375 31.515625 L 35.886719 31.515625 Z "/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.4 KiB |
6
cockatrice/resources/icons/sync.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="520pt" height="520pt" viewBox="0 0 520 520" version="1.1">
|
||||
<g id="surface1">
|
||||
<path style=" stroke:none;fill-rule:nonzero;fill:#000000;fill-opacity:1;" d="M 240 0 L 240 45 C 130.078125 55.234375 43.75 147.421875 43.75 260 C 43.75 318.671875 67.03125 371.640625 105 410.625 L 159.375 361.875 C 133.203125 335.859375 116.875 299.765625 116.875 260 C 116.875 187.8125 170.46875 127.96875 240 118.125 L 240 160 L 368.125 80 Z M 415 109.375 L 361.25 158.125 C 387.421875 184.140625 403.125 220.3125 403.125 260 C 403.125 332.265625 349.609375 391.484375 280 401.25 L 280 360 L 151.875 440 L 280 520 L 280 475 C 389.921875 464.765625 476.25 372.578125 476.25 260 C 476.25 201.328125 452.96875 148.359375 415 109.375 Z "/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 856 B |
BIN
cockatrice/resources/tips/images/counter_expression.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
cockatrice/resources/tips/images/setpt.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
cockatrice/resources/tips/images/shortcuts.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
@@ -63,6 +63,7 @@
|
||||
<br>Change Life: CTRL + L
|
||||
<br>All shortcuts can be customized via Settings->Shortcuts!
|
||||
</text>
|
||||
<image>shortcuts.png</image>
|
||||
<date>2018-03-01</date>
|
||||
</tip>
|
||||
<tip>
|
||||
@@ -83,4 +84,16 @@
|
||||
<image>face_down.png</image>
|
||||
<date>2018-03-01</date>
|
||||
</tip>
|
||||
</tips>
|
||||
<tip>
|
||||
<title>Counter expressions</title>
|
||||
<text>When setting a counter value, you can type a math expression in the box and the counter will be set to the result.<br>The "x" variable contains the current counter value.</text>
|
||||
<image>counter_expression.png</image>
|
||||
<date>2019-02-02</date>
|
||||
</tip>
|
||||
<tip>
|
||||
<title>Power and Toughness</title>
|
||||
<text>You can add and subtract to a creature's stats.<br>With a card selected, set the power and toughness ( default: ctrl + p ) and enter +3/-1 to increase power by three while decreasing toughness by one.<br>You can also reset it to the original value ( default: ctrl + alt + 0 ).</text>
|
||||
<image>setpt.png</image>
|
||||
<date>2019-03-02</date>
|
||||
</tip>
|
||||
</tips>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "abstractcarddragitem.h"
|
||||
|
||||
#include "carddatabase.h"
|
||||
|
||||
#include <QCursor>
|
||||
#include <QDebug>
|
||||
#include <QGraphicsSceneMouseEvent>
|
||||
@@ -39,7 +41,6 @@ AbstractCardDragItem::AbstractCardDragItem(AbstractCardItem *_item,
|
||||
|
||||
AbstractCardDragItem::~AbstractCardDragItem()
|
||||
{
|
||||
qDebug("CardDragItem destructor");
|
||||
for (int i = 0; i < childDrags.size(); i++)
|
||||
delete childDrags[i];
|
||||
}
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
#include <QCursor>
|
||||
#include <QGraphicsScene>
|
||||
#include <QGraphicsSceneMouseEvent>
|
||||
#include <QPainter>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#ifdef _WIN32
|
||||
#include "round.h"
|
||||
#endif /* _WIN32 */
|
||||
#include "abstractcarditem.h"
|
||||
|
||||
#include "carddatabase.h"
|
||||
#include "gamescene.h"
|
||||
#include "main.h"
|
||||
#include "pictureloader.h"
|
||||
#include "settingscache.h"
|
||||
|
||||
#include <QCursor>
|
||||
#include <QGraphicsScene>
|
||||
#include <QGraphicsSceneMouseEvent>
|
||||
#include <QPainter>
|
||||
#include <algorithm>
|
||||
|
||||
AbstractCardItem::AbstractCardItem(const QString &_name, Player *_owner, int _id, QGraphicsItem *parent)
|
||||
: ArrowTarget(_owner, parent), id(_id), name(_name), tapped(false), facedown(false), tapAngle(0),
|
||||
bgColor(Qt::transparent), isHovered(false), realZValue(0)
|
||||
@@ -22,7 +20,7 @@ AbstractCardItem::AbstractCardItem(const QString &_name, Player *_owner, int _id
|
||||
setFlag(ItemIsSelectable);
|
||||
setCacheMode(DeviceCoordinateCache);
|
||||
|
||||
connect(settingsCache, SIGNAL(displayCardNamesChanged()), this, SLOT(callUpdate()));
|
||||
connect(&SettingsCache::instance(), SIGNAL(displayCardNamesChanged()), this, SLOT(callUpdate()));
|
||||
cardInfoUpdated();
|
||||
}
|
||||
|
||||
@@ -45,8 +43,16 @@ void AbstractCardItem::pixmapUpdated()
|
||||
void AbstractCardItem::cardInfoUpdated()
|
||||
{
|
||||
info = db->getCard(name);
|
||||
if (info)
|
||||
|
||||
if (!info && !name.isEmpty()) {
|
||||
QVariantHash properties = QVariantHash();
|
||||
|
||||
info = CardInfo::newInstance(name, "", true, QVariantHash(), QList<CardRelation *>(), QList<CardRelation *>(),
|
||||
CardInfoPerSetMap(), false, -1, false);
|
||||
}
|
||||
if (info.data()) {
|
||||
connect(info.data(), SIGNAL(pixmapUpdated()), this, SLOT(pixmapUpdated()));
|
||||
}
|
||||
|
||||
cacheBgColor();
|
||||
update();
|
||||
@@ -66,21 +72,19 @@ QSizeF AbstractCardItem::getTranslatedSize(QPainter *painter) const
|
||||
|
||||
void AbstractCardItem::transformPainter(QPainter *painter, const QSizeF &translatedSize, int angle)
|
||||
{
|
||||
const int MAX_FONT_SIZE = settingsCache->getMaxFontSize();
|
||||
const int MAX_FONT_SIZE = SettingsCache::instance().getMaxFontSize();
|
||||
const int fontSize = std::max(9, MAX_FONT_SIZE);
|
||||
|
||||
QRectF totalBoundingRect = painter->combinedTransform().mapRect(boundingRect());
|
||||
|
||||
painter->resetTransform();
|
||||
int scale = resetPainterTransform(painter);
|
||||
|
||||
QTransform pixmapTransform;
|
||||
pixmapTransform.translate(totalBoundingRect.width() / 2, totalBoundingRect.height() / 2);
|
||||
pixmapTransform.rotate(angle);
|
||||
pixmapTransform.translate(-translatedSize.width() / 2, -translatedSize.height() / 2);
|
||||
painter->setTransform(pixmapTransform);
|
||||
painter->translate(totalBoundingRect.width() / 2, totalBoundingRect.height() / 2);
|
||||
painter->rotate(angle);
|
||||
painter->translate(-translatedSize.width() / 2, -translatedSize.height() / 2);
|
||||
|
||||
QFont f;
|
||||
f.setPixelSize(fontSize);
|
||||
f.setPixelSize(fontSize * scale);
|
||||
|
||||
painter->setFont(f);
|
||||
}
|
||||
@@ -127,7 +131,7 @@ void AbstractCardItem::paintPicture(QPainter *painter, const QSizeF &translatedS
|
||||
else
|
||||
painter->drawRect(QRectF(1, 1, CARD_WIDTH - 2, CARD_HEIGHT - 1.5));
|
||||
|
||||
if (translatedPixmap.isNull() || settingsCache->getDisplayCardNames() || facedown) {
|
||||
if (translatedPixmap.isNull() || SettingsCache::instance().getDisplayCardNames() || facedown) {
|
||||
painter->save();
|
||||
transformPainter(painter, translatedSize, angle);
|
||||
painter->setPen(Qt::white);
|
||||
@@ -197,7 +201,7 @@ void AbstractCardItem::setHovered(bool _hovered)
|
||||
processHoverEvent();
|
||||
isHovered = _hovered;
|
||||
setZValue(_hovered ? 2000000004 : realZValue);
|
||||
setScale(_hovered && settingsCache->getScaleCards() ? 1.1 : 1);
|
||||
setScale(_hovered && SettingsCache::instance().getScaleCards() ? 1.1 : 1);
|
||||
setTransformOriginPoint(_hovered ? CARD_WIDTH / 2 : 0, _hovered ? CARD_HEIGHT / 2 : 0);
|
||||
update();
|
||||
}
|
||||
@@ -250,7 +254,7 @@ void AbstractCardItem::setTapped(bool _tapped, bool canAnimate)
|
||||
return;
|
||||
|
||||
tapped = _tapped;
|
||||
if (settingsCache->getTapAnimation() && canAnimate)
|
||||
if (SettingsCache::instance().getTapAnimation() && canAnimate)
|
||||
static_cast<GameScene *>(scene())->registerAnimationItem(this);
|
||||
else {
|
||||
tapAngle = tapped ? 90 : 0;
|
||||
@@ -266,7 +270,6 @@ void AbstractCardItem::setFaceDown(bool _facedown)
|
||||
{
|
||||
facedown = _facedown;
|
||||
update();
|
||||
emit updateCardMenu(this);
|
||||
}
|
||||
|
||||
void AbstractCardItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
||||
@@ -281,14 +284,14 @@ void AbstractCardItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
||||
}
|
||||
if (event->button() == Qt::LeftButton)
|
||||
setCursor(Qt::ClosedHandCursor);
|
||||
else if (event->button() == Qt::MidButton)
|
||||
else if (event->button() == Qt::MiddleButton)
|
||||
emit showCardInfoPopup(event->screenPos(), name);
|
||||
event->accept();
|
||||
}
|
||||
|
||||
void AbstractCardItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
if (event->button() == Qt::MidButton)
|
||||
if (event->button() == Qt::MiddleButton)
|
||||
emit deleteCardInfoPopup(name);
|
||||
|
||||
// This function ensures the parent function doesn't mess around with our selection.
|
||||
|
||||
@@ -36,7 +36,6 @@ signals:
|
||||
void hovered(AbstractCardItem *card);
|
||||
void showCardInfoPopup(QPoint pos, QString cardName);
|
||||
void deleteCardInfoPopup(QString cardName);
|
||||
void updateCardMenu(AbstractCardItem *card);
|
||||
void sigPixmapUpdated();
|
||||
void cardShiftClicked(QString cardName);
|
||||
|
||||
@@ -49,7 +48,10 @@ public:
|
||||
{
|
||||
return Type;
|
||||
}
|
||||
AbstractCardItem(const QString &_name = QString(), Player *_owner = 0, int _id = -1, QGraphicsItem *parent = 0);
|
||||
AbstractCardItem(const QString &_name = QString(),
|
||||
Player *_owner = nullptr,
|
||||
int _id = -1,
|
||||
QGraphicsItem *parent = nullptr);
|
||||
~AbstractCardItem();
|
||||
QRectF boundingRect() const;
|
||||
QSizeF getTranslatedSize(QPainter *painter) const;
|
||||
|
||||
@@ -19,9 +19,11 @@
|
||||
#include "pb/event_user_message.pb.h"
|
||||
#include "pb/server_message.pb.h"
|
||||
#include "pending_command.h"
|
||||
|
||||
#include <google/protobuf/descriptor.h>
|
||||
|
||||
AbstractClient::AbstractClient(QObject *parent) : QObject(parent), nextCmdId(0), status(StatusDisconnected)
|
||||
AbstractClient::AbstractClient(QObject *parent)
|
||||
: QObject(parent), nextCmdId(0), status(StatusDisconnected), serverSupportsPasswordHash(false)
|
||||
{
|
||||
qRegisterMetaType<QVariant>("QVariant");
|
||||
qRegisterMetaType<CommandContainer>("CommandContainer");
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "pb/response.pb.h"
|
||||
#include "pb/serverinfo_user.pb.h"
|
||||
|
||||
#include <QMutex>
|
||||
#include <QObject>
|
||||
#include <QVariant>
|
||||
@@ -39,6 +40,7 @@ enum ClientStatus
|
||||
StatusRequestingForgotPassword,
|
||||
StatusSubmitForgotPasswordReset,
|
||||
StatusSubmitForgotPasswordChallenge,
|
||||
StatusGettingPasswordSalt,
|
||||
};
|
||||
|
||||
class AbstractClient : public QObject
|
||||
@@ -86,7 +88,7 @@ protected slots:
|
||||
protected:
|
||||
QMap<int, PendingCommand *> pendingCommands;
|
||||
QString userName, password, email, country, realName, token;
|
||||
int gender;
|
||||
bool serverSupportsPasswordHash;
|
||||
void setStatus(ClientStatus _status);
|
||||
int getNewCmdId()
|
||||
{
|
||||
@@ -95,7 +97,7 @@ protected:
|
||||
virtual void sendCommandContainer(const CommandContainer &cont) = 0;
|
||||
|
||||
public:
|
||||
AbstractClient(QObject *parent = 0);
|
||||
AbstractClient(QObject *parent = nullptr);
|
||||
~AbstractClient();
|
||||
|
||||
ClientStatus getStatus() const
|
||||
@@ -106,7 +108,11 @@ public:
|
||||
void sendCommand(const CommandContainer &cont);
|
||||
void sendCommand(PendingCommand *pend);
|
||||
|
||||
const QString getUserName()
|
||||
bool getServerSupportsPasswordHash() const
|
||||
{
|
||||
return serverSupportsPasswordHash;
|
||||
}
|
||||
const QString &getUserName() const
|
||||
{
|
||||
return userName;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
#include "abstractcounter.h"
|
||||
|
||||
#include "expression.h"
|
||||
#include "pb/command_inc_counter.pb.h"
|
||||
#include "pb/command_set_counter.pb.h"
|
||||
#include "player.h"
|
||||
#include "settingscache.h"
|
||||
#include "translatecountername.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QApplication>
|
||||
#include <QGraphicsSceneHoverEvent>
|
||||
#include <QGraphicsSceneMouseEvent>
|
||||
#include <QKeyEvent>
|
||||
#include <QMenu>
|
||||
#include <QPainter>
|
||||
#include <QString>
|
||||
|
||||
AbstractCounter::AbstractCounter(Player *_player,
|
||||
int _id,
|
||||
@@ -15,22 +22,24 @@ AbstractCounter::AbstractCounter(Player *_player,
|
||||
bool _shownInCounterArea,
|
||||
int _value,
|
||||
bool _useNameForShortcut,
|
||||
QGraphicsItem *parent)
|
||||
QGraphicsItem *parent,
|
||||
QWidget *_game)
|
||||
: QGraphicsItem(parent), player(_player), id(_id), name(_name), value(_value),
|
||||
useNameForShortcut(_useNameForShortcut), hovered(false), aDec(0), aInc(0), dialogSemaphore(false),
|
||||
deleteAfterDialog(false), shownInCounterArea(_shownInCounterArea)
|
||||
useNameForShortcut(_useNameForShortcut), hovered(false), aDec(nullptr), aInc(nullptr), dialogSemaphore(false),
|
||||
deleteAfterDialog(false), shownInCounterArea(_shownInCounterArea), game(_game)
|
||||
{
|
||||
setAcceptHoverEvents(true);
|
||||
|
||||
shortcutActive = false;
|
||||
|
||||
if (player->getLocal()) {
|
||||
menu = new QMenu(name);
|
||||
if (player->getLocalOrJudge()) {
|
||||
QString displayName = TranslateCounterName::getDisplayName(_name);
|
||||
menu = new TearOffMenu(displayName);
|
||||
aSet = new QAction(this);
|
||||
connect(aSet, SIGNAL(triggered()), this, SLOT(setCounter()));
|
||||
menu->addAction(aSet);
|
||||
menu->addSeparator();
|
||||
for (int i = 10; i >= -10; --i)
|
||||
for (int i = 10; i >= -10; --i) {
|
||||
if (i == 0) {
|
||||
menu->addSeparator();
|
||||
} else {
|
||||
@@ -43,10 +52,12 @@ AbstractCounter::AbstractCounter(Player *_player,
|
||||
connect(aIncrement, SIGNAL(triggered()), this, SLOT(incrementCounter()));
|
||||
menu->addAction(aIncrement);
|
||||
}
|
||||
} else
|
||||
}
|
||||
} else {
|
||||
menu = nullptr;
|
||||
}
|
||||
|
||||
connect(&settingsCache->shortcuts(), SIGNAL(shortCutchanged()), this, SLOT(refreshShortcuts()));
|
||||
connect(&SettingsCache::instance().shortcuts(), SIGNAL(shortCutChanged()), this, SLOT(refreshShortcuts()));
|
||||
refreshShortcuts();
|
||||
retranslateUi();
|
||||
}
|
||||
@@ -76,16 +87,17 @@ void AbstractCounter::setShortcutsActive()
|
||||
if (!player->getLocal()) {
|
||||
return;
|
||||
}
|
||||
ShortcutsSettings &shortcuts = SettingsCache::instance().shortcuts();
|
||||
if (name == "life") {
|
||||
shortcutActive = true;
|
||||
aSet->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aSet"));
|
||||
aDec->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aDec"));
|
||||
aInc->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aInc"));
|
||||
aSet->setShortcuts(shortcuts.getShortcut("Player/aSet"));
|
||||
aDec->setShortcuts(shortcuts.getShortcut("Player/aDec"));
|
||||
aInc->setShortcuts(shortcuts.getShortcut("Player/aInc"));
|
||||
} else if (useNameForShortcut) {
|
||||
shortcutActive = true;
|
||||
aSet->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aSetCounter_" + name));
|
||||
aDec->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aDecCounter_" + name));
|
||||
aInc->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aIncCounter_" + name));
|
||||
aSet->setShortcuts(shortcuts.getShortcut("Player/aSetCounter_" + name));
|
||||
aDec->setShortcuts(shortcuts.getShortcut("Player/aDecCounter_" + name));
|
||||
aInc->setShortcuts(shortcuts.getShortcut("Player/aIncCounter_" + name));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,8 +113,9 @@ void AbstractCounter::setShortcutsInactive()
|
||||
|
||||
void AbstractCounter::refreshShortcuts()
|
||||
{
|
||||
if (shortcutActive)
|
||||
if (shortcutActive) {
|
||||
setShortcutsActive();
|
||||
}
|
||||
}
|
||||
|
||||
void AbstractCounter::setValue(int _value)
|
||||
@@ -113,8 +126,12 @@ void AbstractCounter::setValue(int _value)
|
||||
|
||||
void AbstractCounter::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
if (isUnderMouse() && player->getLocal()) {
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
if (isUnderMouse() && player->getLocalOrJudge()) {
|
||||
if (event->button() == Qt::MiddleButton || (QApplication::keyboardModifiers() & Qt::ShiftModifier)) {
|
||||
if (menu)
|
||||
menu->exec(event->screenPos());
|
||||
event->accept();
|
||||
} else if (event->button() == Qt::LeftButton) {
|
||||
Command_IncCounter cmd;
|
||||
cmd.set_counter_id(id);
|
||||
cmd.set_delta(1);
|
||||
@@ -126,10 +143,6 @@ void AbstractCounter::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
||||
cmd.set_delta(-1);
|
||||
player->sendGameCommand(cmd);
|
||||
event->accept();
|
||||
} else if (event->button() == Qt::MidButton) {
|
||||
if (menu)
|
||||
menu->exec(event->screenPos());
|
||||
event->accept();
|
||||
}
|
||||
} else
|
||||
event->ignore();
|
||||
@@ -158,20 +171,60 @@ void AbstractCounter::incrementCounter()
|
||||
|
||||
void AbstractCounter::setCounter()
|
||||
{
|
||||
bool ok;
|
||||
dialogSemaphore = true;
|
||||
int newValue = QInputDialog::getInt(0, tr("Set counter"), tr("New value for counter '%1':").arg(name), value,
|
||||
-2000000000, 2000000000, 1, &ok);
|
||||
AbstractCounterDialog dialog(name, QString::number(value), game);
|
||||
const int ok = dialog.exec();
|
||||
|
||||
if (deleteAfterDialog) {
|
||||
deleteLater();
|
||||
return;
|
||||
}
|
||||
dialogSemaphore = false;
|
||||
|
||||
if (!ok)
|
||||
return;
|
||||
|
||||
Expression exp(value);
|
||||
int newValue = static_cast<int>(exp.parse(dialog.textValue()));
|
||||
|
||||
Command_SetCounter cmd;
|
||||
cmd.set_counter_id(id);
|
||||
cmd.set_value(newValue);
|
||||
player->sendGameCommand(cmd);
|
||||
}
|
||||
|
||||
AbstractCounterDialog::AbstractCounterDialog(const QString &name, const QString &value, QWidget *parent)
|
||||
: QInputDialog(parent)
|
||||
{
|
||||
setWindowTitle(tr("Set counter"));
|
||||
setLabelText(tr("New value for counter '%1':").arg(name));
|
||||
setTextValue(value);
|
||||
qApp->installEventFilter(this);
|
||||
}
|
||||
|
||||
bool AbstractCounterDialog::eventFilter(QObject *obj, QEvent *event)
|
||||
{
|
||||
Q_UNUSED(obj);
|
||||
if (event->type() == QEvent::KeyPress) {
|
||||
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
|
||||
switch (keyEvent->key()) {
|
||||
case Qt::Key_Up:
|
||||
changeValue(+1);
|
||||
return true;
|
||||
case Qt::Key_Down:
|
||||
changeValue(-1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void AbstractCounterDialog::changeValue(int diff)
|
||||
{
|
||||
bool ok;
|
||||
int curValue = textValue().toInt(&ok);
|
||||
if (!ok)
|
||||
return;
|
||||
curValue += diff;
|
||||
setTextValue(QString::number(curValue));
|
||||
}
|
||||
|
||||
@@ -1,16 +1,22 @@
|
||||
#ifndef COUNTER_H
|
||||
#define COUNTER_H
|
||||
|
||||
#include "tearoffmenu.h"
|
||||
|
||||
#include <QGraphicsItem>
|
||||
#include <QInputDialog>
|
||||
|
||||
class Player;
|
||||
class QMenu;
|
||||
class QAction;
|
||||
class QKeyEvent;
|
||||
class QMenu;
|
||||
class QString;
|
||||
|
||||
class AbstractCounter : public QObject, public QGraphicsItem
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(QGraphicsItem)
|
||||
|
||||
protected:
|
||||
Player *player;
|
||||
int id;
|
||||
@@ -18,15 +24,18 @@ protected:
|
||||
int value;
|
||||
bool useNameForShortcut, hovered;
|
||||
|
||||
void mousePressEvent(QGraphicsSceneMouseEvent *event);
|
||||
void hoverEnterEvent(QGraphicsSceneHoverEvent *event);
|
||||
void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);
|
||||
void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
|
||||
void hoverEnterEvent(QGraphicsSceneHoverEvent *event) override;
|
||||
void hoverLeaveEvent(QGraphicsSceneHoverEvent *event) override;
|
||||
|
||||
private:
|
||||
QAction *aSet, *aDec, *aInc;
|
||||
QMenu *menu;
|
||||
TearOffMenu *menu;
|
||||
bool dialogSemaphore, deleteAfterDialog;
|
||||
bool shownInCounterArea;
|
||||
bool shortcutActive;
|
||||
QWidget *game;
|
||||
|
||||
private slots:
|
||||
void refreshShortcuts();
|
||||
void incrementCounter();
|
||||
@@ -39,14 +48,20 @@ public:
|
||||
bool _shownInCounterArea,
|
||||
int _value,
|
||||
bool _useNameForShortcut = false,
|
||||
QGraphicsItem *parent = 0);
|
||||
~AbstractCounter();
|
||||
QGraphicsItem *parent = nullptr,
|
||||
QWidget *game = nullptr);
|
||||
~AbstractCounter() override;
|
||||
|
||||
void retranslateUi();
|
||||
void setValue(int _value);
|
||||
void setShortcutsActive();
|
||||
void setShortcutsInactive();
|
||||
void delCounter();
|
||||
|
||||
QMenu *getMenu() const
|
||||
{
|
||||
return menu;
|
||||
}
|
||||
void retranslateUi();
|
||||
|
||||
int getId() const
|
||||
{
|
||||
@@ -64,12 +79,17 @@ public:
|
||||
{
|
||||
return value;
|
||||
}
|
||||
void setValue(int _value);
|
||||
void delCounter();
|
||||
};
|
||||
|
||||
void setShortcutsActive();
|
||||
void setShortcutsInactive();
|
||||
bool shortcutActive;
|
||||
class AbstractCounterDialog : public QInputDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
AbstractCounterDialog(const QString &name, const QString &value, QWidget *parent = nullptr);
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *obj, QEvent *event);
|
||||
void changeValue(int diff);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "abstractgraphicsitem.h"
|
||||
|
||||
#include <QPainter>
|
||||
|
||||
void AbstractGraphicsItem::paintNumberEllipse(int number,
|
||||
@@ -16,7 +17,12 @@ void AbstractGraphicsItem::paintNumberEllipse(int number,
|
||||
font.setWeight(QFont::Bold);
|
||||
|
||||
QFontMetrics fm(font);
|
||||
double w = fm.width(numStr) * 1.3;
|
||||
double w = 1.3 *
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0))
|
||||
fm.horizontalAdvance(numStr);
|
||||
#else
|
||||
fm.width(numStr);
|
||||
#endif
|
||||
double h = fm.height() * 1.3;
|
||||
if (w < h)
|
||||
w = h;
|
||||
@@ -49,3 +55,11 @@ void AbstractGraphicsItem::paintNumberEllipse(int number,
|
||||
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
int resetPainterTransform(QPainter *painter)
|
||||
{
|
||||
painter->resetTransform();
|
||||
auto tx = painter->deviceTransform().inverted();
|
||||
painter->setTransform(tx);
|
||||
return tx.isScaling() ? 1.0 / tx.m11() : 1;
|
||||
}
|
||||
|
||||
@@ -21,9 +21,11 @@ protected:
|
||||
void paintNumberEllipse(int number, int radius, const QColor &color, int position, int count, QPainter *painter);
|
||||
|
||||
public:
|
||||
AbstractGraphicsItem(QGraphicsItem *parent = 0) : QObject(), QGraphicsItem(parent)
|
||||
AbstractGraphicsItem(QGraphicsItem *parent = nullptr) : QObject(), QGraphicsItem(parent)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
int resetPainterTransform(QPainter *painter);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <cmath>
|
||||
|
||||
#include "arrowitem.h"
|
||||
|
||||
#include "carddatabase.h"
|
||||
#include "carditem.h"
|
||||
#include "cardzone.h"
|
||||
#include "player.h"
|
||||
#include "playertarget.h"
|
||||
#include "settingscache.h"
|
||||
#include <QDebug>
|
||||
#include <QGraphicsScene>
|
||||
#include <QGraphicsSceneMouseEvent>
|
||||
#include <QPainter>
|
||||
|
||||
#include "color.h"
|
||||
#include "pb/command_attach_card.pb.h"
|
||||
#include "pb/command_create_arrow.pb.h"
|
||||
#include "pb/command_delete_arrow.pb.h"
|
||||
#include "player.h"
|
||||
#include "playertarget.h"
|
||||
#include "settingscache.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QGraphicsScene>
|
||||
#include <QGraphicsSceneMouseEvent>
|
||||
#include <QPainter>
|
||||
#include <cmath>
|
||||
|
||||
ArrowItem::ArrowItem(Player *_player, int _id, ArrowTarget *_startItem, ArrowTarget *_targetItem, const QColor &_color)
|
||||
: QGraphicsItem(), player(_player), id(_id), startItem(_startItem), targetItem(_targetItem), color(_color),
|
||||
@@ -240,12 +240,12 @@ void ArrowDragItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
|
||||
if (startZone->getName().compare("hand") == 0) {
|
||||
startCard->playCard(false);
|
||||
CardInfoPtr ci = startCard->getInfo();
|
||||
if (ci && (((!settingsCache->getPlayToStack() && ci->getTableRow() == 3) ||
|
||||
((settingsCache->getPlayToStack() && ci->getTableRow() != 0) &&
|
||||
if (ci && (((!SettingsCache::instance().getPlayToStack() && ci->getTableRow() == 3) ||
|
||||
((SettingsCache::instance().getPlayToStack() && ci->getTableRow() != 0) &&
|
||||
startCard->getZone()->getName().toStdString() != "stack"))))
|
||||
cmd.set_start_zone("stack");
|
||||
else
|
||||
cmd.set_start_zone(settingsCache->getPlayToStack() ? "stack" : "table");
|
||||
cmd.set_start_zone(SettingsCache::instance().getPlayToStack() ? "stack" : "table");
|
||||
}
|
||||
player->sendGameCommand(cmd);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "arrowtarget.h"
|
||||
|
||||
#include "arrowitem.h"
|
||||
#include "player.h"
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define ARROWTARGET_H
|
||||
|
||||
#include "abstractgraphicsitem.h"
|
||||
|
||||
#include <QList>
|
||||
|
||||
class Player;
|
||||
@@ -18,7 +19,7 @@ private:
|
||||
QList<ArrowItem *> arrowsFrom, arrowsTo;
|
||||
|
||||
public:
|
||||
ArrowTarget(Player *_owner, QGraphicsItem *parent = 0);
|
||||
ArrowTarget(Player *_owner, QGraphicsItem *parent = nullptr);
|
||||
~ArrowTarget();
|
||||
|
||||
Player *getOwner() const
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
#include "carddatabase.h"
|
||||
|
||||
#include "carddbparser/cockatricexml3.h"
|
||||
#include "carddbparser/cockatricexml4.h"
|
||||
#include "game_specific_terms.h"
|
||||
#include "pictureloader.h"
|
||||
#include "settingscache.h"
|
||||
#include "spoilerbackgroundupdater.h"
|
||||
@@ -6,30 +10,15 @@
|
||||
#include <QCryptographicHash>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QDirIterator>
|
||||
#include <QFile>
|
||||
#include <QMessageBox>
|
||||
#include <QRegularExpression>
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
const int CardDatabase::versionNeeded = 3;
|
||||
const char *CardDatabase::TOKENS_SETNAME = "TK";
|
||||
|
||||
static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardSetPtr &set)
|
||||
{
|
||||
if (set.isNull()) {
|
||||
qDebug() << "&operator<< set is nullptr";
|
||||
return xml;
|
||||
}
|
||||
|
||||
xml.writeStartElement("set");
|
||||
xml.writeTextElement("name", set->getShortName());
|
||||
xml.writeTextElement("longname", set->getLongName());
|
||||
xml.writeTextElement("settype", set->getSetType());
|
||||
xml.writeTextElement("releasedate", set->getReleaseDate().toString(Qt::ISODate));
|
||||
xml.writeEndElement();
|
||||
|
||||
return xml;
|
||||
}
|
||||
|
||||
CardSet::CardSet(const QString &_shortName,
|
||||
const QString &_longName,
|
||||
const QString &_setType,
|
||||
@@ -81,27 +70,27 @@ QString CardSet::getCorrectedShortName() const
|
||||
|
||||
void CardSet::loadSetOptions()
|
||||
{
|
||||
sortKey = settingsCache->cardDatabase().getSortKey(shortName);
|
||||
enabled = settingsCache->cardDatabase().isEnabled(shortName);
|
||||
isknown = settingsCache->cardDatabase().isKnown(shortName);
|
||||
sortKey = SettingsCache::instance().cardDatabase().getSortKey(shortName);
|
||||
enabled = SettingsCache::instance().cardDatabase().isEnabled(shortName);
|
||||
isknown = SettingsCache::instance().cardDatabase().isKnown(shortName);
|
||||
}
|
||||
|
||||
void CardSet::setSortKey(unsigned int _sortKey)
|
||||
{
|
||||
sortKey = _sortKey;
|
||||
settingsCache->cardDatabase().setSortKey(shortName, _sortKey);
|
||||
SettingsCache::instance().cardDatabase().setSortKey(shortName, _sortKey);
|
||||
}
|
||||
|
||||
void CardSet::setEnabled(bool _enabled)
|
||||
{
|
||||
enabled = _enabled;
|
||||
settingsCache->cardDatabase().setEnabled(shortName, _enabled);
|
||||
SettingsCache::instance().cardDatabase().setEnabled(shortName, _enabled);
|
||||
}
|
||||
|
||||
void CardSet::setIsKnown(bool _isknown)
|
||||
{
|
||||
isknown = _isknown;
|
||||
settingsCache->cardDatabase().setIsKnown(shortName, _isknown);
|
||||
SettingsCache::instance().cardDatabase().setIsKnown(shortName, _isknown);
|
||||
}
|
||||
|
||||
class SetList::KeyCompareFunctor
|
||||
@@ -120,7 +109,7 @@ public:
|
||||
|
||||
void SetList::sortByKey()
|
||||
{
|
||||
qSort(begin(), end(), KeyCompareFunctor());
|
||||
std::sort(begin(), end(), KeyCompareFunctor());
|
||||
}
|
||||
|
||||
int SetList::getEnabledSetsNum()
|
||||
@@ -224,30 +213,23 @@ void SetList::guessSortKeys()
|
||||
}
|
||||
}
|
||||
|
||||
CardInfoPerSet::CardInfoPerSet(const CardSetPtr &_set) : set(_set)
|
||||
{
|
||||
}
|
||||
|
||||
CardInfo::CardInfo(const QString &_name,
|
||||
bool _isToken,
|
||||
const QString &_manacost,
|
||||
const QString &_cmc,
|
||||
const QString &_cardtype,
|
||||
const QString &_powtough,
|
||||
const QString &_text,
|
||||
const QStringList &_colors,
|
||||
bool _isToken,
|
||||
QVariantHash _properties,
|
||||
const QList<CardRelation *> &_relatedCards,
|
||||
const QList<CardRelation *> &_reverseRelatedCards,
|
||||
bool _upsideDownArt,
|
||||
const QString &_loyalty,
|
||||
CardInfoPerSetMap _sets,
|
||||
bool _cipt,
|
||||
int _tableRow,
|
||||
const SetList &_sets,
|
||||
const QStringMap &_customPicURLs,
|
||||
MuidMap _muIds,
|
||||
QStringMap _collectorNumbers,
|
||||
QStringMap _rarities)
|
||||
: name(_name), isToken(_isToken), sets(_sets), manacost(_manacost), cmc(_cmc), cardtype(_cardtype),
|
||||
powtough(_powtough), text(_text), colors(_colors), relatedCards(_relatedCards),
|
||||
reverseRelatedCards(_reverseRelatedCards), setsNames(), upsideDownArt(_upsideDownArt), loyalty(_loyalty),
|
||||
customPicURLs(_customPicURLs), muIds(std::move(_muIds)), collectorNumbers(std::move(_collectorNumbers)),
|
||||
rarities(std::move(_rarities)), cipt(_cipt), tableRow(_tableRow)
|
||||
bool _upsideDownArt)
|
||||
: name(_name), text(_text), isToken(_isToken), properties(std::move(_properties)), relatedCards(_relatedCards),
|
||||
reverseRelatedCards(_reverseRelatedCards), sets(std::move(_sets)), cipt(_cipt), tableRow(_tableRow),
|
||||
upsideDownArt(_upsideDownArt)
|
||||
{
|
||||
pixmapCacheKey = QLatin1String("card_") + name;
|
||||
simpleName = CardInfo::simplifyName(name);
|
||||
@@ -261,76 +243,27 @@ CardInfo::~CardInfo()
|
||||
}
|
||||
|
||||
CardInfoPtr CardInfo::newInstance(const QString &_name,
|
||||
bool _isToken,
|
||||
const QString &_manacost,
|
||||
const QString &_cmc,
|
||||
const QString &_cardtype,
|
||||
const QString &_powtough,
|
||||
const QString &_text,
|
||||
const QStringList &_colors,
|
||||
bool _isToken,
|
||||
QVariantHash _properties,
|
||||
const QList<CardRelation *> &_relatedCards,
|
||||
const QList<CardRelation *> &_reverseRelatedCards,
|
||||
bool _upsideDownArt,
|
||||
const QString &_loyalty,
|
||||
CardInfoPerSetMap _sets,
|
||||
bool _cipt,
|
||||
int _tableRow,
|
||||
const SetList &_sets,
|
||||
const QStringMap &_customPicURLs,
|
||||
MuidMap _muIds,
|
||||
QStringMap _collectorNumbers,
|
||||
QStringMap _rarities)
|
||||
bool _upsideDownArt)
|
||||
{
|
||||
CardInfoPtr ptr(new CardInfo(_name, _isToken, _manacost, _cmc, _cardtype, _powtough, _text, _colors, _relatedCards,
|
||||
_reverseRelatedCards, _upsideDownArt, _loyalty, _cipt, _tableRow, _sets,
|
||||
_customPicURLs, std::move(_muIds), std::move(_collectorNumbers),
|
||||
std::move(_rarities)));
|
||||
CardInfoPtr ptr(new CardInfo(_name, _text, _isToken, std::move(_properties), _relatedCards, _reverseRelatedCards,
|
||||
_sets, _cipt, _tableRow, _upsideDownArt));
|
||||
ptr->setSmartPointer(ptr);
|
||||
|
||||
for (int i = 0; i < _sets.size(); i++) {
|
||||
_sets[i]->append(ptr);
|
||||
for (const CardInfoPerSet &set : _sets) {
|
||||
set.getPtr()->append(ptr);
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
QString CardInfo::getMainCardType() const
|
||||
{
|
||||
QString result = getCardType();
|
||||
/*
|
||||
Legendary Artifact Creature - Golem
|
||||
Instant // Instant
|
||||
*/
|
||||
|
||||
int pos;
|
||||
if ((pos = result.indexOf('-')) != -1) {
|
||||
result.remove(pos, result.length());
|
||||
}
|
||||
|
||||
if ((pos = result.indexOf("—")) != -1) {
|
||||
result.remove(pos, result.length());
|
||||
}
|
||||
|
||||
if ((pos = result.indexOf("//")) != -1) {
|
||||
result.remove(pos, result.length());
|
||||
}
|
||||
|
||||
result = result.simplified();
|
||||
/*
|
||||
Legendary Artifact Creature
|
||||
Instant
|
||||
*/
|
||||
|
||||
if ((pos = result.lastIndexOf(' ')) != -1) {
|
||||
result = result.mid(pos + 1);
|
||||
}
|
||||
/*
|
||||
Creature
|
||||
Instant
|
||||
*/
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QString CardInfo::getCorrectedName() const
|
||||
{
|
||||
QString result = name;
|
||||
@@ -338,26 +271,21 @@ QString CardInfo::getCorrectedName() const
|
||||
return result.remove(" // ").remove(':').remove('"').remove('?').replace('/', ' ');
|
||||
}
|
||||
|
||||
void CardInfo::addToSet(CardSetPtr set)
|
||||
void CardInfo::addToSet(const CardSetPtr &_set, const CardInfoPerSet _info)
|
||||
{
|
||||
if (set.isNull()) {
|
||||
qDebug() << "addToSet(nullptr)";
|
||||
return;
|
||||
}
|
||||
|
||||
set->append(smartThis);
|
||||
sets << set;
|
||||
_set->append(smartThis);
|
||||
sets.insert(_set->getShortName(), _info);
|
||||
|
||||
refreshCachedSetNames();
|
||||
}
|
||||
|
||||
void CardInfo::refreshCachedSetNames()
|
||||
{
|
||||
// update the cached list of set names
|
||||
QStringList setList;
|
||||
for (int i = 0; i < sets.size(); i++) {
|
||||
if (sets[i]->getEnabled()) {
|
||||
setList << sets[i]->getShortName();
|
||||
// update the cached list of set names
|
||||
for (const auto &set : sets) {
|
||||
if (set.getPtr()->getEnabled()) {
|
||||
setList << set.getPtr()->getShortName();
|
||||
}
|
||||
}
|
||||
setsNames = setList.join(", ");
|
||||
@@ -365,155 +293,60 @@ void CardInfo::refreshCachedSetNames()
|
||||
|
||||
QString CardInfo::simplifyName(const QString &name)
|
||||
{
|
||||
QString simpleName(name);
|
||||
static const QRegularExpression spaceOrSplit("(\\s+|\\/\\/.*)");
|
||||
static const QRegularExpression nonAlnum("[^a-z0-9]");
|
||||
|
||||
QString simpleName = name.toLower();
|
||||
|
||||
// remove spaces and right halves of split cards
|
||||
simpleName.remove(spaceOrSplit);
|
||||
|
||||
// So Aetherling would work, but not Ætherling since 'Æ' would get replaced
|
||||
// with nothing.
|
||||
simpleName.replace("æ", "ae");
|
||||
simpleName.replace("Æ", "AE");
|
||||
|
||||
// Replace Jötun Grunt with Jotun Grunt.
|
||||
simpleName = simpleName.normalized(QString::NormalizationForm_KD);
|
||||
|
||||
// Replace dashes with spaces so that we can say "garruk the veil cursed"
|
||||
// instead of the unintuitive "garruk the veilcursed".
|
||||
simpleName = simpleName.replace("-", " ");
|
||||
|
||||
simpleName.remove(QRegExp("[^a-zA-Z0-9 ]"));
|
||||
simpleName = simpleName.toLower();
|
||||
// remove all non alphanumeric characters from the name
|
||||
simpleName.remove(nonAlnum);
|
||||
return simpleName;
|
||||
}
|
||||
|
||||
const QChar CardInfo::getColorChar() const
|
||||
{
|
||||
QString colors = getColors();
|
||||
switch (colors.size()) {
|
||||
case 0:
|
||||
return QChar();
|
||||
case 1:
|
||||
return colors.first().isEmpty() ? QChar() : colors.first().at(0);
|
||||
return colors.at(0);
|
||||
default:
|
||||
return QChar('m');
|
||||
}
|
||||
}
|
||||
|
||||
static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfoPtr &info)
|
||||
{
|
||||
if (info.isNull()) {
|
||||
qDebug() << "operator<< info is nullptr";
|
||||
return xml;
|
||||
}
|
||||
|
||||
xml.writeStartElement("card");
|
||||
xml.writeTextElement("name", info->getName());
|
||||
|
||||
const SetList &sets = info->getSets();
|
||||
QString tmpString;
|
||||
QString tmpSet;
|
||||
for (int i = 0; i < sets.size(); i++) {
|
||||
xml.writeStartElement("set");
|
||||
|
||||
tmpSet = sets[i]->getShortName();
|
||||
xml.writeAttribute("rarity", info->getRarity(tmpSet));
|
||||
xml.writeAttribute("muId", QString::number(info->getMuId(tmpSet)));
|
||||
|
||||
tmpString = info->getCollectorNumber(tmpSet);
|
||||
if (!tmpString.isEmpty()) {
|
||||
xml.writeAttribute("num", info->getCollectorNumber(tmpSet));
|
||||
}
|
||||
|
||||
tmpString = info->getCustomPicURL(tmpSet);
|
||||
if (!tmpString.isEmpty()) {
|
||||
xml.writeAttribute("picURL", tmpString);
|
||||
}
|
||||
|
||||
xml.writeCharacters(tmpSet);
|
||||
xml.writeEndElement();
|
||||
}
|
||||
const QStringList &colors = info->getColors();
|
||||
for (int i = 0; i < colors.size(); i++) {
|
||||
xml.writeTextElement("color", colors[i]);
|
||||
}
|
||||
|
||||
const QList<CardRelation *> related = info->getRelatedCards();
|
||||
for (auto i : related) {
|
||||
xml.writeStartElement("related");
|
||||
if (i->getDoesAttach()) {
|
||||
xml.writeAttribute("attach", "attach");
|
||||
}
|
||||
if (i->getIsCreateAllExclusion()) {
|
||||
xml.writeAttribute("exclude", "exclude");
|
||||
}
|
||||
|
||||
if (i->getIsVariable()) {
|
||||
if (1 == i->getDefaultCount()) {
|
||||
xml.writeAttribute("count", "x");
|
||||
} else {
|
||||
xml.writeAttribute("count", "x=" + QString::number(i->getDefaultCount()));
|
||||
}
|
||||
} else if (1 != i->getDefaultCount()) {
|
||||
xml.writeAttribute("count", QString::number(i->getDefaultCount()));
|
||||
}
|
||||
xml.writeCharacters(i->getName());
|
||||
xml.writeEndElement();
|
||||
}
|
||||
const QList<CardRelation *> reverseRelated = info->getReverseRelatedCards();
|
||||
for (auto i : reverseRelated) {
|
||||
xml.writeStartElement("reverse-related");
|
||||
if (i->getDoesAttach()) {
|
||||
xml.writeAttribute("attach", "attach");
|
||||
}
|
||||
|
||||
if (i->getIsCreateAllExclusion()) {
|
||||
xml.writeAttribute("exclude", "exclude");
|
||||
}
|
||||
|
||||
if (i->getIsVariable()) {
|
||||
if (1 == i->getDefaultCount()) {
|
||||
xml.writeAttribute("count", "x");
|
||||
} else {
|
||||
xml.writeAttribute("count", "x=" + QString::number(i->getDefaultCount()));
|
||||
}
|
||||
} else if (1 != i->getDefaultCount()) {
|
||||
xml.writeAttribute("count", QString::number(i->getDefaultCount()));
|
||||
}
|
||||
xml.writeCharacters(i->getName());
|
||||
xml.writeEndElement();
|
||||
}
|
||||
xml.writeTextElement("manacost", info->getManaCost());
|
||||
xml.writeTextElement("cmc", info->getCmc());
|
||||
xml.writeTextElement("type", info->getCardType());
|
||||
if (!info->getPowTough().isEmpty()) {
|
||||
xml.writeTextElement("pt", info->getPowTough());
|
||||
}
|
||||
xml.writeTextElement("tablerow", QString::number(info->getTableRow()));
|
||||
xml.writeTextElement("text", info->getText());
|
||||
if (info->getMainCardType() == "Planeswalker") {
|
||||
xml.writeTextElement("loyalty", info->getLoyalty());
|
||||
}
|
||||
if (info->getCipt()) {
|
||||
xml.writeTextElement("cipt", "1");
|
||||
}
|
||||
if (info->getIsToken()) {
|
||||
xml.writeTextElement("token", "1");
|
||||
}
|
||||
if (info->getUpsideDownArt()) {
|
||||
xml.writeTextElement("upsidedown", "1");
|
||||
}
|
||||
|
||||
xml.writeEndElement(); // card
|
||||
|
||||
return xml;
|
||||
}
|
||||
|
||||
CardDatabase::CardDatabase(QObject *parent) : QObject(parent), loadStatus(NotLoaded)
|
||||
{
|
||||
qRegisterMetaType<CardInfoPtr>("CardInfoPtr");
|
||||
connect(settingsCache, SIGNAL(cardDatabasePathChanged()), this, SLOT(loadCardDatabases()));
|
||||
qRegisterMetaType<CardInfoPtr>("CardSetPtr");
|
||||
|
||||
// add new parsers here
|
||||
availableParsers << new CockatriceXml4Parser;
|
||||
availableParsers << new CockatriceXml3Parser;
|
||||
|
||||
for (auto &parser : availableParsers) {
|
||||
connect(parser, SIGNAL(addCard(CardInfoPtr)), this, SLOT(addCard(CardInfoPtr)), Qt::DirectConnection);
|
||||
connect(parser, SIGNAL(addSet(CardSetPtr)), this, SLOT(addSet(CardSetPtr)), Qt::DirectConnection);
|
||||
}
|
||||
|
||||
connect(&SettingsCache::instance(), SIGNAL(cardDatabasePathChanged()), this, SLOT(loadCardDatabases()));
|
||||
}
|
||||
|
||||
CardDatabase::~CardDatabase()
|
||||
{
|
||||
clear();
|
||||
qDeleteAll(availableParsers);
|
||||
}
|
||||
|
||||
void CardDatabase::clear()
|
||||
@@ -532,6 +365,7 @@ void CardDatabase::clear()
|
||||
simpleNameCards.clear();
|
||||
|
||||
sets.clear();
|
||||
ICardDatabaseParser::clearSetlist();
|
||||
|
||||
loadStatus = NotLoaded;
|
||||
|
||||
@@ -545,6 +379,15 @@ void CardDatabase::addCard(CardInfoPtr card)
|
||||
return;
|
||||
}
|
||||
|
||||
// if card already exists just add the new set property
|
||||
if (cards.contains(card->getName())) {
|
||||
CardInfoPtr sameCard = cards[card->getName()];
|
||||
for (const CardInfoPerSet &set : card->getSets()) {
|
||||
sameCard->addToSet(set.getPtr(), set);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
addCardMutex->lock();
|
||||
cards.insert(card->getName(), card);
|
||||
simpleNameCards.insert(card->getSimpleName(), card);
|
||||
@@ -559,19 +402,18 @@ void CardDatabase::removeCard(CardInfoPtr card)
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (CardRelation *cardRelation, card->getRelatedCards())
|
||||
for (auto *cardRelation : card->getRelatedCards())
|
||||
cardRelation->deleteLater();
|
||||
|
||||
foreach (CardRelation *cardRelation, card->getReverseRelatedCards())
|
||||
for (auto *cardRelation : card->getReverseRelatedCards())
|
||||
cardRelation->deleteLater();
|
||||
|
||||
foreach (CardRelation *cardRelation, card->getReverseRelatedCards2Me())
|
||||
for (auto *cardRelation : card->getReverseRelatedCards2Me())
|
||||
cardRelation->deleteLater();
|
||||
|
||||
removeCardMutex->lock();
|
||||
cards.remove(card->getName());
|
||||
simpleNameCards.remove(card->getSimpleName());
|
||||
card.clear();
|
||||
removeCardMutex->unlock();
|
||||
emit cardRemoved(card);
|
||||
}
|
||||
@@ -598,6 +440,19 @@ CardInfoPtr CardDatabase::getCardBySimpleName(const QString &cardName) const
|
||||
return getCardFromMap(simpleNameCards, CardInfo::simplifyName(cardName));
|
||||
}
|
||||
|
||||
CardInfoPtr CardDatabase::guessCard(const QString &cardName) const
|
||||
{
|
||||
CardInfoPtr temp = getCard(cardName);
|
||||
if (temp == nullptr) { // get card by simple name instead
|
||||
temp = getCardBySimpleName(cardName);
|
||||
if (temp == nullptr) { // still could not find the card, so simplify the cardName too
|
||||
QString simpleCardName = CardInfo::simplifyName(cardName);
|
||||
temp = getCardBySimpleName(simpleCardName);
|
||||
}
|
||||
}
|
||||
return temp; // returns nullptr if not found
|
||||
}
|
||||
|
||||
CardSetPtr CardDatabase::getSet(const QString &setName)
|
||||
{
|
||||
if (sets.contains(setName)) {
|
||||
@@ -609,6 +464,11 @@ CardSetPtr CardDatabase::getSet(const QString &setName)
|
||||
}
|
||||
}
|
||||
|
||||
void CardDatabase::addSet(CardSetPtr set)
|
||||
{
|
||||
sets.insert(set->getShortName(), set);
|
||||
}
|
||||
|
||||
SetList CardDatabase::getSetList() const
|
||||
{
|
||||
SetList result;
|
||||
@@ -620,159 +480,6 @@ SetList CardDatabase::getSetList() const
|
||||
return result;
|
||||
}
|
||||
|
||||
void CardDatabase::loadSetsFromXml(QXmlStreamReader &xml)
|
||||
{
|
||||
while (!xml.atEnd()) {
|
||||
if (xml.readNext() == QXmlStreamReader::EndElement) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (xml.name() == "set") {
|
||||
QString shortName, longName, setType;
|
||||
QDate releaseDate;
|
||||
while (!xml.atEnd()) {
|
||||
if (xml.readNext() == QXmlStreamReader::EndElement) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (xml.name() == "name") {
|
||||
shortName = xml.readElementText();
|
||||
} else if (xml.name() == "longname") {
|
||||
longName = xml.readElementText();
|
||||
} else if (xml.name() == "settype") {
|
||||
setType = xml.readElementText();
|
||||
} else if (xml.name() == "releasedate") {
|
||||
releaseDate = QDate::fromString(xml.readElementText(), Qt::ISODate);
|
||||
} else if (xml.name() != "") {
|
||||
qDebug() << "[XMLReader] Unknown set property" << xml.name() << ", trying to continue anyway";
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
CardSetPtr newSet = getSet(shortName);
|
||||
newSet->setLongName(longName);
|
||||
newSet->setSetType(setType);
|
||||
newSet->setReleaseDate(releaseDate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CardDatabase::loadCardsFromXml(QXmlStreamReader &xml)
|
||||
{
|
||||
while (!xml.atEnd()) {
|
||||
if (xml.readNext() == QXmlStreamReader::EndElement) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (xml.name() == "card") {
|
||||
QString name, manacost, cmc, type, pt, text, loyalty;
|
||||
QStringList colors;
|
||||
QList<CardRelation *> relatedCards, reverseRelatedCards;
|
||||
QStringMap customPicURLs;
|
||||
MuidMap muids;
|
||||
QStringMap collectorNumbers, rarities;
|
||||
SetList sets;
|
||||
int tableRow = 0;
|
||||
bool cipt = false;
|
||||
bool isToken = false;
|
||||
bool upsideDown = false;
|
||||
while (!xml.atEnd()) {
|
||||
if (xml.readNext() == QXmlStreamReader::EndElement) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (xml.name() == "name") {
|
||||
name = xml.readElementText();
|
||||
} else if (xml.name() == "manacost") {
|
||||
manacost = xml.readElementText();
|
||||
} else if (xml.name() == "cmc") {
|
||||
cmc = xml.readElementText();
|
||||
} else if (xml.name() == "type") {
|
||||
type = xml.readElementText();
|
||||
} else if (xml.name() == "pt") {
|
||||
pt = xml.readElementText();
|
||||
} else if (xml.name() == "text") {
|
||||
text = xml.readElementText();
|
||||
} else if (xml.name() == "set") {
|
||||
QXmlStreamAttributes attrs = xml.attributes();
|
||||
QString setName = xml.readElementText();
|
||||
sets.append(getSet(setName));
|
||||
if (attrs.hasAttribute("muId")) {
|
||||
muids[setName] = attrs.value("muId").toString().toInt();
|
||||
}
|
||||
|
||||
if (attrs.hasAttribute("picURL")) {
|
||||
customPicURLs[setName] = attrs.value("picURL").toString();
|
||||
}
|
||||
|
||||
if (attrs.hasAttribute("num")) {
|
||||
collectorNumbers[setName] = attrs.value("num").toString();
|
||||
}
|
||||
|
||||
if (attrs.hasAttribute("rarity")) {
|
||||
rarities[setName] = attrs.value("rarity").toString();
|
||||
}
|
||||
} else if (xml.name() == "color") {
|
||||
colors << xml.readElementText();
|
||||
} else if (xml.name() == "related" || xml.name() == "reverse-related") {
|
||||
bool attach = false;
|
||||
bool exclude = false;
|
||||
bool variable = false;
|
||||
int count = 1;
|
||||
QXmlStreamAttributes attrs = xml.attributes();
|
||||
QString cardName = xml.readElementText();
|
||||
if (attrs.hasAttribute("count")) {
|
||||
if (attrs.value("count").toString().indexOf("x=") == 0) {
|
||||
variable = true;
|
||||
count = attrs.value("count").toString().remove(0, 2).toInt();
|
||||
} else if (attrs.value("count").toString().indexOf("x") == 0) {
|
||||
variable = true;
|
||||
} else {
|
||||
count = attrs.value("count").toString().toInt();
|
||||
}
|
||||
|
||||
if (count < 1) {
|
||||
count = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (attrs.hasAttribute("attach")) {
|
||||
attach = true;
|
||||
}
|
||||
|
||||
if (attrs.hasAttribute("exclude")) {
|
||||
exclude = true;
|
||||
}
|
||||
|
||||
auto *relation = new CardRelation(cardName, attach, exclude, variable, count);
|
||||
if (xml.name() == "reverse-related") {
|
||||
reverseRelatedCards << relation;
|
||||
} else {
|
||||
relatedCards << relation;
|
||||
}
|
||||
} else if (xml.name() == "tablerow") {
|
||||
tableRow = xml.readElementText().toInt();
|
||||
} else if (xml.name() == "cipt") {
|
||||
cipt = (xml.readElementText() == "1");
|
||||
} else if (xml.name() == "upsidedown") {
|
||||
upsideDown = (xml.readElementText() == "1");
|
||||
} else if (xml.name() == "loyalty") {
|
||||
loyalty = xml.readElementText();
|
||||
} else if (xml.name() == "token") {
|
||||
isToken = static_cast<bool>(xml.readElementText().toInt());
|
||||
} else if (xml.name() != "") {
|
||||
qDebug() << "[XMLReader] Unknown card property" << xml.name() << ", trying to continue anyway";
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
addCard(CardInfo::newInstance(name, isToken, manacost, cmc, type, pt, text, colors, relatedCards,
|
||||
reverseRelatedCards, upsideDown, loyalty, cipt, tableRow, sets, customPicURLs,
|
||||
muids, collectorNumbers, rarities));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CardInfoPtr CardDatabase::getCardFromMap(const CardNameMap &cardMap, const QString &cardName) const
|
||||
{
|
||||
if (cardMap.contains(cardName))
|
||||
@@ -789,85 +496,21 @@ LoadStatus CardDatabase::loadFromFile(const QString &fileName)
|
||||
return FileError;
|
||||
}
|
||||
|
||||
QXmlStreamReader xml(&file);
|
||||
while (!xml.atEnd()) {
|
||||
if (xml.readNext() == QXmlStreamReader::StartElement) {
|
||||
if (xml.name() != "cockatrice_carddatabase") {
|
||||
return Invalid;
|
||||
}
|
||||
|
||||
int version = xml.attributes().value("version").toString().toInt();
|
||||
if (version < versionNeeded) {
|
||||
qDebug() << "[XMLReader] Version too old: " << version;
|
||||
return VersionTooOld;
|
||||
}
|
||||
|
||||
while (!xml.atEnd()) {
|
||||
if (xml.readNext() == QXmlStreamReader::EndElement) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (xml.name() == "sets") {
|
||||
loadSetsFromXml(xml);
|
||||
} else if (xml.name() == "cards") {
|
||||
loadCardsFromXml(xml);
|
||||
} else if (xml.name() != "") {
|
||||
qDebug() << "[XMLReader] Unknown item" << xml.name() << ", trying to continue anyway";
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
for (auto parser : availableParsers) {
|
||||
file.reset();
|
||||
if (parser->getCanParseFile(fileName, file)) {
|
||||
file.reset();
|
||||
parser->parseFile(file);
|
||||
return Ok;
|
||||
}
|
||||
}
|
||||
|
||||
if (cards.isEmpty()) {
|
||||
return NoCards;
|
||||
}
|
||||
|
||||
return Ok;
|
||||
}
|
||||
|
||||
bool CardDatabase::saveToFile(const QString &fileName, bool tokens)
|
||||
{
|
||||
QFile file(fileName);
|
||||
if (!file.open(QIODevice::WriteOnly)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QXmlStreamWriter xml(&file);
|
||||
|
||||
xml.setAutoFormatting(true);
|
||||
xml.writeStartDocument();
|
||||
xml.writeStartElement("cockatrice_carddatabase");
|
||||
xml.writeAttribute("version", QString::number(versionNeeded));
|
||||
|
||||
if (!tokens) {
|
||||
xml.writeStartElement("sets");
|
||||
QHashIterator<QString, CardSetPtr> setIterator(sets);
|
||||
while (setIterator.hasNext()) {
|
||||
xml << setIterator.next().value();
|
||||
}
|
||||
|
||||
xml.writeEndElement(); // sets
|
||||
}
|
||||
|
||||
xml.writeStartElement("cards");
|
||||
QHashIterator<QString, CardInfoPtr> cardIterator(cards);
|
||||
while (cardIterator.hasNext()) {
|
||||
CardInfoPtr card = cardIterator.next().value();
|
||||
if (tokens == card->getIsToken()) {
|
||||
xml << card;
|
||||
}
|
||||
}
|
||||
xml.writeEndElement(); // cards
|
||||
|
||||
xml.writeEndElement(); // cockatrice_carddatabase
|
||||
xml.writeEndDocument();
|
||||
|
||||
return true;
|
||||
return Invalid;
|
||||
}
|
||||
|
||||
LoadStatus CardDatabase::loadCardDatabase(const QString &path)
|
||||
{
|
||||
auto startTime = QTime::currentTime();
|
||||
LoadStatus tempLoadStatus = NotLoaded;
|
||||
if (!path.isEmpty()) {
|
||||
loadFromFileMutex->lock();
|
||||
@@ -875,8 +518,9 @@ LoadStatus CardDatabase::loadCardDatabase(const QString &path)
|
||||
loadFromFileMutex->unlock();
|
||||
}
|
||||
|
||||
int msecs = startTime.msecsTo(QTime::currentTime());
|
||||
qDebug() << "[CardDatabase] loadCardDatabase(): Path =" << path << "Status =" << tempLoadStatus
|
||||
<< "Cards =" << cards.size() << "Sets=" << sets.size();
|
||||
<< "Cards =" << cards.size() << "Sets =" << sets.size() << QString("%1ms").arg(msecs);
|
||||
|
||||
return tempLoadStatus;
|
||||
}
|
||||
@@ -889,27 +533,29 @@ LoadStatus CardDatabase::loadCardDatabases()
|
||||
|
||||
clear(); // remove old db
|
||||
|
||||
loadStatus = loadCardDatabase(settingsCache->getCardDatabasePath()); // load main card database
|
||||
loadCardDatabase(settingsCache->getTokenDatabasePath()); // load tokens database
|
||||
loadCardDatabase(settingsCache->getSpoilerCardDatabasePath()); // load spoilers database
|
||||
loadStatus = loadCardDatabase(SettingsCache::instance().getCardDatabasePath()); // load main card database
|
||||
loadCardDatabase(SettingsCache::instance().getTokenDatabasePath()); // load tokens database
|
||||
loadCardDatabase(SettingsCache::instance().getSpoilerCardDatabasePath()); // load spoilers database
|
||||
|
||||
// load custom card databases
|
||||
QDir dir(settingsCache->getCustomCardDatabasePath());
|
||||
foreach (QString fileName,
|
||||
dir.entryList(QStringList("*.xml"), QDir::Files | QDir::Readable, QDir::Name | QDir::IgnoreCase)) {
|
||||
loadCardDatabase(dir.absoluteFilePath(fileName));
|
||||
// find all custom card databases, recursively & following symlinks
|
||||
// then load them alphabetically
|
||||
QDirIterator customDatabaseIterator(SettingsCache::instance().getCustomCardDatabasePath(), QStringList() << "*.xml",
|
||||
QDir::Files, QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
|
||||
QStringList databasePaths;
|
||||
while (customDatabaseIterator.hasNext()) {
|
||||
customDatabaseIterator.next();
|
||||
databasePaths.push_back(customDatabaseIterator.filePath());
|
||||
}
|
||||
databasePaths.sort();
|
||||
|
||||
for (auto i = 0; i < databasePaths.size(); ++i) {
|
||||
const auto &databasePath = databasePaths.at(i);
|
||||
qDebug() << "Loading Custom Set" << i << "(" << databasePath << ")";
|
||||
loadCardDatabase(databasePath);
|
||||
}
|
||||
|
||||
// AFTER all the cards have been loaded
|
||||
|
||||
// reorder sets (TODO: refactor, this smells)
|
||||
SetList allSets;
|
||||
QHashIterator<QString, CardSetPtr> setsIterator(sets);
|
||||
while (setsIterator.hasNext()) {
|
||||
allSets.append(setsIterator.next().value());
|
||||
}
|
||||
allSets.sortByKey();
|
||||
|
||||
// resolve the reverse-related tags
|
||||
refreshCachedReverseRelatedCards();
|
||||
|
||||
@@ -935,20 +581,13 @@ void CardDatabase::refreshCachedReverseRelatedCards()
|
||||
continue;
|
||||
}
|
||||
|
||||
QString relatedCardName;
|
||||
if (card->getPowTough().size() > 0) {
|
||||
relatedCardName = card->getPowTough() + " " + card->getName(); // "n/n name"
|
||||
} else {
|
||||
relatedCardName = card->getName(); // "name"
|
||||
}
|
||||
|
||||
foreach (CardRelation *cardRelation, card->getReverseRelatedCards()) {
|
||||
const QString &targetCard = cardRelation->getName();
|
||||
if (!cards.contains(targetCard)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto *newCardRelation = new CardRelation(relatedCardName, cardRelation->getDoesAttach(),
|
||||
auto *newCardRelation = new CardRelation(card->getName(), cardRelation->getDoesAttach(),
|
||||
cardRelation->getIsCreateAllExclusion(),
|
||||
cardRelation->getIsVariable(), cardRelation->getDefaultCount());
|
||||
cards.value(targetCard)->addReverseRelatedCards2Me(newCardRelation);
|
||||
@@ -956,23 +595,6 @@ void CardDatabase::refreshCachedReverseRelatedCards()
|
||||
}
|
||||
}
|
||||
|
||||
QStringList CardDatabase::getAllColors() const
|
||||
{
|
||||
QSet<QString> colors;
|
||||
QHashIterator<QString, CardInfoPtr> cardIterator(cards);
|
||||
while (cardIterator.hasNext()) {
|
||||
const QStringList &cardColors = cardIterator.next().value()->getColors();
|
||||
if (cardColors.isEmpty()) {
|
||||
colors.insert("X");
|
||||
} else {
|
||||
for (int i = 0; i < cardColors.size(); ++i) {
|
||||
colors.insert(cardColors[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return colors.toList();
|
||||
}
|
||||
|
||||
QStringList CardDatabase::getAllMainCardTypes() const
|
||||
{
|
||||
QSet<QString> types;
|
||||
@@ -980,7 +602,7 @@ QStringList CardDatabase::getAllMainCardTypes() const
|
||||
while (cardIterator.hasNext()) {
|
||||
types.insert(cardIterator.next().value()->getMainCardType());
|
||||
}
|
||||
return types.toList();
|
||||
return types.values();
|
||||
}
|
||||
|
||||
void CardDatabase::checkUnknownSets()
|
||||
@@ -988,7 +610,7 @@ void CardDatabase::checkUnknownSets()
|
||||
SetList sets = getSetList();
|
||||
|
||||
if (sets.getEnabledSetsNum()) {
|
||||
// if some sets are first found on thus run, ask the user
|
||||
// if some sets are first found on this run, ask the user
|
||||
int numUnknownSets = sets.getUnknownSetsNum();
|
||||
QStringList unknownSetNames = sets.getUnknownSetsNames();
|
||||
if (numUnknownSets > 0) {
|
||||
@@ -1031,33 +653,21 @@ void CardDatabase::notifyEnabledSetsChanged()
|
||||
|
||||
bool CardDatabase::saveCustomTokensToFile()
|
||||
{
|
||||
QString fileName =
|
||||
SettingsCache::instance().getCustomCardDatabasePath() + "/" + CardDatabase::TOKENS_SETNAME + ".xml";
|
||||
|
||||
SetNameMap tmpSets;
|
||||
CardSetPtr customTokensSet = getSet(CardDatabase::TOKENS_SETNAME);
|
||||
QString fileName = settingsCache->getCustomCardDatabasePath() + "/" + CardDatabase::TOKENS_SETNAME + ".xml";
|
||||
QFile file(fileName);
|
||||
if (!file.open(QIODevice::WriteOnly)) {
|
||||
return false;
|
||||
}
|
||||
tmpSets.insert(CardDatabase::TOKENS_SETNAME, customTokensSet);
|
||||
|
||||
QXmlStreamWriter xml(&file);
|
||||
|
||||
xml.setAutoFormatting(true);
|
||||
xml.writeStartDocument();
|
||||
xml.writeStartElement("cockatrice_carddatabase");
|
||||
xml.writeAttribute("version", QString::number(versionNeeded));
|
||||
|
||||
xml.writeStartElement("cards");
|
||||
QHashIterator<QString, CardInfoPtr> cardIterator(cards);
|
||||
while (cardIterator.hasNext()) {
|
||||
CardInfoPtr card = cardIterator.next().value();
|
||||
if (card->getSets().contains(customTokensSet)) {
|
||||
xml << card;
|
||||
CardNameMap tmpCards;
|
||||
for (const CardInfoPtr &card : cards) {
|
||||
if (card->getSets().contains(CardDatabase::TOKENS_SETNAME)) {
|
||||
tmpCards.insert(card->getName(), card);
|
||||
}
|
||||
}
|
||||
xml.writeEndElement(); // cards
|
||||
|
||||
xml.writeEndElement(); // cockatrice_carddatabase
|
||||
xml.writeEndDocument();
|
||||
|
||||
availableParsers.first()->saveToFile(tmpSets, tmpCards, fileName);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1077,4 +687,46 @@ void CardInfo::resetReverseRelatedCards2Me()
|
||||
cardRelation->deleteLater();
|
||||
}
|
||||
reverseRelatedCardsToMe = QList<CardRelation *>();
|
||||
}
|
||||
}
|
||||
|
||||
// Back-compatibility methods. Remove ASAP
|
||||
const QString CardInfo::getCardType() const
|
||||
{
|
||||
return getProperty(Mtg::CardType);
|
||||
}
|
||||
void CardInfo::setCardType(const QString &value)
|
||||
{
|
||||
setProperty(Mtg::CardType, value);
|
||||
}
|
||||
const QString CardInfo::getCmc() const
|
||||
{
|
||||
return getProperty(Mtg::ConvertedManaCost);
|
||||
}
|
||||
const QString CardInfo::getColors() const
|
||||
{
|
||||
return getProperty(Mtg::Colors);
|
||||
}
|
||||
void CardInfo::setColors(const QString &value)
|
||||
{
|
||||
setProperty(Mtg::Colors, value);
|
||||
}
|
||||
const QString CardInfo::getLoyalty() const
|
||||
{
|
||||
return getProperty(Mtg::Loyalty);
|
||||
}
|
||||
const QString CardInfo::getMainCardType() const
|
||||
{
|
||||
return getProperty(Mtg::MainCardType);
|
||||
}
|
||||
const QString CardInfo::getManaCost() const
|
||||
{
|
||||
return getProperty(Mtg::ManaCost);
|
||||
}
|
||||
const QString CardInfo::getPowTough() const
|
||||
{
|
||||
return getProperty(Mtg::PowTough);
|
||||
}
|
||||
void CardInfo::setPowTough(const QString &value)
|
||||
{
|
||||
setProperty(Mtg::PowTough, value);
|
||||
}
|
||||
|
||||
@@ -2,24 +2,28 @@
|
||||
#define CARDDATABASE_H
|
||||
|
||||
#include <QBasicMutex>
|
||||
#include <QDataStream>
|
||||
#include <QDate>
|
||||
#include <QHash>
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
#include <QMetaType>
|
||||
#include <QPixmap>
|
||||
#include <QXmlStreamReader>
|
||||
#include <QSharedPointer>
|
||||
#include <QStringList>
|
||||
#include <QVariant>
|
||||
#include <QVector>
|
||||
#include <utility>
|
||||
|
||||
class CardDatabase;
|
||||
class CardInfo;
|
||||
class CardInfoPerSet;
|
||||
class CardSet;
|
||||
class CardRelation;
|
||||
class ICardDatabaseParser;
|
||||
|
||||
typedef QMap<QString, QString> QStringMap;
|
||||
typedef QMap<QString, int> MuidMap;
|
||||
typedef QSharedPointer<CardInfo> CardInfoPtr;
|
||||
typedef QSharedPointer<CardSet> CardSetPtr;
|
||||
typedef QMap<QString, CardInfoPerSet> CardInfoPerSetMap;
|
||||
|
||||
Q_DECLARE_METATYPE(CardInfoPtr)
|
||||
|
||||
@@ -58,15 +62,15 @@ public:
|
||||
{
|
||||
return releaseDate;
|
||||
}
|
||||
void setLongName(QString &_longName)
|
||||
void setLongName(const QString &_longName)
|
||||
{
|
||||
longName = _longName;
|
||||
}
|
||||
void setSetType(QString &_setType)
|
||||
void setSetType(const QString &_setType)
|
||||
{
|
||||
setType = _setType;
|
||||
}
|
||||
void setReleaseDate(QDate &_releaseDate)
|
||||
void setReleaseDate(const QDate &_releaseDate)
|
||||
{
|
||||
releaseDate = _releaseDate;
|
||||
}
|
||||
@@ -111,175 +115,166 @@ public:
|
||||
QStringList getUnknownSetsNames();
|
||||
};
|
||||
|
||||
class CardInfoPerSet
|
||||
{
|
||||
public:
|
||||
explicit CardInfoPerSet(const CardSetPtr &_set = QSharedPointer<CardSet>(nullptr));
|
||||
~CardInfoPerSet() = default;
|
||||
|
||||
private:
|
||||
CardSetPtr set;
|
||||
// per-set card properties;
|
||||
QVariantHash properties;
|
||||
|
||||
public:
|
||||
const CardSetPtr getPtr() const
|
||||
{
|
||||
return set;
|
||||
}
|
||||
const QStringList getProperties() const
|
||||
{
|
||||
return properties.keys();
|
||||
}
|
||||
const QString getProperty(const QString &propertyName) const
|
||||
{
|
||||
return properties.value(propertyName).toString();
|
||||
}
|
||||
void setProperty(const QString &_name, const QString &_value)
|
||||
{
|
||||
properties.insert(_name, _value);
|
||||
}
|
||||
};
|
||||
|
||||
class CardInfo : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
CardInfoPtr smartThis;
|
||||
// The card name
|
||||
QString name;
|
||||
|
||||
/*
|
||||
* The name without punctuation or capitalization, for better card tag name
|
||||
* recognition.
|
||||
*/
|
||||
// The name without punctuation or capitalization, for better card name recognition.
|
||||
QString simpleName;
|
||||
|
||||
bool isToken;
|
||||
SetList sets;
|
||||
QString manacost;
|
||||
QString cmc;
|
||||
QString cardtype;
|
||||
QString powtough;
|
||||
// The key used to identify this card in the cache
|
||||
QString pixmapCacheKey;
|
||||
// card text
|
||||
QString text;
|
||||
QStringList colors;
|
||||
|
||||
// whether this is not a "real" card but a token
|
||||
bool isToken;
|
||||
// basic card properties; common for all the sets
|
||||
QVariantHash properties;
|
||||
// the cards i'm related to
|
||||
QList<CardRelation *> relatedCards;
|
||||
|
||||
// the card i'm reverse-related to
|
||||
QList<CardRelation *> reverseRelatedCards;
|
||||
|
||||
// the cards thare are reverse-related to me
|
||||
QList<CardRelation *> reverseRelatedCardsToMe;
|
||||
|
||||
// card sets
|
||||
CardInfoPerSetMap sets;
|
||||
// cached set names
|
||||
QString setsNames;
|
||||
|
||||
bool upsideDownArt;
|
||||
QString loyalty;
|
||||
QStringMap customPicURLs;
|
||||
MuidMap muIds;
|
||||
QStringMap collectorNumbers;
|
||||
QStringMap rarities;
|
||||
// positioning properties; used by UI
|
||||
bool cipt;
|
||||
int tableRow;
|
||||
QString pixmapCacheKey;
|
||||
bool upsideDownArt;
|
||||
|
||||
public:
|
||||
explicit CardInfo(const QString &_name = QString(),
|
||||
bool _isToken = false,
|
||||
const QString &_manacost = QString(),
|
||||
const QString &_cmc = QString(),
|
||||
const QString &_cardtype = QString(),
|
||||
const QString &_powtough = QString(),
|
||||
const QString &_text = QString(),
|
||||
const QStringList &_colors = QStringList(),
|
||||
bool _isToken = false,
|
||||
QVariantHash _properties = QVariantHash(),
|
||||
const QList<CardRelation *> &_relatedCards = QList<CardRelation *>(),
|
||||
const QList<CardRelation *> &_reverseRelatedCards = QList<CardRelation *>(),
|
||||
bool _upsideDownArt = false,
|
||||
const QString &_loyalty = QString(),
|
||||
CardInfoPerSetMap _sets = CardInfoPerSetMap(),
|
||||
bool _cipt = false,
|
||||
int _tableRow = 0,
|
||||
const SetList &_sets = SetList(),
|
||||
const QStringMap &_customPicURLs = QStringMap(),
|
||||
MuidMap muids = MuidMap(),
|
||||
QStringMap _collectorNumbers = QStringMap(),
|
||||
QStringMap _rarities = QStringMap());
|
||||
bool _upsideDownArt = false);
|
||||
~CardInfo() override;
|
||||
|
||||
static CardInfoPtr newInstance(const QString &_name = QString(),
|
||||
bool _isToken = false,
|
||||
const QString &_manacost = QString(),
|
||||
const QString &_cmc = QString(),
|
||||
const QString &_cardtype = QString(),
|
||||
const QString &_powtough = QString(),
|
||||
const QString &_text = QString(),
|
||||
const QStringList &_colors = QStringList(),
|
||||
bool _isToken = false,
|
||||
QVariantHash _properties = QVariantHash(),
|
||||
const QList<CardRelation *> &_relatedCards = QList<CardRelation *>(),
|
||||
const QList<CardRelation *> &_reverseRelatedCards = QList<CardRelation *>(),
|
||||
bool _upsideDownArt = false,
|
||||
const QString &_loyalty = QString(),
|
||||
CardInfoPerSetMap _sets = CardInfoPerSetMap(),
|
||||
bool _cipt = false,
|
||||
int _tableRow = 0,
|
||||
const SetList &_sets = SetList(),
|
||||
const QStringMap &_customPicURLs = QStringMap(),
|
||||
MuidMap muids = MuidMap(),
|
||||
QStringMap _collectorNumbers = QStringMap(),
|
||||
QStringMap _rarities = QStringMap());
|
||||
bool _upsideDownArt = false);
|
||||
|
||||
void setSmartPointer(CardInfoPtr _ptr)
|
||||
{
|
||||
smartThis = _ptr;
|
||||
smartThis = std::move(_ptr);
|
||||
}
|
||||
|
||||
// basic properties
|
||||
inline const QString &getName() const
|
||||
{
|
||||
return name;
|
||||
}
|
||||
inline const QString &getSetsNames() const
|
||||
{
|
||||
return setsNames;
|
||||
}
|
||||
const QString &getSimpleName() const
|
||||
{
|
||||
return simpleName;
|
||||
}
|
||||
bool getIsToken() const
|
||||
{
|
||||
return isToken;
|
||||
}
|
||||
const SetList &getSets() const
|
||||
{
|
||||
return sets;
|
||||
}
|
||||
inline const QString &getManaCost() const
|
||||
{
|
||||
return manacost;
|
||||
}
|
||||
inline const QString &getCmc() const
|
||||
{
|
||||
return cmc;
|
||||
}
|
||||
inline const QString &getCardType() const
|
||||
{
|
||||
return cardtype;
|
||||
}
|
||||
inline const QString &getPowTough() const
|
||||
{
|
||||
return powtough;
|
||||
}
|
||||
const QString &getText() const
|
||||
{
|
||||
return text;
|
||||
}
|
||||
const QString &getPixmapCacheKey() const
|
||||
{
|
||||
return pixmapCacheKey;
|
||||
}
|
||||
const QString &getLoyalty() const
|
||||
|
||||
const QString &getText() const
|
||||
{
|
||||
return loyalty;
|
||||
}
|
||||
bool getCipt() const
|
||||
{
|
||||
return cipt;
|
||||
}
|
||||
// void setManaCost(const QString &_manaCost) { manacost = _manaCost; emit cardInfoChanged(smartThis); }
|
||||
// void setCmc(const QString &_cmc) { cmc = _cmc; emit cardInfoChanged(smartThis); }
|
||||
void setCardType(const QString &_cardType)
|
||||
{
|
||||
cardtype = _cardType;
|
||||
emit cardInfoChanged(smartThis);
|
||||
}
|
||||
void setPowTough(const QString &_powTough)
|
||||
{
|
||||
powtough = _powTough;
|
||||
emit cardInfoChanged(smartThis);
|
||||
return text;
|
||||
}
|
||||
void setText(const QString &_text)
|
||||
{
|
||||
text = _text;
|
||||
emit cardInfoChanged(smartThis);
|
||||
}
|
||||
void setColors(const QStringList &_colors)
|
||||
|
||||
bool getIsToken() const
|
||||
{
|
||||
colors = _colors;
|
||||
return isToken;
|
||||
}
|
||||
const QStringList getProperties() const
|
||||
{
|
||||
return properties.keys();
|
||||
}
|
||||
const QString getProperty(const QString &propertyName) const
|
||||
{
|
||||
return properties.value(propertyName).toString();
|
||||
}
|
||||
void setProperty(const QString &_name, const QString &_value)
|
||||
{
|
||||
properties.insert(_name, _value);
|
||||
emit cardInfoChanged(smartThis);
|
||||
}
|
||||
const QChar getColorChar() const;
|
||||
const QStringList &getColors() const
|
||||
bool hasProperty(const QString &propertyName) const
|
||||
{
|
||||
return colors;
|
||||
return properties.contains(propertyName);
|
||||
}
|
||||
const CardInfoPerSetMap &getSets() const
|
||||
{
|
||||
return sets;
|
||||
}
|
||||
const QString &getSetsNames() const
|
||||
{
|
||||
return setsNames;
|
||||
}
|
||||
const QString getSetProperty(const QString &setName, const QString &propertyName) const
|
||||
{
|
||||
if (!sets.contains(setName))
|
||||
return "";
|
||||
return sets[setName].getProperty(propertyName);
|
||||
}
|
||||
void setSetProperty(const QString &setName, const QString &_name, const QString &_value)
|
||||
{
|
||||
if (!sets.contains(setName))
|
||||
return;
|
||||
|
||||
sets[setName].setProperty(_name, _value);
|
||||
emit cardInfoChanged(smartThis);
|
||||
}
|
||||
|
||||
// related cards
|
||||
const QList<CardRelation *> &getRelatedCards() const
|
||||
{
|
||||
return relatedCards;
|
||||
@@ -292,37 +287,24 @@ public:
|
||||
{
|
||||
return reverseRelatedCardsToMe;
|
||||
}
|
||||
const QList<CardRelation *> getAllRelatedCards() const
|
||||
{
|
||||
QList<CardRelation *> result;
|
||||
result.append(getRelatedCards());
|
||||
result.append(getReverseRelatedCards2Me());
|
||||
return result;
|
||||
}
|
||||
void resetReverseRelatedCards2Me();
|
||||
void addReverseRelatedCards2Me(CardRelation *cardRelation)
|
||||
{
|
||||
reverseRelatedCardsToMe.append(cardRelation);
|
||||
}
|
||||
bool getUpsideDownArt() const
|
||||
|
||||
// positioning
|
||||
bool getCipt() const
|
||||
{
|
||||
return upsideDownArt;
|
||||
return cipt;
|
||||
}
|
||||
QString getCustomPicURL(const QString &set) const
|
||||
{
|
||||
return customPicURLs.value(set);
|
||||
}
|
||||
int getMuId(const QString &set) const
|
||||
{
|
||||
return muIds.value(set);
|
||||
}
|
||||
QString getCollectorNumber(const QString &set) const
|
||||
{
|
||||
return collectorNumbers.value(set);
|
||||
}
|
||||
QString getRarity(const QString &set) const
|
||||
{
|
||||
return rarities.value(set);
|
||||
}
|
||||
QStringMap getRarities() const
|
||||
{
|
||||
return rarities;
|
||||
}
|
||||
QString getMainCardType() const;
|
||||
QString getCorrectedName() const;
|
||||
int getTableRow() const
|
||||
{
|
||||
return tableRow;
|
||||
@@ -331,22 +313,31 @@ public:
|
||||
{
|
||||
tableRow = _tableRow;
|
||||
}
|
||||
// void setLoyalty(int _loyalty) { loyalty = _loyalty; emit cardInfoChanged(smartThis); }
|
||||
// void setCustomPicURL(const QString &_set, const QString &_customPicURL) { customPicURLs.insert(_set,
|
||||
// _customPicURL); }
|
||||
void setMuId(const QString &_set, const int &_muId)
|
||||
bool getUpsideDownArt() const
|
||||
{
|
||||
muIds.insert(_set, _muId);
|
||||
return upsideDownArt;
|
||||
}
|
||||
void setSetNumber(const QString &_set, const QString &_setNumber)
|
||||
const QChar getColorChar() const;
|
||||
|
||||
// Back-compatibility methods. Remove ASAP
|
||||
const QString getCardType() const;
|
||||
void setCardType(const QString &value);
|
||||
const QString getCmc() const;
|
||||
const QString getColors() const;
|
||||
void setColors(const QString &value);
|
||||
const QString getLoyalty() const;
|
||||
const QString getMainCardType() const;
|
||||
const QString getManaCost() const;
|
||||
const QString getPowTough() const;
|
||||
void setPowTough(const QString &value);
|
||||
|
||||
// methods using per-set properties
|
||||
QString getCustomPicURL(const QString &set) const
|
||||
{
|
||||
collectorNumbers.insert(_set, _setNumber);
|
||||
return getSetProperty(set, "picurl");
|
||||
}
|
||||
void setRarity(const QString &_set, const QString &_setNumber)
|
||||
{
|
||||
rarities.insert(_set, _setNumber);
|
||||
}
|
||||
void addToSet(CardSetPtr set);
|
||||
QString getCorrectedName() const;
|
||||
void addToSet(const CardSetPtr &_set, CardInfoPerSet _info = CardInfoPerSet());
|
||||
void emitPixmapUpdated()
|
||||
{
|
||||
emit pixmapUpdated();
|
||||
@@ -398,11 +389,9 @@ protected:
|
||||
|
||||
LoadStatus loadStatus;
|
||||
|
||||
private:
|
||||
static const int versionNeeded;
|
||||
void loadCardsFromXml(QXmlStreamReader &xml);
|
||||
void loadSetsFromXml(QXmlStreamReader &xml);
|
||||
QVector<ICardDatabaseParser *> availableParsers;
|
||||
|
||||
private:
|
||||
CardInfoPtr getCardFromMap(const CardNameMap &cardMap, const QString &cardName) const;
|
||||
void checkUnknownSets();
|
||||
void refreshCachedReverseRelatedCards();
|
||||
@@ -417,10 +406,10 @@ public:
|
||||
explicit CardDatabase(QObject *parent = nullptr);
|
||||
~CardDatabase() override;
|
||||
void clear();
|
||||
void addCard(CardInfoPtr card);
|
||||
void removeCard(CardInfoPtr card);
|
||||
CardInfoPtr getCard(const QString &cardName) const;
|
||||
QList<CardInfoPtr> getCards(const QStringList &cardNames) const;
|
||||
CardInfoPtr guessCard(const QString &cardName) const;
|
||||
|
||||
/*
|
||||
* Get a card by its simple name. The name will be simplified in this
|
||||
@@ -435,9 +424,7 @@ public:
|
||||
}
|
||||
SetList getSetList() const;
|
||||
LoadStatus loadFromFile(const QString &fileName);
|
||||
bool saveToFile(const QString &fileName, bool tokens = false);
|
||||
bool saveCustomTokensToFile();
|
||||
QStringList getAllColors() const;
|
||||
QStringList getAllMainCardTypes() const;
|
||||
LoadStatus getLoadStatus() const
|
||||
{
|
||||
@@ -449,7 +436,9 @@ public:
|
||||
|
||||
public slots:
|
||||
LoadStatus loadCardDatabases();
|
||||
private slots:
|
||||
void addCard(CardInfoPtr card);
|
||||
void addSet(CardSetPtr set);
|
||||
protected slots:
|
||||
LoadStatus loadCardDatabase(const QString &path);
|
||||
signals:
|
||||
void cardDatabaseLoadingFailed();
|
||||
@@ -502,4 +491,4 @@ public:
|
||||
return defaultCount;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#include "carddatabasemodel.h"
|
||||
|
||||
#include "filtertree.h"
|
||||
|
||||
#include <QMap>
|
||||
|
||||
#define CARDDBMODEL_COLUMNS 6
|
||||
|
||||
CardDatabaseModel::CardDatabaseModel(CardDatabase *_db, bool _showOnlyCardsFromEnabledSets, QObject *parent)
|
||||
@@ -13,9 +16,12 @@ CardDatabaseModel::CardDatabaseModel(CardDatabase *_db, bool _showOnlyCardsFromE
|
||||
cardDatabaseEnabledSetsChanged();
|
||||
}
|
||||
|
||||
CardDatabaseModel::~CardDatabaseModel()
|
||||
{
|
||||
}
|
||||
CardDatabaseModel::~CardDatabaseModel() = default;
|
||||
|
||||
QMap<wchar_t, wchar_t> CardDatabaseDisplayModel::characterTranslation = {{L'“', L'\"'},
|
||||
{L'”', L'\"'},
|
||||
{L'‘', L'\''},
|
||||
{L'’', L'\''}};
|
||||
|
||||
int CardDatabaseModel::rowCount(const QModelIndex & /*parent*/) const
|
||||
{
|
||||
@@ -47,7 +53,7 @@ QVariant CardDatabaseModel::data(const QModelIndex &index, int role) const
|
||||
case PTColumn:
|
||||
return card->getPowTough();
|
||||
case ColorColumn:
|
||||
return card->getColors().join("");
|
||||
return card->getColors();
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
@@ -91,8 +97,8 @@ bool CardDatabaseModel::checkCardHasAtLeastOneEnabledSet(CardInfoPtr card)
|
||||
if (!showOnlyCardsFromEnabledSets)
|
||||
return true;
|
||||
|
||||
for (CardSetPtr set : card->getSets()) {
|
||||
if (set->getEnabled())
|
||||
for (const auto &set : card->getSets()) {
|
||||
if (set.getPtr()->getEnabled())
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -139,12 +145,16 @@ void CardDatabaseModel::cardRemoved(CardInfoPtr card)
|
||||
endRemoveRows();
|
||||
}
|
||||
|
||||
CardDatabaseDisplayModel::CardDatabaseDisplayModel(QObject *parent) : QSortFilterProxyModel(parent), isToken(ShowAll)
|
||||
CardDatabaseDisplayModel::CardDatabaseDisplayModel(QObject *parent)
|
||||
: QSortFilterProxyModel(parent), isToken(ShowAll), filterString(nullptr)
|
||||
{
|
||||
filterTree = nullptr;
|
||||
setFilterCaseSensitivity(Qt::CaseInsensitive);
|
||||
setSortCaseSensitivity(Qt::CaseInsensitive);
|
||||
|
||||
dirtyTimer.setSingleShot(true);
|
||||
connect(&dirtyTimer, &QTimer::timeout, this, &CardDatabaseDisplayModel::invalidate);
|
||||
|
||||
loadedRowCount = 0;
|
||||
}
|
||||
|
||||
@@ -281,6 +291,13 @@ bool CardDatabaseDisplayModel::filterAcceptsRow(int sourceRow, const QModelIndex
|
||||
if (((isToken == ShowTrue) && !info->getIsToken()) || ((isToken == ShowFalse) && info->getIsToken()))
|
||||
return false;
|
||||
|
||||
if (filterString != nullptr) {
|
||||
if (filterTree != nullptr && !filterTree->acceptsCard(info)) {
|
||||
return false;
|
||||
}
|
||||
return filterString->check(info);
|
||||
}
|
||||
|
||||
return rowMatchesCardName(info);
|
||||
}
|
||||
|
||||
@@ -324,6 +341,17 @@ void CardDatabaseDisplayModel::filterTreeChanged()
|
||||
invalidate();
|
||||
}
|
||||
|
||||
const QString CardDatabaseDisplayModel::sanitizeCardName(const QString &dirtyName, const QMap<wchar_t, wchar_t> &table)
|
||||
{
|
||||
std::wstring toReturn = dirtyName.toStdWString();
|
||||
for (wchar_t &ch : toReturn) {
|
||||
if (table.contains(ch)) {
|
||||
ch = table.value(ch);
|
||||
}
|
||||
}
|
||||
return QString::fromStdWString(toReturn);
|
||||
}
|
||||
|
||||
TokenDisplayModel::TokenDisplayModel(QObject *parent) : CardDatabaseDisplayModel(parent)
|
||||
{
|
||||
}
|
||||
@@ -339,3 +367,19 @@ int TokenDisplayModel::rowCount(const QModelIndex &parent) const
|
||||
// always load all tokens at start
|
||||
return QSortFilterProxyModel::rowCount(parent);
|
||||
}
|
||||
|
||||
TokenEditModel::TokenEditModel(QObject *parent) : CardDatabaseDisplayModel(parent)
|
||||
{
|
||||
}
|
||||
|
||||
bool TokenEditModel::filterAcceptsRow(int sourceRow, const QModelIndex & /*sourceParent*/) const
|
||||
{
|
||||
CardInfoPtr info = static_cast<CardDatabaseModel *>(sourceModel())->getCard(sourceRow);
|
||||
return info->getIsToken() && info->getSets().contains(CardDatabase::TOKENS_SETNAME) && rowMatchesCardName(info);
|
||||
}
|
||||
|
||||
int TokenEditModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
// always load all tokens at start
|
||||
return QSortFilterProxyModel::rowCount(parent);
|
||||
}
|
||||
|
||||
@@ -2,10 +2,13 @@
|
||||
#define CARDDATABASEMODEL_H
|
||||
|
||||
#include "carddatabase.h"
|
||||
#include "filter_string.h"
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QList>
|
||||
#include <QSet>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QTimer>
|
||||
|
||||
class FilterTree;
|
||||
|
||||
@@ -26,12 +29,12 @@ public:
|
||||
{
|
||||
SortRole = Qt::UserRole
|
||||
};
|
||||
CardDatabaseModel(CardDatabase *_db, bool _showOnlyCardsFromEnabledSets, QObject *parent = 0);
|
||||
~CardDatabaseModel();
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const;
|
||||
int columnCount(const QModelIndex &parent = QModelIndex()) const;
|
||||
QVariant data(const QModelIndex &index, int role) const;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
|
||||
CardDatabaseModel(CardDatabase *_db, bool _showOnlyCardsFromEnabledSets, QObject *parent = nullptr);
|
||||
~CardDatabaseModel() override;
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
|
||||
CardDatabase *getDatabase() const
|
||||
{
|
||||
return db;
|
||||
@@ -67,77 +70,86 @@ public:
|
||||
|
||||
private:
|
||||
FilterBool isToken;
|
||||
QString cardNameBeginning, cardName, cardText;
|
||||
QString searchTerm;
|
||||
QString cardName, cardText;
|
||||
QSet<QString> cardNameSet, cardTypes, cardColors;
|
||||
FilterTree *filterTree;
|
||||
FilterString *filterString;
|
||||
int loadedRowCount;
|
||||
QTimer dirtyTimer;
|
||||
|
||||
/** The translation table that will be used for sanitizeCardName. */
|
||||
static QMap<wchar_t, wchar_t> characterTranslation;
|
||||
|
||||
public:
|
||||
CardDatabaseDisplayModel(QObject *parent = 0);
|
||||
explicit CardDatabaseDisplayModel(QObject *parent = nullptr);
|
||||
void setFilterTree(FilterTree *filterTree);
|
||||
void setIsToken(FilterBool _isToken)
|
||||
{
|
||||
isToken = _isToken;
|
||||
invalidate();
|
||||
}
|
||||
void setCardNameBeginning(const QString &_beginning)
|
||||
{
|
||||
cardNameBeginning = _beginning;
|
||||
invalidate();
|
||||
dirty();
|
||||
}
|
||||
|
||||
void setCardName(const QString &_cardName)
|
||||
{
|
||||
cardName = _cardName;
|
||||
invalidate();
|
||||
if (filterString != nullptr) {
|
||||
delete filterString;
|
||||
filterString = nullptr;
|
||||
}
|
||||
cardName = sanitizeCardName(_cardName, characterTranslation);
|
||||
dirty();
|
||||
}
|
||||
void setStringFilter(const QString &_src)
|
||||
{
|
||||
delete filterString;
|
||||
filterString = new FilterString(_src);
|
||||
dirty();
|
||||
}
|
||||
void setCardNameSet(const QSet<QString> &_cardNameSet)
|
||||
{
|
||||
cardNameSet = _cardNameSet;
|
||||
invalidate();
|
||||
dirty();
|
||||
}
|
||||
void setSearchTerm(const QString &_searchTerm)
|
||||
|
||||
void dirty()
|
||||
{
|
||||
searchTerm = _searchTerm;
|
||||
}
|
||||
void setCardText(const QString &_cardText)
|
||||
{
|
||||
cardText = _cardText;
|
||||
invalidate();
|
||||
}
|
||||
void setCardTypes(const QSet<QString> &_cardTypes)
|
||||
{
|
||||
cardTypes = _cardTypes;
|
||||
invalidate();
|
||||
}
|
||||
void setCardColors(const QSet<QString> &_cardColors)
|
||||
{
|
||||
cardColors = _cardColors;
|
||||
invalidate();
|
||||
dirtyTimer.start(20);
|
||||
}
|
||||
void clearFilterAll();
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const;
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
|
||||
protected:
|
||||
bool lessThan(const QModelIndex &left, const QModelIndex &right) const;
|
||||
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
|
||||
static int lessThanNumerically(const QString &left, const QString &right);
|
||||
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const;
|
||||
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
|
||||
bool rowMatchesCardName(CardInfoPtr info) const;
|
||||
bool canFetchMore(const QModelIndex &parent) const;
|
||||
void fetchMore(const QModelIndex &parent);
|
||||
bool canFetchMore(const QModelIndex &parent) const override;
|
||||
void fetchMore(const QModelIndex &parent) override;
|
||||
private slots:
|
||||
void filterTreeChanged();
|
||||
/** Will translate all undesirable characters in DIRTYNAME according to the TABLE. */
|
||||
const QString sanitizeCardName(const QString &dirtyName, const QMap<wchar_t, wchar_t> &table);
|
||||
};
|
||||
|
||||
class TokenDisplayModel : public CardDatabaseDisplayModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
TokenDisplayModel(QObject *parent = 0);
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const;
|
||||
explicit TokenDisplayModel(QObject *parent = nullptr);
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
|
||||
protected:
|
||||
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const;
|
||||
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
|
||||
};
|
||||
|
||||
class TokenEditModel : public CardDatabaseDisplayModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit TokenEditModel(QObject *parent = nullptr);
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
|
||||
protected:
|
||||
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
27
cockatrice/src/carddbparser/carddatabaseparser.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#include "carddatabaseparser.h"
|
||||
|
||||
SetNameMap ICardDatabaseParser::sets;
|
||||
|
||||
void ICardDatabaseParser::clearSetlist()
|
||||
{
|
||||
sets.clear();
|
||||
}
|
||||
|
||||
CardSetPtr ICardDatabaseParser::internalAddSet(const QString &setName,
|
||||
const QString &longName,
|
||||
const QString &setType,
|
||||
const QDate &releaseDate)
|
||||
{
|
||||
if (sets.contains(setName)) {
|
||||
return sets.value(setName);
|
||||
}
|
||||
|
||||
CardSetPtr newSet = CardSet::newInstance(setName);
|
||||
newSet->setLongName(longName);
|
||||
newSet->setSetType(setType);
|
||||
newSet->setReleaseDate(releaseDate);
|
||||
|
||||
sets.insert(setName, newSet);
|
||||
emit addSet(newSet);
|
||||
return newSet;
|
||||
}
|
||||
43
cockatrice/src/carddbparser/carddatabaseparser.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#ifndef CARDDATABASE_PARSER_H
|
||||
#define CARDDATABASE_PARSER_H
|
||||
|
||||
#include "../carddatabase.h"
|
||||
|
||||
#include <QIODevice>
|
||||
#include <QString>
|
||||
|
||||
#define COCKATRICE_XML_XSI_NAMESPACE "http://www.w3.org/2001/XMLSchema-instance"
|
||||
|
||||
class ICardDatabaseParser : public QObject
|
||||
{
|
||||
public:
|
||||
~ICardDatabaseParser() override = default;
|
||||
|
||||
virtual bool getCanParseFile(const QString &name, QIODevice &device) = 0;
|
||||
virtual void parseFile(QIODevice &device) = 0;
|
||||
virtual bool saveToFile(SetNameMap sets,
|
||||
CardNameMap cards,
|
||||
const QString &fileName,
|
||||
const QString &sourceUrl = "unknown",
|
||||
const QString &sourceVersion = "unknown") = 0;
|
||||
static void clearSetlist();
|
||||
|
||||
protected:
|
||||
/*
|
||||
* A cached list of the available sets, needed to cross-reference sets from cards.
|
||||
* Shared between all parsers
|
||||
*/
|
||||
static SetNameMap sets;
|
||||
|
||||
CardSetPtr internalAddSet(const QString &setName,
|
||||
const QString &longName = "",
|
||||
const QString &setType = "",
|
||||
const QDate &releaseDate = QDate());
|
||||
signals:
|
||||
virtual void addCard(CardInfoPtr card) = 0;
|
||||
virtual void addSet(CardSetPtr set) = 0;
|
||||
};
|
||||
|
||||
Q_DECLARE_INTERFACE(ICardDatabaseParser, "ICardDatabaseParser")
|
||||
|
||||
#endif
|
||||
457
cockatrice/src/carddbparser/cockatricexml3.cpp
Normal file
@@ -0,0 +1,457 @@
|
||||
#include "cockatricexml3.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QXmlStreamReader>
|
||||
#include <version_string.h>
|
||||
|
||||
#define COCKATRICE_XML3_TAGNAME "cockatrice_carddatabase"
|
||||
#define COCKATRICE_XML3_TAGVER 3
|
||||
#define COCKATRICE_XML3_SCHEMALOCATION \
|
||||
"https://raw.githubusercontent.com/Cockatrice/Cockatrice/master/doc/carddatabase_v3/cards.xsd"
|
||||
|
||||
bool CockatriceXml3Parser::getCanParseFile(const QString &fileName, QIODevice &device)
|
||||
{
|
||||
qDebug() << "[CockatriceXml3Parser] Trying to parse: " << fileName;
|
||||
|
||||
if (!fileName.endsWith(".xml", Qt::CaseInsensitive)) {
|
||||
qDebug() << "[CockatriceXml3Parser] Parsing failed: wrong extension";
|
||||
return false;
|
||||
}
|
||||
|
||||
QXmlStreamReader xml(&device);
|
||||
while (!xml.atEnd()) {
|
||||
if (xml.readNext() == QXmlStreamReader::StartElement) {
|
||||
if (xml.name() == COCKATRICE_XML3_TAGNAME) {
|
||||
int version = xml.attributes().value("version").toString().toInt();
|
||||
if (version == COCKATRICE_XML3_TAGVER) {
|
||||
return true;
|
||||
} else {
|
||||
qDebug() << "[CockatriceXml3Parser] Parsing failed: wrong version" << version;
|
||||
return false;
|
||||
}
|
||||
|
||||
} else {
|
||||
qDebug() << "[CockatriceXml3Parser] Parsing failed: wrong element tag" << xml.name();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CockatriceXml3Parser::parseFile(QIODevice &device)
|
||||
{
|
||||
QXmlStreamReader xml(&device);
|
||||
while (!xml.atEnd()) {
|
||||
if (xml.readNext() == QXmlStreamReader::StartElement) {
|
||||
while (!xml.atEnd()) {
|
||||
if (xml.readNext() == QXmlStreamReader::EndElement) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (xml.name() == "sets") {
|
||||
loadSetsFromXml(xml);
|
||||
} else if (xml.name() == "cards") {
|
||||
loadCardsFromXml(xml);
|
||||
} else if (xml.name() != "") {
|
||||
qDebug() << "[CockatriceXml3Parser] Unknown item" << xml.name() << ", trying to continue anyway";
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CockatriceXml3Parser::loadSetsFromXml(QXmlStreamReader &xml)
|
||||
{
|
||||
while (!xml.atEnd()) {
|
||||
if (xml.readNext() == QXmlStreamReader::EndElement) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (xml.name() == "set") {
|
||||
QString shortName, longName, setType;
|
||||
QDate releaseDate;
|
||||
while (!xml.atEnd()) {
|
||||
if (xml.readNext() == QXmlStreamReader::EndElement) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (xml.name() == "name") {
|
||||
shortName = xml.readElementText(QXmlStreamReader::IncludeChildElements);
|
||||
} else if (xml.name() == "longname") {
|
||||
longName = xml.readElementText(QXmlStreamReader::IncludeChildElements);
|
||||
} else if (xml.name() == "settype") {
|
||||
setType = xml.readElementText(QXmlStreamReader::IncludeChildElements);
|
||||
} else if (xml.name() == "releasedate") {
|
||||
releaseDate =
|
||||
QDate::fromString(xml.readElementText(QXmlStreamReader::IncludeChildElements), Qt::ISODate);
|
||||
} else if (xml.name() != "") {
|
||||
qDebug() << "[CockatriceXml3Parser] Unknown set property" << xml.name()
|
||||
<< ", trying to continue anyway";
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
internalAddSet(shortName, longName, setType, releaseDate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString CockatriceXml3Parser::getMainCardType(QString &type)
|
||||
{
|
||||
QString result = type;
|
||||
/*
|
||||
Legendary Artifact Creature - Golem
|
||||
Instant // Instant
|
||||
*/
|
||||
|
||||
int pos;
|
||||
if ((pos = result.indexOf('-')) != -1) {
|
||||
result.remove(pos, result.length());
|
||||
}
|
||||
|
||||
if ((pos = result.indexOf("—")) != -1) {
|
||||
result.remove(pos, result.length());
|
||||
}
|
||||
|
||||
if ((pos = result.indexOf("//")) != -1) {
|
||||
result.remove(pos, result.length());
|
||||
}
|
||||
|
||||
result = result.simplified();
|
||||
/*
|
||||
Legendary Artifact Creature
|
||||
Instant
|
||||
*/
|
||||
|
||||
if ((pos = result.lastIndexOf(' ')) != -1) {
|
||||
result = result.mid(pos + 1);
|
||||
}
|
||||
/*
|
||||
Creature
|
||||
Instant
|
||||
*/
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void CockatriceXml3Parser::loadCardsFromXml(QXmlStreamReader &xml)
|
||||
{
|
||||
while (!xml.atEnd()) {
|
||||
if (xml.readNext() == QXmlStreamReader::EndElement) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (xml.name() == "card") {
|
||||
QString name = QString("");
|
||||
QString text = QString("");
|
||||
QVariantHash properties = QVariantHash();
|
||||
QString colors = QString("");
|
||||
QList<CardRelation *> relatedCards, reverseRelatedCards;
|
||||
CardInfoPerSetMap sets = CardInfoPerSetMap();
|
||||
int tableRow = 0;
|
||||
bool cipt = false;
|
||||
bool isToken = false;
|
||||
bool upsideDown = false;
|
||||
|
||||
while (!xml.atEnd()) {
|
||||
if (xml.readNext() == QXmlStreamReader::EndElement) {
|
||||
break;
|
||||
}
|
||||
// variable - assigned properties
|
||||
if (xml.name() == "name") {
|
||||
name = xml.readElementText(QXmlStreamReader::IncludeChildElements);
|
||||
} else if (xml.name() == "text") {
|
||||
text = xml.readElementText(QXmlStreamReader::IncludeChildElements);
|
||||
} else if (xml.name() == "color") {
|
||||
colors.append(xml.readElementText(QXmlStreamReader::IncludeChildElements));
|
||||
} else if (xml.name() == "token") {
|
||||
isToken = static_cast<bool>(xml.readElementText(QXmlStreamReader::IncludeChildElements).toInt());
|
||||
// generic properties
|
||||
} else if (xml.name() == "manacost") {
|
||||
properties.insert("manacost", xml.readElementText(QXmlStreamReader::IncludeChildElements));
|
||||
} else if (xml.name() == "cmc") {
|
||||
properties.insert("cmc", xml.readElementText(QXmlStreamReader::IncludeChildElements));
|
||||
} else if (xml.name() == "type") {
|
||||
QString type = xml.readElementText(QXmlStreamReader::IncludeChildElements);
|
||||
properties.insert("type", type);
|
||||
properties.insert("maintype", getMainCardType(type));
|
||||
} else if (xml.name() == "pt") {
|
||||
properties.insert("pt", xml.readElementText(QXmlStreamReader::IncludeChildElements));
|
||||
} else if (xml.name() == "loyalty") {
|
||||
properties.insert("loyalty", xml.readElementText(QXmlStreamReader::IncludeChildElements));
|
||||
// positioning info
|
||||
} else if (xml.name() == "tablerow") {
|
||||
tableRow = xml.readElementText(QXmlStreamReader::IncludeChildElements).toInt();
|
||||
} else if (xml.name() == "cipt") {
|
||||
cipt = (xml.readElementText(QXmlStreamReader::IncludeChildElements) == "1");
|
||||
} else if (xml.name() == "upsidedown") {
|
||||
upsideDown = (xml.readElementText(QXmlStreamReader::IncludeChildElements) == "1");
|
||||
// sets
|
||||
} else if (xml.name() == "set") {
|
||||
// NOTE: attributes must be read before readElementText()
|
||||
QXmlStreamAttributes attrs = xml.attributes();
|
||||
QString setName = xml.readElementText(QXmlStreamReader::IncludeChildElements);
|
||||
CardInfoPerSet setInfo(internalAddSet(setName));
|
||||
if (attrs.hasAttribute("muId")) {
|
||||
setInfo.setProperty("muid", attrs.value("muId").toString());
|
||||
}
|
||||
|
||||
if (attrs.hasAttribute("muId")) {
|
||||
setInfo.setProperty("uuid", attrs.value("uuId").toString());
|
||||
}
|
||||
|
||||
if (attrs.hasAttribute("picURL")) {
|
||||
setInfo.setProperty("picurl", attrs.value("picURL").toString());
|
||||
}
|
||||
|
||||
if (attrs.hasAttribute("num")) {
|
||||
setInfo.setProperty("num", attrs.value("num").toString());
|
||||
}
|
||||
|
||||
if (attrs.hasAttribute("rarity")) {
|
||||
setInfo.setProperty("rarity", attrs.value("rarity").toString());
|
||||
}
|
||||
sets.insert(setName, setInfo);
|
||||
// relatd cards
|
||||
} else if (xml.name() == "related" || xml.name() == "reverse-related") {
|
||||
bool attach = false;
|
||||
bool exclude = false;
|
||||
bool variable = false;
|
||||
int count = 1;
|
||||
QXmlStreamAttributes attrs = xml.attributes();
|
||||
QString cardName = xml.readElementText(QXmlStreamReader::IncludeChildElements);
|
||||
if (attrs.hasAttribute("count")) {
|
||||
if (attrs.value("count").toString().indexOf("x=") == 0) {
|
||||
variable = true;
|
||||
count = attrs.value("count").toString().remove(0, 2).toInt();
|
||||
} else if (attrs.value("count").toString().indexOf("x") == 0) {
|
||||
variable = true;
|
||||
} else {
|
||||
count = attrs.value("count").toString().toInt();
|
||||
}
|
||||
|
||||
if (count < 1) {
|
||||
count = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (attrs.hasAttribute("attach")) {
|
||||
attach = true;
|
||||
}
|
||||
|
||||
if (attrs.hasAttribute("exclude")) {
|
||||
exclude = true;
|
||||
}
|
||||
|
||||
auto *relation = new CardRelation(cardName, attach, exclude, variable, count);
|
||||
if (xml.name() == "reverse-related") {
|
||||
reverseRelatedCards << relation;
|
||||
} else {
|
||||
relatedCards << relation;
|
||||
}
|
||||
} else if (xml.name() != "") {
|
||||
qDebug() << "[CockatriceXml3Parser] Unknown card property" << xml.name()
|
||||
<< ", trying to continue anyway";
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
properties.insert("colors", colors);
|
||||
CardInfoPtr newCard = CardInfo::newInstance(name, text, isToken, properties, relatedCards,
|
||||
reverseRelatedCards, sets, cipt, tableRow, upsideDown);
|
||||
emit addCard(newCard);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardSetPtr &set)
|
||||
{
|
||||
if (set.isNull()) {
|
||||
qDebug() << "&operator<< set is nullptr";
|
||||
return xml;
|
||||
}
|
||||
|
||||
xml.writeStartElement("set");
|
||||
xml.writeTextElement("name", set->getShortName());
|
||||
xml.writeTextElement("longname", set->getLongName());
|
||||
xml.writeTextElement("settype", set->getSetType());
|
||||
xml.writeTextElement("releasedate", set->getReleaseDate().toString(Qt::ISODate));
|
||||
xml.writeEndElement();
|
||||
|
||||
return xml;
|
||||
}
|
||||
|
||||
static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfoPtr &info)
|
||||
{
|
||||
if (info.isNull()) {
|
||||
qDebug() << "operator<< info is nullptr";
|
||||
return xml;
|
||||
}
|
||||
|
||||
QString tmpString;
|
||||
|
||||
xml.writeStartElement("card");
|
||||
|
||||
// variable - assigned properties
|
||||
xml.writeTextElement("name", info->getName());
|
||||
xml.writeTextElement("text", info->getText());
|
||||
if (info->getIsToken()) {
|
||||
xml.writeTextElement("token", "1");
|
||||
}
|
||||
|
||||
// generic properties
|
||||
xml.writeTextElement("manacost", info->getProperty("manacost"));
|
||||
xml.writeTextElement("cmc", info->getProperty("cmc"));
|
||||
xml.writeTextElement("type", info->getProperty("type"));
|
||||
|
||||
int colorSize = info->getColors().size();
|
||||
for (int i = 0; i < colorSize; ++i) {
|
||||
xml.writeTextElement("color", info->getColors().at(i));
|
||||
}
|
||||
|
||||
tmpString = info->getProperty("pt");
|
||||
if (!tmpString.isEmpty()) {
|
||||
xml.writeTextElement("pt", tmpString);
|
||||
}
|
||||
|
||||
tmpString = info->getProperty("loyalty");
|
||||
if (!tmpString.isEmpty()) {
|
||||
xml.writeTextElement("loyalty", tmpString);
|
||||
}
|
||||
|
||||
// sets
|
||||
const CardInfoPerSetMap sets = info->getSets();
|
||||
for (CardInfoPerSet set : sets) {
|
||||
xml.writeStartElement("set");
|
||||
xml.writeAttribute("rarity", set.getProperty("rarity"));
|
||||
xml.writeAttribute("muId", set.getProperty("muid"));
|
||||
xml.writeAttribute("uuId", set.getProperty("uuid"));
|
||||
|
||||
tmpString = set.getProperty("num");
|
||||
if (!tmpString.isEmpty()) {
|
||||
xml.writeAttribute("num", tmpString);
|
||||
}
|
||||
|
||||
tmpString = set.getProperty("picurl");
|
||||
if (!tmpString.isEmpty()) {
|
||||
xml.writeAttribute("picURL", tmpString);
|
||||
}
|
||||
|
||||
xml.writeCharacters(set.getPtr()->getShortName());
|
||||
xml.writeEndElement();
|
||||
}
|
||||
|
||||
// related cards
|
||||
const QList<CardRelation *> related = info->getRelatedCards();
|
||||
for (auto i : related) {
|
||||
xml.writeStartElement("related");
|
||||
if (i->getDoesAttach()) {
|
||||
xml.writeAttribute("attach", "attach");
|
||||
}
|
||||
if (i->getIsCreateAllExclusion()) {
|
||||
xml.writeAttribute("exclude", "exclude");
|
||||
}
|
||||
|
||||
if (i->getIsVariable()) {
|
||||
if (1 == i->getDefaultCount()) {
|
||||
xml.writeAttribute("count", "x");
|
||||
} else {
|
||||
xml.writeAttribute("count", "x=" + QString::number(i->getDefaultCount()));
|
||||
}
|
||||
} else if (1 != i->getDefaultCount()) {
|
||||
xml.writeAttribute("count", QString::number(i->getDefaultCount()));
|
||||
}
|
||||
xml.writeCharacters(i->getName());
|
||||
xml.writeEndElement();
|
||||
}
|
||||
const QList<CardRelation *> reverseRelated = info->getReverseRelatedCards();
|
||||
for (auto i : reverseRelated) {
|
||||
xml.writeStartElement("reverse-related");
|
||||
if (i->getDoesAttach()) {
|
||||
xml.writeAttribute("attach", "attach");
|
||||
}
|
||||
|
||||
if (i->getIsCreateAllExclusion()) {
|
||||
xml.writeAttribute("exclude", "exclude");
|
||||
}
|
||||
|
||||
if (i->getIsVariable()) {
|
||||
if (1 == i->getDefaultCount()) {
|
||||
xml.writeAttribute("count", "x");
|
||||
} else {
|
||||
xml.writeAttribute("count", "x=" + QString::number(i->getDefaultCount()));
|
||||
}
|
||||
} else if (1 != i->getDefaultCount()) {
|
||||
xml.writeAttribute("count", QString::number(i->getDefaultCount()));
|
||||
}
|
||||
xml.writeCharacters(i->getName());
|
||||
xml.writeEndElement();
|
||||
}
|
||||
|
||||
// positioning
|
||||
xml.writeTextElement("tablerow", QString::number(info->getTableRow()));
|
||||
if (info->getCipt()) {
|
||||
xml.writeTextElement("cipt", "1");
|
||||
}
|
||||
if (info->getUpsideDownArt()) {
|
||||
xml.writeTextElement("upsidedown", "1");
|
||||
}
|
||||
|
||||
xml.writeEndElement(); // card
|
||||
|
||||
return xml;
|
||||
}
|
||||
|
||||
bool CockatriceXml3Parser::saveToFile(SetNameMap sets,
|
||||
CardNameMap cards,
|
||||
const QString &fileName,
|
||||
const QString &sourceUrl,
|
||||
const QString &sourceVersion)
|
||||
{
|
||||
QFile file(fileName);
|
||||
if (!file.open(QIODevice::WriteOnly)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QXmlStreamWriter xml(&file);
|
||||
|
||||
xml.setAutoFormatting(true);
|
||||
xml.writeStartDocument();
|
||||
xml.writeStartElement(COCKATRICE_XML3_TAGNAME);
|
||||
xml.writeAttribute("version", QString::number(COCKATRICE_XML3_TAGVER));
|
||||
xml.writeAttribute("xmlns:xsi", COCKATRICE_XML_XSI_NAMESPACE);
|
||||
xml.writeAttribute("xsi:schemaLocation", COCKATRICE_XML3_SCHEMALOCATION);
|
||||
|
||||
xml.writeStartElement("info");
|
||||
xml.writeTextElement("author", QCoreApplication::applicationName() + QString(" %1").arg(VERSION_STRING));
|
||||
xml.writeTextElement("createdAt", QDateTime::currentDateTimeUtc().toString(Qt::ISODate));
|
||||
xml.writeTextElement("sourceUrl", sourceUrl);
|
||||
xml.writeTextElement("sourceVersion", sourceVersion);
|
||||
xml.writeEndElement();
|
||||
|
||||
if (sets.count() > 0) {
|
||||
xml.writeStartElement("sets");
|
||||
for (CardSetPtr set : sets) {
|
||||
xml << set;
|
||||
}
|
||||
xml.writeEndElement();
|
||||
}
|
||||
|
||||
if (cards.count() > 0) {
|
||||
xml.writeStartElement("cards");
|
||||
for (CardInfoPtr card : cards) {
|
||||
xml << card;
|
||||
}
|
||||
xml.writeEndElement();
|
||||
}
|
||||
|
||||
xml.writeEndElement(); // cockatrice_carddatabase
|
||||
xml.writeEndDocument();
|
||||
|
||||
return true;
|
||||
}
|
||||
32
cockatrice/src/carddbparser/cockatricexml3.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#ifndef COCKATRICE_XML3_H
|
||||
#define COCKATRICE_XML3_H
|
||||
|
||||
#include "carddatabaseparser.h"
|
||||
|
||||
#include <QXmlStreamReader>
|
||||
|
||||
class CockatriceXml3Parser : public ICardDatabaseParser
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(ICardDatabaseParser)
|
||||
public:
|
||||
CockatriceXml3Parser() = default;
|
||||
~CockatriceXml3Parser() override = default;
|
||||
bool getCanParseFile(const QString &name, QIODevice &device) override;
|
||||
void parseFile(QIODevice &device) override;
|
||||
bool saveToFile(SetNameMap sets,
|
||||
CardNameMap cards,
|
||||
const QString &fileName,
|
||||
const QString &sourceUrl = "unknown",
|
||||
const QString &sourceVersion = "unknown") override;
|
||||
|
||||
private:
|
||||
void loadCardsFromXml(QXmlStreamReader &xml);
|
||||
void loadSetsFromXml(QXmlStreamReader &xml);
|
||||
QString getMainCardType(QString &type);
|
||||
signals:
|
||||
void addCard(CardInfoPtr card) override;
|
||||
void addSet(CardSetPtr set) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
386
cockatrice/src/carddbparser/cockatricexml4.cpp
Normal file
@@ -0,0 +1,386 @@
|
||||
#include "cockatricexml4.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QXmlStreamReader>
|
||||
#include <version_string.h>
|
||||
|
||||
#define COCKATRICE_XML4_TAGNAME "cockatrice_carddatabase"
|
||||
#define COCKATRICE_XML4_TAGVER 4
|
||||
#define COCKATRICE_XML4_SCHEMALOCATION \
|
||||
"https://raw.githubusercontent.com/Cockatrice/Cockatrice/master/doc/carddatabase_v4/cards.xsd"
|
||||
|
||||
bool CockatriceXml4Parser::getCanParseFile(const QString &fileName, QIODevice &device)
|
||||
{
|
||||
qDebug() << "[CockatriceXml4Parser] Trying to parse: " << fileName;
|
||||
|
||||
if (!fileName.endsWith(".xml", Qt::CaseInsensitive)) {
|
||||
qDebug() << "[CockatriceXml4Parser] Parsing failed: wrong extension";
|
||||
return false;
|
||||
}
|
||||
|
||||
QXmlStreamReader xml(&device);
|
||||
while (!xml.atEnd()) {
|
||||
if (xml.readNext() == QXmlStreamReader::StartElement) {
|
||||
if (xml.name() == COCKATRICE_XML4_TAGNAME) {
|
||||
int version = xml.attributes().value("version").toString().toInt();
|
||||
if (version == COCKATRICE_XML4_TAGVER) {
|
||||
return true;
|
||||
} else {
|
||||
qDebug() << "[CockatriceXml4Parser] Parsing failed: wrong version" << version;
|
||||
return false;
|
||||
}
|
||||
|
||||
} else {
|
||||
qDebug() << "[CockatriceXml4Parser] Parsing failed: wrong element tag" << xml.name();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CockatriceXml4Parser::parseFile(QIODevice &device)
|
||||
{
|
||||
QXmlStreamReader xml(&device);
|
||||
while (!xml.atEnd()) {
|
||||
if (xml.readNext() == QXmlStreamReader::StartElement) {
|
||||
while (!xml.atEnd()) {
|
||||
if (xml.readNext() == QXmlStreamReader::EndElement) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (xml.name() == "sets") {
|
||||
loadSetsFromXml(xml);
|
||||
} else if (xml.name() == "cards") {
|
||||
loadCardsFromXml(xml);
|
||||
} else if (xml.name() != "") {
|
||||
qDebug() << "[CockatriceXml4Parser] Unknown item" << xml.name() << ", trying to continue anyway";
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CockatriceXml4Parser::loadSetsFromXml(QXmlStreamReader &xml)
|
||||
{
|
||||
while (!xml.atEnd()) {
|
||||
if (xml.readNext() == QXmlStreamReader::EndElement) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (xml.name() == "set") {
|
||||
QString shortName, longName, setType;
|
||||
QDate releaseDate;
|
||||
while (!xml.atEnd()) {
|
||||
if (xml.readNext() == QXmlStreamReader::EndElement) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (xml.name() == "name") {
|
||||
shortName = xml.readElementText(QXmlStreamReader::IncludeChildElements);
|
||||
} else if (xml.name() == "longname") {
|
||||
longName = xml.readElementText(QXmlStreamReader::IncludeChildElements);
|
||||
} else if (xml.name() == "settype") {
|
||||
setType = xml.readElementText(QXmlStreamReader::IncludeChildElements);
|
||||
} else if (xml.name() == "releasedate") {
|
||||
releaseDate =
|
||||
QDate::fromString(xml.readElementText(QXmlStreamReader::IncludeChildElements), Qt::ISODate);
|
||||
} else if (xml.name() != "") {
|
||||
qDebug() << "[CockatriceXml4Parser] Unknown set property" << xml.name()
|
||||
<< ", trying to continue anyway";
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
internalAddSet(shortName, longName, setType, releaseDate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QVariantHash CockatriceXml4Parser::loadCardPropertiesFromXml(QXmlStreamReader &xml)
|
||||
{
|
||||
QVariantHash properties = QVariantHash();
|
||||
while (!xml.atEnd()) {
|
||||
if (xml.readNext() == QXmlStreamReader::EndElement) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (xml.name() != "") {
|
||||
properties.insert(xml.name().toString(), xml.readElementText(QXmlStreamReader::IncludeChildElements));
|
||||
}
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
void CockatriceXml4Parser::loadCardsFromXml(QXmlStreamReader &xml)
|
||||
{
|
||||
while (!xml.atEnd()) {
|
||||
if (xml.readNext() == QXmlStreamReader::EndElement) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (xml.name() == "card") {
|
||||
QString name = QString("");
|
||||
QString text = QString("");
|
||||
QVariantHash properties = QVariantHash();
|
||||
QList<CardRelation *> relatedCards, reverseRelatedCards;
|
||||
CardInfoPerSetMap sets = CardInfoPerSetMap();
|
||||
int tableRow = 0;
|
||||
bool cipt = false;
|
||||
bool isToken = false;
|
||||
bool upsideDown = false;
|
||||
|
||||
while (!xml.atEnd()) {
|
||||
if (xml.readNext() == QXmlStreamReader::EndElement) {
|
||||
break;
|
||||
}
|
||||
// variable - assigned properties
|
||||
if (xml.name() == "name") {
|
||||
name = xml.readElementText(QXmlStreamReader::IncludeChildElements);
|
||||
} else if (xml.name() == "text") {
|
||||
text = xml.readElementText(QXmlStreamReader::IncludeChildElements);
|
||||
} else if (xml.name() == "token") {
|
||||
isToken = static_cast<bool>(xml.readElementText(QXmlStreamReader::IncludeChildElements).toInt());
|
||||
// generic properties
|
||||
} else if (xml.name() == "prop") {
|
||||
properties = loadCardPropertiesFromXml(xml);
|
||||
// positioning info
|
||||
} else if (xml.name() == "tablerow") {
|
||||
tableRow = xml.readElementText(QXmlStreamReader::IncludeChildElements).toInt();
|
||||
} else if (xml.name() == "cipt") {
|
||||
cipt = (xml.readElementText(QXmlStreamReader::IncludeChildElements) == "1");
|
||||
} else if (xml.name() == "upsidedown") {
|
||||
upsideDown = (xml.readElementText(QXmlStreamReader::IncludeChildElements) == "1");
|
||||
// sets
|
||||
} else if (xml.name() == "set") {
|
||||
// NOTE: attributes but be read before readElementText()
|
||||
QXmlStreamAttributes attrs = xml.attributes();
|
||||
QString setName = xml.readElementText(QXmlStreamReader::IncludeChildElements);
|
||||
auto set = internalAddSet(setName);
|
||||
if (set->getEnabled()) {
|
||||
CardInfoPerSet setInfo(set);
|
||||
for (QXmlStreamAttribute attr : attrs) {
|
||||
QString attrName = attr.name().toString();
|
||||
if (attrName == "picURL")
|
||||
attrName = "picurl";
|
||||
setInfo.setProperty(attrName, attr.value().toString());
|
||||
}
|
||||
sets.insert(setName, setInfo);
|
||||
}
|
||||
// relatd cards
|
||||
} else if (xml.name() == "related" || xml.name() == "reverse-related") {
|
||||
bool attach = false;
|
||||
bool exclude = false;
|
||||
bool variable = false;
|
||||
int count = 1;
|
||||
QXmlStreamAttributes attrs = xml.attributes();
|
||||
QString cardName = xml.readElementText(QXmlStreamReader::IncludeChildElements);
|
||||
if (attrs.hasAttribute("count")) {
|
||||
if (attrs.value("count").toString().indexOf("x=") == 0) {
|
||||
variable = true;
|
||||
count = attrs.value("count").toString().remove(0, 2).toInt();
|
||||
} else if (attrs.value("count").toString().indexOf("x") == 0) {
|
||||
variable = true;
|
||||
} else {
|
||||
count = attrs.value("count").toString().toInt();
|
||||
}
|
||||
|
||||
if (count < 1) {
|
||||
count = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (attrs.hasAttribute("attach")) {
|
||||
attach = true;
|
||||
}
|
||||
|
||||
if (attrs.hasAttribute("exclude")) {
|
||||
exclude = true;
|
||||
}
|
||||
|
||||
auto *relation = new CardRelation(cardName, attach, exclude, variable, count);
|
||||
if (xml.name() == "reverse-related") {
|
||||
reverseRelatedCards << relation;
|
||||
} else {
|
||||
relatedCards << relation;
|
||||
}
|
||||
} else if (xml.name() != "") {
|
||||
qDebug() << "[CockatriceXml4Parser] Unknown card property" << xml.name()
|
||||
<< ", trying to continue anyway";
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
CardInfoPtr newCard = CardInfo::newInstance(name, text, isToken, properties, relatedCards,
|
||||
reverseRelatedCards, sets, cipt, tableRow, upsideDown);
|
||||
emit addCard(newCard);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardSetPtr &set)
|
||||
{
|
||||
if (set.isNull()) {
|
||||
qDebug() << "&operator<< set is nullptr";
|
||||
return xml;
|
||||
}
|
||||
|
||||
xml.writeStartElement("set");
|
||||
xml.writeTextElement("name", set->getShortName());
|
||||
xml.writeTextElement("longname", set->getLongName());
|
||||
xml.writeTextElement("settype", set->getSetType());
|
||||
xml.writeTextElement("releasedate", set->getReleaseDate().toString(Qt::ISODate));
|
||||
xml.writeEndElement();
|
||||
|
||||
return xml;
|
||||
}
|
||||
|
||||
static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfoPtr &info)
|
||||
{
|
||||
if (info.isNull()) {
|
||||
qDebug() << "operator<< info is nullptr";
|
||||
return xml;
|
||||
}
|
||||
|
||||
QString tmpString;
|
||||
|
||||
xml.writeStartElement("card");
|
||||
|
||||
// variable - assigned properties
|
||||
xml.writeTextElement("name", info->getName());
|
||||
xml.writeTextElement("text", info->getText());
|
||||
if (info->getIsToken()) {
|
||||
xml.writeTextElement("token", "1");
|
||||
}
|
||||
|
||||
// generic properties
|
||||
xml.writeStartElement("prop");
|
||||
for (QString propName : info->getProperties()) {
|
||||
xml.writeTextElement(propName, info->getProperty(propName));
|
||||
}
|
||||
xml.writeEndElement();
|
||||
|
||||
// sets
|
||||
for (CardInfoPerSet set : info->getSets()) {
|
||||
xml.writeStartElement("set");
|
||||
for (QString propName : set.getProperties()) {
|
||||
xml.writeAttribute(propName, set.getProperty(propName));
|
||||
}
|
||||
|
||||
xml.writeCharacters(set.getPtr()->getShortName());
|
||||
xml.writeEndElement();
|
||||
}
|
||||
|
||||
// related cards
|
||||
const QList<CardRelation *> related = info->getRelatedCards();
|
||||
for (auto i : related) {
|
||||
xml.writeStartElement("related");
|
||||
if (i->getDoesAttach()) {
|
||||
xml.writeAttribute("attach", "attach");
|
||||
}
|
||||
if (i->getIsCreateAllExclusion()) {
|
||||
xml.writeAttribute("exclude", "exclude");
|
||||
}
|
||||
|
||||
if (i->getIsVariable()) {
|
||||
if (1 == i->getDefaultCount()) {
|
||||
xml.writeAttribute("count", "x");
|
||||
} else {
|
||||
xml.writeAttribute("count", "x=" + QString::number(i->getDefaultCount()));
|
||||
}
|
||||
} else if (1 != i->getDefaultCount()) {
|
||||
xml.writeAttribute("count", QString::number(i->getDefaultCount()));
|
||||
}
|
||||
xml.writeCharacters(i->getName());
|
||||
xml.writeEndElement();
|
||||
}
|
||||
const QList<CardRelation *> reverseRelated = info->getReverseRelatedCards();
|
||||
for (auto i : reverseRelated) {
|
||||
xml.writeStartElement("reverse-related");
|
||||
if (i->getDoesAttach()) {
|
||||
xml.writeAttribute("attach", "attach");
|
||||
}
|
||||
|
||||
if (i->getIsCreateAllExclusion()) {
|
||||
xml.writeAttribute("exclude", "exclude");
|
||||
}
|
||||
|
||||
if (i->getIsVariable()) {
|
||||
if (1 == i->getDefaultCount()) {
|
||||
xml.writeAttribute("count", "x");
|
||||
} else {
|
||||
xml.writeAttribute("count", "x=" + QString::number(i->getDefaultCount()));
|
||||
}
|
||||
} else if (1 != i->getDefaultCount()) {
|
||||
xml.writeAttribute("count", QString::number(i->getDefaultCount()));
|
||||
}
|
||||
xml.writeCharacters(i->getName());
|
||||
xml.writeEndElement();
|
||||
}
|
||||
|
||||
// positioning
|
||||
xml.writeTextElement("tablerow", QString::number(info->getTableRow()));
|
||||
if (info->getCipt()) {
|
||||
xml.writeTextElement("cipt", "1");
|
||||
}
|
||||
if (info->getUpsideDownArt()) {
|
||||
xml.writeTextElement("upsidedown", "1");
|
||||
}
|
||||
|
||||
xml.writeEndElement(); // card
|
||||
|
||||
return xml;
|
||||
}
|
||||
|
||||
bool CockatriceXml4Parser::saveToFile(SetNameMap sets,
|
||||
CardNameMap cards,
|
||||
const QString &fileName,
|
||||
const QString &sourceUrl,
|
||||
const QString &sourceVersion)
|
||||
{
|
||||
QFile file(fileName);
|
||||
if (!file.open(QIODevice::WriteOnly)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QXmlStreamWriter xml(&file);
|
||||
|
||||
xml.setAutoFormatting(true);
|
||||
xml.writeStartDocument();
|
||||
xml.writeStartElement(COCKATRICE_XML4_TAGNAME);
|
||||
xml.writeAttribute("version", QString::number(COCKATRICE_XML4_TAGVER));
|
||||
xml.writeAttribute("xmlns:xsi", COCKATRICE_XML_XSI_NAMESPACE);
|
||||
xml.writeAttribute("xsi:schemaLocation", COCKATRICE_XML4_SCHEMALOCATION);
|
||||
|
||||
xml.writeStartElement("info");
|
||||
xml.writeTextElement("author", QCoreApplication::applicationName() + QString(" %1").arg(VERSION_STRING));
|
||||
xml.writeTextElement("createdAt", QDateTime::currentDateTimeUtc().toString(Qt::ISODate));
|
||||
xml.writeTextElement("sourceUrl", sourceUrl);
|
||||
xml.writeTextElement("sourceVersion", sourceVersion);
|
||||
xml.writeEndElement();
|
||||
|
||||
if (sets.count() > 0) {
|
||||
xml.writeStartElement("sets");
|
||||
for (CardSetPtr set : sets) {
|
||||
xml << set;
|
||||
}
|
||||
xml.writeEndElement();
|
||||
}
|
||||
|
||||
if (cards.count() > 0) {
|
||||
xml.writeStartElement("cards");
|
||||
for (CardInfoPtr card : cards) {
|
||||
xml << card;
|
||||
}
|
||||
xml.writeEndElement();
|
||||
}
|
||||
|
||||
xml.writeEndElement(); // cockatrice_carddatabase
|
||||
xml.writeEndDocument();
|
||||
|
||||
return true;
|
||||
}
|
||||
32
cockatrice/src/carddbparser/cockatricexml4.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#ifndef COCKATRICE_XML4_H
|
||||
#define COCKATRICE_XML4_H
|
||||
|
||||
#include "carddatabaseparser.h"
|
||||
|
||||
#include <QXmlStreamReader>
|
||||
|
||||
class CockatriceXml4Parser : public ICardDatabaseParser
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(ICardDatabaseParser)
|
||||
public:
|
||||
CockatriceXml4Parser() = default;
|
||||
~CockatriceXml4Parser() override = default;
|
||||
bool getCanParseFile(const QString &name, QIODevice &device) override;
|
||||
void parseFile(QIODevice &device) override;
|
||||
bool saveToFile(SetNameMap sets,
|
||||
CardNameMap cards,
|
||||
const QString &fileName,
|
||||
const QString &sourceUrl = "unknown",
|
||||
const QString &sourceVersion = "unknown") override;
|
||||
|
||||
private:
|
||||
QVariantHash loadCardPropertiesFromXml(QXmlStreamReader &xml);
|
||||
void loadCardsFromXml(QXmlStreamReader &xml);
|
||||
void loadSetsFromXml(QXmlStreamReader &xml);
|
||||
signals:
|
||||
void addCard(CardInfoPtr card) override;
|
||||
void addSet(CardSetPtr set) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,9 +1,11 @@
|
||||
#include "carddragitem.h"
|
||||
|
||||
#include "carditem.h"
|
||||
#include "cardzone.h"
|
||||
#include "gamescene.h"
|
||||
#include "tablezone.h"
|
||||
#include "zoneviewzone.h"
|
||||
|
||||
#include <QCursor>
|
||||
#include <QGraphicsSceneMouseEvent>
|
||||
#include <QPainter>
|
||||
|
||||
@@ -1,47 +1,49 @@
|
||||
#include "cardfilter.h"
|
||||
|
||||
const char *CardFilter::typeName(Type t)
|
||||
const QString CardFilter::typeName(Type t)
|
||||
{
|
||||
switch (t) {
|
||||
case TypeAnd:
|
||||
return "AND";
|
||||
return tr("AND", "Logical conjunction operator used in card filter");
|
||||
case TypeOr:
|
||||
return "OR";
|
||||
return tr("OR", "Logical disjunction operator used in card filter");
|
||||
case TypeAndNot:
|
||||
return "AND NOT";
|
||||
return tr("AND NOT", "Negated logical conjunction operator used in card filter");
|
||||
case TypeOrNot:
|
||||
return "OR NOT";
|
||||
return tr("OR NOT", "Negated logical disjunction operator used in card filter");
|
||||
default:
|
||||
return "";
|
||||
return QString("");
|
||||
}
|
||||
}
|
||||
|
||||
const char *CardFilter::attrName(Attr a)
|
||||
const QString CardFilter::attrName(Attr a)
|
||||
{
|
||||
switch (a) {
|
||||
case AttrName:
|
||||
return "Name";
|
||||
return tr("Name");
|
||||
case AttrType:
|
||||
return "Type";
|
||||
return tr("Type");
|
||||
case AttrColor:
|
||||
return "Color";
|
||||
return tr("Color");
|
||||
case AttrText:
|
||||
return "Text";
|
||||
return tr("Text");
|
||||
case AttrSet:
|
||||
return "Set";
|
||||
return tr("Set");
|
||||
case AttrManaCost:
|
||||
return "Mana Cost";
|
||||
return tr("Mana Cost");
|
||||
case AttrCmc:
|
||||
return "CMC";
|
||||
return tr("Mana Value");
|
||||
case AttrRarity:
|
||||
return "Rarity";
|
||||
return tr("Rarity");
|
||||
case AttrPow:
|
||||
return "Power";
|
||||
return tr("Power");
|
||||
case AttrTough:
|
||||
return "Toughness";
|
||||
return tr("Toughness");
|
||||
case AttrLoyalty:
|
||||
return "Loyalty";
|
||||
return tr("Loyalty");
|
||||
case AttrFormat:
|
||||
return tr("Format");
|
||||
default:
|
||||
return "";
|
||||
return QString("");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
#ifndef CARDFILTER_H
|
||||
#define CARDFILTER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <utility>
|
||||
|
||||
class CardFilter
|
||||
class CardFilter : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum Type
|
||||
{
|
||||
@@ -15,7 +19,7 @@ public:
|
||||
TypeEnd
|
||||
};
|
||||
|
||||
/* if you add an atribute here you also need to
|
||||
/* if you add an attribute here you also need to
|
||||
* add its string representation in attrName */
|
||||
enum Attr
|
||||
{
|
||||
@@ -30,6 +34,7 @@ public:
|
||||
AttrText,
|
||||
AttrTough,
|
||||
AttrType,
|
||||
AttrFormat,
|
||||
AttrEnd
|
||||
};
|
||||
|
||||
@@ -39,7 +44,7 @@ private:
|
||||
enum Attr a;
|
||||
|
||||
public:
|
||||
CardFilter(QString term, Type type, Attr attr) : trm(term), t(type), a(attr){};
|
||||
CardFilter(QString &term, Type type, Attr attr) : trm(term), t(type), a(attr){};
|
||||
|
||||
Type type() const
|
||||
{
|
||||
@@ -54,8 +59,8 @@ public:
|
||||
return a;
|
||||
}
|
||||
|
||||
static const char *typeName(Type t);
|
||||
static const char *attrName(Attr a);
|
||||
static const QString typeName(Type t);
|
||||
static const QString attrName(Attr a);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <QSplitter>
|
||||
#include <QVBoxLayout>
|
||||
#include <utility>
|
||||
|
||||
CardFrame::CardFrame(const QString &cardName, QWidget *parent) : QTabWidget(parent), info(nullptr), cardTextOnly(false)
|
||||
{
|
||||
@@ -16,6 +17,7 @@ CardFrame::CardFrame(const QString &cardName, QWidget *parent) : QTabWidget(pare
|
||||
pic->setObjectName("pic");
|
||||
text = new CardInfoText();
|
||||
text->setObjectName("text");
|
||||
connect(text, SIGNAL(linkActivated(const QString &)), this, SLOT(setCard(const QString &)));
|
||||
|
||||
tab1 = new QWidget(this);
|
||||
tab2 = new QWidget(this);
|
||||
@@ -53,7 +55,7 @@ CardFrame::CardFrame(const QString &cardName, QWidget *parent) : QTabWidget(pare
|
||||
tab3Layout->addWidget(splitter);
|
||||
tab3->setLayout(tab3Layout);
|
||||
|
||||
setViewMode(settingsCache->getCardInfoViewMode());
|
||||
setViewMode(SettingsCache::instance().getCardInfoViewMode());
|
||||
|
||||
setCard(db->getCard(cardName));
|
||||
}
|
||||
@@ -84,7 +86,7 @@ void CardFrame::setViewMode(int mode)
|
||||
break;
|
||||
}
|
||||
|
||||
settingsCache->setCardInfoViewMode(mode);
|
||||
SettingsCache::instance().setCardInfoViewMode(mode);
|
||||
}
|
||||
|
||||
void CardFrame::setCard(CardInfoPtr card)
|
||||
@@ -93,10 +95,10 @@ void CardFrame::setCard(CardInfoPtr card)
|
||||
disconnect(info.data(), nullptr, this, nullptr);
|
||||
}
|
||||
|
||||
info = card;
|
||||
info = std::move(card);
|
||||
|
||||
if (info) {
|
||||
connect(info.data(), SIGNAL(destroyed()), this, SLOT(clear()));
|
||||
connect(info.data(), SIGNAL(destroyed()), this, SLOT(clearCard()));
|
||||
}
|
||||
|
||||
text->setCard(info);
|
||||
@@ -105,7 +107,7 @@ void CardFrame::setCard(CardInfoPtr card)
|
||||
|
||||
void CardFrame::setCard(const QString &cardName)
|
||||
{
|
||||
setCard(db->getCardBySimpleName(cardName));
|
||||
setCard(db->guessCard(cardName));
|
||||
}
|
||||
|
||||
void CardFrame::setCard(AbstractCardItem *card)
|
||||
@@ -115,7 +117,7 @@ void CardFrame::setCard(AbstractCardItem *card)
|
||||
}
|
||||
}
|
||||
|
||||
void CardFrame::clear()
|
||||
void CardFrame::clearCard()
|
||||
{
|
||||
setCard((CardInfoPtr) nullptr);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#ifndef CARDFRAME_H
|
||||
#define CARDFRAME_H
|
||||
|
||||
#include <QTabWidget>
|
||||
|
||||
#include "carddatabase.h"
|
||||
|
||||
#include <QTabWidget>
|
||||
|
||||
class AbstractCardItem;
|
||||
class CardInfoPicture;
|
||||
class CardInfoText;
|
||||
@@ -37,7 +37,7 @@ public slots:
|
||||
void setCard(CardInfoPtr card);
|
||||
void setCard(const QString &cardName);
|
||||
void setCard(AbstractCardItem *card);
|
||||
void clear();
|
||||
void clearCard();
|
||||
void setViewMode(int mode);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#include "cardinfopicture.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include <QStyle>
|
||||
#include <QWidget>
|
||||
|
||||
#include "carditem.h"
|
||||
#include "main.h"
|
||||
#include "pictureloader.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include <QStyle>
|
||||
#include <QWidget>
|
||||
|
||||
CardInfoPicture::CardInfoPicture(QWidget *parent) : QWidget(parent), info(nullptr), pixmapDirty(true)
|
||||
{
|
||||
setMinimumHeight(100);
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#ifndef CARDINFOPICTURE_H
|
||||
#define CARDINFOPICTURE_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
#include "carddatabase.h"
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
class AbstractCardItem;
|
||||
|
||||
class CardInfoPicture : public QWidget
|
||||
@@ -17,7 +17,7 @@ private:
|
||||
bool pixmapDirty;
|
||||
|
||||
public:
|
||||
CardInfoPicture(QWidget *parent = 0);
|
||||
CardInfoPicture(QWidget *parent = nullptr);
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *event);
|
||||
|
||||
@@ -1,55 +1,27 @@
|
||||
#include "cardinfotext.h"
|
||||
|
||||
#include "carditem.h"
|
||||
#include "game_specific_terms.h"
|
||||
#include "main.h"
|
||||
|
||||
#include <QGridLayout>
|
||||
#include <QLabel>
|
||||
#include <QTextEdit>
|
||||
|
||||
CardInfoText::CardInfoText(QWidget *parent) : QFrame(parent), info(nullptr)
|
||||
{
|
||||
nameLabel1 = new QLabel;
|
||||
nameLabel2 = new QLabel;
|
||||
nameLabel2->setWordWrap(true);
|
||||
nameLabel2->setTextInteractionFlags(Qt::TextBrowserInteraction);
|
||||
manacostLabel1 = new QLabel;
|
||||
manacostLabel2 = new QLabel;
|
||||
manacostLabel2->setWordWrap(true);
|
||||
manacostLabel2->setTextInteractionFlags(Qt::TextBrowserInteraction);
|
||||
colorLabel1 = new QLabel;
|
||||
colorLabel2 = new QLabel;
|
||||
colorLabel2->setWordWrap(true);
|
||||
colorLabel2->setTextInteractionFlags(Qt::TextBrowserInteraction);
|
||||
cardtypeLabel1 = new QLabel;
|
||||
cardtypeLabel2 = new QLabel;
|
||||
cardtypeLabel2->setWordWrap(true);
|
||||
cardtypeLabel2->setTextInteractionFlags(Qt::TextBrowserInteraction);
|
||||
powtoughLabel1 = new QLabel;
|
||||
powtoughLabel2 = new QLabel;
|
||||
powtoughLabel2->setTextInteractionFlags(Qt::TextBrowserInteraction);
|
||||
loyaltyLabel1 = new QLabel;
|
||||
loyaltyLabel2 = new QLabel;
|
||||
loyaltyLabel1->setTextInteractionFlags(Qt::TextBrowserInteraction);
|
||||
nameLabel = new QLabel;
|
||||
nameLabel->setOpenExternalLinks(false);
|
||||
nameLabel->setWordWrap(true);
|
||||
connect(nameLabel, SIGNAL(linkActivated(const QString &)), this, SIGNAL(linkActivated(const QString &)));
|
||||
|
||||
textLabel = new QTextEdit();
|
||||
textLabel->setReadOnly(true);
|
||||
|
||||
QGridLayout *grid = new QGridLayout(this);
|
||||
int row = 0;
|
||||
grid->addWidget(nameLabel1, row, 0);
|
||||
grid->addWidget(nameLabel2, row++, 1);
|
||||
grid->addWidget(manacostLabel1, row, 0);
|
||||
grid->addWidget(manacostLabel2, row++, 1);
|
||||
grid->addWidget(colorLabel1, row, 0);
|
||||
grid->addWidget(colorLabel2, row++, 1);
|
||||
grid->addWidget(cardtypeLabel1, row, 0);
|
||||
grid->addWidget(cardtypeLabel2, row++, 1);
|
||||
grid->addWidget(powtoughLabel1, row, 0);
|
||||
grid->addWidget(powtoughLabel2, row++, 1);
|
||||
grid->addWidget(loyaltyLabel1, row, 0);
|
||||
grid->addWidget(loyaltyLabel2, row++, 1);
|
||||
grid->addWidget(textLabel, row, 0, -1, 2);
|
||||
grid->setRowStretch(row, 1);
|
||||
auto *grid = new QGridLayout(this);
|
||||
grid->addWidget(nameLabel, 0, 0);
|
||||
grid->addWidget(textLabel, 1, 0, -1, 2);
|
||||
grid->setRowStretch(1, 1);
|
||||
grid->setColumnStretch(1, 1);
|
||||
|
||||
retranslateUi();
|
||||
@@ -57,31 +29,53 @@ CardInfoText::CardInfoText(QWidget *parent) : QFrame(parent), info(nullptr)
|
||||
|
||||
void CardInfoText::setCard(CardInfoPtr card)
|
||||
{
|
||||
if (card) {
|
||||
nameLabel2->setText(card->getName());
|
||||
manacostLabel2->setText(card->getManaCost());
|
||||
colorLabel2->setText(card->getColors().join(""));
|
||||
cardtypeLabel2->setText(card->getCardType());
|
||||
powtoughLabel2->setText(card->getPowTough());
|
||||
loyaltyLabel2->setText(card->getLoyalty());
|
||||
textLabel->setText(card->getText());
|
||||
} else {
|
||||
nameLabel2->setText("");
|
||||
manacostLabel2->setText("");
|
||||
colorLabel2->setText("");
|
||||
cardtypeLabel2->setText("");
|
||||
powtoughLabel2->setText("");
|
||||
loyaltyLabel2->setText("");
|
||||
if (card == nullptr) {
|
||||
nameLabel->setText("");
|
||||
textLabel->setText("");
|
||||
return;
|
||||
}
|
||||
|
||||
QString text = "<table width=\"100%\" border=0 cellspacing=0 cellpadding=0>";
|
||||
text += QString("<tr><td>%1</td><td width=\"5\"></td><td>%2</td></tr>")
|
||||
.arg(tr("Name:"), card->getName().toHtmlEscaped());
|
||||
|
||||
QStringList cardProps = card->getProperties();
|
||||
for (const QString &key : cardProps) {
|
||||
if (key.contains("-"))
|
||||
continue;
|
||||
QString keyText = Mtg::getNicePropertyName(key).toHtmlEscaped() + ":";
|
||||
text +=
|
||||
QString("<tr><td>%1</td><td></td><td>%2</td></tr>").arg(keyText, card->getProperty(key).toHtmlEscaped());
|
||||
}
|
||||
|
||||
auto relatedCards = card->getAllRelatedCards();
|
||||
if (!relatedCards.empty()) {
|
||||
text += QString("<tr><td>%1</td><td width=\"5\"></td><td>").arg(tr("Related cards:"));
|
||||
|
||||
for (auto *relatedCard : relatedCards) {
|
||||
QString tmp = relatedCard->getName().toHtmlEscaped();
|
||||
text += "<a href=\"" + tmp + "\">" + tmp + "</a><br>";
|
||||
}
|
||||
|
||||
text += "</td></tr>";
|
||||
}
|
||||
|
||||
text += "</table>";
|
||||
nameLabel->setText(text);
|
||||
textLabel->setText(card->getText());
|
||||
}
|
||||
|
||||
void CardInfoText::setInvalidCardName(const QString &cardName)
|
||||
{
|
||||
nameLabel->setText(tr("Unknown card:") + " " + cardName);
|
||||
textLabel->setText("");
|
||||
}
|
||||
|
||||
void CardInfoText::retranslateUi()
|
||||
{
|
||||
nameLabel1->setText(tr("Name:"));
|
||||
manacostLabel1->setText(tr("Mana cost:"));
|
||||
colorLabel1->setText(tr("Color(s):"));
|
||||
cardtypeLabel1->setText(tr("Card type:"));
|
||||
powtoughLabel1->setText(tr("P / T:"));
|
||||
loyaltyLabel1->setText(tr("Loyalty:"));
|
||||
/*
|
||||
* There's no way we can really translate the text currently being rendered.
|
||||
* The best we can do is invalidate the current text.
|
||||
*/
|
||||
setInvalidCardName("");
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#ifndef CARDINFOTEXT_H
|
||||
#define CARDINFOTEXT_H
|
||||
|
||||
#include <QFrame>
|
||||
|
||||
#include "carddatabase.h"
|
||||
|
||||
#include <QFrame>
|
||||
class QLabel;
|
||||
class QTextEdit;
|
||||
|
||||
@@ -12,20 +12,17 @@ class CardInfoText : public QFrame
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
QLabel *nameLabel1, *nameLabel2;
|
||||
QLabel *manacostLabel1, *manacostLabel2;
|
||||
QLabel *colorLabel1, *colorLabel2;
|
||||
QLabel *cardtypeLabel1, *cardtypeLabel2;
|
||||
QLabel *powtoughLabel1, *powtoughLabel2;
|
||||
QLabel *loyaltyLabel1, *loyaltyLabel2;
|
||||
QLabel *nameLabel;
|
||||
QTextEdit *textLabel;
|
||||
|
||||
CardInfoPtr info;
|
||||
|
||||
public:
|
||||
CardInfoText(QWidget *parent = 0);
|
||||
explicit CardInfoText(QWidget *parent = nullptr);
|
||||
void retranslateUi();
|
||||
void setInvalidCardName(const QString &cardName);
|
||||
|
||||
signals:
|
||||
void linkActivated(const QString &link);
|
||||
public slots:
|
||||
void setCard(CardInfoPtr card);
|
||||
};
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
#include "cardinfowidget.h"
|
||||
|
||||
#include "cardinfopicture.h"
|
||||
#include "cardinfotext.h"
|
||||
#include "carditem.h"
|
||||
#include "main.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <utility>
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 13, 0))
|
||||
#include <QScreen>
|
||||
#else
|
||||
#include <QDesktopWidget>
|
||||
#endif
|
||||
#include <QVBoxLayout>
|
||||
|
||||
CardInfoWidget::CardInfoWidget(const QString &cardName, QWidget *parent, Qt::WindowFlags flags)
|
||||
@@ -14,8 +22,9 @@ CardInfoWidget::CardInfoWidget(const QString &cardName, QWidget *parent, Qt::Win
|
||||
pic->setObjectName("pic");
|
||||
text = new CardInfoText();
|
||||
text->setObjectName("text");
|
||||
connect(text, SIGNAL(linkActivated(const QString &)), this, SLOT(setCard(const QString &)));
|
||||
|
||||
QVBoxLayout *layout = new QVBoxLayout();
|
||||
auto *layout = new QVBoxLayout();
|
||||
layout->setObjectName("layout");
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->setSpacing(0);
|
||||
@@ -24,9 +33,14 @@ CardInfoWidget::CardInfoWidget(const QString &cardName, QWidget *parent, Qt::Win
|
||||
setLayout(layout);
|
||||
|
||||
setFrameStyle(QFrame::Panel | QFrame::Raised);
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 13, 0))
|
||||
int pixmapHeight = qApp->primaryScreen()->geometry().height() / 3;
|
||||
#else
|
||||
QDesktopWidget desktopWidget;
|
||||
int pixmapHeight = desktopWidget.screenGeometry().height() / 3;
|
||||
int pixmapWidth = pixmapHeight / aspectRatio;
|
||||
#endif
|
||||
int pixmapWidth = static_cast<int>(pixmapHeight / aspectRatio);
|
||||
pic->setFixedWidth(pixmapWidth);
|
||||
pic->setFixedHeight(pixmapHeight);
|
||||
setFixedWidth(pixmapWidth + 150);
|
||||
@@ -41,7 +55,7 @@ void CardInfoWidget::setCard(CardInfoPtr card)
|
||||
{
|
||||
if (info)
|
||||
disconnect(info.data(), nullptr, this, nullptr);
|
||||
info = card;
|
||||
info = std::move(card);
|
||||
if (info)
|
||||
connect(info.data(), SIGNAL(destroyed()), this, SLOT(clear()));
|
||||
|
||||
@@ -51,7 +65,10 @@ void CardInfoWidget::setCard(CardInfoPtr card)
|
||||
|
||||
void CardInfoWidget::setCard(const QString &cardName)
|
||||
{
|
||||
setCard(db->getCardBySimpleName(cardName));
|
||||
setCard(db->guessCard(cardName));
|
||||
if (info == nullptr) {
|
||||
text->setInvalidCardName(cardName);
|
||||
}
|
||||
}
|
||||
|
||||
void CardInfoWidget::setCard(AbstractCardItem *card)
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
#ifndef CARDINFOWIDGET_H
|
||||
#define CARDINFOWIDGET_H
|
||||
|
||||
#include "carddatabase.h"
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QFrame>
|
||||
#include <QStringList>
|
||||
|
||||
#include "carddatabase.h"
|
||||
|
||||
class CardInfoPicture;
|
||||
class CardInfoText;
|
||||
class AbstractCardItem;
|
||||
@@ -22,7 +22,7 @@ private:
|
||||
CardInfoText *text;
|
||||
|
||||
public:
|
||||
CardInfoWidget(const QString &cardName, QWidget *parent = 0, Qt::WindowFlags f = 0);
|
||||
explicit CardInfoWidget(const QString &cardName, QWidget *parent = nullptr, Qt::WindowFlags f = {});
|
||||
|
||||
public slots:
|
||||
void setCard(CardInfoPtr card);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "carditem.h"
|
||||
|
||||
#include "arrowitem.h"
|
||||
#include "carddatabase.h"
|
||||
#include "carddragitem.h"
|
||||
@@ -11,14 +12,20 @@
|
||||
#include "tab_game.h"
|
||||
#include "tablezone.h"
|
||||
#include "zoneviewzone.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QGraphicsSceneMouseEvent>
|
||||
#include <QMenu>
|
||||
#include <QPainter>
|
||||
|
||||
CardItem::CardItem(Player *_owner, const QString &_name, int _cardid, bool _revealedCard, QGraphicsItem *parent)
|
||||
: AbstractCardItem(_name, _owner, _cardid, parent), zone(0), revealedCard(_revealedCard), attacking(false),
|
||||
destroyOnZoneChange(false), doesntUntap(false), dragItem(0), attachedTo(0)
|
||||
CardItem::CardItem(Player *_owner,
|
||||
const QString &_name,
|
||||
int _cardid,
|
||||
bool _revealedCard,
|
||||
QGraphicsItem *parent,
|
||||
CardZone *_zone)
|
||||
: AbstractCardItem(_name, _owner, _cardid, parent), zone(_zone), revealedCard(_revealedCard), attacking(false),
|
||||
destroyOnZoneChange(false), doesntUntap(false), dragItem(nullptr), attachedTo(nullptr)
|
||||
{
|
||||
owner->addCard(this);
|
||||
|
||||
@@ -27,7 +34,6 @@ CardItem::CardItem(Player *_owner, const QString &_name, int _cardid, bool _reve
|
||||
moveMenu = new QMenu;
|
||||
|
||||
retranslateUi();
|
||||
emit updateCardMenu(this);
|
||||
}
|
||||
|
||||
CardItem::~CardItem()
|
||||
@@ -46,22 +52,22 @@ CardItem::~CardItem()
|
||||
|
||||
void CardItem::prepareDelete()
|
||||
{
|
||||
if (owner) {
|
||||
if (owner != nullptr) {
|
||||
if (owner->getCardMenu() == cardMenu) {
|
||||
owner->setCardMenu(0);
|
||||
owner->getGame()->setActiveCard(0);
|
||||
owner->setCardMenu(nullptr);
|
||||
owner->getGame()->setActiveCard(nullptr);
|
||||
}
|
||||
owner = 0;
|
||||
owner = nullptr;
|
||||
}
|
||||
|
||||
while (!attachedCards.isEmpty()) {
|
||||
attachedCards.first()->setZone(0); // so that it won't try to call reorganizeCards()
|
||||
attachedCards.first()->setAttachedTo(0);
|
||||
attachedCards.first()->setZone(nullptr); // so that it won't try to call reorganizeCards()
|
||||
attachedCards.first()->setAttachedTo(nullptr);
|
||||
}
|
||||
|
||||
if (attachedTo) {
|
||||
if (attachedTo != nullptr) {
|
||||
attachedTo->removeAttachedCard(this);
|
||||
attachedTo = 0;
|
||||
attachedTo = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +80,6 @@ void CardItem::deleteLater()
|
||||
void CardItem::setZone(CardZone *_zone)
|
||||
{
|
||||
zone = _zone;
|
||||
emit updateCardMenu(this);
|
||||
}
|
||||
|
||||
void CardItem::retranslateUi()
|
||||
@@ -106,17 +111,12 @@ void CardItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
|
||||
painter->save();
|
||||
transformPainter(painter, translatedSize, tapAngle);
|
||||
|
||||
if (info) {
|
||||
QStringList ptSplit = pt.split("/");
|
||||
QStringList ptDbSplit = info->getPowTough().split("/");
|
||||
|
||||
if (getFaceDown() || ptDbSplit.at(0) != ptSplit.at(0) || ptDbSplit.at(1) != ptSplit.at(1))
|
||||
painter->setPen(QColor(255, 150, 0));
|
||||
else
|
||||
painter->setPen(Qt::white);
|
||||
} else {
|
||||
if (!getFaceDown() && info && pt == info->getPowTough()) {
|
||||
painter->setPen(Qt::white);
|
||||
} else {
|
||||
painter->setPen(QColor(255, 150, 0)); // dark orange
|
||||
}
|
||||
|
||||
painter->setBackground(Qt::black);
|
||||
painter->setBackgroundMode(Qt::OpaqueMode);
|
||||
|
||||
@@ -142,7 +142,6 @@ void CardItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
|
||||
|
||||
if (getBeingPointedAt()) {
|
||||
painter->fillRect(boundingRect(), QBrush(QColor(255, 0, 0, 100)));
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
if (doesntUntap) {
|
||||
@@ -160,6 +159,8 @@ void CardItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
|
||||
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
void CardItem::setAttacking(bool _attacking)
|
||||
@@ -197,23 +198,25 @@ void CardItem::setPT(const QString &_pt)
|
||||
|
||||
void CardItem::setAttachedTo(CardItem *_attachedTo)
|
||||
{
|
||||
if (attachedTo)
|
||||
if (attachedTo != nullptr) {
|
||||
attachedTo->removeAttachedCard(this);
|
||||
}
|
||||
|
||||
gridPoint.setX(-1);
|
||||
attachedTo = _attachedTo;
|
||||
if (attachedTo) {
|
||||
if (attachedTo != nullptr) {
|
||||
setParentItem(attachedTo->getZone());
|
||||
attachedTo->addAttachedCard(this);
|
||||
if (zone != attachedTo->getZone())
|
||||
if (zone != attachedTo->getZone()) {
|
||||
attachedTo->getZone()->reorganizeCards();
|
||||
} else
|
||||
}
|
||||
} else {
|
||||
setParentItem(zone);
|
||||
}
|
||||
|
||||
if (zone)
|
||||
if (zone != nullptr) {
|
||||
zone->reorganizeCards();
|
||||
|
||||
emit updateCardMenu(this);
|
||||
}
|
||||
}
|
||||
|
||||
void CardItem::resetState()
|
||||
@@ -267,9 +270,10 @@ CardDragItem *CardItem::createDragItem(int _id, const QPointF &_pos, const QPoin
|
||||
|
||||
void CardItem::deleteDragItem()
|
||||
{
|
||||
if (dragItem)
|
||||
if (dragItem) {
|
||||
dragItem->deleteLater();
|
||||
dragItem = NULL;
|
||||
}
|
||||
dragItem = nullptr;
|
||||
}
|
||||
|
||||
void CardItem::drawArrow(const QColor &arrowColor)
|
||||
@@ -317,10 +321,10 @@ void CardItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
|
||||
2 * QApplication::startDragDistance())
|
||||
return;
|
||||
if (zone->getIsView()) {
|
||||
const ZoneViewZone *const view = static_cast<const ZoneViewZone *const>(zone);
|
||||
const ZoneViewZone *view = static_cast<const ZoneViewZone *>(zone);
|
||||
if (view->getRevealZone() && !view->getWriteableRevealZone())
|
||||
return;
|
||||
} else if (!owner->getLocal())
|
||||
} else if (!owner->getLocalOrJudge())
|
||||
return;
|
||||
|
||||
bool forceFaceDown = event->modifiers().testFlag(Qt::ShiftModifier);
|
||||
@@ -351,7 +355,7 @@ void CardItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
|
||||
void CardItem::playCard(bool faceDown)
|
||||
{
|
||||
// Do nothing if the card belongs to another player
|
||||
if (!owner->getLocal())
|
||||
if (!owner->getLocalOrJudge())
|
||||
return;
|
||||
|
||||
TableZone *tz = qobject_cast<TableZone *>(zone);
|
||||
@@ -364,15 +368,14 @@ void CardItem::playCard(bool faceDown)
|
||||
void CardItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
if (event->button() == Qt::RightButton) {
|
||||
if (cardMenu && !cardMenu->isEmpty() && owner) {
|
||||
owner->updateCardMenu(this);
|
||||
if (cardMenu != nullptr && !cardMenu->isEmpty() && owner != nullptr) {
|
||||
cardMenu->exec(event->screenPos());
|
||||
}
|
||||
} else if ((event->modifiers() != Qt::AltModifier) && (event->button() == Qt::LeftButton) &&
|
||||
(!settingsCache->getDoubleClickToPlay())) {
|
||||
(!SettingsCache::instance().getDoubleClickToPlay())) {
|
||||
bool hideCard = false;
|
||||
if (zone && zone->getIsView()) {
|
||||
ZoneViewZone *view = static_cast<ZoneViewZone *>(zone);
|
||||
auto *view = static_cast<ZoneViewZone *>(zone);
|
||||
if (view->getRevealZone() && !view->getWriteableRevealZone())
|
||||
hideCard = true;
|
||||
}
|
||||
@@ -383,13 +386,15 @@ void CardItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
|
||||
}
|
||||
}
|
||||
|
||||
setCursor(Qt::OpenHandCursor);
|
||||
if (owner != nullptr) { // cards without owner will be deleted
|
||||
setCursor(Qt::OpenHandCursor);
|
||||
}
|
||||
AbstractCardItem::mouseReleaseEvent(event);
|
||||
}
|
||||
|
||||
void CardItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
if ((event->modifiers() != Qt::AltModifier) && (settingsCache->getDoubleClickToPlay()) &&
|
||||
if ((event->modifiers() != Qt::AltModifier) && (SettingsCache::instance().getDoubleClickToPlay()) &&
|
||||
(event->buttons() == Qt::LeftButton)) {
|
||||
if (revealedCard)
|
||||
zone->removeCard(this);
|
||||
@@ -402,10 +407,19 @@ void CardItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
|
||||
bool CardItem::animationEvent()
|
||||
{
|
||||
int rotation = ROTATION_DEGREES_PER_FRAME;
|
||||
bool animationIncomplete = true;
|
||||
if (!tapped)
|
||||
rotation *= -1;
|
||||
|
||||
tapAngle += rotation;
|
||||
if (tapped && (tapAngle > 90)) {
|
||||
tapAngle = 90;
|
||||
animationIncomplete = false;
|
||||
}
|
||||
if (!tapped && (tapAngle < 0)) {
|
||||
tapAngle = 0;
|
||||
animationIncomplete = false;
|
||||
}
|
||||
|
||||
setTransform(QTransform()
|
||||
.translate(CARD_WIDTH_HALF, CARD_HEIGHT_HALF)
|
||||
@@ -414,20 +428,18 @@ bool CardItem::animationEvent()
|
||||
setHovered(false);
|
||||
update();
|
||||
|
||||
if ((tapped && (tapAngle >= 90)) || (!tapped && (tapAngle <= 0)))
|
||||
return false;
|
||||
return true;
|
||||
return animationIncomplete;
|
||||
}
|
||||
|
||||
QVariant CardItem::itemChange(GraphicsItemChange change, const QVariant &value)
|
||||
{
|
||||
if ((change == ItemSelectedHasChanged) && owner) {
|
||||
if ((change == ItemSelectedHasChanged) && owner != nullptr) {
|
||||
if (value == true) {
|
||||
owner->setCardMenu(cardMenu);
|
||||
owner->getGame()->setActiveCard(this);
|
||||
} else if (owner->getCardMenu() == cardMenu) {
|
||||
owner->setCardMenu(0);
|
||||
owner->getGame()->setActiveCard(0);
|
||||
owner->setCardMenu(nullptr);
|
||||
owner->getGame()->setActiveCard(nullptr);
|
||||
}
|
||||
}
|
||||
return QGraphicsItem::itemChange(change, value);
|
||||
|
||||
@@ -52,7 +52,8 @@ public:
|
||||
const QString &_name = QString(),
|
||||
int _cardid = -1,
|
||||
bool revealedCard = false,
|
||||
QGraphicsItem *parent = 0);
|
||||
QGraphicsItem *parent = nullptr,
|
||||
CardZone *_zone = nullptr);
|
||||
~CardItem();
|
||||
void retranslateUi();
|
||||
CardZone *getZone() const
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
#include "cardlist.h"
|
||||
|
||||
#include "carddatabase.h"
|
||||
#include "carditem.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
CardList::CardList(bool _contentsKnown) : QList<CardItem *>(), contentsKnown(_contentsKnown)
|
||||
{
|
||||
}
|
||||
@@ -56,5 +59,5 @@ public:
|
||||
void CardList::sort(int flags)
|
||||
{
|
||||
compareFunctor cf(flags);
|
||||
qSort(begin(), end(), cf);
|
||||
std::sort(begin(), end(), cf);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
#include "cardzone.h"
|
||||
|
||||
#include "carditem.h"
|
||||
#include "pb/command_move_card.pb.h"
|
||||
#include "pb/serverinfo_user.pb.h"
|
||||
#include "player.h"
|
||||
#include "zoneviewzone.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QDebug>
|
||||
#include <QGraphicsSceneMouseEvent>
|
||||
@@ -16,7 +18,7 @@ CardZone::CardZone(Player *_p,
|
||||
bool _contentsKnown,
|
||||
QGraphicsItem *parent,
|
||||
bool _isView)
|
||||
: AbstractGraphicsItem(parent), player(_p), name(_name), cards(_contentsKnown), view(NULL), menu(NULL),
|
||||
: AbstractGraphicsItem(parent), player(_p), name(_name), cards(_contentsKnown), views{}, menu(nullptr),
|
||||
doubleClickAction(0), hasCardAttr(_hasCardAttr), isShufflable(_isShufflable), isView(_isView)
|
||||
{
|
||||
if (!isView)
|
||||
@@ -26,7 +28,11 @@ CardZone::CardZone(Player *_p,
|
||||
CardZone::~CardZone()
|
||||
{
|
||||
qDebug() << "CardZone destructor: " << name;
|
||||
delete view;
|
||||
for (auto *view : views) {
|
||||
if (view != nullptr) {
|
||||
view->deleteLater();
|
||||
}
|
||||
}
|
||||
clearContents();
|
||||
}
|
||||
|
||||
@@ -118,9 +124,11 @@ void CardZone::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
||||
|
||||
void CardZone::addCard(CardItem *card, bool reorganize, int x, int y)
|
||||
{
|
||||
if (view)
|
||||
if ((x <= view->getCards().size()) || (view->getNumberCards() == -1))
|
||||
for (auto *view : views) {
|
||||
if ((x <= view->getCards().size()) || (view->getNumberCards() == -1)) {
|
||||
view->addCard(new CardItem(player, card->getName(), card->getId()), reorganize, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
card->setZone(this);
|
||||
addCardImpl(card, x, y);
|
||||
@@ -166,8 +174,9 @@ CardItem *CardZone::takeCard(int position, int cardId, bool /*canResize*/)
|
||||
|
||||
CardItem *c = cards.takeAt(position);
|
||||
|
||||
if (view)
|
||||
for (auto *view : views) {
|
||||
view->removeCard(position);
|
||||
}
|
||||
|
||||
c->setId(cardId);
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "abstractgraphicsitem.h"
|
||||
#include "cardlist.h"
|
||||
#include "translation.h"
|
||||
|
||||
#include <QString>
|
||||
|
||||
class Player;
|
||||
@@ -20,7 +21,7 @@ protected:
|
||||
Player *player;
|
||||
QString name;
|
||||
CardList cards;
|
||||
ZoneViewZone *view;
|
||||
QList<ZoneViewZone *> views;
|
||||
QMenu *menu;
|
||||
QAction *doubleClickAction;
|
||||
bool hasCardAttr;
|
||||
@@ -52,7 +53,7 @@ public:
|
||||
bool _hasCardAttr,
|
||||
bool _isShufflable,
|
||||
bool _contentsKnown,
|
||||
QGraphicsItem *parent = 0,
|
||||
QGraphicsItem *parent = nullptr,
|
||||
bool _isView = false);
|
||||
~CardZone();
|
||||
void retranslateUi();
|
||||
@@ -97,13 +98,9 @@ public:
|
||||
// takeCard() finds a card by position and removes it from the zone and from all of its views.
|
||||
virtual CardItem *takeCard(int position, int cardId, bool canResize = true);
|
||||
void removeCard(CardItem *card);
|
||||
ZoneViewZone *getView() const
|
||||
QList<ZoneViewZone *> &getViews()
|
||||
{
|
||||
return view;
|
||||
}
|
||||
void setView(ZoneViewZone *_view)
|
||||
{
|
||||
view = _view;
|
||||
return views;
|
||||
}
|
||||
virtual void reorganizeCards() = 0;
|
||||
virtual QPointF closestGridPoint(const QPointF &point);
|
||||
|
||||
@@ -1,20 +1,25 @@
|
||||
#include "chatview.h"
|
||||
|
||||
#include "../pixmapgenerator.h"
|
||||
#include "../settingscache.h"
|
||||
#include "../soundengine.h"
|
||||
#include "../tab_userlists.h"
|
||||
#include "../tab_account.h"
|
||||
#include "../user_context_menu.h"
|
||||
#include "user_level.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDateTime>
|
||||
#include <QDesktopServices>
|
||||
#include <QMouseEvent>
|
||||
#include <QScrollBar>
|
||||
#include <QTextEdit>
|
||||
|
||||
const QColor DEFAULT_MENTION_COLOR = QColor(194, 31, 47);
|
||||
const QColor OTHER_USER_COLOR = QColor(0, 65, 255); // dark blue
|
||||
const QString SERVER_MESSAGE_COLOR = "#851515";
|
||||
|
||||
UserMessagePosition::UserMessagePosition(QTextCursor &cursor)
|
||||
{
|
||||
block = cursor.block();
|
||||
relativePosition = cursor.position() - block.position();
|
||||
}
|
||||
|
||||
ChatView::ChatView(const TabSupervisor *_tabSupervisor,
|
||||
const UserlistProxy *_userlistProxy,
|
||||
@@ -24,17 +29,32 @@ ChatView::ChatView(const TabSupervisor *_tabSupervisor,
|
||||
: QTextBrowser(parent), tabSupervisor(_tabSupervisor), game(_game), userlistProxy(_userlistProxy), evenNumber(true),
|
||||
showTimestamps(_showTimestamps), hoveredItemType(HoveredNothing)
|
||||
{
|
||||
document()->setDefaultStyleSheet("a { text-decoration: none; color: blue; }");
|
||||
if (palette().windowText().color().lightness() > 200) {
|
||||
document()->setDefaultStyleSheet(R"(
|
||||
a { text-decoration: none; color: rgb(71,158,252); }
|
||||
.blue { color: rgb(71,158,252); }
|
||||
)");
|
||||
serverMessageColor = QColor(0xFF, 0x73, 0x83);
|
||||
otherUserColor = otherUserColor.lighter(150);
|
||||
linkColor = QColor(71, 158, 252);
|
||||
} else {
|
||||
document()->setDefaultStyleSheet(R"(
|
||||
a { text-decoration: none; color: blue; }
|
||||
.blue { color: blue }
|
||||
)");
|
||||
linkColor = palette().link().color();
|
||||
}
|
||||
|
||||
userContextMenu = new UserContextMenu(tabSupervisor, this, game);
|
||||
connect(userContextMenu, SIGNAL(openMessageDialog(QString, bool)), this, SIGNAL(openMessageDialog(QString, bool)));
|
||||
|
||||
userName = userlistProxy->getOwnUsername();
|
||||
mention = "@" + userName;
|
||||
ownUserName = userlistProxy->getOwnUsername();
|
||||
mention = "@" + ownUserName;
|
||||
|
||||
mentionFormat.setFontWeight(QFont::Bold);
|
||||
|
||||
mentionFormatOtherUser.setFontWeight(QFont::Bold);
|
||||
mentionFormatOtherUser.setForeground(Qt::blue);
|
||||
mentionFormatOtherUser.setForeground(linkColor);
|
||||
mentionFormatOtherUser.setAnchor(true);
|
||||
|
||||
viewport()->setCursor(Qt::IBeamCursor);
|
||||
@@ -53,14 +73,18 @@ QTextCursor ChatView::prepareBlock(bool same)
|
||||
{
|
||||
lastSender.clear();
|
||||
|
||||
QTextCursor cursor(document()->lastBlock());
|
||||
QTextCursor cursor(document());
|
||||
cursor.movePosition(QTextCursor::End);
|
||||
if (same) {
|
||||
cursor.insertHtml("<br>");
|
||||
} else {
|
||||
QTextBlockFormat blockFormat;
|
||||
if ((evenNumber = !evenNumber))
|
||||
blockFormat.setBackground(palette().alternateBase());
|
||||
blockFormat.setBackground(palette().window());
|
||||
else
|
||||
blockFormat.setBackground(palette().base());
|
||||
|
||||
blockFormat.setForeground(palette().text());
|
||||
blockFormat.setBottomMargin(4);
|
||||
cursor.insertBlock(blockFormat);
|
||||
}
|
||||
@@ -80,8 +104,9 @@ void ChatView::appendHtmlServerMessage(const QString &html, bool optionalIsBold,
|
||||
{
|
||||
bool atBottom = verticalScrollBar()->value() >= verticalScrollBar()->maximum();
|
||||
|
||||
QString htmlText = "<font color=" + ((optionalFontColor.size() > 0) ? optionalFontColor : SERVER_MESSAGE_COLOR) +
|
||||
">" + QDateTime::currentDateTime().toString("[hh:mm:ss] ") + html + "</font>";
|
||||
QString htmlText =
|
||||
"<font color=" + ((optionalFontColor.size() > 0) ? optionalFontColor : serverMessageColor.name()) + ">" +
|
||||
QDateTime::currentDateTime().toString("[hh:mm:ss] ") + html + "</font>";
|
||||
|
||||
if (optionalIsBold)
|
||||
htmlText = "<b>" + htmlText + "</b>";
|
||||
@@ -95,7 +120,7 @@ void ChatView::appendCardTag(QTextCursor &cursor, const QString &cardName)
|
||||
{
|
||||
QTextCharFormat oldFormat = cursor.charFormat();
|
||||
QTextCharFormat anchorFormat = oldFormat;
|
||||
anchorFormat.setForeground(Qt::blue);
|
||||
anchorFormat.setForeground(linkColor);
|
||||
anchorFormat.setAnchor(true);
|
||||
anchorFormat.setAnchorHref("card://" + cardName);
|
||||
anchorFormat.setFontItalic(true);
|
||||
@@ -108,14 +133,14 @@ void ChatView::appendCardTag(QTextCursor &cursor, const QString &cardName)
|
||||
void ChatView::appendUrlTag(QTextCursor &cursor, QString url)
|
||||
{
|
||||
if (!url.contains("://"))
|
||||
url.prepend("http://");
|
||||
url.prepend("https://");
|
||||
|
||||
QTextCharFormat oldFormat = cursor.charFormat();
|
||||
QTextCharFormat anchorFormat = oldFormat;
|
||||
anchorFormat.setForeground(Qt::blue);
|
||||
anchorFormat.setForeground(linkColor);
|
||||
anchorFormat.setAnchor(true);
|
||||
anchorFormat.setAnchorHref(url);
|
||||
anchorFormat.setUnderlineColor(Qt::blue);
|
||||
anchorFormat.setUnderlineColor(linkColor);
|
||||
anchorFormat.setFontUnderline(true);
|
||||
|
||||
cursor.setCharFormat(anchorFormat);
|
||||
@@ -125,81 +150,95 @@ void ChatView::appendUrlTag(QTextCursor &cursor, QString url)
|
||||
|
||||
void ChatView::appendMessage(QString message,
|
||||
RoomMessageTypeFlags messageType,
|
||||
QString sender,
|
||||
const QString &userName,
|
||||
UserLevelFlags userLevel,
|
||||
QString UserPrivLevel,
|
||||
bool playerBold)
|
||||
{
|
||||
bool atBottom = verticalScrollBar()->value() >= verticalScrollBar()->maximum();
|
||||
bool sameSender = (sender == lastSender) && !lastSender.isEmpty();
|
||||
// messageType should be Event_RoomSay::UserMessage though we don't actually check
|
||||
bool isUserMessage = !(userName.toLower() == "servatrice" || userName.isEmpty());
|
||||
bool sameSender = isUserMessage && userName == lastSender;
|
||||
QTextCursor cursor = prepareBlock(sameSender);
|
||||
lastSender = sender;
|
||||
lastSender = userName;
|
||||
|
||||
// timestamp
|
||||
if (showTimestamps && (!sameSender || sender.toLower() == "servatrice") && !sender.isEmpty()) {
|
||||
if (showTimestamps && ((!sameSender && isUserMessage) || userName.toLower() == "servatrice")) {
|
||||
QTextCharFormat timeFormat;
|
||||
timeFormat.setForeground(QColor(SERVER_MESSAGE_COLOR));
|
||||
if (sender.isEmpty())
|
||||
timeFormat.setFontWeight(QFont::Bold);
|
||||
timeFormat.setForeground(serverMessageColor);
|
||||
timeFormat.setFontWeight(QFont::Bold);
|
||||
cursor.setCharFormat(timeFormat);
|
||||
cursor.insertText(QDateTime::currentDateTime().toString("[hh:mm:ss] "));
|
||||
}
|
||||
|
||||
// nickname
|
||||
if (sender.toLower() != "servatrice") {
|
||||
if (isUserMessage) {
|
||||
QTextCharFormat senderFormat;
|
||||
if (sender == userName) {
|
||||
if (userName == ownUserName) {
|
||||
senderFormat.setForeground(QBrush(getCustomMentionColor()));
|
||||
senderFormat.setFontWeight(QFont::Bold);
|
||||
} else {
|
||||
senderFormat.setForeground(QBrush(OTHER_USER_COLOR));
|
||||
if (playerBold)
|
||||
senderFormat.setForeground(QBrush(otherUserColor));
|
||||
if (playerBold) {
|
||||
senderFormat.setFontWeight(QFont::Bold);
|
||||
}
|
||||
}
|
||||
senderFormat.setAnchor(true);
|
||||
senderFormat.setAnchorHref("user://" + QString::number(userLevel) + "_" + sender);
|
||||
senderFormat.setAnchorHref("user://" + QString::number(userLevel) + "_" + userName);
|
||||
if (sameSender) {
|
||||
cursor.insertText(" ");
|
||||
} else {
|
||||
if (!sender.isEmpty()) {
|
||||
const int pixelSize = QFontInfo(cursor.charFormat().font()).pixelSize();
|
||||
bool isBuddy = userlistProxy->isUserBuddy(sender);
|
||||
cursor.insertImage(
|
||||
UserLevelPixmapGenerator::generatePixmap(pixelSize, userLevel, isBuddy, UserPrivLevel).toImage());
|
||||
cursor.insertText(" ");
|
||||
}
|
||||
const int pixelSize = QFontInfo(cursor.charFormat().font()).pixelSize();
|
||||
bool isBuddy = userlistProxy->isUserBuddy(userName);
|
||||
cursor.insertImage(
|
||||
UserLevelPixmapGenerator::generatePixmap(pixelSize, userLevel, isBuddy, UserPrivLevel).toImage());
|
||||
cursor.insertText(" ");
|
||||
cursor.setCharFormat(senderFormat);
|
||||
if (!sender.isEmpty())
|
||||
sender.append(": ");
|
||||
cursor.insertText(sender);
|
||||
cursor.insertText(userName);
|
||||
cursor.insertText(": ");
|
||||
userMessagePositions[userName].append(cursor);
|
||||
}
|
||||
}
|
||||
|
||||
// use different color for server messages
|
||||
defaultFormat = QTextCharFormat();
|
||||
if (sender.isEmpty()) {
|
||||
switch (messageType) {
|
||||
case Event_RoomSay::Welcome:
|
||||
defaultFormat.setForeground(Qt::darkGreen);
|
||||
defaultFormat.setFontWeight(QFont::Bold);
|
||||
break;
|
||||
case Event_RoomSay::ChatHistory:
|
||||
defaultFormat.setForeground(Qt::gray);
|
||||
defaultFormat.setFontWeight(QFont::Light);
|
||||
defaultFormat.setFontItalic(true);
|
||||
break;
|
||||
default:
|
||||
defaultFormat.setForeground(Qt::darkGreen);
|
||||
defaultFormat.setFontWeight(QFont::Bold);
|
||||
if (!isUserMessage) {
|
||||
if (messageType == Event_RoomSay::ChatHistory) {
|
||||
defaultFormat.setForeground(Qt::gray); // FIXME : hardcoded color
|
||||
defaultFormat.setFontWeight(QFont::Light);
|
||||
defaultFormat.setFontItalic(true);
|
||||
static const QRegularExpression userNameRegex("^(\\[[^\\]]*\\]\\s)(\\S+):\\s");
|
||||
auto match = userNameRegex.match(message);
|
||||
if (match.hasMatch()) {
|
||||
cursor.setCharFormat(defaultFormat);
|
||||
UserMessagePosition pos(cursor);
|
||||
pos.relativePosition = match.captured(0).length(); // set message start
|
||||
auto before = match.captured(1);
|
||||
auto sentBy = match.captured(2);
|
||||
cursor.insertText(before); // add message timestamp
|
||||
QTextCharFormat senderFormat(defaultFormat);
|
||||
senderFormat.setAnchor(true);
|
||||
// this underscore is important, it is used to add the user level, but in this case the level is
|
||||
// unknown, if the name contains an underscore it would split up the name
|
||||
senderFormat.setAnchorHref("user://_" + sentBy);
|
||||
cursor.setCharFormat(senderFormat);
|
||||
cursor.insertText(sentBy); // add username with href so it shows the menu
|
||||
userMessagePositions[sentBy].append(pos); // save message position
|
||||
message.remove(0, pos.relativePosition - 2); // do not remove semicolon
|
||||
}
|
||||
} else {
|
||||
defaultFormat.setForeground(Qt::darkGreen); // FIXME : hardcoded color
|
||||
defaultFormat.setFontWeight(QFont::Bold);
|
||||
}
|
||||
} else if (sender.toLower() == "servatrice") {
|
||||
defaultFormat.setForeground(Qt::darkGreen);
|
||||
defaultFormat.setFontWeight(QFont::Bold);
|
||||
}
|
||||
cursor.setCharFormat(defaultFormat);
|
||||
|
||||
bool mentionEnabled = settingsCache->getChatMention();
|
||||
highlightedWords = settingsCache->getHighlightWords().split(' ', QString::SkipEmptyParts);
|
||||
bool mentionEnabled = SettingsCache::instance().getChatMention();
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
|
||||
highlightedWords = SettingsCache::instance().getHighlightWords().split(' ', Qt::SkipEmptyParts);
|
||||
#else
|
||||
highlightedWords = SettingsCache::instance().getHighlightWords().split(' ', QString::SkipEmptyParts);
|
||||
#endif
|
||||
|
||||
// parse the message
|
||||
while (message.size()) {
|
||||
@@ -210,7 +249,7 @@ void ChatView::appendMessage(QString message,
|
||||
break;
|
||||
case '@':
|
||||
if (mentionEnabled) {
|
||||
checkMention(cursor, message, sender, userLevel);
|
||||
checkMention(cursor, message, userName, userLevel);
|
||||
} else {
|
||||
cursor.insertText(c, defaultFormat);
|
||||
message = message.mid(1);
|
||||
@@ -280,7 +319,7 @@ void ChatView::checkTag(QTextCursor &cursor, QString &message)
|
||||
checkWord(cursor, message);
|
||||
}
|
||||
|
||||
void ChatView::checkMention(QTextCursor &cursor, QString &message, QString &sender, UserLevelFlags userLevel)
|
||||
void ChatView::checkMention(QTextCursor &cursor, QString &message, const QString &userName, UserLevelFlags userLevel)
|
||||
{
|
||||
const QRegExp notALetterOrNumber = QRegExp("[^a-zA-Z0-9]");
|
||||
|
||||
@@ -292,16 +331,16 @@ void ChatView::checkMention(QTextCursor &cursor, QString &message, QString &send
|
||||
const ServerInfo_User *onlineUser = userlistProxy->getOnlineUser(fullMentionUpToSpaceOrEnd);
|
||||
if (onlineUser) // Is there a user online named this?
|
||||
{
|
||||
if (userName.toLower() == fullMentionUpToSpaceOrEnd.toLower()) // Is this user you?
|
||||
if (ownUserName.toLower() == fullMentionUpToSpaceOrEnd.toLower()) // Is this user you?
|
||||
{
|
||||
// You have received a valid mention!!
|
||||
soundEngine->playSound("chat_mention");
|
||||
mentionFormat.setBackground(QBrush(getCustomMentionColor()));
|
||||
mentionFormat.setForeground(settingsCache->getChatMentionForeground() ? QBrush(Qt::white)
|
||||
: QBrush(Qt::black));
|
||||
mentionFormat.setForeground(SettingsCache::instance().getChatMentionForeground() ? QBrush(Qt::white)
|
||||
: QBrush(Qt::black));
|
||||
cursor.insertText(mention, mentionFormat);
|
||||
message = message.mid(mention.size());
|
||||
showSystemPopup(sender);
|
||||
showSystemPopup(userName);
|
||||
} else {
|
||||
QString correctUserName = QString::fromStdString(onlineUser->name());
|
||||
mentionFormatOtherUser.setAnchorHref("user://" + QString::number(onlineUser->user_level()) + "_" +
|
||||
@@ -319,11 +358,11 @@ void ChatView::checkMention(QTextCursor &cursor, QString &message, QString &send
|
||||
// Moderator Sending Global Message
|
||||
soundEngine->playSound("all_mention");
|
||||
mentionFormat.setBackground(QBrush(getCustomMentionColor()));
|
||||
mentionFormat.setForeground(settingsCache->getChatMentionForeground() ? QBrush(Qt::white)
|
||||
: QBrush(Qt::black));
|
||||
mentionFormat.setForeground(SettingsCache::instance().getChatMentionForeground() ? QBrush(Qt::white)
|
||||
: QBrush(Qt::black));
|
||||
cursor.insertText("@" + fullMentionUpToSpaceOrEnd, mentionFormat);
|
||||
message = message.mid(fullMentionUpToSpaceOrEnd.size() + 1);
|
||||
showSystemPopup(sender);
|
||||
showSystemPopup(userName);
|
||||
|
||||
cursor.setCharFormat(defaultFormat);
|
||||
return;
|
||||
@@ -367,8 +406,8 @@ void ChatView::checkWord(QTextCursor &cursor, QString &message)
|
||||
if (fullWordUpToSpaceOrEnd.compare(word, Qt::CaseInsensitive) == 0) {
|
||||
// You have received a valid mention of custom word!!
|
||||
highlightFormat.setBackground(QBrush(getCustomHighlightColor()));
|
||||
highlightFormat.setForeground(settingsCache->getChatHighlightForeground() ? QBrush(Qt::white)
|
||||
: QBrush(Qt::black));
|
||||
highlightFormat.setForeground(SettingsCache::instance().getChatHighlightForeground() ? QBrush(Qt::white)
|
||||
: QBrush(Qt::black));
|
||||
cursor.insertText(fullWordUpToSpaceOrEnd, highlightFormat);
|
||||
cursor.insertText(rest, defaultFormat);
|
||||
QApplication::alert(this);
|
||||
@@ -421,26 +460,25 @@ void ChatView::actMessageClicked()
|
||||
emit messageClickedSignal();
|
||||
}
|
||||
|
||||
void ChatView::showSystemPopup(QString &sender)
|
||||
void ChatView::showSystemPopup(const QString &userName)
|
||||
{
|
||||
QApplication::alert(this);
|
||||
if (settingsCache->getShowMentionPopup()) {
|
||||
QString ref = sender.left(sender.length() - 2);
|
||||
emit showMentionPopup(ref);
|
||||
if (SettingsCache::instance().getShowMentionPopup()) {
|
||||
emit showMentionPopup(userName);
|
||||
}
|
||||
}
|
||||
|
||||
QColor ChatView::getCustomMentionColor()
|
||||
{
|
||||
QColor customColor;
|
||||
customColor.setNamedColor("#" + settingsCache->getChatMentionColor());
|
||||
customColor.setNamedColor("#" + SettingsCache::instance().getChatMentionColor());
|
||||
return customColor.isValid() ? customColor : DEFAULT_MENTION_COLOR;
|
||||
}
|
||||
|
||||
QColor ChatView::getCustomHighlightColor()
|
||||
{
|
||||
QColor customColor;
|
||||
customColor.setNamedColor("#" + settingsCache->getChatHighlightColor());
|
||||
customColor.setNamedColor("#" + SettingsCache::instance().getChatHighlightColor());
|
||||
return customColor.isValid() ? customColor : DEFAULT_MENTION_COLOR;
|
||||
}
|
||||
|
||||
@@ -450,6 +488,28 @@ void ChatView::clearChat()
|
||||
lastSender = "";
|
||||
}
|
||||
|
||||
void ChatView::redactMessages(const QString &userName, int amount)
|
||||
{
|
||||
auto &messagePositions = userMessagePositions[userName];
|
||||
bool removedLastMessage = false;
|
||||
QTextCursor cursor(document());
|
||||
for (; !messagePositions.isEmpty() && amount != 0; --amount) {
|
||||
auto position = messagePositions.takeLast(); // go backwards from last message
|
||||
cursor.setPosition(position.block.position()); // move to start of block, then continue to start of message
|
||||
cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, position.relativePosition);
|
||||
cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); // select until end of block
|
||||
cursor.removeSelectedText();
|
||||
// if the cursor is at the end of the text it is possible to add text to this block still
|
||||
removedLastMessage |= cursor.atEnd();
|
||||
// we will readd this position later
|
||||
}
|
||||
if (removedLastMessage) {
|
||||
cursor.movePosition(QTextCursor::End);
|
||||
messagePositions.append(cursor);
|
||||
// note that this message might stay empty, this is not harmful as it will simply remove nothing the next time
|
||||
}
|
||||
}
|
||||
|
||||
void ChatView::enterEvent(QEvent * /*event*/)
|
||||
{
|
||||
setMouseTracking(true);
|
||||
@@ -505,18 +565,18 @@ void ChatView::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
switch (hoveredItemType) {
|
||||
case HoveredCard: {
|
||||
if ((event->button() == Qt::MidButton) || (event->button() == Qt::LeftButton))
|
||||
if ((event->button() == Qt::MiddleButton) || (event->button() == Qt::LeftButton))
|
||||
emit showCardInfoPopup(event->globalPos(), hoveredContent);
|
||||
break;
|
||||
}
|
||||
case HoveredUser: {
|
||||
if (event->button() != Qt::MidButton) {
|
||||
if (event->button() != Qt::MiddleButton) {
|
||||
const int delimiterIndex = hoveredContent.indexOf("_");
|
||||
const QString userName = hoveredContent.mid(delimiterIndex + 1);
|
||||
switch (event->button()) {
|
||||
case Qt::RightButton: {
|
||||
UserLevelFlags userLevel(hoveredContent.left(delimiterIndex).toInt());
|
||||
userContextMenu->showContextMenu(event->globalPos(), userName, userLevel);
|
||||
userContextMenu->showContextMenu(event->globalPos(), userName, userLevel, this);
|
||||
break;
|
||||
}
|
||||
case Qt::LeftButton: {
|
||||
@@ -540,7 +600,7 @@ void ChatView::mousePressEvent(QMouseEvent *event)
|
||||
|
||||
void ChatView::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
if ((event->button() == Qt::MidButton) || (event->button() == Qt::LeftButton))
|
||||
if ((event->button() == Qt::MiddleButton) || (event->button() == Qt::LeftButton))
|
||||
emit deleteCardInfoPopup(QString("_"));
|
||||
|
||||
QTextBrowser::mouseReleaseEvent(event);
|
||||
|
||||