Compare commits
624 Commits
v2.40.0dev
...
v2.63.0dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5e37b5ef77 | ||
|
|
78bb5e7cc0 | ||
|
|
96c248f477 | ||
|
|
696b195bb5 | ||
|
|
b0fa8f1b8c | ||
|
|
6e173551ed | ||
|
|
2fccc7487b | ||
|
|
2f5b674b75 | ||
|
|
80ced86ace | ||
|
|
de93777f99 | ||
|
|
f23d1e4921 | ||
|
|
e8e22017cd | ||
|
|
e79f97964a | ||
|
|
138e7cd706 | ||
|
|
ce6fd4bd3a | ||
|
|
6e4ca641d7 | ||
|
|
46c6381cd9 | ||
|
|
126105cd0d | ||
|
|
fedaa70d9d | ||
|
|
d1707a0dca | ||
|
|
9065d348d3 | ||
|
|
602916c23f | ||
|
|
0d43a8a4c3 | ||
|
|
1e6431a8b3 | ||
|
|
b6067ff0a2 | ||
|
|
89e426815d | ||
|
|
cf0e88495d | ||
|
|
7c67f18be5 | ||
|
|
9ab7e0dbac | ||
|
|
7e92b58c62 | ||
|
|
9c519b878e | ||
|
|
bb07dd8553 | ||
|
|
60555e72d8 | ||
|
|
a457d1d629 | ||
|
|
0e62b29028 | ||
|
|
8a0cb156b2 | ||
|
|
654f8ed7f5 | ||
|
|
cfa927dcd0 | ||
|
|
4383481a5f | ||
|
|
22063842f9 | ||
|
|
0e5d29a8f9 | ||
|
|
5ee16476dc | ||
|
|
2abb73eab8 | ||
|
|
547d8be21e | ||
|
|
b6d8582867 | ||
|
|
87fd358c4e | ||
|
|
0f4b082b4b | ||
|
|
59847c3756 | ||
|
|
b5d3dc6d56 | ||
|
|
3500867336 | ||
|
|
ea26d57566 | ||
|
|
45f0743c57 | ||
|
|
08d4e852d3 | ||
|
|
7edf1f93cb | ||
|
|
31cfa7c93a | ||
|
|
da1d42b578 | ||
|
|
502e5a8d8e | ||
|
|
087867bdca | ||
|
|
86ad17b075 | ||
|
|
b8ad4772b8 | ||
|
|
e0e5be5870 | ||
|
|
8b2f7c56db | ||
|
|
0ca1a675c2 | ||
|
|
6a0bfce262 | ||
|
|
362aebe658 | ||
|
|
bbe3e931f7 | ||
|
|
f5743af6a4 | ||
|
|
dbc2993fb9 | ||
|
|
219173c43e | ||
|
|
6074cfe15a | ||
|
|
f0a72f4307 | ||
|
|
ecc3f9fa7e | ||
|
|
c660e4058c | ||
|
|
7664b00b59 | ||
|
|
5721beacf5 | ||
|
|
13f3793515 | ||
|
|
f8e0520344 | ||
|
|
2c8e306dc2 | ||
|
|
7c37d8fd74 | ||
|
|
88129c0df4 | ||
|
|
fa67efd9cb | ||
|
|
38a345df93 | ||
|
|
b41ec8a5a9 | ||
|
|
4ba9bb7b99 | ||
|
|
f3c8f485b9 | ||
|
|
8d204dd173 | ||
|
|
f870d7e6d2 | ||
|
|
715997041a | ||
|
|
003191d9a4 | ||
|
|
9302d79e1d | ||
|
|
401fc671d4 | ||
|
|
fbadcf0af6 | ||
|
|
c7f600f88c | ||
|
|
229dd0ddae | ||
|
|
8883b18025 | ||
|
|
ea9b67cedd | ||
|
|
c4cf1d1ecb | ||
|
|
f76f7e85db | ||
|
|
b807e2a36b | ||
|
|
2bb35a15ce | ||
|
|
376bda7a94 | ||
|
|
a642bdc8c9 | ||
|
|
71f3e7b858 | ||
|
|
88f5ed2da5 | ||
|
|
3b9bbae0c3 | ||
|
|
6d474492ea | ||
|
|
ebc61efef8 | ||
|
|
3892dfc78c | ||
|
|
7b5c95213f | ||
|
|
1312177e0e | ||
|
|
5b0df0a9c3 | ||
|
|
954a164922 | ||
|
|
1ea6136cb2 | ||
|
|
da2b6bbb6d | ||
|
|
87007af5f8 | ||
|
|
dd5ab872b1 | ||
|
|
65fb46885a | ||
|
|
fad1e401b2 | ||
|
|
d59f8afd8a | ||
|
|
7c42d00219 | ||
|
|
32fd38ad7c | ||
|
|
777ba69ac4 | ||
|
|
9a3bde872b | ||
|
|
01354a0a83 | ||
|
|
8c3b8589d5 | ||
|
|
451b5d4312 | ||
|
|
d204e70afc | ||
|
|
ce9ce17ad2 | ||
|
|
6a7cdda91a | ||
|
|
f38c61da51 | ||
|
|
735827a25b | ||
|
|
d3fcdcbe47 | ||
|
|
a3dce73663 | ||
|
|
594b58388f | ||
|
|
fa6be2edfb | ||
|
|
1177968b02 | ||
|
|
c04b370410 | ||
|
|
038b95531d | ||
|
|
56bc5c3376 | ||
|
|
f5d8be7861 | ||
|
|
5366c23db2 | ||
|
|
d147db22f1 | ||
|
|
28ebcd2739 | ||
|
|
2f57eb6ea6 | ||
|
|
3a7ee4699e | ||
|
|
fa7f991fae | ||
|
|
0be8b429aa | ||
|
|
7d94e2de7d | ||
|
|
6d32db5827 | ||
|
|
0fbb318d8a | ||
|
|
cd013e8287 | ||
|
|
e667453c1e | ||
|
|
24e2db0f88 | ||
|
|
3c47f8c6bb | ||
|
|
42c2b3f253 | ||
|
|
71d6830ac0 | ||
|
|
ac2fb01a7c | ||
|
|
fc66c212fc | ||
|
|
9625fafda7 | ||
|
|
6b1aca4306 | ||
|
|
fd5a094304 | ||
|
|
a3142ff62f | ||
|
|
58ebfd3643 | ||
|
|
e272267842 | ||
|
|
b0256542bc | ||
|
|
9b8fd24987 | ||
|
|
9c68889a6d | ||
|
|
a4ed6e8066 | ||
|
|
9fe8163a69 | ||
|
|
9f9b496726 | ||
|
|
3b8fa68a4c | ||
|
|
9f5a649b04 | ||
|
|
fb45736c14 | ||
|
|
b8a58fc7a6 | ||
|
|
b04c521805 | ||
|
|
526695fa9a | ||
|
|
d6be77d107 | ||
|
|
907da343b1 | ||
|
|
3fadccc715 | ||
|
|
82a912858f | ||
|
|
cc1fdddc0a | ||
|
|
e314ecf887 | ||
|
|
94b1c8b029 | ||
|
|
6e3b7ff132 | ||
|
|
ff14855808 | ||
|
|
4761857b84 | ||
|
|
bb53e75bfe | ||
|
|
cc146401b6 | ||
|
|
465e43ed45 | ||
|
|
f89a8a9f87 | ||
|
|
768c417c8e | ||
|
|
494b6353b8 | ||
|
|
69ef7825af | ||
|
|
e0a71754ce | ||
|
|
b959d9dd07 | ||
|
|
2c2455a644 | ||
|
|
e2dbfb52e9 | ||
|
|
612591b59b | ||
|
|
3e00d5d4ae | ||
|
|
539be1527b | ||
|
|
d00db1e167 | ||
|
|
fc6d5fea48 | ||
|
|
ecf4d6c0de | ||
|
|
f5f7f92166 | ||
|
|
f5d7e754a6 | ||
|
|
be85080d8d | ||
|
|
6ed34be118 | ||
|
|
59dceadfee | ||
|
|
2a74c779c8 | ||
|
|
f63331454b | ||
|
|
d105748348 | ||
|
|
cdd7077346 | ||
|
|
f965497186 | ||
|
|
6b06b5b46c | ||
|
|
d1e19406d0 | ||
|
|
2a3c586be6 | ||
|
|
23f014c32e | ||
|
|
0ac6a14ebb | ||
|
|
0dc2632fc4 | ||
|
|
fc43691275 | ||
|
|
21a9b779f3 | ||
|
|
b8dc55a3a6 | ||
|
|
b7a5b33ff6 | ||
|
|
3593d16bd1 | ||
|
|
57830fd5a5 | ||
|
|
313d793175 | ||
|
|
f8e20e23bb | ||
|
|
1959681250 | ||
|
|
0192c77df1 | ||
|
|
b709629d7b | ||
|
|
3071aead5c | ||
|
|
3f2df69022 | ||
|
|
e9a48817a7 | ||
|
|
ed19ea5f2f | ||
|
|
19366a09b4 | ||
|
|
f8f01cd63c | ||
|
|
21a0e2b9c5 | ||
|
|
0ba8ba33ef | ||
|
|
346184d06a | ||
|
|
46fc9e65bb | ||
|
|
2d51da4d02 | ||
|
|
b5e77415cf | ||
|
|
4c75daf36c | ||
|
|
4a120b8abf | ||
|
|
fc43510fbc | ||
|
|
28c7ff54aa | ||
|
|
89f1e0a126 | ||
|
|
b6ff1cada9 | ||
|
|
cc0d6465d6 | ||
|
|
c49a914584 | ||
|
|
e6e6568571 | ||
|
|
7120000202 | ||
|
|
c3f7078053 | ||
|
|
b70f02fa3e | ||
|
|
8d893ce648 | ||
|
|
c230480464 | ||
|
|
da7a4a2df0 | ||
|
|
62bde4b8c9 | ||
|
|
43967b70ae | ||
|
|
782c007200 | ||
|
|
5812d333d9 | ||
|
|
c461bf469e | ||
|
|
d5c8c92b48 | ||
|
|
1abffd6c5d | ||
|
|
a824cc5e48 | ||
|
|
db7afffd65 | ||
|
|
4d02a1292f | ||
|
|
00f72294d4 | ||
|
|
0d45be0597 | ||
|
|
f60090fc79 | ||
|
|
713807df13 | ||
|
|
c020bd8898 | ||
|
|
39ee8586d4 | ||
|
|
653a1585dc | ||
|
|
2dcf011020 | ||
|
|
555e72a27a | ||
|
|
85e740d75a | ||
|
|
d604ed300a | ||
|
|
3aea4e5f23 | ||
|
|
a54adddc74 | ||
|
|
dda397fea3 | ||
|
|
3cd718350e | ||
|
|
e87860a700 | ||
|
|
451d21eaa0 | ||
|
|
718f9ef859 | ||
|
|
193adc4ba5 | ||
|
|
cdfa52e9ed | ||
|
|
2752b288ad | ||
|
|
8dce968780 | ||
|
|
b889da7e68 | ||
|
|
0dd7dea708 | ||
|
|
8af1c8da7d | ||
|
|
8d4f83156d | ||
|
|
798bdf3fe0 | ||
|
|
a7cf4aab76 | ||
|
|
d910e324f3 | ||
|
|
d800db5ccb | ||
|
|
6c5d0a3e4f | ||
|
|
9389a0421f | ||
|
|
6f3ab6cee8 | ||
|
|
258dcd49a0 | ||
|
|
5f0320d1e3 | ||
|
|
54052899f9 | ||
|
|
eda53e9318 | ||
|
|
4d8a891fd8 | ||
|
|
2d4b97afe6 | ||
|
|
49d1ec17e8 | ||
|
|
cfecacacf9 | ||
|
|
a9112c04a3 | ||
|
|
5f2a74691c | ||
|
|
17c22df0f4 | ||
|
|
c53bc82929 | ||
|
|
7113f41b9a | ||
|
|
aa66d63085 | ||
|
|
140ee70dc3 | ||
|
|
5023a6cbae | ||
|
|
aafa77baed | ||
|
|
16dabd57ca | ||
|
|
8668d2dd55 | ||
|
|
5c5f37a9ae | ||
|
|
621c36f1cd | ||
|
|
be2cc523c2 | ||
|
|
bd83c403a1 | ||
|
|
043f533a10 | ||
|
|
57a23affd0 | ||
|
|
b678152ac6 | ||
|
|
747bbc8200 | ||
|
|
871cf42d88 | ||
|
|
28533d60d3 | ||
|
|
ca12b3c94f | ||
|
|
81e449dd83 | ||
|
|
368f2c33a9 | ||
|
|
c445bb4423 | ||
|
|
9b216b5d48 | ||
|
|
4b932d210f | ||
|
|
a1ca29fb30 | ||
|
|
876c85ee19 | ||
|
|
ac1d935712 | ||
|
|
2ccc829d3b | ||
|
|
061ab27286 | ||
|
|
d127dd9a7e | ||
|
|
6d13ab8bcb | ||
|
|
e5d95dd4d8 | ||
|
|
08c5c9a4b7 | ||
|
|
115ea9a36c | ||
|
|
a484698a9d | ||
|
|
bafaed1d81 | ||
|
|
c56f274a5f | ||
|
|
02f7fbf1b1 | ||
|
|
96214eaa27 | ||
|
|
3ed85112c6 | ||
|
|
fed39b75e3 | ||
|
|
8a3defded5 | ||
|
|
2fef60eaaa | ||
|
|
88970c7a95 | ||
|
|
fddaf27055 | ||
|
|
dd575bd3a0 | ||
|
|
0c9b73727b | ||
|
|
807622a1ec | ||
|
|
ade9fc389a | ||
|
|
b6c5f67085 | ||
|
|
7d86d58993 | ||
|
|
a7ab046bc6 | ||
|
|
d126b62d63 | ||
|
|
c94693a72d | ||
|
|
9e50de96f3 | ||
|
|
ea335b7d41 | ||
|
|
4838f69c40 | ||
|
|
e67083a79b | ||
|
|
b103e576d2 | ||
|
|
fb81e6c043 | ||
|
|
84716af82b | ||
|
|
30dd77d462 | ||
|
|
9148611a8e | ||
|
|
e7be51b70e | ||
|
|
194e4657eb | ||
|
|
b472adb404 | ||
|
|
b08986ba74 | ||
|
|
7e5150be8c | ||
|
|
57d8a672d9 | ||
|
|
261d510ccb | ||
|
|
47dbfbd6d5 | ||
|
|
cbbf4863fb | ||
|
|
858b0b243a | ||
|
|
369f62bd68 | ||
|
|
53385f2458 | ||
|
|
885ad4deb3 | ||
|
|
7d3371c379 | ||
|
|
0ce3b861a4 | ||
|
|
48ee543c00 | ||
|
|
e07e2bc4f7 | ||
|
|
370cb7eae6 | ||
|
|
d94116ea1c | ||
|
|
17dec4d732 | ||
|
|
effb7e6429 | ||
|
|
63f7762e34 | ||
|
|
76ff52aea8 | ||
|
|
cd12279404 | ||
|
|
263929b6e3 | ||
|
|
759135d3fe | ||
|
|
d240f547cc | ||
|
|
0a4f3481da | ||
|
|
d883935d8c | ||
|
|
235ca94dba | ||
|
|
2dabfe4c79 | ||
|
|
b248cdefdd | ||
|
|
6172ceda0f | ||
|
|
ba236bcb54 | ||
|
|
b4e115eb7b | ||
|
|
52b567a06d | ||
|
|
dd1f7e224c | ||
|
|
338b298077 | ||
|
|
512b370e3e | ||
|
|
a14210f356 | ||
|
|
f81cf4ee7b | ||
|
|
87b072b567 | ||
|
|
e21789d29c | ||
|
|
dd93f348e6 | ||
|
|
cae8088ad3 | ||
|
|
a92cbe92f1 | ||
|
|
52fd0bb13f | ||
|
|
afcddeea70 | ||
|
|
e5eb001cf3 | ||
|
|
0d186ba56b | ||
|
|
e2273f90b4 | ||
|
|
d6501df509 | ||
|
|
96d639996a | ||
|
|
625c52720d | ||
|
|
69221eac24 | ||
|
|
5d95663f63 | ||
|
|
74daf99aed | ||
|
|
eaf637d1d9 | ||
|
|
c262ea6e35 | ||
|
|
128de58b83 | ||
|
|
1c541c82bf | ||
|
|
1824d1b866 | ||
|
|
47eb8d422f | ||
|
|
38ddf83da4 | ||
|
|
90025f22e5 | ||
|
|
9e71ed88e9 | ||
|
|
58142a8641 | ||
|
|
d86f0f40ac | ||
|
|
62944e4ce3 | ||
|
|
812201d8b3 | ||
|
|
52abd5620f | ||
|
|
be07d8e338 | ||
|
|
242fbba6d6 | ||
|
|
b21b756fa6 | ||
|
|
7f1e0fcc58 | ||
|
|
072a53eabc | ||
|
|
492adb4dfd | ||
|
|
3115268fb8 | ||
|
|
0631bd65e1 | ||
|
|
e47112a1f2 | ||
|
|
5fa7b2c86a | ||
|
|
6b7a4b3f9d | ||
|
|
0ba65cab1f | ||
|
|
f6120f09ac | ||
|
|
00a6835f50 | ||
|
|
4995c7994f | ||
|
|
d75419d858 | ||
|
|
771dfca1c8 | ||
|
|
620ce89a05 | ||
|
|
73a5f62d90 | ||
|
|
3982670dff | ||
|
|
73dc056e61 | ||
|
|
79d2ded836 | ||
|
|
55bc0cd40f | ||
|
|
9da09a279e | ||
|
|
889047f891 | ||
|
|
97625d11ab | ||
|
|
8f645fa425 | ||
|
|
21bd4272d9 | ||
|
|
e3e7f92b8d | ||
|
|
332c91d661 | ||
|
|
22f37995cf | ||
|
|
92119c01f6 | ||
|
|
719125657f | ||
|
|
95841c44dc | ||
|
|
e7b3040c0f | ||
|
|
78af68cac2 | ||
|
|
9621a54257 | ||
|
|
3e5eb989f9 | ||
|
|
975d6f8776 | ||
|
|
4e89a87ec1 | ||
|
|
eef644fb4c | ||
|
|
8131dd4ace | ||
|
|
c8059d6132 | ||
|
|
cc008a57e1 | ||
|
|
ce6910fd63 | ||
|
|
bfc580cf7c | ||
|
|
7892e637b2 | ||
|
|
6543a2c225 | ||
|
|
1e59d3d6ac | ||
|
|
fd2f76ee41 | ||
|
|
11d0566433 | ||
|
|
b8d84d3af2 | ||
|
|
1831fea819 | ||
|
|
d15322a57c | ||
|
|
9e01d15e60 | ||
|
|
48981460ab | ||
|
|
eaca4a179f | ||
|
|
e249cf917b | ||
|
|
23ee164a76 | ||
|
|
1de7a4ea82 | ||
|
|
289acc099c | ||
|
|
99ddc36027 | ||
|
|
ce3678debb | ||
|
|
0e36794578 | ||
|
|
6d67b23a7e | ||
|
|
a33f48a83b | ||
|
|
a95d69623b | ||
|
|
11c060103a | ||
|
|
7578532949 | ||
|
|
a067c77c4c | ||
|
|
ac6b6f70eb | ||
|
|
c6dd22f04f | ||
|
|
2e1f53184a | ||
|
|
3159d399a6 | ||
|
|
357dab2964 | ||
|
|
41c93efd83 | ||
|
|
baf10b24d6 | ||
|
|
628aa9d905 | ||
|
|
0ab485115c | ||
|
|
f9248dec6f | ||
|
|
9d5ea487d5 | ||
|
|
e76ce71701 | ||
|
|
5a91e01746 | ||
|
|
355e6dd0fe | ||
|
|
49da362dc3 | ||
|
|
27101732ca | ||
|
|
ed48a8b5d0 | ||
|
|
460cf26236 | ||
|
|
5a6fe373f1 | ||
|
|
cceaed8d61 | ||
|
|
d9df4958a6 | ||
|
|
e43e7ba51e | ||
|
|
50e76deab7 | ||
|
|
3c6eb6d054 | ||
|
|
e109cce36b | ||
|
|
dc997f0dc4 | ||
|
|
f8895a14c5 | ||
|
|
893f9f8467 | ||
|
|
c612545636 | ||
|
|
4fe729c512 | ||
|
|
22eabcea8d | ||
|
|
a7d52c9fc2 | ||
|
|
207e9018a8 | ||
|
|
a9598d4fc9 | ||
|
|
ccd3498594 | ||
|
|
60015fb3b8 | ||
|
|
14afc83e86 | ||
|
|
216dd2a787 | ||
|
|
71d088b90a | ||
|
|
17712d8b7d | ||
|
|
4a224ea9a5 | ||
|
|
6664b5620a | ||
|
|
a98e898bd8 | ||
|
|
e98ae5de39 | ||
|
|
a51fbbc238 | ||
|
|
ebd7a1a4ad | ||
|
|
63c9d98e3f | ||
|
|
14aafef866 | ||
|
|
55d6bb57d6 | ||
|
|
4bb71dbdb8 | ||
|
|
c9117c2a73 | ||
|
|
0a193939ed | ||
|
|
a0a27720ef | ||
|
|
d38417de9e | ||
|
|
2de214d7cd | ||
|
|
d9d546ca25 | ||
|
|
2c0e6fa73b | ||
|
|
9dc18ac81b | ||
|
|
86e171f13d | ||
|
|
20f222b9bf | ||
|
|
01721f9dac | ||
|
|
e57b7e5a8f | ||
|
|
4ba037d6e1 | ||
|
|
f89061a9d8 | ||
|
|
52b8b4bc6c | ||
|
|
51e351d1ba | ||
|
|
518b4abf8c | ||
|
|
67119fcff1 | ||
|
|
b1beebda26 | ||
|
|
f7ea438ebc | ||
|
|
ee6b57ecc1 | ||
|
|
e17fe434ee | ||
|
|
7cad0b2658 | ||
|
|
9e06106a1c | ||
|
|
ae115b640a | ||
|
|
0c292e8d6e | ||
|
|
68fad93282 | ||
|
|
9067c0b8b3 | ||
|
|
3d4849799b | ||
|
|
fc848cd2d0 | ||
|
|
72b18f7935 | ||
|
|
f9b4c7b5ea | ||
|
|
014d389dc8 | ||
|
|
bded2d505f | ||
|
|
4e83f52532 | ||
|
|
ec0b543393 | ||
|
|
cff94a12ee | ||
|
|
da39975ba8 | ||
|
|
82515343ee | ||
|
|
bb38ba6564 | ||
|
|
3f6aa91966 | ||
|
|
e86e5933b5 | ||
|
|
000c1bc38f | ||
|
|
8b1553df03 | ||
|
|
2731e9962b | ||
|
|
89d0ee4c77 | ||
|
|
76536a5dcc | ||
|
|
41d6828024 | ||
|
|
bf4b4ab3c0 | ||
|
|
d7d4643a33 | ||
|
|
166635d0a1 | ||
|
|
076e1999af | ||
|
|
69caff50c3 | ||
|
|
0db72f7a8c | ||
|
|
43ac76ac1f | ||
|
|
279c912703 | ||
|
|
e54cec47f2 | ||
|
|
50293081d1 | ||
|
|
4f526d7cc4 |
@@ -1,18 +1,72 @@
|
||||
image:
|
||||
- Visual Studio 2019
|
||||
- macos
|
||||
clone_depth: 400
|
||||
|
||||
- Ubuntu2204
|
||||
- Visual Studio 2022
|
||||
- macos-catalina
|
||||
for:
|
||||
-
|
||||
matrix:
|
||||
only:
|
||||
- image: Visual Studio 2019
|
||||
- image: Ubuntu2204
|
||||
environment:
|
||||
PYTHON: "C:\\Python37-x64"
|
||||
APPVEYOR_SSH_KEY: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDhb96UEXy8yOy/f+riX/8kKbNx/lOfIZ4pP4Cw3Gj3DmnTwEnxtRtyc+xtaxOsKbt+7+EAXFpCzYX+jHMhtd0QtWB7dbey8DBg31g0f8C5EPquqROibVbhzr/F3f6/d52FFfq6Y/CWaAvLjezvipr+zOOsIFcVusqtXdPJQ/LtUJ0LS5d4lFiw5ELHSxHIpqwGwyb7PbR3ufEFoqbr8eYiCH+vlBob72ArPfo2f3u0sMvpGYmjVVu2jj4FEY2h89sLrGyFdNWBoyumRhkb38+WSAuyPa/Y21+g+S8sRzIlkwbxicGNMtrMIi6zHEIGAgA06Sw2psP807h730PPOVaWjUcU3ojNW8hH3nPizF74pT82+iP7/fFC4PXLP+tBa+8OoHC5yiO7QKUKprMSqVa1qOm8fHbrzglplKJXfzSfUtSE+AQ+HtHhuUWKI+0LBLDrsOJwI5hbsPOAuiZ5I3VfqfAOck6SH9TcmlapVmQEypc7d7oeeUtZSOuIWKXp068= dfx@aw"
|
||||
APPIMAGE_TOOL: appimage-builder-x86_64.AppImage
|
||||
DEPLOY_DIR: AppDir/opt/pyfa
|
||||
# APPVEYOR_SSH_BLOCK: true
|
||||
cache:
|
||||
- /home/appveyor/.cache/pip -> requirements.txt
|
||||
# init:
|
||||
# - sh: curl -sflL 'https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-ssh.sh' | bash -e -
|
||||
install:
|
||||
- sh: sudo DEBIAN_FRONTEND=noninteractive apt-get -y update --allow-releaseinfo-change
|
||||
# AppImage dependencies
|
||||
- sh: sudo DEBIAN_FRONTEND=noninteractive apt-get -y install libfuse2
|
||||
# Preparation script dependencies
|
||||
- sh: sudo DEBIAN_FRONTEND=noninteractive apt-get -y install python3-wxgtk4.0 python3-sqlalchemy python3-logbook
|
||||
before_build:
|
||||
# Prepare pyfa data
|
||||
- sh: find locale/ -type f -name "*.po" -exec msgen "{}" -o "{}" \;
|
||||
- sh: pyenv global system
|
||||
- sh: python3 -B scripts/compile_lang.py
|
||||
- sh: python3 -B scripts/dump_crowdin_progress.py
|
||||
- sh: python3 -B db_update.py
|
||||
- sh: export PYFA_VERSION="$(python3 -B scripts/dump_version.py)"
|
||||
- sh: mkdir build
|
||||
# Download packaging tool
|
||||
- sh: curl -o $APPIMAGE_TOOL -L https://github.com/AppImageCrafters/appimage-builder/releases/download/v1.1.0/appimage-builder-1.1.0-x86_64.AppImage
|
||||
- sh: chmod +x $APPIMAGE_TOOL
|
||||
build_script:
|
||||
- sh: mkdir -p AppDir/opt/pyfa
|
||||
- sh: cp -r eos graphs gui imgs locale service utils eve.db config.py pyfa.py db_update.py README.md LICENSE version.yml AppDir/opt/pyfa/
|
||||
- sh: mkdir -p AppDir/usr/share/icons/hicolor/64x64/apps/
|
||||
- sh: cp imgs/gui/pyfa64.png AppDir/usr/share/icons/hicolor/64x64/apps/pyfa.png
|
||||
- sh: ./$APPIMAGE_TOOL --recipe dist_assets/linux/AppImageBuilder.yml
|
||||
after_build:
|
||||
- sh: ls -la
|
||||
artifacts:
|
||||
- path: pyfa-$PYFA_VERSION-linux.AppImage
|
||||
deploy:
|
||||
tag: $PYFA_VERSION
|
||||
release: pyfa $PYFA_VERSION
|
||||
description: 'Release description'
|
||||
provider: GitHub
|
||||
auth_token:
|
||||
secure: M94o0xMtzxrvlKpqMcXU2KfbJdd3aYJ3UxWzePUz/pkT1/Ojiis052CiLsLVyzJg
|
||||
draft: true
|
||||
force_update: false
|
||||
# deploy on tag push only
|
||||
on:
|
||||
APPVEYOR_REPO_TAG: true
|
||||
-
|
||||
matrix:
|
||||
only:
|
||||
- image: Visual Studio 2022
|
||||
environment:
|
||||
PYTHON: "C:\\Python311-x64"
|
||||
# Should be enabled only for build process debugging
|
||||
# init:
|
||||
# - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
|
||||
cache:
|
||||
- C:\users\appveyor\appdata\local\pip\cache\ -> requirements.txt
|
||||
install:
|
||||
- ps: echo("OS version:")
|
||||
- ps: "[System.Environment]::OSVersion.Version"
|
||||
@@ -49,10 +103,8 @@ for:
|
||||
# pip will build them from source using the MSVC compiler matching the
|
||||
# target Python version and architecture
|
||||
- ps: echo("Install pip requirements:")
|
||||
# This one is needed to build wxpython 4.0.6 on windows
|
||||
- cmd: "python -m pip install pathlib2"
|
||||
- cmd: "python -m pip install -r requirements.txt"
|
||||
- cmd: "python -m pip install PyInstaller==3.6"
|
||||
- cmd: "python -m pip install PyInstaller==6.0.0"
|
||||
before_build:
|
||||
# directory that will contain the built files
|
||||
- ps: $env:PYFA_DIST_DIR = "c:\projects\$env:APPVEYOR_PROJECT_SLUG\dist"
|
||||
@@ -68,7 +120,7 @@ for:
|
||||
# Build gamedata DB
|
||||
- cmd: "python db_update.py"
|
||||
# Build command for PyInstaller
|
||||
- cmd: "python -m PyInstaller --noupx --clean --windowed --noconsole -y pyfa.spec"
|
||||
- cmd: "python -m PyInstaller --clean -y pyfa.spec"
|
||||
# Copy over manifest (See pyfa-org/pyfa#1622)
|
||||
- ps: xcopy /y dist_assets\win\pyfa.exe.manifest $env:PYFA_DIST_DIR\pyfa\
|
||||
# InnoScript EXE building. This is in a separate script because I don't feel like copying over the logic to AppVeyor script right now...
|
||||
@@ -95,11 +147,14 @@ for:
|
||||
-
|
||||
matrix:
|
||||
only:
|
||||
- image: macos
|
||||
# Should be enabled only for build process debugging
|
||||
- image: macos-catalina
|
||||
environment:
|
||||
APPVEYOR_SSH_KEY: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDJDW/+oYNGOiPvwuwAL9tc/LQgg58aosIVpMYfepQZ20V+VZnHpZh8IRDA8Jo5xht19p2PksA+hFgqA0kpKtrSkuiWdE8rATQItfk4gf7yB0yGasJGGQZYazy9k/9XtmYkq2HHOOeEqdxvrICddJQ88MLCLT9lJENSUP/YS/yGcjZFXVxE11pTeIcqlCRU+3eYa1v7BeNvXIKNhZoK5orXWrtuH3cy8jrSns/u70aYfJ6B2jA8CnWnDbuvpeQtEY61SQqlKUsSArNa8NAsXj41wr3Ar9gAG9330w7EMTqlutk8HZO35uHI0q5qinUhaQYufPPrVkb2L/N+ZCfu0fnh appveyor"
|
||||
cache:
|
||||
- /Users/appveyor/Library/Caches/pip/ -> requirements.txt
|
||||
init:
|
||||
# - sh: curl -sflL 'https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-ssh.sh' | bash -e -
|
||||
- sh: source ~/venv3.7/bin/activate
|
||||
- sh: source ~/venv3.11/bin/activate
|
||||
install:
|
||||
- sh: bash scripts/osx-setup.sh
|
||||
build_script:
|
||||
@@ -110,6 +165,9 @@ for:
|
||||
after_build:
|
||||
- sh: export PYFA_VERSION="$(python3 scripts/dump_version.py)"
|
||||
- sh: bash scripts/osx-package.sh
|
||||
# on_finish:
|
||||
# - sh: export APPVEYOR_SSH_BLOCK=true
|
||||
# - sh: curl -sflL 'https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-ssh.sh' | bash -e -
|
||||
artifacts:
|
||||
- path: dist/pyfa*-mac.zip
|
||||
before_deploy:
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -90,6 +90,7 @@ target/
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
PyfaEnv/
|
||||
|
||||
# celery beat schedule file
|
||||
celerybeat-schedule
|
||||
@@ -123,3 +124,6 @@ gitversion
|
||||
|
||||
*.fsdbinary
|
||||
/locale/progress.json
|
||||
|
||||
# vscode settings
|
||||
.vscode
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## Requirements
|
||||
|
||||
- Python 3.7
|
||||
- Python 3.11
|
||||
- Git CLI installed
|
||||
- Python, pip and git are all available as command-line commands (add to the path if needed)
|
||||
|
||||
|
||||
19
README.md
19
README.md
@@ -1,27 +1,30 @@
|
||||
# pyfa
|
||||
|
||||
[](https://pyfainvite.azurewebsites.net/) [](https://travis-ci.org/pyfa-org/Pyfa)
|
||||
[]([https://travis-ci.org/pyfa-org/Pyfa](https://ci.appveyor.com/project/pyfa-org/pyfa))
|
||||
|
||||

|
||||
|
||||
## What is it?
|
||||
|
||||
Pyfa, short for **py**thon **f**itting **a**ssistant, allows you to create, experiment with, and save ship fittings without being in game. Open source and written in Python, it is available on any platform where Python 3 and wxWidgets are available, including Windows, Mac OS X, and Linux.
|
||||
Pyfa, short for **py**thon **f**itting **a**ssistant, allows you to create, experiment with, and save ship fittings without being in game. Open source and written in Python, it is available on any platform where Python 3 and wxWidgets are available, including Windows, macOS, and Linux.
|
||||
|
||||
## Latest Version and Changelogs
|
||||
The latest version along with release notes can always be found on the project's [releases](https://github.com/pyfa-org/Pyfa/releases) page. Pyfa will notify you if you are running an outdated version.
|
||||
|
||||
## Installation
|
||||
Windows and OS X users are supplied self-contained builds of pyfa on the [latest releases](https://github.com/pyfa-org/Pyfa/releases/latest) page. An `.exe` installer is also available for Windows builds. Linux users can run pyfa using their distribution's Python interpreter. There is no official self-contained package for Linux, however, there are a number of third-party packages available through distribution-specific repositories.
|
||||
Windows, macOS, and Linux users are supplied self-contained builds of pyfa on the [latest releases](https://github.com/pyfa-org/Pyfa/releases/latest) page.
|
||||
|
||||
### OS X
|
||||
Apart from the official release, there is also a [Homebrew](http://brew.sh) option for installing pyfa on OS X. Please note this is maintained by a third-party and is not tested by pyfa developers. Simply fire up in terminal:
|
||||
### Third Party Packages
|
||||
Please note that these packages are maintained by third-parties and are not evaluated by the pyfa developers.
|
||||
|
||||
#### macOS
|
||||
Apart from the official release, there is also a [Homebrew](https://formulae.brew.sh/cask/pyfa) option for installing pyfa on macOS. Simply fire up in terminal:
|
||||
```
|
||||
$ brew install Caskroom/cask/pyfa
|
||||
$ brew install --cask pyfa
|
||||
```
|
||||
|
||||
### Linux Distro-specific Packages
|
||||
The following is a list of pyfa packages available for certain distributions. Please note that these packages are maintained by third-parties and are not evaluated by the pyfa developers.
|
||||
#### Linux Distro-specific Packages
|
||||
The following is a list of pyfa packages available for certain distributions.
|
||||
|
||||
* Arch: https://aur.archlinux.org/packages/pyfa/
|
||||
* Gentoo: https://github.com/ZeroPointEnergy/gentoo-pyfa-overlay
|
||||
|
||||
26
config.py
26
config.py
@@ -9,6 +9,7 @@ import hashlib
|
||||
from eos.const import FittingSlot
|
||||
|
||||
from cryptography.fernet import Fernet
|
||||
from collections import namedtuple
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
@@ -28,6 +29,7 @@ saveInRoot = False
|
||||
evemonMinVersion = "4081"
|
||||
|
||||
minItemSearchLength = 3
|
||||
minItemSearchLengthCjk = 1
|
||||
|
||||
pyfaPath = None
|
||||
savePath = None
|
||||
@@ -43,9 +45,16 @@ experimentalFeatures = None
|
||||
version = None
|
||||
language = None
|
||||
|
||||
API_CLIENT_ID = '095d8cd841ac40b581330919b49fe746'
|
||||
ApiServer = namedtuple('ApiBase', ['name', 'sso', 'esi', 'client_id', 'callback', 'supports_auto_login'])
|
||||
supported_servers = {
|
||||
"Tranquility": ApiServer("Tranquility", "login.eveonline.com", "esi.evetech.net", '095d8cd841ac40b581330919b49fe746', 'https://pyfa-org.github.io/Pyfa/callback', True),
|
||||
# No point having SISI: https://developers.eveonline.com/blog/article/removing-datasource-singularity
|
||||
# "Singularity": ApiServer("Singularity", "sisilogin.testeveonline.com", "esi.evetech.net", 'b9c3cc79448f449ab17f3aebd018842e', 'https://pyfa-org.github.io/Pyfa/callback'),
|
||||
"Serenity": ApiServer("Serenity", "login.evepc.163.com", "ali-esi.evepc.163.com", 'bc90aa496a404724a93f41b4f4e97761', 'https://ali-esi.evepc.163.com/ui/oauth2-redirect.html', False)
|
||||
}
|
||||
|
||||
SSO_LOGOFF_SERENITY='https://login.evepc.163.com/account/logoff'
|
||||
ESI_CACHE = 'esi_cache'
|
||||
SSO_CALLBACK = 'https://pyfa-org.github.io/Pyfa/callback'
|
||||
|
||||
LOGLEVEL_MAP = {
|
||||
"critical": CRITICAL,
|
||||
@@ -57,13 +66,22 @@ LOGLEVEL_MAP = {
|
||||
|
||||
CATALOG = 'lang'
|
||||
|
||||
|
||||
slotColourMapDark = {
|
||||
FittingSlot.LOW: wx.Colour(44, 36, 19), # yellow = low slots 24/13
|
||||
FittingSlot.MED: wx.Colour(28, 39, 51), # blue = mid slots 8.1/9.5
|
||||
FittingSlot.HIGH: wx.Colour(53, 31, 34), # red = high slots 6.5/11.5
|
||||
FittingSlot.RIG: '',
|
||||
FittingSlot.SUBSYSTEM: ''}
|
||||
errColorDark = wx.Colour(70, 20, 20)
|
||||
slotColourMap = {
|
||||
FittingSlot.LOW: wx.Colour(250, 235, 204), # yellow = low slots
|
||||
FittingSlot.MED: wx.Colour(188, 215, 241), # blue = mid slots
|
||||
FittingSlot.HIGH: wx.Colour(235, 204, 209), # red = high slots
|
||||
FittingSlot.RIG: '',
|
||||
FittingSlot.SUBSYSTEM: ''
|
||||
}
|
||||
FittingSlot.SUBSYSTEM: ''}
|
||||
errColor = wx.Colour(204, 51, 51)
|
||||
|
||||
|
||||
def getClientSecret():
|
||||
return clientHash
|
||||
|
||||
189
db_update.py
189
db_update.py
@@ -117,8 +117,7 @@ def update_db():
|
||||
for k, v in compiled_data.items():
|
||||
row = {}
|
||||
row.update(v)
|
||||
if keyIdName not in row:
|
||||
row[keyIdName] = int(k)
|
||||
row[keyIdName] = int(k)
|
||||
data.append(row)
|
||||
return data
|
||||
|
||||
@@ -139,16 +138,19 @@ def update_db():
|
||||
for row in data:
|
||||
if (
|
||||
# Apparently people really want Civilian modules available
|
||||
(row['typeName_en-us'].startswith('Civilian') and "Shuttle" not in row['typeName_en-us']) or
|
||||
row['typeName_en-us'] == 'Capsule' or
|
||||
row['groupID'] == 4033 # destructible effect beacons
|
||||
(row['typeName_en-us'].startswith('Civilian') and "Shuttle" not in row['typeName_en-us'])
|
||||
or row['typeName_en-us'] == 'Capsule'
|
||||
or row['groupID'] == 4033 # destructible effect beacons
|
||||
or row['typeID'] == 82941 # Metenox service
|
||||
or re.match(r'AIR .+Booster.*', row['typeName_en-us'])
|
||||
):
|
||||
row['published'] = True
|
||||
# Nearly useless and clutter search results too much
|
||||
elif (
|
||||
row['typeName_en-us'].startswith('Limited Synth ') or
|
||||
row['typeName_en-us'].startswith('Expired ') or
|
||||
row['typeName_en-us'].endswith(' Filament') and (
|
||||
row['typeName_en-us'].startswith('Limited Synth ')
|
||||
or row['typeName_en-us'].startswith('Expired ')
|
||||
or re.match(r'Mining Blitz .+ Booster Dose .+', row['typeName_en-us'])
|
||||
or row['typeName_en-us'].endswith(' Filament') and (
|
||||
"'Needlejack'" not in row['typeName_en-us'] and
|
||||
"'Devana'" not in row['typeName_en-us'] and
|
||||
"'Pochven'" not in row['typeName_en-us'] and
|
||||
@@ -543,7 +545,7 @@ def update_db():
|
||||
continue
|
||||
typeName = row.get('typeName_en-us', '')
|
||||
# Regular sets matching
|
||||
m = re.match('(?P<grade>(High|Mid|Low)-grade) (?P<set>\w+) (?P<implant>(Alpha|Beta|Gamma|Delta|Epsilon|Omega))', typeName, re.IGNORECASE)
|
||||
m = re.match(r'(?P<grade>(High|Mid|Low)-grade) (?P<set>\w+) (?P<implant>(Alpha|Beta|Gamma|Delta|Epsilon|Omega))', typeName, re.IGNORECASE)
|
||||
if m:
|
||||
implantSets.setdefault((m.group('grade'), m.group('set')), set()).add(row['typeID'])
|
||||
# Special set matching
|
||||
@@ -600,7 +602,7 @@ def update_db():
|
||||
eos.gamedata.Item.name.like('%mutated%'),
|
||||
eos.gamedata.Item.name.like('%_PLACEHOLDER%'),
|
||||
# Drifter weapons are published for some reason
|
||||
eos.gamedata.Item.name.in_(('Lux Kontos', 'Lux Xiphos'))
|
||||
eos.gamedata.Item.name.in_(('Lux Kontos', 'Lux Xiphos', 'Lux Ballistra', 'Lux Kopis'))
|
||||
)).all():
|
||||
if 'Asteroid Mining Crystal' in item.name:
|
||||
continue
|
||||
@@ -616,6 +618,16 @@ def update_db():
|
||||
eos.db.gamedata_session.delete(cat)
|
||||
|
||||
# Unused normally, can be useful for customizing items
|
||||
def _copyItem(srcName, tgtTypeID, tgtName):
|
||||
eveType = eos.db.gamedata_session.query(eos.gamedata.Item).filter(eos.gamedata.Item.name == srcName).one()
|
||||
eos.db.gamedata_session.expunge(eveType)
|
||||
sqlalchemy.orm.make_transient(eveType)
|
||||
eveType.ID = tgtTypeID
|
||||
for suffix in eos.config.translation_mapping.values():
|
||||
setattr(eveType, f'typeName{suffix}', tgtName)
|
||||
eos.db.gamedata_session.add(eveType)
|
||||
eos.db.gamedata_session.flush()
|
||||
|
||||
def _hardcodeAttribs(typeID, attrMap):
|
||||
for attrName, value in attrMap.items():
|
||||
try:
|
||||
@@ -624,7 +636,7 @@ def update_db():
|
||||
except sqlalchemy.orm.exc.NoResultFound:
|
||||
attrInfo = eos.db.gamedata_session.query(eos.gamedata.AttributeInfo).filter(eos.gamedata.AttributeInfo.name == attrName).one()
|
||||
attr = eos.gamedata.Attribute()
|
||||
attr.ID = attrInfo.ID
|
||||
attr.attributeID = attrInfo.ID
|
||||
attr.typeID = typeID
|
||||
attr.value = value
|
||||
eos.db.gamedata_session.add(attr)
|
||||
@@ -640,6 +652,161 @@ def update_db():
|
||||
effect.effectName = effectName
|
||||
item.effects[effectName] = effect
|
||||
|
||||
def hardcodeSuppressionTackleRange():
|
||||
beaconTypeID = 79839
|
||||
attrMap = {
|
||||
'warfareBuff1ID': 2405,
|
||||
'warfareBuff1Value': 10}
|
||||
effectMap = {100000: 'pyfaCustomSuppressionTackleRange'}
|
||||
_hardcodeAttribs(beaconTypeID, attrMap)
|
||||
_hardcodeEffects(beaconTypeID, effectMap)
|
||||
|
||||
|
||||
def hardcodeShapash():
|
||||
shapashTypeID = 1000000
|
||||
_copyItem(srcName='Utu', tgtTypeID=shapashTypeID, tgtName='Shapash')
|
||||
attrMap = {
|
||||
# Fitting
|
||||
'powerOutput': 50,
|
||||
'cpuOutput': 225,
|
||||
'capacitorCapacity': 420,
|
||||
'rechargeRate': 187500,
|
||||
# Slots
|
||||
'hiSlots': 3,
|
||||
'medSlots': 4,
|
||||
'lowSlots': 4,
|
||||
'launcherSlotsLeft': 0,
|
||||
'turretSlotsLeft': 3,
|
||||
# Rigs
|
||||
'rigSlots': 2,
|
||||
'rigSize': 1,
|
||||
'upgradeCapacity': 400,
|
||||
# Shield
|
||||
'shieldCapacity': 575,
|
||||
'shieldRechargeRate': 625000,
|
||||
'shieldEmDamageResonance': 1 - 0.0,
|
||||
'shieldThermalDamageResonance': 1 - 0.6,
|
||||
'shieldKineticDamageResonance': 1 - 0.85,
|
||||
'shieldExplosiveDamageResonance': 1 - 0.5,
|
||||
# Armor
|
||||
'armorHP': 1015,
|
||||
'armorEmDamageResonance': 1 - 0.5,
|
||||
'armorThermalDamageResonance': 1 - 0.675,
|
||||
'armorKineticDamageResonance': 1 - 0.8375,
|
||||
'armorExplosiveDamageResonance': 1 - 0.1,
|
||||
# Structure
|
||||
'hp': 1274,
|
||||
'emDamageResonance': 1 - 0.33,
|
||||
'thermalDamageResonance': 1 - 0.33,
|
||||
'kineticDamageResonance': 1 - 0.33,
|
||||
'explosiveDamageResonance': 1 - 0.33,
|
||||
'mass': 1215000,
|
||||
'volume': 29500,
|
||||
'capacity': 165,
|
||||
# Navigation
|
||||
'maxVelocity': 325,
|
||||
'agility': 3.467,
|
||||
'warpSpeedMultiplier': 5.5,
|
||||
# Drones
|
||||
'droneCapacity': 75,
|
||||
'droneBandwidth': 25,
|
||||
# Targeting
|
||||
'maxTargetRange': 49000,
|
||||
'maxLockedTargets': 6,
|
||||
'scanRadarStrength': 0,
|
||||
'scanLadarStrength': 0,
|
||||
'scanMagnetometricStrength': 9,
|
||||
'scanGravimetricStrength': 0,
|
||||
'signatureRadius': 39,
|
||||
'scanResolution': 550,
|
||||
# Misc
|
||||
'energyWarfareResistance': 0,
|
||||
'stasisWebifierResistance': 0,
|
||||
'weaponDisruptionResistance': 0}
|
||||
effectMap = {
|
||||
100100: 'pyfaCustomShapashAfArAmount',
|
||||
100101: 'pyfaCustomShapashAfShtTrackingOptimal',
|
||||
100102: 'pyfaCustomShapashGfShtDamage',
|
||||
100103: 'pyfaCustomShapashGfPointRange',
|
||||
100104: 'pyfaCustomShapashGfPropOverheat',
|
||||
100105: 'pyfaCustomShapashRolePlateMass',
|
||||
100106: 'pyfaCustomShapashRoleHeat'}
|
||||
_hardcodeAttribs(shapashTypeID, attrMap)
|
||||
_hardcodeEffects(shapashTypeID, effectMap)
|
||||
|
||||
def hardcodeCybele():
|
||||
cybeleTypeID = 1000001
|
||||
_copyItem(srcName='Adrestia', tgtTypeID=cybeleTypeID, tgtName='Cybele')
|
||||
attrMap = {
|
||||
# Fitting
|
||||
'powerOutput': 1284,
|
||||
'cpuOutput': 400,
|
||||
'capacitorCapacity': 2400,
|
||||
'rechargeRate': 334000,
|
||||
'hiSlots': 5,
|
||||
'medSlots': 4,
|
||||
'lowSlots': 6,
|
||||
'launcherSlotsLeft': 0,
|
||||
'turretSlotsLeft': 5,
|
||||
# Rigs
|
||||
'rigSlots': 2,
|
||||
'rigSize': 2,
|
||||
'upgradeCapacity': 400,
|
||||
# Shield
|
||||
'shieldCapacity': 1200,
|
||||
'shieldRechargeRate': 1250000,
|
||||
'shieldEmDamageResonance': 1 - 0.0,
|
||||
'shieldThermalDamageResonance': 1 - 0.5,
|
||||
'shieldKineticDamageResonance': 1 - 0.9,
|
||||
'shieldExplosiveDamageResonance': 1 - 0.5,
|
||||
# Armor
|
||||
'armorHP': 1900,
|
||||
'armorEmDamageResonance': 1 - 0.5,
|
||||
'armorThermalDamageResonance': 1 - 0.69,
|
||||
'armorKineticDamageResonance': 1 - 0.85,
|
||||
'armorExplosiveDamageResonance': 1 - 0.1,
|
||||
# Structure
|
||||
'hp': 2300,
|
||||
'emDamageResonance': 1 - 0.33,
|
||||
'thermalDamageResonance': 1 - 0.33,
|
||||
'kineticDamageResonance': 1 - 0.33,
|
||||
'explosiveDamageResonance': 1 - 0.33,
|
||||
'mass': 11100000,
|
||||
'volume': 112000,
|
||||
'capacity': 450,
|
||||
# Navigation
|
||||
'maxVelocity': 235,
|
||||
'agility': 0.457,
|
||||
'warpSpeedMultiplier': 4.5,
|
||||
# Drones
|
||||
'droneCapacity': 100,
|
||||
'droneBandwidth': 50,
|
||||
# Targeting
|
||||
'maxTargetRange': 60000,
|
||||
'maxLockedTargets': 6,
|
||||
'scanRadarStrength': 0,
|
||||
'scanLadarStrength': 0,
|
||||
'scanMagnetometricStrength': 15,
|
||||
'scanGravimetricStrength': 0,
|
||||
'signatureRadius': 115,
|
||||
'scanResolution': 330,
|
||||
# Misc
|
||||
'energyWarfareResistance': 0,
|
||||
'stasisWebifierResistance': 0,
|
||||
'weaponDisruptionResistance': 0}
|
||||
effectMap = {
|
||||
100200: 'pyfaCustomCybeleHacMhtFalloff',
|
||||
100201: 'pyfaCustomCybeleHacMhtTracking',
|
||||
100202: 'pyfaCustomCybeleGcMhtDamage',
|
||||
100203: 'pyfaCustomCybeleGcArAmount',
|
||||
100204: 'pyfaCustomCybeleGcPointRange',
|
||||
100205: 'pyfaCustomCybeleRoleVelocity',
|
||||
100206: 'pyfaCustomCybeleRolePlateMass'}
|
||||
_hardcodeAttribs(cybeleTypeID, attrMap)
|
||||
_hardcodeEffects(cybeleTypeID, effectMap)
|
||||
|
||||
hardcodeSuppressionTackleRange()
|
||||
|
||||
eos.db.gamedata_session.commit()
|
||||
eos.db.gamedata_engine.execute('VACUUM')
|
||||
|
||||
|
||||
74
dist_assets/linux/AppImageBuilder.yml
Normal file
74
dist_assets/linux/AppImageBuilder.yml
Normal file
@@ -0,0 +1,74 @@
|
||||
version: 1
|
||||
|
||||
AppDir:
|
||||
path: ./AppDir
|
||||
|
||||
app_info:
|
||||
id: pyfa
|
||||
name: pyfa
|
||||
icon: pyfa
|
||||
version: '{{PYFA_VERSION}}'
|
||||
exec: usr/bin/python3.11
|
||||
exec_args: "-s $APPDIR/opt/pyfa/pyfa.py $@"
|
||||
|
||||
apt:
|
||||
arch: [ amd64 ]
|
||||
sources:
|
||||
- sourceline: 'deb http://us.archive.ubuntu.com/ubuntu jammy main restricted universe multiverse'
|
||||
key_url: 'http://keyserver.ubuntu.com/pks/lookup?op=get&search=0x871920d1991bc93c'
|
||||
- sourceline: 'deb http://us.archive.ubuntu.com/ubuntu jammy-updates main restricted universe multiverse'
|
||||
key_url: 'http://keyserver.ubuntu.com/pks/lookup?op=get&search=0x871920d1991bc93c'
|
||||
- sourceline: 'deb http://us.archive.ubuntu.com/ubuntu jammy-backports main restricted universe multiverse'
|
||||
key_url: 'http://keyserver.ubuntu.com/pks/lookup?op=get&search=0x871920d1991bc93c'
|
||||
- sourceline: 'deb http://us.archive.ubuntu.com/ubuntu jammy-security main restricted universe multiverse'
|
||||
key_url: 'http://keyserver.ubuntu.com/pks/lookup?op=get&search=0x871920d1991bc93c'
|
||||
- sourceline: 'deb https://ppa.launchpadcontent.net/deadsnakes/ppa/ubuntu jammy main'
|
||||
key_url: 'http://keyserver.ubuntu.com/pks/lookup?op=get&search=0xf23c5a6cf475977595c89f51ba6932366a755776'
|
||||
|
||||
include:
|
||||
- python3.11
|
||||
# wx dependencies
|
||||
- libgtk-3-0
|
||||
- librsvg2-common # GTK3 recommendation; without it, search in char editor crashes
|
||||
- libwebkit2gtk-4.0-37 # Needed for wx's HTML lib
|
||||
# Unknown
|
||||
- libpcre2-32-0 # https://github.com/pyfa-org/Pyfa/issues/2572
|
||||
- libnotify4 # https://github.com/pyfa-org/Pyfa/issues/2598
|
||||
- libwayland-client0 # https://github.com/pyfa-org/Pyfa/issues/2600
|
||||
exclude:
|
||||
- hicolor-icon-theme
|
||||
- humanity-icon-theme
|
||||
- ubuntu-mono
|
||||
|
||||
after_bundle:
|
||||
# Install python dependencies to bundled interpreter
|
||||
- export PYTHONHOME="AppDir/usr"
|
||||
- export PYTHONPATH="AppDir/usr/lib/python3.11/site-packages"
|
||||
- curl -L https://bootstrap.pypa.io/get-pip.py -o get-pip.py
|
||||
- AppDir/usr/bin/python3.11 get-pip.py
|
||||
# Just to bundle certificates with AppImage
|
||||
- AppDir/usr/bin/python3.11 -s -m pip install certifi
|
||||
- AppDir/usr/bin/python3.11 -s -m pip install -f https://extras.wxpython.org/wxPython4/extras/linux/gtk3/ubuntu-22.04 -r requirements.txt
|
||||
|
||||
files:
|
||||
exclude:
|
||||
- usr/lib/x86_64-linux-gnu/gconv
|
||||
- usr/share/man
|
||||
- usr/share/doc/*/README.*
|
||||
- usr/share/doc/*/changelog.*
|
||||
- usr/share/doc/*/NEWS.*
|
||||
- usr/share/doc/*/TODO.*
|
||||
- usr/include
|
||||
|
||||
runtime:
|
||||
env:
|
||||
PYTHONHOME: '${APPDIR}/usr'
|
||||
PYTHONPATH: '${APPDIR}/usr/lib/python3.11/site-packages'
|
||||
SSL_CERT_FILE: '${APPDIR}/usr/local/lib/python3.11/dist-packages/certifi/cacert.pem'
|
||||
# Workaround for https://github.com/AppImageCrafters/appimage-builder/issues/336
|
||||
XDG_DATA_DIRS: '${APPDIR}/usr/local/share:${APPDIR}/usr/share:/usr/local/share:/usr/share:$XDG_DATA_DIRS'
|
||||
|
||||
AppImage:
|
||||
sign-key: None
|
||||
arch: x86_64
|
||||
file_name: 'pyfa-{{PYFA_VERSION}}-linux.AppImage'
|
||||
@@ -1,70 +0,0 @@
|
||||
# -*- mode: python -*-
|
||||
|
||||
import os
|
||||
from itertools import chain
|
||||
import subprocess
|
||||
|
||||
label = subprocess.check_output([
|
||||
"git", "describe", "--tags"]).strip()
|
||||
|
||||
with open('gitversion', 'w+') as f:
|
||||
f.write(label.decode())
|
||||
|
||||
block_cipher = None
|
||||
|
||||
|
||||
added_files = [
|
||||
( 'imgs/gui/*.png', 'imgs/gui' ),
|
||||
( 'imgs/gui/*.gif', 'imgs/gui' ),
|
||||
( 'imgs/icons/*.png', 'imgs/icons' ),
|
||||
( 'imgs/renders/*.png', 'imgs/renders' ),
|
||||
( 'dist_assets/win/pyfa.ico', '.' ),
|
||||
( 'dist_assets/cacert.pem', '.' ),
|
||||
( 'eve.db', '.' ),
|
||||
( 'README.md', '.' ),
|
||||
( 'LICENSE', '.' ),
|
||||
( 'gitversion', '.' ),
|
||||
]
|
||||
|
||||
import_these = []
|
||||
|
||||
# Walk directories that do dynamic importing
|
||||
paths = ('eos/effects', 'eos/db/migrations', 'service/conversions')
|
||||
for root, folders, files in chain.from_iterable(os.walk(path) for path in paths):
|
||||
for file_ in files:
|
||||
if file_.endswith(".py") and not file_.startswith("_"):
|
||||
mod_name = "{}.{}".format(
|
||||
root.replace("/", "."),
|
||||
file_.split(".py")[0],
|
||||
)
|
||||
import_these.append(mod_name)
|
||||
|
||||
|
||||
a = Analysis(['pyfa.py'],
|
||||
pathex=[],
|
||||
binaries=[],
|
||||
datas=added_files,
|
||||
hiddenimports=import_these,
|
||||
hookspath=[],
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
win_no_prefer_redirects=False,
|
||||
win_private_assemblies=False,
|
||||
cipher=block_cipher)
|
||||
pyz = PYZ(a.pure, a.zipped_data,
|
||||
cipher=block_cipher)
|
||||
exe = EXE(pyz,
|
||||
a.scripts,
|
||||
exclude_binaries=True,
|
||||
name='pyfa',
|
||||
debug=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
console=True )
|
||||
coll = COLLECT(exe,
|
||||
a.binaries,
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
strip=False,
|
||||
upx=True,
|
||||
name='pyfa')
|
||||
@@ -80,6 +80,7 @@ exe = EXE(pyz,
|
||||
app = BUNDLE(
|
||||
exe,
|
||||
name='pyfa.app',
|
||||
version=os.getenv('PYFA_VERSION'),
|
||||
icon=icon,
|
||||
bundle_identifier=None,
|
||||
info_plist={
|
||||
@@ -88,5 +89,7 @@ app = BUNDLE(
|
||||
'CFBundleName': 'pyfa',
|
||||
'CFBundleDisplayName': 'pyfa',
|
||||
'CFBundleIdentifier': 'org.pyfaorg.pyfa',
|
||||
'CFBundleVersion': os.getenv('PYFA_VERSION'),
|
||||
'CFBundleShortVersionString': os.getenv('PYFA_VERSION'),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -14,7 +14,7 @@ with open("version.yml", 'r') as file:
|
||||
os.environ["PYFA_DIST_DIR"] = os.path.join(os.getcwd(), 'dist')
|
||||
|
||||
os.environ["PYFA_VERSION"] = version
|
||||
iscc = "C:\Program Files (x86)\Inno Setup 6\ISCC.exe"
|
||||
iscc = r"C:\Program Files (x86)\Inno Setup 6\ISCC.exe"
|
||||
|
||||
source = os.path.join(os.environ["PYFA_DIST_DIR"], "pyfa")
|
||||
|
||||
|
||||
@@ -42,10 +42,10 @@ CloseApplications=yes
|
||||
DefaultDirName={pf}\{#MyAppName}
|
||||
DefaultGroupName={#MyAppName}
|
||||
AllowNoIcons=yes
|
||||
LicenseFile={#MyAppDir}\LICENSE
|
||||
LicenseFile={#MyAppDir}\app\LICENSE
|
||||
OutputDir={#MyOutputDir}
|
||||
OutputBaseFilename={#MyOutputFile}
|
||||
SetupIconFile={#MyAppDir}\pyfa.ico
|
||||
SetupIconFile={#MyAppDir}\app\pyfa.ico
|
||||
SolidCompression=yes
|
||||
|
||||
[Languages]
|
||||
|
||||
@@ -10,10 +10,15 @@
|
||||
</trustInfo>
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!-- Windows Vista -->
|
||||
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
|
||||
<!-- Windows 7 -->
|
||||
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
|
||||
<!-- Windows 8 -->
|
||||
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
|
||||
<!-- Windows 8.1 -->
|
||||
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
|
||||
<!-- Windows 10 and Windows 11 -->
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
|
||||
</application>
|
||||
</compatibility>
|
||||
|
||||
1
docs/_config.yml
Normal file
1
docs/_config.yml
Normal file
@@ -0,0 +1 @@
|
||||
theme: jekyll-theme-midnight
|
||||
104
docs/callback.html
Normal file
104
docs/callback.html
Normal file
@@ -0,0 +1,104 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>pyfa Authentication Proxy</title>
|
||||
<style type="text/css">
|
||||
|
||||
|
||||
body { width: 700px; margin: 150px auto; }
|
||||
h1 { font-size: 40px; }
|
||||
h2 { font-size: 32px; }
|
||||
body { font: 20px Helvetica, sans-serif; color: #333; }
|
||||
#article { display: block; text-align: left; width: 650px; margin: 0 auto; }
|
||||
.hidden { display:none; }
|
||||
.success { color: #28a745; }
|
||||
.error { color: #dc3545; }
|
||||
textarea { width: 100%; height: 100px; }
|
||||
hr { border-width: 1px 0 0 0; border-style: solid; border-color: #333; }
|
||||
button { cursor: pointer; background-color: white; border: 1px solid #333; color: #333; padding: 8px 11px; text-align: center; text-decoration: none; display: inline-block; }
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body { background-color: #333; color: white; }
|
||||
textarea { width: 100%; height: 100px; background-color: #aaa;}
|
||||
button { background-color: #333; border: 1px solid white; color: white; }
|
||||
hr { border-color: white; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- Layout from Short Circuit's CREST login. Shout out! https://github.com/farshield/shortcircuit -->
|
||||
<h1>pyfa</h1>
|
||||
<div id="mode0" class="hidden">
|
||||
<p id="mode0-msg">Processing request...</p>
|
||||
<button id="showBtn" onClick="showCode()">Show Code</button>
|
||||
</div>
|
||||
<div id="mode1" class="hidden">
|
||||
<p id="mode1-p">Please copy and paste the token below into pyfa.</p>
|
||||
<textarea id="authCodeText" readonly></textarea>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<script>
|
||||
debugger;
|
||||
function showCode() {
|
||||
var element = document.getElementById(`mode1`);
|
||||
element.classList.remove("hidden");
|
||||
element = document.getElementById(`showBtn`);
|
||||
element.classList.add("hidden");
|
||||
}
|
||||
|
||||
function httpPostAsync(url, callback)
|
||||
{
|
||||
var xmlHttp = new XMLHttpRequest();
|
||||
xmlHttp.onreadystatechange = function() {
|
||||
if (xmlHttp.readyState == XMLHttpRequest.DONE)
|
||||
callback(xmlHttp);
|
||||
}
|
||||
xmlHttp.open("GET", url, true); // true for asynchronous
|
||||
xmlHttp.send(null);
|
||||
}
|
||||
|
||||
const urlSearchParams = new URLSearchParams(window.location.search);
|
||||
const params = Object.fromEntries(urlSearchParams.entries());
|
||||
let stateInfo;
|
||||
try {
|
||||
stateInfo = JSON.parse(atob(params.state))
|
||||
} catch (err) {
|
||||
// something has happened and we cannot continue.
|
||||
// todo: show a simple message and exit.
|
||||
throw err;
|
||||
}
|
||||
|
||||
// we always want to show the text box for manual.
|
||||
var element = document.getElementById(`mode${stateInfo.mode}`);
|
||||
element.classList.remove("hidden");
|
||||
|
||||
if (stateInfo.mode === 0) {
|
||||
let p = document.getElementById(`mode1-p`);
|
||||
p.prepend(document.createElement("hr"))
|
||||
}
|
||||
debugger;
|
||||
document.getElementById(`authCodeText`).value = btoa(JSON.stringify(params))
|
||||
|
||||
if (stateInfo.mode == 0) { // auto / server mode
|
||||
httpPostAsync(stateInfo.redirect + window.location.search, (req)=>{
|
||||
debugger;
|
||||
const msgDiv = document.getElementById(`mode0-msg`);
|
||||
if (req.status === 200) {
|
||||
msgDiv.innerHTML ="<span class='success'>Success!</span> You may close this window and return to the application.";
|
||||
return;
|
||||
} else if (req.status === 0){
|
||||
msgDiv.innerHTML = "<span class='error'>Error!</span> Server response not received.<p><small>The local pyfa server may have timed out, or may not have started correctly.</small></p>";
|
||||
} else if (req.status === 400){
|
||||
msgDiv.innerHTML = `<span class='error'>Error!</span> <p><small>${req.responseText}</small></p>`
|
||||
} else {
|
||||
msgDiv.innerHTML = `<span class='error'>Error!</span> <p><small>There was an unknown error. Please report this to the pyfa issues page.</p><p><textarea readdonly>${req.responseText}</textarea></small></p>`
|
||||
}
|
||||
showCode()
|
||||
// todo: bad request error when it's not an error itself, but rather
|
||||
})
|
||||
// todo: post message to local EVE server
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
37
docs/index.md
Normal file
37
docs/index.md
Normal file
@@ -0,0 +1,37 @@
|
||||
## Welcome to GitHub Pages
|
||||
|
||||
You can use the [editor on GitHub](https://github.com/pyfa-org/Pyfa/edit/gh-pages/index.md) to maintain and preview the content for your website in Markdown files.
|
||||
|
||||
Whenever you commit to this repository, GitHub Pages will run [Jekyll](https://jekyllrb.com/) to rebuild the pages in your site, from the content in your Markdown files.
|
||||
|
||||
### Markdown
|
||||
|
||||
Markdown is a lightweight and easy-to-use syntax for styling your writing. It includes conventions for
|
||||
|
||||
```markdown
|
||||
Syntax highlighted code block
|
||||
|
||||
# Header 1
|
||||
## Header 2
|
||||
### Header 3
|
||||
|
||||
- Bulleted
|
||||
- List
|
||||
|
||||
1. Numbered
|
||||
2. List
|
||||
|
||||
**Bold** and _Italic_ and `Code` text
|
||||
|
||||
[Link](url) and 
|
||||
```
|
||||
|
||||
For more details see [GitHub Flavored Markdown](https://guides.github.com/features/mastering-markdown/).
|
||||
|
||||
### Jekyll Themes
|
||||
|
||||
Your Pages site will use the layout and styles from the Jekyll theme you have selected in your [repository settings](https://github.com/pyfa-org/Pyfa/settings/pages). The name of this theme is saved in the Jekyll `_config.yml` configuration file.
|
||||
|
||||
### Support or Contact
|
||||
|
||||
Having trouble with Pages? Check out our [documentation](https://docs.github.com/categories/github-pages-basics/) or [contact support](https://support.github.com/contact) and we’ll help you sort it out.
|
||||
@@ -51,7 +51,7 @@ mapper(DynamicItemAttribute, dynamicAttributes_table,
|
||||
properties={"info": relation(AttributeInfo, lazy=False)})
|
||||
|
||||
mapper(DynamicItemItem, dynamicApplicable_table, properties={
|
||||
"mutaplasmid": relation(DynamicItem),
|
||||
"mutaplasmid": relation(DynamicItem, viewonly=True),
|
||||
})
|
||||
|
||||
DynamicItemAttribute.ID = association_proxy("info", "attributeID")
|
||||
|
||||
@@ -69,7 +69,8 @@ props = {
|
||||
primaryjoin=dynamicApplicable_table.c.applicableTypeID == items_table.c.typeID,
|
||||
secondaryjoin=dynamicApplicable_table.c.typeID == DynamicItem.typeID,
|
||||
secondary=dynamicApplicable_table,
|
||||
backref="applicableItems"
|
||||
backref="applicableItems",
|
||||
viewonly=True
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ for modName in iterNamespace(__name__, __path__):
|
||||
# loop through python files, extracting update number and function, and
|
||||
# adding it to a list
|
||||
modname_tail = modName.rsplit('.', 1)[-1]
|
||||
m = re.match("^upgrade(?P<index>\d+)$", modname_tail)
|
||||
m = re.match(r"^upgrade(?P<index>\d+)$", modname_tail)
|
||||
if not m:
|
||||
continue
|
||||
index = int(m.group("index"))
|
||||
|
||||
@@ -7,7 +7,7 @@ Migration 1
|
||||
loaded as they no longer exist in the database. We therefore replace these
|
||||
modules with their new replacements
|
||||
|
||||
Based on http://community.eveonline.com/news/patch-notes/patch-notes-for-oceanus/
|
||||
Based on https://www.eveonline.com/news/view/patch-notes-for-oceanus
|
||||
and output of itemDiff.py
|
||||
"""
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Migration 25
|
||||
|
||||
- Converts T3C fitting configurations based on the spreadsheet noted here:
|
||||
https://community.eveonline.com/news/patch-notes/patch-notes-for-july-2017-release
|
||||
https://www.eveonline.com/news/view/patch-notes-for-july-2017-release
|
||||
|
||||
(csv copies can be found on the pyfa repo in case the official documents are deleted)
|
||||
|
||||
@@ -4228,8 +4228,8 @@ def upgrade(saveddata_engine):
|
||||
# We don't have a conversion for this. I don't think this will ever happen, but who knows
|
||||
continue
|
||||
|
||||
# It doesn't actully matter which old module is replaced with which new module, so we don't have to worry
|
||||
# about module position or anything like that. Just doe a straight up record UPDATE
|
||||
# It doesn't actually matter which old module is replaced with which new module, so we don't have to worry
|
||||
# about module position or anything like that. Just do a straight up record UPDATE
|
||||
for i, old in enumerate(oldModules[:4]):
|
||||
saveddata_engine.execute("UPDATE modules SET itemID = ? WHERE ID = ?", (newModules[i], old[0]))
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ Migration 4
|
||||
from database), which causes pyfa to crash. We therefore replace these
|
||||
modules with their new replacements
|
||||
|
||||
Based on http://community.eveonline.com/news/patch-notes/patch-notes-for-proteus/
|
||||
Based on https://www.eveonline.com/news/view/patch-notes-for-proteus
|
||||
and output of itemDiff.py
|
||||
"""
|
||||
|
||||
|
||||
36
eos/db/migrations/upgrade47.py
Normal file
36
eos/db/migrations/upgrade47.py
Normal file
@@ -0,0 +1,36 @@
|
||||
"""
|
||||
Migration 46
|
||||
|
||||
- add support for server selection for SSO characters
|
||||
"""
|
||||
import sqlalchemy
|
||||
|
||||
tmpTable = """
|
||||
CREATE TABLE ssoCharacterTemp (
|
||||
ID INTEGER NOT NULL,
|
||||
client VARCHAR NOT NULL,
|
||||
characterID INTEGER NOT NULL,
|
||||
characterName VARCHAR NOT NULL,
|
||||
refreshToken VARCHAR NOT NULL,
|
||||
accessToken VARCHAR NOT NULL,
|
||||
accessTokenExpires DATETIME NOT NULL,
|
||||
created DATETIME,
|
||||
modified DATETIME,
|
||||
server VARCHAR,
|
||||
PRIMARY KEY (ID),
|
||||
CONSTRAINT "uix_client_server_characterID" UNIQUE (client, server, characterID),
|
||||
CONSTRAINT "uix_client_server_characterName" UNIQUE (client, server, characterName)
|
||||
)
|
||||
"""
|
||||
|
||||
def upgrade(saveddata_engine):
|
||||
try:
|
||||
saveddata_engine.execute("SELECT server FROM ssoCharacter LIMIT 1")
|
||||
except sqlalchemy.exc.DatabaseError:
|
||||
saveddata_engine.execute(tmpTable)
|
||||
saveddata_engine.execute(
|
||||
"INSERT INTO ssoCharacterTemp (ID, client, characterID, characterName, refreshToken, accessToken, accessTokenExpires, created, modified, server) "
|
||||
"SELECT ID, client, characterID, characterName, refreshToken, accessToken, accessTokenExpires, created, modified, 'Tranquility' "
|
||||
"FROM ssoCharacter")
|
||||
saveddata_engine.execute("DROP TABLE ssoCharacter")
|
||||
saveddata_engine.execute("ALTER TABLE ssoCharacterTemp RENAME TO ssoCharacter")
|
||||
15
eos/db/migrations/upgrade48.py
Normal file
15
eos/db/migrations/upgrade48.py
Normal file
@@ -0,0 +1,15 @@
|
||||
"""
|
||||
Migration 48
|
||||
|
||||
- added pilot security column (CONCORD ships)
|
||||
"""
|
||||
|
||||
|
||||
import sqlalchemy
|
||||
|
||||
|
||||
def upgrade(saveddata_engine):
|
||||
try:
|
||||
saveddata_engine.execute("SELECT pilotSecurity FROM fits LIMIT 1")
|
||||
except sqlalchemy.exc.DatabaseError:
|
||||
saveddata_engine.execute("ALTER TABLE fits ADD COLUMN pilotSecurity FLOAT")
|
||||
15
eos/db/migrations/upgrade49.py
Normal file
15
eos/db/migrations/upgrade49.py
Normal file
@@ -0,0 +1,15 @@
|
||||
"""
|
||||
Migration 49
|
||||
|
||||
- added hp column to targetResists table
|
||||
"""
|
||||
|
||||
|
||||
import sqlalchemy
|
||||
|
||||
|
||||
def upgrade(saveddata_engine):
|
||||
try:
|
||||
saveddata_engine.execute("SELECT hp FROM targetResists LIMIT 1;")
|
||||
except sqlalchemy.exc.DatabaseError:
|
||||
saveddata_engine.execute("ALTER TABLE targetResists ADD COLUMN hp FLOAT;")
|
||||
@@ -40,18 +40,18 @@ characters_table = Table("characters", saveddata_meta,
|
||||
Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now))
|
||||
|
||||
sso_table = Table("ssoCharacter", saveddata_meta,
|
||||
Column("ID", Integer, primary_key=True),
|
||||
Column("client", String, nullable=False),
|
||||
Column("characterID", Integer, nullable=False),
|
||||
Column("characterName", String, nullable=False),
|
||||
Column("refreshToken", String, nullable=False),
|
||||
Column("accessToken", String, nullable=False),
|
||||
Column("accessTokenExpires", DateTime, nullable=False),
|
||||
Column("created", DateTime, nullable=True, default=datetime.datetime.now),
|
||||
Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now),
|
||||
UniqueConstraint('client', 'characterID', name='uix_client_characterID'),
|
||||
UniqueConstraint('client', 'characterName', name='uix_client_characterName')
|
||||
)
|
||||
Column("ID", Integer, primary_key=True),
|
||||
Column("client", String, nullable=False),
|
||||
Column("characterID", Integer, nullable=False),
|
||||
Column("characterName", String, nullable=False),
|
||||
Column("server", String, nullable=False),
|
||||
Column("refreshToken", String, nullable=False),
|
||||
Column("accessToken", String, nullable=False),
|
||||
Column("accessTokenExpires", DateTime, nullable=False),
|
||||
Column("created", DateTime, nullable=True, default=datetime.datetime.now),
|
||||
Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now),
|
||||
UniqueConstraint('client', 'server', 'characterID', name='uix_client_server_characterID'),
|
||||
UniqueConstraint('client', 'server', 'characterName', name='uix_client_server_characterName'))
|
||||
|
||||
sso_character_map_table = Table("ssoCharacterMap", saveddata_meta,
|
||||
Column("characterID", ForeignKey("characters.ID"), primary_key=True),
|
||||
|
||||
@@ -63,7 +63,8 @@ fits_table = Table("fits", saveddata_meta,
|
||||
Column("ignoreRestrictions", Boolean, default=0),
|
||||
Column("created", DateTime, nullable=True, default=datetime.datetime.now),
|
||||
Column("modified", DateTime, nullable=True, default=datetime.datetime.now, onupdate=datetime.datetime.now),
|
||||
Column("systemSecurity", Integer, nullable=True)
|
||||
Column("systemSecurity", Integer, nullable=True),
|
||||
Column("pilotSecurity", Float, nullable=True),
|
||||
)
|
||||
|
||||
projectedFits_table = Table("projectedFits", saveddata_meta,
|
||||
@@ -175,12 +176,13 @@ mapper(es_Fit, fits_table,
|
||||
collection_class=HandledModuleList,
|
||||
primaryjoin=and_(modules_table.c.fitID == fits_table.c.ID, modules_table.c.projected == False), # noqa
|
||||
order_by=modules_table.c.position,
|
||||
overlaps='owner',
|
||||
cascade='all, delete, delete-orphan'),
|
||||
"_Fit__projectedModules": relation(
|
||||
Module,
|
||||
collection_class=HandledProjectedModList,
|
||||
overlaps='owner, _Fit__modules',
|
||||
cascade='all, delete, delete-orphan',
|
||||
single_parent=True,
|
||||
primaryjoin=and_(modules_table.c.fitID == fits_table.c.ID, modules_table.c.projected == True)), # noqa
|
||||
"owner": relation(
|
||||
User,
|
||||
@@ -190,37 +192,37 @@ mapper(es_Fit, fits_table,
|
||||
"_Fit__boosters": relation(
|
||||
Booster,
|
||||
collection_class=HandledBoosterList,
|
||||
cascade='all, delete, delete-orphan',
|
||||
single_parent=True),
|
||||
overlaps='owner',
|
||||
cascade='all, delete, delete-orphan'),
|
||||
"_Fit__drones": relation(
|
||||
Drone,
|
||||
collection_class=HandledDroneCargoList,
|
||||
overlaps='owner',
|
||||
cascade='all, delete, delete-orphan',
|
||||
single_parent=True,
|
||||
primaryjoin=and_(drones_table.c.fitID == fits_table.c.ID, drones_table.c.projected == False)), # noqa
|
||||
"_Fit__fighters": relation(
|
||||
Fighter,
|
||||
collection_class=HandledDroneCargoList,
|
||||
overlaps='owner',
|
||||
cascade='all, delete, delete-orphan',
|
||||
single_parent=True,
|
||||
primaryjoin=and_(fighters_table.c.fitID == fits_table.c.ID, fighters_table.c.projected == False)), # noqa
|
||||
"_Fit__cargo": relation(
|
||||
Cargo,
|
||||
collection_class=HandledDroneCargoList,
|
||||
overlaps='owner',
|
||||
cascade='all, delete, delete-orphan',
|
||||
single_parent=True,
|
||||
primaryjoin=and_(cargo_table.c.fitID == fits_table.c.ID)),
|
||||
"_Fit__projectedDrones": relation(
|
||||
Drone,
|
||||
collection_class=HandledProjectedDroneList,
|
||||
overlaps='owner, _Fit__drones',
|
||||
cascade='all, delete, delete-orphan',
|
||||
single_parent=True,
|
||||
primaryjoin=and_(drones_table.c.fitID == fits_table.c.ID, drones_table.c.projected == True)), # noqa
|
||||
"_Fit__projectedFighters": relation(
|
||||
Fighter,
|
||||
collection_class=HandledProjectedDroneList,
|
||||
overlaps='owner, _Fit__fighters',
|
||||
cascade='all, delete, delete-orphan',
|
||||
single_parent=True,
|
||||
primaryjoin=and_(fighters_table.c.fitID == fits_table.c.ID, fighters_table.c.projected == True)), # noqa
|
||||
"_Fit__implants": relation(
|
||||
Implant,
|
||||
|
||||
@@ -493,9 +493,12 @@ def getSsoCharacters(clientHash, eager=None):
|
||||
|
||||
|
||||
@cachedQuery(SsoCharacter, 1, "lookfor", "clientHash")
|
||||
def getSsoCharacter(lookfor, clientHash, eager=None):
|
||||
def getSsoCharacter(lookfor, clientHash, server=None, eager=None):
|
||||
filter = SsoCharacter.client == clientHash
|
||||
|
||||
if server is not None:
|
||||
filter = and_(filter, SsoCharacter.server == server)
|
||||
|
||||
if isinstance(lookfor, int):
|
||||
filter = and_(filter, SsoCharacter.ID == lookfor)
|
||||
elif isinstance(lookfor, str):
|
||||
|
||||
@@ -37,6 +37,7 @@ targetProfiles_table = Table(
|
||||
Column('maxVelocity', Float, nullable=True),
|
||||
Column('signatureRadius', Float, nullable=True),
|
||||
Column('radius', Float, nullable=True),
|
||||
Column('hp', Float, nullable=True),
|
||||
Column('ownerID', ForeignKey('users.ID'), nullable=True),
|
||||
Column('created', DateTime, nullable=True, default=datetime.datetime.now),
|
||||
Column('modified', DateTime, nullable=True, onupdate=datetime.datetime.now))
|
||||
@@ -48,4 +49,5 @@ mapper(
|
||||
'rawName': targetProfiles_table.c.name,
|
||||
'_maxVelocity': targetProfiles_table.c.maxVelocity,
|
||||
'_signatureRadius': targetProfiles_table.c.signatureRadius,
|
||||
'_radius': targetProfiles_table.c.radius})
|
||||
'_radius': targetProfiles_table.c.radius,
|
||||
'_hp': targetProfiles_table.c.hp})
|
||||
|
||||
5026
eos/effects.py
5026
eos/effects.py
File diff suppressed because it is too large
Load Diff
@@ -343,7 +343,9 @@ class Item(EqBase):
|
||||
500018: "mordu",
|
||||
500019: "sansha",
|
||||
500020: "serpentis",
|
||||
500026: "triglavian"
|
||||
500026: "triglavian",
|
||||
500027: "upwell",
|
||||
500029: "deathless",
|
||||
}
|
||||
|
||||
@property
|
||||
@@ -351,11 +353,7 @@ class Item(EqBase):
|
||||
if self.__race is None:
|
||||
|
||||
try:
|
||||
if (
|
||||
self.category.name == 'Structure' or
|
||||
# Here until CCP puts their shit together
|
||||
self.name in ("Thunderchild", "Stormbringer", "Skybreaker")
|
||||
):
|
||||
if self.category.name == 'Structure':
|
||||
self.__race = "upwell"
|
||||
else:
|
||||
self.__race = self.factionMap[self.factionID]
|
||||
@@ -377,7 +375,8 @@ class Item(EqBase):
|
||||
16 : "jove",
|
||||
32 : "sansha", # Incrusion Sansha
|
||||
128: "ore",
|
||||
135: "triglavian"
|
||||
135: "triglavian",
|
||||
168: "upwell",
|
||||
}
|
||||
# Race is None by default
|
||||
race = None
|
||||
@@ -571,13 +570,18 @@ class DynamicItem(EqBase):
|
||||
@property
|
||||
def shortName(self):
|
||||
name = self.item.customName
|
||||
keywords = ('Decayed', 'Gravid', 'Unstable', 'Radical')
|
||||
keywords = (
|
||||
'Decayed', 'Glorified Decayed',
|
||||
'Gravid', 'Glorified Gravid',
|
||||
'Unstable', 'Glorified Unstable',
|
||||
'Radical', 'Glorified Radical')
|
||||
for kw in keywords:
|
||||
if name.startswith(f'{kw} '):
|
||||
name = kw
|
||||
m = re.match('(?P<mutagrade>\S+) (?P<dronetype>\S+) Drone (?P<mutatype>\S+) Mutaplasmid', name)
|
||||
m = re.match(r'(?P<mutagrade>(Glorified )?\S+) (?P<dronetype>\S+) Drone (?P<mutatype>\S+) Mutaplasmid', name)
|
||||
if m:
|
||||
name = '{} {}'.format(m.group('mutagrade'), m.group('mutatype'))
|
||||
name = name.replace('Glorified ', 'Gl. ')
|
||||
return name
|
||||
|
||||
|
||||
|
||||
@@ -59,106 +59,115 @@ BUILTINS = OrderedDict([
|
||||
(-20, (_c(_t('Exotic Plasma')) + _t('Baryon'), 0, 59737, 0, 40263)),
|
||||
(-21, (_c(_t('Exotic Plasma')) + _t('Tetryon'), 0, 69208, 0, 30792)),
|
||||
(-22, (_c(_t('Exotic Plasma')) + '|' + _t('[T2] Occult'), 0, 55863, 0, 44137)),
|
||||
(-23, (_c(_t('Hybrid Charges')) + '|' + _t('[T2] Spike'), 0, 4, 4, 0)),
|
||||
(-24, (_c(_t('Hybrid Charges')) + '|' + _t('[T2] Null'), 0, 6, 5, 0)),
|
||||
(-25, (_c(_t('Hybrid Charges')) + _t('Iron'), 0, 2, 3, 0)),
|
||||
(-26, (_c(_t('Hybrid Charges')) + _t('Tungsten'), 0, 2, 4, 0)),
|
||||
(-27, (_c(_t('Hybrid Charges')) + _t('Iridium'), 0, 3, 4, 0)),
|
||||
(-28, (_c(_t('Hybrid Charges')) + _t('Lead'), 0, 3, 5, 0)),
|
||||
(-29, (_c(_t('Hybrid Charges')) + _t('Thorium'), 0, 4, 5, 0)),
|
||||
(-30, (_c(_t('Hybrid Charges')) + _t('Uranium'), 0, 4, 6, 0)),
|
||||
(-31, (_c(_t('Hybrid Charges')) + _t('Plutonium'), 0, 5, 6, 0)),
|
||||
(-32, (_c(_t('Hybrid Charges')) + _t('Antimatter'), 0, 5, 7, 0)),
|
||||
(-33, (_c(_t('Hybrid Charges')) + '|' + _t('[T2] Javelin'), 0, 8, 6, 0)),
|
||||
(-34, (_c(_t('Hybrid Charges')) + '|' + _t('[T2] Void'), 0, 7.7, 7.7, 0)),
|
||||
(-35, (_c(_t('Projectile Ammo')) + '|' + _t('[T2] Tremor'), 0, 0, 3, 5)),
|
||||
(-36, (_c(_t('Projectile Ammo')) + '|' + _t('[T2] Barrage'), 0, 0, 5, 6)),
|
||||
(-37, (_c(_t('Projectile Ammo')) + _t('Carbonized Lead'), 0, 0, 4, 1)),
|
||||
(-38, (_c(_t('Projectile Ammo')) + _t('Nuclear'), 0, 0, 1, 4)),
|
||||
(-39, (_c(_t('Projectile Ammo')) + _t('Proton'), 3, 0, 2, 0)),
|
||||
(-40, (_c(_t('Projectile Ammo')) + _t('Depleted Uranium'), 0, 3, 2, 3)),
|
||||
(-41, (_c(_t('Projectile Ammo')) + _t('Titanium Sabot'), 0, 0, 6, 2)),
|
||||
(-42, (_c(_t('Projectile Ammo')) + _t('EMP'), 9, 0, 1, 2)),
|
||||
(-43, (_c(_t('Projectile Ammo')) + _t('Phased Plasma'), 0, 10, 2, 0)),
|
||||
(-44, (_c(_t('Projectile Ammo')) + _t('Fusion'), 0, 0, 2, 10)),
|
||||
(-45, (_c(_t('Projectile Ammo')) + '|' + _t('[T2] Quake'), 0, 0, 5, 9)),
|
||||
(-46, (_c(_t('Projectile Ammo')) + '|' + _t('[T2] Hail'), 0, 0, 3.3, 12.1)),
|
||||
(-47, (_c(_t('Missiles')) + _t('Mjolnir'), 1, 0, 0, 0)),
|
||||
(-48, (_c(_t('Missiles')) + _t('Inferno'), 0, 1, 0, 0)),
|
||||
(-49, (_c(_t('Missiles')) + _t('Scourge'), 0, 0, 1, 0)),
|
||||
(-50, (_c(_t('Missiles')) + _t('Nova'), 0, 0, 0, 1)),
|
||||
(-51, (_c(_t('Bombs')) + _t('Electron Bomb'), 6400, 0, 0, 0)),
|
||||
(-52, (_c(_t('Bombs')) + _t('Scorch Bomb'), 0, 6400, 0, 0)),
|
||||
(-53, (_c(_t('Bombs')) + _t('Concussion Bomb'), 0, 0, 6400, 0)),
|
||||
(-54, (_c(_t('Bombs')) + _t('Shrapnel Bomb'), 0, 0, 0, 6400)),
|
||||
# Different sizes of packs do different damage ratios, the values here
|
||||
# are average of ratios across sizes
|
||||
(-23, (_c(_t('Condenser Packs')) + '|' + _t('[T2] StrikeSnipe'), 51817, 0, 48183, 0)),
|
||||
(-24, (_c(_t('Condenser Packs')) + _t('MesmerFlux'), 76476, 0, 23524, 0)),
|
||||
(-25, (_c(_t('Condenser Packs')) + _t('SlamBolt'), 23376, 0, 76624, 0)),
|
||||
(-26, (_c(_t('Condenser Packs')) + _t('BlastShot'), 19820, 0, 80180, 0)),
|
||||
(-27, (_c(_t('Condenser Packs')) + _t('GalvaSurge'), 80206, 0, 19794, 0)),
|
||||
(-28, (_c(_t('Condenser Packs')) + '|' + _t('[T2] ElectroPunch'), 50547, 0, 49453, 0)),
|
||||
|
||||
(-29, (_c(_t('Hybrid Charges')) + '|' + _t('[T2] Spike'), 0, 4, 4, 0)),
|
||||
(-30, (_c(_t('Hybrid Charges')) + '|' + _t('[T2] Null'), 0, 6, 5, 0)),
|
||||
(-31, (_c(_t('Hybrid Charges')) + _t('Iron'), 0, 2, 3, 0)),
|
||||
(-32, (_c(_t('Hybrid Charges')) + _t('Tungsten'), 0, 2, 4, 0)),
|
||||
(-33, (_c(_t('Hybrid Charges')) + _t('Iridium'), 0, 3, 4, 0)),
|
||||
(-34, (_c(_t('Hybrid Charges')) + _t('Lead'), 0, 3, 5, 0)),
|
||||
(-35, (_c(_t('Hybrid Charges')) + _t('Thorium'), 0, 4, 5, 0)),
|
||||
(-36, (_c(_t('Hybrid Charges')) + _t('Uranium'), 0, 4, 6, 0)),
|
||||
(-37, (_c(_t('Hybrid Charges')) + _t('Plutonium'), 0, 5, 6, 0)),
|
||||
(-38, (_c(_t('Hybrid Charges')) + _t('Antimatter'), 0, 5, 7, 0)),
|
||||
(-39, (_c(_t('Hybrid Charges')) + '|' + _t('[T2] Javelin'), 0, 8, 6, 0)),
|
||||
(-40, (_c(_t('Hybrid Charges')) + '|' + _t('[T2] Void'), 0, 7.7, 7.7, 0)),
|
||||
(-41, (_c(_t('Projectile Ammo')) + '|' + _t('[T2] Tremor'), 0, 0, 3, 5)),
|
||||
(-42, (_c(_t('Projectile Ammo')) + '|' + _t('[T2] Barrage'), 0, 0, 5, 6)),
|
||||
(-43, (_c(_t('Projectile Ammo')) + _t('Carbonized Lead'), 0, 0, 4, 1)),
|
||||
(-44, (_c(_t('Projectile Ammo')) + _t('Nuclear'), 0, 0, 1, 4)),
|
||||
(-45, (_c(_t('Projectile Ammo')) + _t('Proton'), 3, 0, 2, 0)),
|
||||
(-46, (_c(_t('Projectile Ammo')) + _t('Depleted Uranium'), 0, 3, 2, 3)),
|
||||
(-47, (_c(_t('Projectile Ammo')) + _t('Titanium Sabot'), 0, 0, 6, 2)),
|
||||
(-48, (_c(_t('Projectile Ammo')) + _t('EMP'), 9, 0, 1, 2)),
|
||||
(-49, (_c(_t('Projectile Ammo')) + _t('Phased Plasma'), 0, 10, 2, 0)),
|
||||
(-50, (_c(_t('Projectile Ammo')) + _t('Fusion'), 0, 0, 2, 10)),
|
||||
(-51, (_c(_t('Projectile Ammo')) + '|' + _t('[T2] Quake'), 0, 0, 5, 9)),
|
||||
(-52, (_c(_t('Projectile Ammo')) + '|' + _t('[T2] Hail'), 0, 0, 3.3, 12.1)),
|
||||
(-53, (_c(_t('Missiles')) + _t('Mjolnir'), 1, 0, 0, 0)),
|
||||
(-54, (_c(_t('Missiles')) + _t('Inferno'), 0, 1, 0, 0)),
|
||||
(-55, (_c(_t('Missiles')) + _t('Scourge'), 0, 0, 1, 0)),
|
||||
(-56, (_c(_t('Missiles')) + _t('Nova'), 0, 0, 0, 1)),
|
||||
(-57, (_c(_t('Bombs')) + _t('Electron Bomb'), 6400, 0, 0, 0)),
|
||||
(-58, (_c(_t('Bombs')) + _t('Scorch Bomb'), 0, 6400, 0, 0)),
|
||||
(-59, (_c(_t('Bombs')) + _t('Concussion Bomb'), 0, 0, 6400, 0)),
|
||||
(-60, (_c(_t('Bombs')) + _t('Shrapnel Bomb'), 0, 0, 0, 6400)),
|
||||
# Source: ticket #2067 and #2265
|
||||
(-55, (_c(_t('NPC')) + _c(_t('Abyssal')) + _t('All'), 126, 427, 218, 230)),
|
||||
(-109, (_c(_t('NPC')) + _c(_t('Abyssal')) + _t('Angel'), 450, 72, 80, 398)),
|
||||
(-107, (_c(_t('NPC')) + _c(_t('Abyssal')) + _t('Concord'), 53, 559, 94, 295)),
|
||||
(-56, (_c(_t('NPC')) + _c(_t('Abyssal')) + _t('Drifter'), 250, 250, 250, 250)),
|
||||
(-57, (_c(_t('NPC')) + _c(_t('Abyssal')) + _t('Drones'), 250, 250, 250, 250)),
|
||||
(-58, (_c(_t('NPC')) + _c(_t('Abyssal')) + _t('Overmind'), 0, 410, 590, 0)),
|
||||
(-108, (_c(_t('NPC')) + _c(_t('Abyssal')) + _t('Sansha'), 569, 431, 0, 0)),
|
||||
(-59, (_c(_t('NPC')) + _c(_t('Abyssal')) + _t('Seeker'), 402, 402, 98, 98)),
|
||||
(-60, (_c(_t('NPC')) + _c(_t('Abyssal')) + _t('Sleeper'), 313, 313, 187, 187)),
|
||||
(-61, (_c(_t('NPC')) + _c(_t('Abyssal')) + _t('Triglavian'), 0, 615, 0, 385)),
|
||||
(-62, (_c(_t('NPC')) + _c(_t('Asteroid')) + _t('Angel Cartel'), 1838, 562, 2215, 3838)),
|
||||
(-63, (_c(_t('NPC')) + _c(_t('Asteroid')) + _t('Blood Raiders'), 5067, 4214, 0, 0)),
|
||||
(-64, (_c(_t('NPC')) + _c(_t('Asteroid')) + _t('Guristas'), 0, 1828, 7413, 0)),
|
||||
(-65, (_c(_t('NPC')) + _c(_t('Asteroid')) + _t('Rogue Drone'), 394, 666, 1090, 1687)),
|
||||
(-66, (_c(_t('NPC')) + _c(_t('Asteroid')) + _t('Sanshas Nation'), 5586, 4112, 0, 0)),
|
||||
(-67, (_c(_t('NPC')) + _c(_t('Asteroid')) + _t('Serpentis'), 0, 5373, 4813, 0)),
|
||||
(-68, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Cruor (Blood Raiders)'), 90, 90, 0, 0)),
|
||||
(-69, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Dramiel (Angel)'), 55, 0, 20, 96)),
|
||||
(-70, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Daredevil (Serpentis)'), 0, 110, 154, 0)),
|
||||
(-71, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Succubus (Sanshas Nation)'), 135, 30, 0, 0)),
|
||||
(-72, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Worm (Guristas)'), 0, 0, 228, 0)),
|
||||
(-73, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Enyo'), 0, 147, 147, 0)),
|
||||
(-74, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Hawk'), 0, 0, 247, 0)),
|
||||
(-75, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Jaguar'), 36, 0, 50, 182)),
|
||||
(-76, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Vengeance'), 232, 0, 0, 0)),
|
||||
(-77, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Ashimmu (Blood Raiders)'), 260, 100, 0, 0)),
|
||||
(-78, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Talos'), 0, 413, 413, 0)),
|
||||
(-79, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Sentinel'), 0, 75, 0, 90)),
|
||||
(-80, (_c(_t('NPC')) + _c(_t('Deadspace')) + _t('Angel Cartel'), 369, 533, 1395, 3302)),
|
||||
(-81, (_c(_t('NPC')) + _c(_t('Deadspace')) + _t('Blood Raiders'), 6040, 5052, 10, 15)),
|
||||
(-82, (_c(_t('NPC')) + _c(_t('Deadspace')) + _t('Guristas'), 0, 1531, 9680, 0)),
|
||||
(-83, (_c(_t('NPC')) + _c(_t('Deadspace')) + _t('Rogue Drone'), 276, 1071, 1069, 871)),
|
||||
(-84, (_c(_t('NPC')) + _c(_t('Deadspace')) + _t('Sanshas Nation'), 3009, 2237, 0, 0)),
|
||||
(-85, (_c(_t('NPC')) + _c(_t('Deadspace')) + _t('Serpentis'), 0, 3110, 1929, 0)),
|
||||
(-61, (_c(_t('NPC')) + _c(_t('Abyssal')) + _t('All'), 126, 427, 218, 230)),
|
||||
(-62, (_c(_t('NPC')) + _c(_t('Abyssal')) + _t('Angel'), 450, 72, 80, 398)),
|
||||
(-63, (_c(_t('NPC')) + _c(_t('Abyssal')) + _t('Concord'), 53, 559, 94, 295)),
|
||||
(-64, (_c(_t('NPC')) + _c(_t('Abyssal')) + _t('Drifter'), 250, 250, 250, 250)),
|
||||
(-65, (_c(_t('NPC')) + _c(_t('Abyssal')) + _t('Drones'), 250, 250, 250, 250)),
|
||||
(-66, (_c(_t('NPC')) + _c(_t('Abyssal')) + _t('Overmind'), 0, 410, 590, 0)),
|
||||
(-67, (_c(_t('NPC')) + _c(_t('Abyssal')) + _t('Sansha'), 569, 431, 0, 0)),
|
||||
(-68, (_c(_t('NPC')) + _c(_t('Abyssal')) + _t('Seeker'), 402, 402, 98, 98)),
|
||||
(-69, (_c(_t('NPC')) + _c(_t('Abyssal')) + _t('Sleeper'), 313, 313, 187, 187)),
|
||||
(-70, (_c(_t('NPC')) + _c(_t('Abyssal')) + _t('Triglavian'), 0, 615, 0, 385)),
|
||||
(-71, (_c(_t('NPC')) + _c(_t('Asteroid')) + _t('Angel Cartel'), 1838, 562, 2215, 3838)),
|
||||
(-72, (_c(_t('NPC')) + _c(_t('Asteroid')) + _t('Blood Raiders'), 5067, 4214, 0, 0)),
|
||||
(-73, (_c(_t('NPC')) + _c(_t('Asteroid')) + _t('Guristas'), 0, 1828, 7413, 0)),
|
||||
(-74, (_c(_t('NPC')) + _c(_t('Asteroid')) + _t('Rogue Drone'), 394, 666, 1090, 1687)),
|
||||
(-75, (_c(_t('NPC')) + _c(_t('Asteroid')) + _t('Sanshas Nation'), 5586, 4112, 0, 0)),
|
||||
(-76, (_c(_t('NPC')) + _c(_t('Asteroid')) + _t('Serpentis'), 0, 5373, 4813, 0)),
|
||||
(-77, (_c(_t('NPC')) + _c(_t('Burner')) + _c(_t('Team')) + _t('Enyo'), 0, 147, 147, 0)),
|
||||
(-78, (_c(_t('NPC')) + _c(_t('Burner')) + _c(_t('Team')) + _t('Hawk'), 0, 0, 247, 0)),
|
||||
(-79, (_c(_t('NPC')) + _c(_t('Burner')) + _c(_t('Team')) + _t('Jaguar'), 36, 0, 50, 182)),
|
||||
(-80, (_c(_t('NPC')) + _c(_t('Burner')) + _c(_t('Team')) + _t('Vengeance'), 232, 0, 0, 0)),
|
||||
(-81, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Cruor'), 90, 90, 0, 0)),
|
||||
(-82, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Dramiel'), 55, 0, 20, 96)),
|
||||
(-83, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Daredevil'), 0, 110, 154, 0)),
|
||||
(-84, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Succubus'), 135, 30, 0, 0)),
|
||||
(-85, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Worm'), 0, 0, 228, 0)),
|
||||
(-86, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Ashimmu'), 260, 100, 0, 0)),
|
||||
(-87, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Talos'), 0, 413, 413, 0)),
|
||||
(-88, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Sentinel'), 0, 0, 75, 90)),
|
||||
(-89, (_c(_t('NPC')) + _c(_t('Deadspace')) + _t('Angel Cartel'), 369, 533, 1395, 3302)),
|
||||
(-90, (_c(_t('NPC')) + _c(_t('Deadspace')) + _t('Blood Raiders'), 6040, 5052, 10, 15)),
|
||||
(-91, (_c(_t('NPC')) + _c(_t('Deadspace')) + _t('Guristas'), 0, 1531, 9680, 0)),
|
||||
(-92, (_c(_t('NPC')) + _c(_t('Deadspace')) + _t('Rogue Drone'), 276, 1071, 1069, 871)),
|
||||
(-93, (_c(_t('NPC')) + _c(_t('Deadspace')) + _t('Sanshas Nation'), 3009, 2237, 0, 0)),
|
||||
(-94, (_c(_t('NPC')) + _c(_t('Deadspace')) + _t('Serpentis'), 0, 3110, 1929, 0)),
|
||||
# Source: ticket #2067
|
||||
(-86, (_c(_t('NPC')) + _c(_t('Invasion')) + _c(_t('Triglavian Entities')) + _t('Dread'), 0, 417, 0, 583)),
|
||||
(-87, (_c(_t('NPC')) + _c(_t('Invasion')) + _c(_t('Triglavian Entities')) + _t('Normal Subcaps'), 0, 610, 0, 390)),
|
||||
(-95, (_c(_t('NPC')) + _c(_t('Invasion')) + _c(_t('Triglavian Entities')) + _t('Dread'), 0, 417, 0, 583)),
|
||||
(-96, (_c(_t('NPC')) + _c(_t('Invasion')) + _c(_t('Triglavian Entities')) + _t('Normal Subcaps'), 0, 610, 0, 390)),
|
||||
# To avoid errors on msgfmt, we have to mark that '0%' is meaning literally 0% with no-python-format.
|
||||
# See also: https://github.com/vslavik/poedit/issues/645
|
||||
(-88, (_c(_t('NPC')) + _c(_t('Invasion')) + _c(_t('Triglavian Entities')) +
|
||||
(-97, (_c(_t('NPC')) + _c(_t('Invasion')) + _c(_t('Triglavian Entities')) +
|
||||
# xgettext:no-python-format
|
||||
_t('Subcaps w/missiles 0% spool up'), 367, 155, 367, 112)),
|
||||
(-89, (_c(_t('NPC')) + _c(_t('Invasion')) + _c(_t('Triglavian Entities')) +
|
||||
(-98, (_c(_t('NPC')) + _c(_t('Invasion')) + _c(_t('Triglavian Entities')) +
|
||||
# xgettext:no-python-format
|
||||
_t('Subcaps w/missiles 50% spool up'), 291, 243, 291, 175)),
|
||||
(-90, (_c(_t('NPC')) + _c(_t('Invasion')) + _c(_t('Triglavian Entities')) +
|
||||
(-99, (_c(_t('NPC')) + _c(_t('Invasion')) + _c(_t('Triglavian Entities')) +
|
||||
# xgettext:no-python-format
|
||||
_t('Subcaps w/missiles 100% spool up'), 241, 301, 241, 217)),
|
||||
(-91, (_c(_t('NPC')) + _c(_t('Invasion')) + _c(_t('Amarr EDENCOM Entities')) + _t('Dread/Subcaps'), 583, 417, 0, 0)),
|
||||
(-92, (_c(_t('NPC')) + _c(_t('Invasion')) + _c(_t('Caldari EDENCOM Entities')) + _t('Dread'), 1000, 0, 0, 0)),
|
||||
(-93, (_c(_t('NPC')) + _c(_t('Invasion')) + _c(_t('Caldari EDENCOM Entities')) + _t('Subcaps'), 511, 21, 29, 440)),
|
||||
(-94, (_c(_t('NPC')) + _c(_t('Invasion')) + _c(_t('Gallente EDENCOM Entities')) + _t('Dread/Subcaps'), 0, 417, 583, 0)),
|
||||
(-95, (_c(_t('NPC')) + _c(_t('Invasion')) + _c(_t('Minmatar EDENCOM Entities')) + _t('Dread'), 0, 0, 583, 417)),
|
||||
(-96, (_c(_t('NPC')) + _c(_t('Invasion')) + _c(_t('Minmatar EDENCOM Entities')) + _t('Subcaps'), 302, 136, 328, 234)),
|
||||
(-110, (_c(_t('NPC')) + _c(_t('Invasion')) + _t('Drifter Entities'), 250, 250, 250, 250)),
|
||||
(-112, (_c(_t('NPC')) + _c(_t('Invasion')) + _t('Sleeper Entities'), 265, 265, 235, 235)),
|
||||
(-111, (_c(_t('NPC')) + _c(_t('Invasion')) + _t('Rogue Drone Entities'), 250, 250, 250, 250)),
|
||||
(-97, (_c(_t('NPC')) + _c(_t('Mission')) + _t('Amarr Empire'), 4464, 3546, 97, 0)),
|
||||
(-98, (_c(_t('NPC')) + _c(_t('Mission')) + _t('Caldari State'), 0, 2139, 4867, 0)),
|
||||
(-99, (_c(_t('NPC')) + _c(_t('Mission')) + _t('CONCORD'), 336, 134, 212, 412)),
|
||||
(-100, (_c(_t('NPC')) + _c(_t('Mission')) + _t('Gallente Federation'), 9, 3712, 2758, 0)),
|
||||
(-101, (_c(_t('NPC')) + _c(_t('Mission')) + _t('Khanid'), 612, 483, 43, 6)),
|
||||
(-102, (_c(_t('NPC')) + _c(_t('Mission')) + _t('Minmatar Republic'), 1024, 388, 1655, 4285)),
|
||||
(-103, (_c(_t('NPC')) + _c(_t('Mission')) + _t('Mordus Legion'), 25, 262, 625, 0)),
|
||||
(-104, (_c(_t('NPC')) + _c(_t('Mission')) + _t('Thukker'), 0, 52, 10, 79)),
|
||||
(-105, (_c(_t('NPC')) + _t('Sansha Incursion'), 1682, 1347, 3678, 3678)),
|
||||
(-106, (_c(_t('NPC')) + _t('Sleepers'), 1472, 1472, 1384, 1384))])
|
||||
(-100, (_c(_t('NPC')) + _c(_t('Invasion')) + _c(_t('Amarr EDENCOM Entities')) + _t('Dread/Subcaps'), 583, 417, 0, 0)),
|
||||
(-101, (_c(_t('NPC')) + _c(_t('Invasion')) + _c(_t('Caldari EDENCOM Entities')) + _t('Dread'), 1000, 0, 0, 0)),
|
||||
(-102, (_c(_t('NPC')) + _c(_t('Invasion')) + _c(_t('Caldari EDENCOM Entities')) + _t('Subcaps'), 511, 21, 29, 440)),
|
||||
(-103, (_c(_t('NPC')) + _c(_t('Invasion')) + _c(_t('Gallente EDENCOM Entities')) + _t('Dread/Subcaps'), 0, 417, 583, 0)),
|
||||
(-104, (_c(_t('NPC')) + _c(_t('Invasion')) + _c(_t('Minmatar EDENCOM Entities')) + _t('Dread'), 0, 0, 583, 417)),
|
||||
(-105, (_c(_t('NPC')) + _c(_t('Invasion')) + _c(_t('Minmatar EDENCOM Entities')) + _t('Subcaps'), 302, 136, 328, 234)),
|
||||
(-106, (_c(_t('NPC')) + _c(_t('Invasion')) + _t('Drifter Entities'), 250, 250, 250, 250)),
|
||||
(-107, (_c(_t('NPC')) + _c(_t('Invasion')) + _t('Sleeper Entities'), 265, 265, 235, 235)),
|
||||
(-108, (_c(_t('NPC')) + _c(_t('Invasion')) + _t('Rogue Drone Entities'), 250, 250, 250, 250)),
|
||||
(-109, (_c(_t('NPC')) + _c(_t('Mission')) + _t('Amarr Empire'), 4464, 3546, 97, 0)),
|
||||
(-110, (_c(_t('NPC')) + _c(_t('Mission')) + _t('Caldari State'), 0, 2139, 4867, 0)),
|
||||
(-111, (_c(_t('NPC')) + _c(_t('Mission')) + _t('CONCORD'), 336, 134, 212, 412)),
|
||||
(-112, (_c(_t('NPC')) + _c(_t('Mission')) + _t('Gallente Federation'), 9, 3712, 2758, 0)),
|
||||
(-113, (_c(_t('NPC')) + _c(_t('Mission')) + _t('Khanid'), 612, 483, 43, 6)),
|
||||
(-114, (_c(_t('NPC')) + _c(_t('Mission')) + _t('Minmatar Republic'), 1024, 388, 1655, 4285)),
|
||||
(-115, (_c(_t('NPC')) + _c(_t('Mission')) + _t('Mordus Legion'), 25, 262, 625, 0)),
|
||||
(-116, (_c(_t('NPC')) + _c(_t('Mission')) + _t('Thukker'), 0, 52, 10, 79)),
|
||||
(-117, (_c(_t('NPC')) + _t('Sansha Incursion'), 1682, 1347, 3678, 3678)),
|
||||
(-118, (_c(_t('NPC')) + _t('Sleepers'), 1472, 1472, 1384, 1384))])
|
||||
|
||||
|
||||
class DamagePattern:
|
||||
@@ -207,11 +216,11 @@ class DamagePattern:
|
||||
pattern.builtin = True
|
||||
cls._builtins[id] = pattern
|
||||
|
||||
def calculateEhp(self, fit):
|
||||
def calculateEhp(self, item):
|
||||
ehp = {}
|
||||
for (type, attr) in (('shield', 'shieldCapacity'), ('armor', 'armorHP'), ('hull', 'hp')):
|
||||
rawCapacity = fit.ship.getModifiedItemAttr(attr)
|
||||
ehp[type] = self.effectivify(fit, rawCapacity, type)
|
||||
rawCapacity = item.getModifiedItemAttr(attr)
|
||||
ehp[type] = self.effectivify(item, rawCapacity, type)
|
||||
|
||||
return ehp
|
||||
|
||||
@@ -227,10 +236,10 @@ class DamagePattern:
|
||||
ereps = {}
|
||||
for field in tankInfo:
|
||||
if field in typeMap:
|
||||
ereps[field] = self.effectivify(fit, tankInfo[field], typeMap[field])
|
||||
ereps[field] = self.effectivify(fit.ship, tankInfo[field], typeMap[field])
|
||||
return ereps
|
||||
|
||||
def effectivify(self, fit, amount, type):
|
||||
def effectivify(self, item, amount, type):
|
||||
type = type if type != "hull" else ""
|
||||
totalDamage = sum((self.emAmount, self.thermalAmount, self.kineticAmount, self.explosiveAmount))
|
||||
specificDivider = 0
|
||||
@@ -239,7 +248,7 @@ class DamagePattern:
|
||||
attrName = "%s%sDamageResonance" % (type, damageType.capitalize())
|
||||
attrName = attrName[0].lower() + attrName[1:]
|
||||
|
||||
resonance = fit.ship.getModifiedItemAttr(attrName)
|
||||
resonance = item.getModifiedItemAttr(attrName)
|
||||
damage = getattr(self, "%sAmount" % damageType)
|
||||
|
||||
specificDivider += damage / float(totalDamage or 1) * resonance
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
import math
|
||||
|
||||
from copy import deepcopy
|
||||
from logbook import Logger
|
||||
from sqlalchemy.orm import reconstructor, validates
|
||||
|
||||
@@ -82,6 +83,7 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut, Mu
|
||||
self.__baseRRAmount = None
|
||||
self.__miningYield = None
|
||||
self.__miningWaste = None
|
||||
self.__ehp = None
|
||||
self.__itemModifiedAttributes = ModifiedAttributeDict()
|
||||
self.__itemModifiedAttributes.original = self._item.attributes
|
||||
self.__itemModifiedAttributes.overrides = self._item.overrides
|
||||
@@ -160,7 +162,7 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut, Mu
|
||||
|
||||
def getVolleyParameters(self, targetProfile=None):
|
||||
if not self.dealsDamage or self.amountActive <= 0:
|
||||
return {0: DmgTypes(0, 0, 0, 0)}
|
||||
return {0: DmgTypes.default()}
|
||||
if self.__baseVolley is None:
|
||||
dmgGetter = self.getModifiedChargeAttr if self.hasAmmo else self.getModifiedItemAttr
|
||||
dmgMult = self.amountActive * (self.getModifiedItemAttr("damageMultiplier", 1))
|
||||
@@ -169,11 +171,8 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut, Mu
|
||||
thermal=(dmgGetter("thermalDamage", 0)) * dmgMult,
|
||||
kinetic=(dmgGetter("kineticDamage", 0)) * dmgMult,
|
||||
explosive=(dmgGetter("explosiveDamage", 0)) * dmgMult)
|
||||
volley = DmgTypes(
|
||||
em=self.__baseVolley.em * (1 - getattr(targetProfile, "emAmount", 0)),
|
||||
thermal=self.__baseVolley.thermal * (1 - getattr(targetProfile, "thermalAmount", 0)),
|
||||
kinetic=self.__baseVolley.kinetic * (1 - getattr(targetProfile, "kineticAmount", 0)),
|
||||
explosive=self.__baseVolley.explosive * (1 - getattr(targetProfile, "explosiveAmount", 0)))
|
||||
volley = deepcopy(self.__baseVolley)
|
||||
volley.profile = targetProfile
|
||||
return {0: volley}
|
||||
|
||||
def getVolley(self, targetProfile=None):
|
||||
@@ -182,16 +181,12 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut, Mu
|
||||
def getDps(self, targetProfile=None):
|
||||
volley = self.getVolley(targetProfile=targetProfile)
|
||||
if not volley:
|
||||
return DmgTypes(0, 0, 0, 0)
|
||||
return DmgTypes.default()
|
||||
cycleParams = self.getCycleParameters()
|
||||
if cycleParams is None:
|
||||
return DmgTypes(0, 0, 0, 0)
|
||||
return DmgTypes.default()
|
||||
dpsFactor = 1 / (cycleParams.averageTime / 1000)
|
||||
dps = DmgTypes(
|
||||
em=volley.em * dpsFactor,
|
||||
thermal=volley.thermal * dpsFactor,
|
||||
kinetic=volley.kinetic * dpsFactor,
|
||||
explosive=volley.explosive * dpsFactor)
|
||||
dps = volley * dpsFactor
|
||||
return dps
|
||||
|
||||
def isRemoteRepping(self, ignoreState=False):
|
||||
@@ -287,6 +282,29 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut, Mu
|
||||
if delay is not None and speed is not None:
|
||||
return delay / 1000.0 * speed
|
||||
|
||||
@property
|
||||
def hp(self):
|
||||
hp = {}
|
||||
for (type, attr) in (('shield', 'shieldCapacity'), ('armor', 'armorHP'), ('hull', 'hp')):
|
||||
hp[type] = self.getModifiedItemAttr(attr)
|
||||
|
||||
return hp
|
||||
|
||||
@property
|
||||
def ehp(self):
|
||||
if self.__ehp is None:
|
||||
if self.owner is None or self.owner.damagePattern is None:
|
||||
ehp = self.hp
|
||||
else:
|
||||
ehp = self.owner.damagePattern.calculateEhp(self)
|
||||
self.__ehp = ehp
|
||||
return self.__ehp
|
||||
|
||||
def calculateShieldRecharge(self):
|
||||
capacity = self.getModifiedItemAttr("shieldCapacity")
|
||||
rechargeRate = self.getModifiedItemAttr("shieldRechargeRate") / 1000.0
|
||||
return 10 / rechargeRate * math.sqrt(0.25) * (1 - math.sqrt(0.25)) * capacity
|
||||
|
||||
# Had to add this to match the falloff property in modules.py
|
||||
# Fscking ship scanners. If you find any other falloff attributes,
|
||||
# Put them in the attrs tuple.
|
||||
@@ -318,6 +336,7 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut, Mu
|
||||
self.__baseRRAmount = None
|
||||
self.__miningYield = None
|
||||
self.__miningWaste = None
|
||||
self.__ehp = None
|
||||
self.itemModifiedAttributes.clear()
|
||||
self.chargeModifiedAttributes.clear()
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
import math
|
||||
|
||||
from copy import deepcopy
|
||||
from logbook import Logger
|
||||
from sqlalchemy.orm import reconstructor, validates
|
||||
|
||||
@@ -96,6 +97,7 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
self.__charge = None
|
||||
self.__baseVolley = None
|
||||
self.__miningyield = None
|
||||
self.__ehp = None
|
||||
self.__itemModifiedAttributes = ModifiedAttributeDict()
|
||||
self.__chargeModifiedAttributes = ModifiedAttributeDict()
|
||||
|
||||
@@ -197,16 +199,14 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
for ability in self.abilities:
|
||||
# Not passing resists here as we want to calculate and store base volley
|
||||
self.__baseVolley[ability.effectID] = {0: ability.getVolley()}
|
||||
adjustedVolley = {}
|
||||
adjustedVolleys = {}
|
||||
for effectID, effectData in self.__baseVolley.items():
|
||||
adjustedVolley[effectID] = {}
|
||||
for volleyTime, volleyValue in effectData.items():
|
||||
adjustedVolley[effectID][volleyTime] = DmgTypes(
|
||||
em=volleyValue.em * (1 - getattr(targetProfile, "emAmount", 0)),
|
||||
thermal=volleyValue.thermal * (1 - getattr(targetProfile, "thermalAmount", 0)),
|
||||
kinetic=volleyValue.kinetic * (1 - getattr(targetProfile, "kineticAmount", 0)),
|
||||
explosive=volleyValue.explosive * (1 - getattr(targetProfile, "explosiveAmount", 0)))
|
||||
return adjustedVolley
|
||||
adjustedVolleys[effectID] = {}
|
||||
for volleyTime, baseVolley in effectData.items():
|
||||
adjustedVolley = deepcopy(baseVolley)
|
||||
adjustedVolley.profile = targetProfile
|
||||
adjustedVolleys[effectID][volleyTime] = adjustedVolley
|
||||
return adjustedVolleys
|
||||
|
||||
def getVolleyPerEffect(self, targetProfile=None):
|
||||
volleyParams = self.getVolleyParametersPerEffect(targetProfile=targetProfile)
|
||||
@@ -217,28 +217,16 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
|
||||
def getVolley(self, targetProfile=None):
|
||||
volleyParams = self.getVolleyParametersPerEffect(targetProfile=targetProfile)
|
||||
em = 0
|
||||
therm = 0
|
||||
kin = 0
|
||||
exp = 0
|
||||
volley = DmgTypes.default()
|
||||
for volleyData in volleyParams.values():
|
||||
em += volleyData[0].em
|
||||
therm += volleyData[0].thermal
|
||||
kin += volleyData[0].kinetic
|
||||
exp += volleyData[0].explosive
|
||||
return DmgTypes(em, therm, kin, exp)
|
||||
volley += volleyData[0]
|
||||
return volley
|
||||
|
||||
def getDps(self, targetProfile=None):
|
||||
em = 0
|
||||
thermal = 0
|
||||
kinetic = 0
|
||||
explosive = 0
|
||||
for dps in self.getDpsPerEffect(targetProfile=targetProfile).values():
|
||||
em += dps.em
|
||||
thermal += dps.thermal
|
||||
kinetic += dps.kinetic
|
||||
explosive += dps.explosive
|
||||
return DmgTypes(em=em, thermal=thermal, kinetic=kinetic, explosive=explosive)
|
||||
dps = DmgTypes.default()
|
||||
for subdps in self.getDpsPerEffect(targetProfile=targetProfile).values():
|
||||
dps += subdps
|
||||
return dps
|
||||
|
||||
def getDpsPerEffect(self, targetProfile=None):
|
||||
if not self.active or self.amount <= 0:
|
||||
@@ -345,6 +333,29 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
if falloff is not None:
|
||||
return falloff
|
||||
|
||||
@property
|
||||
def hp(self):
|
||||
hp = {}
|
||||
for (type, attr) in (('shield', 'shieldCapacity'), ('armor', 'armorHP'), ('hull', 'hp')):
|
||||
hp[type] = self.getModifiedItemAttr(attr)
|
||||
|
||||
return hp
|
||||
|
||||
@property
|
||||
def ehp(self):
|
||||
if self.__ehp is None:
|
||||
if self.owner is None or self.owner.damagePattern is None:
|
||||
ehp = self.hp
|
||||
else:
|
||||
ehp = self.owner.damagePattern.calculateEhp(self)
|
||||
self.__ehp = ehp
|
||||
return self.__ehp
|
||||
|
||||
def calculateShieldRecharge(self):
|
||||
capacity = self.getModifiedItemAttr("shieldCapacity")
|
||||
rechargeRate = self.getModifiedItemAttr("shieldRechargeRate") / 1000.0
|
||||
return 10 / rechargeRate * math.sqrt(0.25) * (1 - math.sqrt(0.25)) * capacity
|
||||
|
||||
@validates("ID", "itemID", "chargeID", "amount")
|
||||
def validator(self, key, val):
|
||||
map = {
|
||||
@@ -361,6 +372,7 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
def clear(self):
|
||||
self.__baseVolley = None
|
||||
self.__miningyield = None
|
||||
self.__ehp = None
|
||||
self.itemModifiedAttributes.clear()
|
||||
self.chargeModifiedAttributes.clear()
|
||||
[x.clear() for x in self.abilities]
|
||||
|
||||
@@ -116,7 +116,7 @@ class FighterAbility:
|
||||
|
||||
def getVolley(self, targetProfile=None):
|
||||
if not self.dealsDamage or not self.active:
|
||||
return DmgTypes(0, 0, 0, 0)
|
||||
return DmgTypes.default()
|
||||
if self.attrPrefix == "fighterAbilityLaunchBomb":
|
||||
em = self.fighter.getModifiedChargeAttr("emDamage", 0)
|
||||
therm = self.fighter.getModifiedChargeAttr("thermalDamage", 0)
|
||||
@@ -128,24 +128,17 @@ class FighterAbility:
|
||||
kin = self.fighter.getModifiedItemAttr("{}DamageKin".format(self.attrPrefix), 0)
|
||||
exp = self.fighter.getModifiedItemAttr("{}DamageExp".format(self.attrPrefix), 0)
|
||||
dmgMult = self.fighter.amount * self.fighter.getModifiedItemAttr("{}DamageMultiplier".format(self.attrPrefix), 1)
|
||||
volley = DmgTypes(
|
||||
em=em * dmgMult * (1 - getattr(targetProfile, "emAmount", 0)),
|
||||
thermal=therm * dmgMult * (1 - getattr(targetProfile, "thermalAmount", 0)),
|
||||
kinetic=kin * dmgMult * (1 - getattr(targetProfile, "kineticAmount", 0)),
|
||||
explosive=exp * dmgMult * (1 - getattr(targetProfile, "explosiveAmount", 0)))
|
||||
volley = DmgTypes(em=em * dmgMult, thermal=therm * dmgMult, kinetic=kin * dmgMult, explosive=exp * dmgMult)
|
||||
volley.profile = targetProfile
|
||||
return volley
|
||||
|
||||
def getDps(self, targetProfile=None, cycleTimeOverride=None):
|
||||
volley = self.getVolley(targetProfile=targetProfile)
|
||||
if not volley:
|
||||
return DmgTypes(0, 0, 0, 0)
|
||||
return DmgTypes.default()
|
||||
cycleTime = cycleTimeOverride if cycleTimeOverride is not None else self.cycleTime
|
||||
dpsFactor = 1 / (cycleTime / 1000)
|
||||
dps = DmgTypes(
|
||||
em=volley.em * dpsFactor,
|
||||
thermal=volley.thermal * dpsFactor,
|
||||
kinetic=volley.kinetic * dpsFactor,
|
||||
explosive=volley.explosive * dpsFactor)
|
||||
dps = volley * dpsFactor
|
||||
return dps
|
||||
|
||||
def clear(self):
|
||||
|
||||
@@ -159,6 +159,12 @@ class Fit:
|
||||
self.gangBoosts = None
|
||||
self.__ecmProjectedList = []
|
||||
self.commandBonuses = {}
|
||||
# Reps received, as a list of (amount, cycle time in seconds)
|
||||
self._hullRr = []
|
||||
self._armorRr = []
|
||||
self._armorRrPreSpool = []
|
||||
self._armorRrFullSpool = []
|
||||
self._shieldRr = []
|
||||
|
||||
def clearFactorReloadDependentData(self):
|
||||
# Here we clear all data known to rely on cycle parameters
|
||||
@@ -550,6 +556,12 @@ class Fit:
|
||||
if stuff is not None and stuff != self:
|
||||
stuff.clear()
|
||||
|
||||
self._hullRr.clear()
|
||||
self._armorRr.clear()
|
||||
self._armorRrPreSpool.clear()
|
||||
self._armorRrFullSpool.clear()
|
||||
self._shieldRr.clear()
|
||||
|
||||
# If this is the active fit that we are clearing, not a projected fit,
|
||||
# then this will run and clear the projected ships and flag the next
|
||||
# iteration to skip this part to prevent recursion.
|
||||
@@ -621,7 +633,7 @@ class Fit:
|
||||
"duration", value)
|
||||
|
||||
if warfareBuffID == 12: # Shield Burst: Shield Extension: Shield HP
|
||||
self.ship.boostItemAttr("shieldCapacity", value, stackingPenalties=True)
|
||||
self.ship.boostItemAttr("shieldCapacity", value)
|
||||
|
||||
if warfareBuffID == 13: # Armor Burst: Armor Energizing: Armor Resistance
|
||||
for damageType in ("Em", "Thermal", "Explosive", "Kinetic"):
|
||||
@@ -640,7 +652,7 @@ class Fit:
|
||||
"duration", value)
|
||||
|
||||
if warfareBuffID == 15: # Armor Burst: Armor Reinforcement: Armor HP
|
||||
self.ship.boostItemAttr("armorHP", value, stackingPenalties=True)
|
||||
self.ship.boostItemAttr("armorHP", value)
|
||||
|
||||
if warfareBuffID == 16: # Information Burst: Sensor Optimization: Scan Resolution
|
||||
self.ship.boostItemAttr("scanResolution", value, stackingPenalties=True)
|
||||
@@ -734,7 +746,7 @@ class Fit:
|
||||
self.ship.boostItemAttr(attr, value, stackingPenalties=True)
|
||||
|
||||
if warfareBuffID == 42: # Erebus Effect Generator : Armor HP bonus
|
||||
self.ship.boostItemAttr("armorHP", value, stackingPenalties=True)
|
||||
self.ship.boostItemAttr("armorHP", value)
|
||||
|
||||
if warfareBuffID == 43: # Erebus Effect Generator : Explosive resistance bonus
|
||||
for attr in ("armorExplosiveDamageResonance", "shieldExplosiveDamageResonance", "explosiveDamageResonance"):
|
||||
@@ -756,7 +768,7 @@ class Fit:
|
||||
self.ship.boostItemAttr(attr, value, stackingPenalties=True)
|
||||
|
||||
if warfareBuffID == 48: # Leviathan Effect Generator : Shield HP bonus
|
||||
self.ship.boostItemAttr("shieldCapacity", value, stackingPenalties=True)
|
||||
self.ship.boostItemAttr("shieldCapacity", value)
|
||||
|
||||
if warfareBuffID == 49: # Leviathan Effect Generator : EM resistance bonus
|
||||
for attr in ("armorEmDamageResonance", "shieldEmDamageResonance", "emDamageResonance"):
|
||||
@@ -870,6 +882,14 @@ class Fit:
|
||||
if warfareBuffID == 100: # Weather_caustic_toxin_scan_resolution_bonus
|
||||
self.ship.boostItemAttr("scanResolution", value, stackingPenalties=True)
|
||||
|
||||
if warfareBuffID == 2405: # Insurgency Suppression Bonus: Interdiction Range
|
||||
self.modules.filteredItemBoost(
|
||||
lambda mod: mod.item.requiresSkill("Navigation"),
|
||||
"maxRange", value, stackingPenalties=True)
|
||||
self.modules.filteredItemBoost(
|
||||
lambda mod: mod.item.group.name == "Stasis Web",
|
||||
"maxRange", value, stackingPenalties=True)
|
||||
|
||||
del self.commandBonuses[warfareBuffID]
|
||||
|
||||
def __resetDependentCalcs(self):
|
||||
@@ -1469,7 +1489,7 @@ class Fit:
|
||||
if self.damagePattern is None:
|
||||
ehp = self.hp
|
||||
else:
|
||||
ehp = self.damagePattern.calculateEhp(self)
|
||||
ehp = self.damagePattern.calculateEhp(self.ship)
|
||||
self.__ehp = ehp
|
||||
|
||||
return self.__ehp
|
||||
@@ -1478,11 +1498,11 @@ class Fit:
|
||||
def tank(self):
|
||||
reps = {
|
||||
"passiveShield": self.calculateShieldRecharge(),
|
||||
"shieldRepair": self.extraAttributes["shieldRepair"],
|
||||
"armorRepair": self.extraAttributes["armorRepair"],
|
||||
"armorRepairPreSpool": self.extraAttributes["armorRepairPreSpool"],
|
||||
"armorRepairFullSpool": self.extraAttributes["armorRepairFullSpool"],
|
||||
"hullRepair": self.extraAttributes["hullRepair"]
|
||||
"shieldRepair": self.extraAttributes["shieldRepair"] + self._getAppliedShieldRr(),
|
||||
"armorRepair": self.extraAttributes["armorRepair"] + self._getAppliedArmorRr(),
|
||||
"armorRepairPreSpool": self.extraAttributes["armorRepairPreSpool"] + self._getAppliedArmorPreSpoolRr(),
|
||||
"armorRepairFullSpool": self.extraAttributes["armorRepairFullSpool"] + self._getAppliedArmorFullSpoolRr(),
|
||||
"hullRepair": self.extraAttributes["hullRepair"] + self._getAppliedHullRr()
|
||||
}
|
||||
return reps
|
||||
|
||||
@@ -1519,11 +1539,11 @@ class Fit:
|
||||
if self.__sustainableTank is None:
|
||||
sustainable = {
|
||||
"passiveShield": self.calculateShieldRecharge(),
|
||||
"shieldRepair": self.extraAttributes["shieldRepair"],
|
||||
"armorRepair": self.extraAttributes["armorRepair"],
|
||||
"armorRepairPreSpool": self.extraAttributes["armorRepairPreSpool"],
|
||||
"armorRepairFullSpool": self.extraAttributes["armorRepairFullSpool"],
|
||||
"hullRepair": self.extraAttributes["hullRepair"]
|
||||
"shieldRepair": self.extraAttributes["shieldRepair"] + self._getAppliedShieldRr(),
|
||||
"armorRepair": self.extraAttributes["armorRepair"] + self._getAppliedArmorRr(),
|
||||
"armorRepairPreSpool": self.extraAttributes["armorRepairPreSpool"] + self._getAppliedArmorPreSpoolRr(),
|
||||
"armorRepairFullSpool": self.extraAttributes["armorRepairFullSpool"] + self._getAppliedArmorFullSpoolRr(),
|
||||
"hullRepair": self.extraAttributes["hullRepair"] + self._getAppliedHullRr()
|
||||
}
|
||||
if not self.capStable or self.factorReload:
|
||||
# Map a local repairer type to the attribute it uses
|
||||
@@ -1668,27 +1688,33 @@ class Fit:
|
||||
self.__droneWaste = droneWaste
|
||||
|
||||
def calculateWeaponDmgStats(self, spoolOptions):
|
||||
weaponVolley = DmgTypes(0, 0, 0, 0)
|
||||
weaponDps = DmgTypes(0, 0, 0, 0)
|
||||
weaponVolley = DmgTypes.default()
|
||||
weaponDps = DmgTypes.default()
|
||||
|
||||
for mod in self.modules:
|
||||
weaponVolley += mod.getVolley(spoolOptions=spoolOptions, targetProfile=self.targetProfile)
|
||||
weaponDps += mod.getDps(spoolOptions=spoolOptions, targetProfile=self.targetProfile)
|
||||
weaponVolley += mod.getVolley(spoolOptions=spoolOptions)
|
||||
weaponDps += mod.getDps(spoolOptions=spoolOptions)
|
||||
|
||||
weaponVolley.profile = self.targetProfile
|
||||
weaponDps.profile = self.targetProfile
|
||||
|
||||
self.__weaponVolleyMap[spoolOptions] = weaponVolley
|
||||
self.__weaponDpsMap[spoolOptions] = weaponDps
|
||||
|
||||
def calculateDroneDmgStats(self):
|
||||
droneVolley = DmgTypes(0, 0, 0, 0)
|
||||
droneDps = DmgTypes(0, 0, 0, 0)
|
||||
droneVolley = DmgTypes.default()
|
||||
droneDps = DmgTypes.default()
|
||||
|
||||
for drone in self.drones:
|
||||
droneVolley += drone.getVolley(targetProfile=self.targetProfile)
|
||||
droneDps += drone.getDps(targetProfile=self.targetProfile)
|
||||
droneVolley += drone.getVolley()
|
||||
droneDps += drone.getDps()
|
||||
|
||||
for fighter in self.fighters:
|
||||
droneVolley += fighter.getVolley(targetProfile=self.targetProfile)
|
||||
droneDps += fighter.getDps(targetProfile=self.targetProfile)
|
||||
droneVolley += fighter.getVolley()
|
||||
droneDps += fighter.getDps()
|
||||
|
||||
droneVolley.profile = self.targetProfile
|
||||
droneDps.profile = self.targetProfile
|
||||
|
||||
self.__droneDps = droneDps
|
||||
self.__droneVolley = droneVolley
|
||||
@@ -1723,6 +1749,18 @@ class Fit:
|
||||
secstatus = FitSystemSecurity.NULLSEC
|
||||
return secstatus
|
||||
|
||||
def getPilotSecurity(self, low_limit=-10, high_limit=5):
|
||||
secstatus = self.pilotSecurity
|
||||
# Not defined -> use character SS, with 0.0 fallback if it fails
|
||||
if secstatus is None:
|
||||
try:
|
||||
secstatus = self.character.secStatus
|
||||
except (SystemExit, KeyboardInterrupt):
|
||||
raise
|
||||
except:
|
||||
secstatus = 0
|
||||
return max(low_limit, min(high_limit, secstatus))
|
||||
|
||||
def activeModulesIter(self):
|
||||
for mod in self.modules:
|
||||
if mod.state >= FittingModuleState.ACTIVE:
|
||||
@@ -1760,6 +1798,38 @@ class Fit:
|
||||
mults.setdefault(stackingGroup, []).append((1 + strength / 100, None))
|
||||
return calculateMultiplier(mults)
|
||||
|
||||
def _getAppliedHullRr(self):
|
||||
return self.__getAppliedRr(self._hullRr)
|
||||
|
||||
def _getAppliedArmorRr(self):
|
||||
return self.__getAppliedRr(self._armorRr)
|
||||
|
||||
def _getAppliedArmorPreSpoolRr(self):
|
||||
return self.__getAppliedRr(self._armorRrPreSpool)
|
||||
|
||||
def _getAppliedArmorFullSpoolRr(self):
|
||||
return self.__getAppliedRr(self._armorRrFullSpool)
|
||||
|
||||
def _getAppliedShieldRr(self):
|
||||
return self.__getAppliedRr(self._shieldRr)
|
||||
|
||||
@staticmethod
|
||||
def __getAppliedRr(rrList):
|
||||
totalRaw = 0
|
||||
for amount, cycleTime in rrList:
|
||||
# That's right, for considerations of RR diminishing returns cycle time is rounded this way
|
||||
totalRaw += amount / int(cycleTime)
|
||||
RR_ADDITION = 7000
|
||||
RR_MULTIPLIER = 20
|
||||
appliedRr = 0
|
||||
for amount, cycleTime in rrList:
|
||||
rrps = amount / int(cycleTime)
|
||||
modified_rrps = RR_ADDITION + (rrps * RR_MULTIPLIER)
|
||||
rrps_mult = 1 - (((rrps + modified_rrps) / (totalRaw + modified_rrps)) - 1) ** 2
|
||||
appliedRr += rrps_mult * amount / cycleTime
|
||||
return appliedRr
|
||||
|
||||
|
||||
def __deepcopy__(self, memo=None):
|
||||
fitCopy = Fit()
|
||||
# Character and owner are not copied
|
||||
@@ -1772,6 +1842,7 @@ class Fit:
|
||||
fitCopy.targetProfile = self.targetProfile
|
||||
fitCopy.implantLocation = self.implantLocation
|
||||
fitCopy.systemSecurity = self.systemSecurity
|
||||
fitCopy.pilotSecurity = self.pilotSecurity
|
||||
fitCopy.notes = self.notes
|
||||
|
||||
for i in self.modules:
|
||||
|
||||
@@ -33,7 +33,7 @@ from eos.utils.cycles import CycleInfo, CycleSequence
|
||||
from eos.utils.default import DEFAULT
|
||||
from eos.utils.float import floatUnerr
|
||||
from eos.utils.spoolSupport import calculateSpoolup, resolveSpoolOptions
|
||||
from eos.utils.stats import DmgTypes, RRTypes
|
||||
from eos.utils.stats import BreacherInfo, DmgTypes, RRTypes
|
||||
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
@@ -122,6 +122,8 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut, M
|
||||
if self.__charge and self.__charge.category.name != "Charge":
|
||||
self.__charge = None
|
||||
|
||||
self.rahPatternOverride = None
|
||||
|
||||
self.__baseVolley = None
|
||||
self.__baseRRAmount = None
|
||||
self.__miningYield = None
|
||||
@@ -451,6 +453,10 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut, M
|
||||
return True
|
||||
return False
|
||||
|
||||
@property
|
||||
def isBreacher(self):
|
||||
return self.charge and 'dotMissileLaunching' in self.charge.effects
|
||||
|
||||
def canDealDamage(self, ignoreState=False):
|
||||
if self.isEmpty:
|
||||
return False
|
||||
@@ -467,75 +473,77 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut, M
|
||||
|
||||
def getVolleyParameters(self, spoolOptions=None, targetProfile=None, ignoreState=False):
|
||||
if self.isEmpty or (self.state < FittingModuleState.ACTIVE and not ignoreState):
|
||||
return {0: DmgTypes(0, 0, 0, 0)}
|
||||
return {0: DmgTypes.default()}
|
||||
if self.__baseVolley is None:
|
||||
self.__baseVolley = {}
|
||||
dmgGetter = self.getModifiedChargeAttr if self.charge else self.getModifiedItemAttr
|
||||
dmgMult = self.getModifiedItemAttr("damageMultiplier", 1)
|
||||
# Some delay attributes have non-0 default value, so we have to pick according to effects
|
||||
if {'superWeaponAmarr', 'superWeaponCaldari', 'superWeaponGallente', 'superWeaponMinmatar', 'lightningWeapon'}.intersection(self.item.effects):
|
||||
dmgDelay = self.getModifiedItemAttr("damageDelayDuration", 0)
|
||||
elif {'doomsdayBeamDOT', 'doomsdaySlash', 'doomsdayConeDOT'}.intersection(self.item.effects):
|
||||
dmgDelay = self.getModifiedItemAttr("doomsdayWarningDuration", 0)
|
||||
if self.isBreacher:
|
||||
dmgDelay = 1
|
||||
subcycles = math.floor(self.getModifiedChargeAttr("dotDuration", 0) / 1000)
|
||||
breacher_info = BreacherInfo(
|
||||
absolute=self.getModifiedChargeAttr("dotMaxDamagePerTick", 0),
|
||||
relative=self.getModifiedChargeAttr("dotMaxHPPercentagePerTick", 0) / 100)
|
||||
for i in range(subcycles):
|
||||
volley = DmgTypes.default()
|
||||
volley.add_breacher(dmgDelay + i, breacher_info)
|
||||
self.__baseVolley[dmgDelay + i] = volley
|
||||
else:
|
||||
dmgDelay = 0
|
||||
dmgDuration = self.getModifiedItemAttr("doomsdayDamageDuration", 0)
|
||||
dmgSubcycle = self.getModifiedItemAttr("doomsdayDamageCycleTime", 0)
|
||||
# Reaper DD can damage each target only once
|
||||
if dmgDuration != 0 and dmgSubcycle != 0 and 'doomsdaySlash' not in self.item.effects:
|
||||
subcycles = math.floor(floatUnerr(dmgDuration / dmgSubcycle))
|
||||
else:
|
||||
subcycles = 1
|
||||
for i in range(subcycles):
|
||||
self.__baseVolley[dmgDelay + dmgSubcycle * i] = DmgTypes(
|
||||
em=(dmgGetter("emDamage", 0)) * dmgMult,
|
||||
thermal=(dmgGetter("thermalDamage", 0)) * dmgMult,
|
||||
kinetic=(dmgGetter("kineticDamage", 0)) * dmgMult,
|
||||
explosive=(dmgGetter("explosiveDamage", 0)) * dmgMult)
|
||||
dmgGetter = self.getModifiedChargeAttr if self.charge else self.getModifiedItemAttr
|
||||
dmgMult = self.getModifiedItemAttr("damageMultiplier", 1)
|
||||
# Some delay attributes have non-0 default value, so we have to pick according to effects
|
||||
if {'superWeaponAmarr', 'superWeaponCaldari', 'superWeaponGallente', 'superWeaponMinmatar', 'lightningWeapon'}.intersection(self.item.effects):
|
||||
dmgDelay = self.getModifiedItemAttr("damageDelayDuration", 0)
|
||||
elif {'doomsdayBeamDOT', 'doomsdaySlash', 'doomsdayConeDOT', 'debuffLance'}.intersection(self.item.effects):
|
||||
dmgDelay = self.getModifiedItemAttr("doomsdayWarningDuration", 0)
|
||||
else:
|
||||
dmgDelay = 0
|
||||
dmgDuration = self.getModifiedItemAttr("doomsdayDamageDuration", 0)
|
||||
dmgSubcycle = self.getModifiedItemAttr("doomsdayDamageCycleTime", 0)
|
||||
# Reaper DD can damage each target only once
|
||||
if dmgDuration != 0 and dmgSubcycle != 0 and 'doomsdaySlash' not in self.item.effects:
|
||||
subcycles = math.floor(floatUnerr(dmgDuration / dmgSubcycle))
|
||||
else:
|
||||
subcycles = 1
|
||||
for i in range(subcycles):
|
||||
self.__baseVolley[dmgDelay + dmgSubcycle * i] = DmgTypes(
|
||||
em=(dmgGetter("emDamage", 0)) * dmgMult,
|
||||
thermal=(dmgGetter("thermalDamage", 0)) * dmgMult,
|
||||
kinetic=(dmgGetter("kineticDamage", 0)) * dmgMult,
|
||||
explosive=(dmgGetter("explosiveDamage", 0)) * dmgMult)
|
||||
spoolType, spoolAmount = resolveSpoolOptions(spoolOptions, self)
|
||||
spoolBoost = calculateSpoolup(
|
||||
self.getModifiedItemAttr("damageMultiplierBonusMax", 0),
|
||||
self.getModifiedItemAttr("damageMultiplierBonusPerCycle", 0),
|
||||
self.rawCycleTime / 1000, spoolType, spoolAmount)[0]
|
||||
spoolMultiplier = 1 + spoolBoost
|
||||
adjustedVolley = {}
|
||||
for volleyTime, volleyValue in self.__baseVolley.items():
|
||||
adjustedVolley[volleyTime] = DmgTypes(
|
||||
em=volleyValue.em * spoolMultiplier * (1 - getattr(targetProfile, "emAmount", 0)),
|
||||
thermal=volleyValue.thermal * spoolMultiplier * (1 - getattr(targetProfile, "thermalAmount", 0)),
|
||||
kinetic=volleyValue.kinetic * spoolMultiplier * (1 - getattr(targetProfile, "kineticAmount", 0)),
|
||||
explosive=volleyValue.explosive * spoolMultiplier * (1 - getattr(targetProfile, "explosiveAmount", 0)))
|
||||
return adjustedVolley
|
||||
adjustedVolleys = {}
|
||||
for volleyTime, baseVolley in self.__baseVolley.items():
|
||||
adjustedVolley = baseVolley * spoolMultiplier
|
||||
adjustedVolley.profile = targetProfile
|
||||
adjustedVolleys[volleyTime] = adjustedVolley
|
||||
return adjustedVolleys
|
||||
|
||||
def getVolley(self, spoolOptions=None, targetProfile=None, ignoreState=False):
|
||||
volleyParams = self.getVolleyParameters(spoolOptions=spoolOptions, targetProfile=targetProfile, ignoreState=ignoreState)
|
||||
if len(volleyParams) == 0:
|
||||
return DmgTypes(0, 0, 0, 0)
|
||||
return DmgTypes.default()
|
||||
return volleyParams[min(volleyParams)]
|
||||
|
||||
def getDps(self, spoolOptions=None, targetProfile=None, ignoreState=False, getSpreadDPS=False):
|
||||
dmgDuringCycle = DmgTypes(0, 0, 0, 0)
|
||||
def getDps(self, spoolOptions=None, targetProfile=None, ignoreState=False):
|
||||
dps = DmgTypes.default()
|
||||
cycleParams = self.getCycleParameters()
|
||||
if cycleParams is None:
|
||||
return dmgDuringCycle
|
||||
return dps
|
||||
volleyParams = self.getVolleyParameters(spoolOptions=spoolOptions, targetProfile=targetProfile, ignoreState=ignoreState)
|
||||
avgCycleTime = cycleParams.averageTime
|
||||
if len(volleyParams) == 0 or avgCycleTime == 0:
|
||||
return dmgDuringCycle
|
||||
for volleyValue in volleyParams.values():
|
||||
dmgDuringCycle += volleyValue
|
||||
dpsFactor = 1 / (avgCycleTime / 1000)
|
||||
dps = DmgTypes(
|
||||
em=dmgDuringCycle.em * dpsFactor,
|
||||
thermal=dmgDuringCycle.thermal * dpsFactor,
|
||||
kinetic=dmgDuringCycle.kinetic * dpsFactor,
|
||||
explosive=dmgDuringCycle.explosive * dpsFactor)
|
||||
if not getSpreadDPS:
|
||||
return dps
|
||||
return {'em':dmgDuringCycle.em * dpsFactor,
|
||||
'therm': dmgDuringCycle.thermal * dpsFactor,
|
||||
'kin': dmgDuringCycle.kinetic * dpsFactor,
|
||||
'exp': dmgDuringCycle.explosive * dpsFactor}
|
||||
if self.isBreacher:
|
||||
return volleyParams[min(volleyParams)]
|
||||
for volleyValue in volleyParams.values():
|
||||
dps += volleyValue
|
||||
dpsFactor = 1 / (avgCycleTime / 1000)
|
||||
dps *= dpsFactor
|
||||
return dps
|
||||
|
||||
def isRemoteRepping(self, ignoreState=False):
|
||||
repParams = self.getRepAmountParameters(ignoreState=ignoreState)
|
||||
@@ -694,16 +702,22 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut, M
|
||||
return False
|
||||
|
||||
# Check max group fitted
|
||||
max = self.getModifiedItemAttr("maxGroupFitted")
|
||||
if max:
|
||||
current = 0 # if self.owner != fit else -1 # Disabled, see #1278
|
||||
for mod in fit.modules:
|
||||
if (mod.item and mod.item.groupID == self.item.groupID and
|
||||
self.getModPosition(fit) != mod.getModPosition(fit)):
|
||||
current += 1
|
||||
# use raw value, since it seems what EVE uses. Example is FAXes with their capacitor boosters,
|
||||
# which have unmodified value of 10, and modified of 1, and you can actually fit multiples
|
||||
try:
|
||||
max = self.item.attributes.get('maxGroupFitted').value
|
||||
except AttributeError:
|
||||
pass
|
||||
else:
|
||||
if max:
|
||||
current = 0 # if self.owner != fit else -1 # Disabled, see #1278
|
||||
for mod in fit.modules:
|
||||
if (mod.item and mod.item.groupID == self.item.groupID and
|
||||
self.getModPosition(fit) != mod.getModPosition(fit)):
|
||||
current += 1
|
||||
|
||||
if current >= max:
|
||||
return False
|
||||
if current >= max:
|
||||
return False
|
||||
|
||||
# Check this only if we're told to do so
|
||||
if hardpointLimit:
|
||||
@@ -941,6 +955,13 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut, M
|
||||
and ((gang and effect.isType("gang")) or not gang):
|
||||
effect.handler(fit, self, context, projectionRange, effect=effect)
|
||||
|
||||
def getCycleParametersForDps(self, reloadOverride=None):
|
||||
# Special hack for breachers, since those are DoT and work independently of gun cycle
|
||||
if self.isBreacher:
|
||||
return CycleInfo(activeTime=1000, inactiveTime=0, quantity=math.inf, isInactivityReload=False)
|
||||
else:
|
||||
return self.getCycleParameters(reloadOverride=reloadOverride)
|
||||
|
||||
def getCycleParameters(self, reloadOverride=None):
|
||||
"""Copied from new eos as well"""
|
||||
# Determine if we'll take into account reload time or not
|
||||
@@ -1075,6 +1096,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut, M
|
||||
copy.spoolType = self.spoolType
|
||||
copy.spoolAmount = self.spoolAmount
|
||||
copy.projectionRange = self.projectionRange
|
||||
copy.rahPatternOverride = self.rahPatternOverride
|
||||
self._mutaApplyMutators(mutatorClass=MutatorModule, targetInstance=copy)
|
||||
|
||||
return copy
|
||||
@@ -1085,6 +1107,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut, M
|
||||
spoolType = self.spoolType
|
||||
spoolAmount = self.spoolAmount
|
||||
projectionRange = self.projectionRange
|
||||
rahPatternOverride = self.rahPatternOverride
|
||||
|
||||
Module.__init__(self, item, self.baseItem, self.mutaplasmid)
|
||||
self.state = state
|
||||
@@ -1093,6 +1116,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut, M
|
||||
self.spoolType = spoolType
|
||||
self.spoolAmount = spoolAmount
|
||||
self.projectionRange = projectionRange
|
||||
self.rahPatternOverride = rahPatternOverride
|
||||
self._mutaApplyMutators(mutatorClass=MutatorModule)
|
||||
|
||||
def __repr__(self):
|
||||
|
||||
@@ -25,10 +25,11 @@ import time
|
||||
|
||||
|
||||
class SsoCharacter:
|
||||
def __init__(self, charID, name, client, accessToken=None, refreshToken=None):
|
||||
def __init__(self, charID, name, client, server, accessToken=None, refreshToken=None):
|
||||
self.characterID = charID
|
||||
self.characterName = name
|
||||
self.client = client
|
||||
self.server = server
|
||||
self.accessToken = accessToken
|
||||
self.refreshToken = refreshToken
|
||||
self.accessTokenExpires = None
|
||||
@@ -37,6 +38,9 @@ class SsoCharacter:
|
||||
def init(self):
|
||||
pass
|
||||
|
||||
@property
|
||||
def characterDisplay(self):
|
||||
return "{} [{}]".format(self.characterName, self.server)
|
||||
def is_token_expired(self):
|
||||
if self.accessTokenExpires is None:
|
||||
return True
|
||||
|
||||
@@ -78,158 +78,166 @@ BUILTINS = OrderedDict([
|
||||
(-37, (_c(_t('NPC')) + _c(_t('Mission')) + _t('Mordus Legion'), 0.32, 0.48, 0.4, 0.62)),
|
||||
(-38, (_c(_t('NPC')) + _c(_t('Other')) + _t('Sleeper'), 0.61, 0.61, 0.61, 0.61)),
|
||||
(-39, (_c(_t('NPC')) + _c(_t('Other')) + _t('Sansha Incursion'), 0.65, 0.63, 0.64, 0.65)),
|
||||
(-40, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Cruor (Blood Raiders)'), 0.8, 0.73, 0.69, 0.67)),
|
||||
(-41, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Dramiel (Angel)'), 0.35, 0.48, 0.61, 0.68)),
|
||||
(-42, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Daredevil (Serpentis)'), 0.69, 0.59, 0.59, 0.43)),
|
||||
(-43, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Succubus (Sanshas Nation)'), 0.35, 0.48, 0.61, 0.68)),
|
||||
(-44, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Worm (Guristas)'), 0.48, 0.58, 0.69, 0.74)),
|
||||
(-45, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Enyo'), 0.58, 0.72, 0.86, 0.24)),
|
||||
(-46, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Hawk'), 0.3, 0.86, 0.79, 0.65)),
|
||||
(-47, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Jaguar'), 0.78, 0.65, 0.48, 0.56)),
|
||||
(-48, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Vengeance'), 0.66, 0.56, 0.75, 0.86)),
|
||||
(-49, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Ashimmu (Blood Raiders)'), 0.8, 0.76, 0.68, 0.7)),
|
||||
(-50, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Talos'), 0.68, 0.59, 0.59, 0.43)),
|
||||
(-51, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Sentinel'), 0.58, 0.45, 0.52, 0.66)),
|
||||
# Anomic Team, source: client data dump
|
||||
(-40, (_c(_t('NPC')) + _c(_t('Burner')) + _c(_t('Team')) + _t('Enyo'), 0.575, 0.724, 0.862, 0.235, 1020, 37, 39)),
|
||||
(-41, (_c(_t('NPC')) + _c(_t('Burner')) + _c(_t('Team')) + _t('Navitas'), 0.681, 0.586, 0.522, 0.49, 870, 30, 35)),
|
||||
(-42, (_c(_t('NPC')) + _c(_t('Burner')) + _c(_t('Team')) + _t('Hawk'), 0.3, 0.86, 0.79, 0.65, 1122, 48, 39)),
|
||||
(-43, (_c(_t('NPC')) + _c(_t('Burner')) + _c(_t('Team')) + _t('Bantam'), 0.344, 0.475, 0.606, 0.672, 1016, 45, 27)),
|
||||
(-44, (_c(_t('NPC')) + _c(_t('Burner')) + _c(_t('Team')) + _t('Jaguar'), 0.781, 0.65, 0.475, 0.563, 1400, 42, 31)),
|
||||
(-45, (_c(_t('NPC')) + _c(_t('Burner')) + _c(_t('Team')) + _t('Burst'), 0.344, 0.475, 0.606, 0.672, 1174, 39, 31)),
|
||||
(-46, (_c(_t('NPC')) + _c(_t('Burner')) + _c(_t('Team')) + _t('Vengeance'), 0.66, 0.558, 0.745, 0.864, 1050, 37, 40)),
|
||||
(-47, (_c(_t('NPC')) + _c(_t('Burner')) + _c(_t('Team')) + _t('Inquisitor'), 0.681, 0.586, 0.522, 0.49, 920, 29, 20.5)),
|
||||
# Anomic Agent & Base, source: client data dump
|
||||
(-48, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Cruor'), 0.795, 0.734, 0.693, 0.672, 900, 18, 20.5)),
|
||||
(-49, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Dramiel'), 0.351, 0.481, 0.611, 0.676, 2100, 11, 25)),
|
||||
(-50, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Daredevil'), 0.685, 0.59, 0.59, 0.433, 1200, 18, 25)),
|
||||
(-51, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Succubus'), 0.351, 0.481, 0.611, 0.676, 4750, 30, 59)),
|
||||
(-52, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Worm'), 0.475, 0.58, 0.685, 0.738, 360, 70, 39)),
|
||||
(-53, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Ashimmu'), 0.8, 0.76, 0.68, 0.7, 500, 120, 137)),
|
||||
(-54, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Sentinel'), 0.575, 0.448, 0.522, 0.66, 500, 50, 39)),
|
||||
(-55, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Talos'), 0.681, 0.586, 0.586, 0.426, 150, 125, 266)),
|
||||
(-56, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Dragonfly'), 0.35, 0.72, 0.70, 0.55, 1200, 15, 35)),
|
||||
(-57, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Mantis'), 0.60, 0.52, 0.71, 0.71, 900, 25, 35)),
|
||||
# Source: ticket #2067
|
||||
(-52, (_c(_t('NPC')) + _c(_t('Invasion')) + _t('Triglavian Entities'), 0.422, 0.367, 0.453, 0.411)),
|
||||
(-53, (_c(_t('NPC')) + _c(_t('Invasion')) + _t('Amarr EDENCOM Entities'), 0.360, 0.310, 0.441, 0.602)),
|
||||
(-54, (_c(_t('NPC')) + _c(_t('Invasion')) + _t('Caldari EDENCOM Entities'), 0.303, 0.610, 0.487, 0.401)),
|
||||
(-55, (_c(_t('NPC')) + _c(_t('Invasion')) + _t('Gallente EDENCOM Entities'), 0.383, 0.414, 0.578, 0.513)),
|
||||
(-56, (_c(_t('NPC')) + _c(_t('Invasion')) + _t('Minmatar EDENCOM Entities'), 0.620, 0.422, 0.355, 0.399)),
|
||||
(-57, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Dark Matter All Tiers')) + _t('Drones'), 0.439, 0.522, 0.529, 0.435)),
|
||||
(-58, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Dark Matter All Tiers')) + _t('Overmind'), 0.643, 0.593, 0.624, 0.639)),
|
||||
(-59, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Dark Matter All Tiers')) + _t('Seeker'), 0.082, 0.082, 0.082, 0.082)),
|
||||
(-60, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Dark Matter All Tiers')) + _t('Triglavian'), 0.494, 0.41, 0.464, 0.376)),
|
||||
(-61, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Dark Matter All Tiers')) + _t('Drifter'), 0.415, 0.415, 0.415, 0.415)),
|
||||
(-62, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Dark Matter All Tiers')) + _t('Sleeper'), 0.435, 0.435, 0.435, 0.435)),
|
||||
(-63, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Dark Matter All Tiers')) + _t('All'), 0.508, 0.474, 0.495, 0.488)),
|
||||
(-64, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T0/T1/T2')) + _t('Drones'), 0.323, 0.522, 0.529, 0.435)),
|
||||
(-65, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T0/T1/T2')) + _t('Overmind'), 0.542, 0.593, 0.624, 0.639)),
|
||||
(-66, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T0/T1/T2')) + _t('Seeker'), 0, 0.082, 0.082, 0.082)),
|
||||
(-67, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T0/T1/T2')) + _t('Triglavian'), 0.356, 0.41, 0.464, 0.376)),
|
||||
(-68, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T0/T1/T2')) + _t('Drifter'), 0.277, 0.415, 0.415, 0.415)),
|
||||
(-69, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T0/T1/T2')) + _t('Sleeper'), 0.329, 0.435, 0.435, 0.435)),
|
||||
(-70, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T0/T1/T2')) + _t('All'), 0.381, 0.474, 0.495, 0.488)),
|
||||
(-71, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T3 (Some T5 Rooms)')) + _t('Drones'), 0.255, 0.522, 0.529, 0.435)),
|
||||
(-72, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T3 (Some T5 Rooms)')) + _t('Overmind'), 0.48, 0.593, 0.624, 0.639)),
|
||||
(-73, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T3 (Some T5 Rooms)')) + _t('Seeker'), 0, 0.082, 0.082, 0.0822)),
|
||||
(-74, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T3 (Some T5 Rooms)')) + _t('Triglavian'), 0.268, 0.41, 0.464, 0.376)),
|
||||
(-75, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T3 (Some T5 Rooms)')) + _t('Drifter'), 0.191, 0.415, 0.415, 0.415)),
|
||||
(-76, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T3 (Some T5 Rooms)')) + _t('Sleeper'), 0.268, 0.435, 0.435, 0.435)),
|
||||
(-77, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T3 (Some T5 Rooms)')) + _t('All'), 0.308, 0.474, 0.495, 0.488)),
|
||||
(-78, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T4/T5/T6')) + _t('Drones'), 0.193, 0.522, 0.529, 0.435)),
|
||||
(-79, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T4/T5/T6')) + _t('Overmind'), 0.423, 0.593, 0.624, 0.639)),
|
||||
(-80, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T4/T5/T6')) + _t('Seeker'), 0, 0.082, 0.082, 0.082)),
|
||||
(-81, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T4/T5/T6')) + _t('Triglavian'), 0.206, 0.41, 0.464, 0.376)),
|
||||
(-82, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T4/T5/T6')) + _t('Drifter'), 0.111, 0.415, 0.415, 0.415)),
|
||||
(-83, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T4/T5/T6')) + _t('Sleeper'), 0.215, 0.435, 0.435, 0.435)),
|
||||
(-84, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T4/T5/T6')) + _t('All'), 0.247, 0.474, 0.495, 0.488)),
|
||||
(-85, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T0/T1/T2')) + _t('Drones'), 0.461, 0.425, 0.541, 0.443)),
|
||||
(-86, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T0/T1/T2')) + _t('Overmind'), 0.666, 0.489, 0.634, 0.646)),
|
||||
(-87, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T0/T1/T2')) + _t('Seeker'), 0.084, 0, 0.084, 0.084)),
|
||||
(-88, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T0/T1/T2')) + _t('Triglavian'), 0.537, 0.269, 0.489, 0.371)),
|
||||
(-89, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T0/T1/T2')) + _t('Drifter'), 0.43, 0.289, 0.43, 0.43)),
|
||||
(-90, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T0/T1/T2')) + _t('Sleeper'), 0.512, 0.402, 0.512, 0.512)),
|
||||
(-91, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T0/T1/T2')) + _t('All'), 0.537, 0.352, 0.512, 0.495)),
|
||||
(-92, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T3 (Some T5 Rooms)')) + _t('Drones'), 0.461, 0.36, 0.541, 0.443)),
|
||||
(-93, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T3 (Some T5 Rooms)')) + _t('Overmind'), 0.666, 0.413, 0.634, 0.646)),
|
||||
(-94, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T3 (Some T5 Rooms)')) + _t('Seeker'), 0.084, 0, 0.084, 0.084)),
|
||||
(-95, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T3 (Some T5 Rooms)')) + _t('Triglavian'), 0.537, 0.166, 0.489, 0.371)),
|
||||
(-96, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T3 (Some T5 Rooms)')) + _t('Drifter'), 0.43, 0.201, 0.43, 0.43)),
|
||||
(-97, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T3 (Some T5 Rooms)')) + _t('Sleeper'), 0.512, 0.337, 0.512, 0.512)),
|
||||
(-98, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T3 (Some T5 Rooms)')) + _t('All'), 0.537, 0.269, 0.512, 0.495)),
|
||||
(-99, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T4/T5/T6')) + _t('Drones'), 0.461, 0.305, 0.541, 0.443)),
|
||||
(-100, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T4/T5/T6')) + _t('Overmind'), 0.666, 0.345, 0.634, 0.646)),
|
||||
(-101, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T4/T5/T6')) + _t('Seeker'), 0.084, 0, 0.084, 0.084)),
|
||||
(-102, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T4/T5/T6')) + _t('Triglavian'), 0.537, 0.085, 0.489, 0.371)),
|
||||
(-103, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T4/T5/T6')) + _t('Drifter'), 0.43, 0.117, 0.43, 0.43)),
|
||||
(-104, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T4/T5/T6')) + _t('Sleeper'), 0.512, 0.276, 0.512, 0.512)),
|
||||
(-105, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T4/T5/T6')) + _t('All'), 0.537, 0.201, 0.512, 0.495)),
|
||||
(-106, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T0/T1/T2')) + _t('Drones'), 0.439, 0.522, 0.417, 0.435)),
|
||||
(-107, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T0/T1/T2')) + _t('Overmind'), 0.643, 0.593, 0.511, 0.639)),
|
||||
(-108, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T0/T1/T2')) + _t('Seeker'), 0.082, 0.082, 0, 0.082)),
|
||||
(-109, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T0/T1/T2')) + _t('Triglavian'), 0.494, 0.41, 0.304, 0.376)),
|
||||
(-110, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T0/T1/T2')) + _t('Drifter'), 0.415, 0.415, 0.277, 0.415)),
|
||||
(-111, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T0/T1/T2')) + _t('Sleeper'), 0.435, 0.435, 0.329, 0.435)),
|
||||
(-112, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T0/T1/T2')) + _t('All'), 0.508, 0.474, 0.359, 0.488)),
|
||||
(-113, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T3 (Some T5 Rooms)')) + _t('Drones'), 0.439, 0.522, 0.351, 0.435)),
|
||||
(-114, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T3 (Some T5 Rooms)')) + _t('Overmind'), 0.643, 0.593, 0.435, 0.639)),
|
||||
(-115, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T3 (Some T5 Rooms)')) + _t('Seeker'), 0.082, 0.082, 0, 0.082)),
|
||||
(-116, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T3 (Some T5 Rooms)')) + _t('Triglavian'), 0.494, 0.41, 0.198, 0.376)),
|
||||
(-117, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T3 (Some T5 Rooms)')) + _t('Drifter'), 0.415, 0.415, 0.191, 0.415)),
|
||||
(-118, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T3 (Some T5 Rooms)')) + _t('Sleeper'), 0.435, 0.435, 0.268, 0.435)),
|
||||
(-119, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T3 (Some T5 Rooms)')) + _t('All'), 0.508, 0.474, 0.276, 0.488)),
|
||||
(-120, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T4/T5/T6')) + _t('Drones'), 0.439, 0.522, 0.293, 0.435)),
|
||||
(-121, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T4/T5/T6')) + _t('Overmind'), 0.643, 0.593, 0.362, 0.639)),
|
||||
(-122, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T4/T5/T6')) + _t('Seeker'), 0.082, 0.082, 0, 0.082)),
|
||||
(-123, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T4/T5/T6')) + _t('Triglavian'), 0.494, 0.41, 0.122, 0.376)),
|
||||
(-124, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T4/T5/T6')) + _t('Drifter'), 0.415, 0.415, 0.111, 0.415)),
|
||||
(-125, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T4/T5/T6')) + _t('Sleeper'), 0.435, 0.435, 0.215, 0.435)),
|
||||
(-126, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T4/T5/T6')) + _t('All'), 0.508, 0.474, 0.208, 0.488)),
|
||||
(-127, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T0/T1/T2')) + _t('Drones'), 0.449, 0.54, 0.549, 0.336)),
|
||||
(-128, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T0/T1/T2')) + _t('Overmind'), 0.619, 0.574, 0.612, 0.522)),
|
||||
(-129, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T0/T1/T2')) + _t('Seeker'), 0.085, 0.085, 0.085, 0)),
|
||||
(-130, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T0/T1/T2')) + _t('Triglavian'), 0.477, 0.4, 0.461, 0.202)),
|
||||
(-131, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T0/T1/T2')) + _t('Drifter'), 0.437, 0.437, 0.437, 0.295)),
|
||||
(-132, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T0/T1/T2')) + _t('Sleeper'), 0.435, 0.435, 0.435, 0.329)),
|
||||
(-133, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T0/T1/T2')) + _t('All'), 0.493, 0.468, 0.492, 0.35)),
|
||||
(-134, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T3 (Some T5 Rooms)')) + _t('Drones'), 0.449, 0.54, 0.549, 0.264)),
|
||||
(-135, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T3 (Some T5 Rooms)')) + _t('Overmind'), 0.619, 0.574, 0.612, 0.449)),
|
||||
(-136, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T3 (Some T5 Rooms)')) + _t('Seeker'), 0.085, 0.085, 0.085, 0)),
|
||||
(-137, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T3 (Some T5 Rooms)')) + _t('Triglavian'), 0.477, 0.4, 0.461, 0.081)),
|
||||
(-138, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T3 (Some T5 Rooms)')) + _t('Drifter'), 0.437, 0.437, 0.437, 0.206)),
|
||||
(-139, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T3 (Some T5 Rooms)')) + _t('Sleeper'), 0.435, 0.435, 0.435, 0.268)),
|
||||
(-140, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T3 (Some T5 Rooms)')) + _t('All'), 0.493, 0.468, 0.492, 0.264)),
|
||||
(-141, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T4/T5/T6')) + _t('Drones'), 0.449, 0.54, 0.549, 0.197)),
|
||||
(-142, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T4/T5/T6')) + _t('Overmind'), 0.619, 0.574, 0.612, 0.379)),
|
||||
(-143, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T4/T5/T6')) + _t('Seeker'), 0.085, 0.085, 0.085, 0)),
|
||||
(-144, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T4/T5/T6')) + _t('Triglavian'), 0.477, 0.4, 0.461, 0.034)),
|
||||
(-145, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T4/T5/T6')) + _t('Drifter'), 0.437, 0.437, 0.437, 0.121)),
|
||||
(-146, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T4/T5/T6')) + _t('Sleeper'), 0.435, 0.435, 0.435, 0.215)),
|
||||
(-147, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T4/T5/T6')) + _t('All'), 0.493, 0.468, 0.492, 0.196)),
|
||||
(-58, (_c(_t('NPC')) + _c(_t('Invasion')) + _t('Triglavian Entities'), 0.422, 0.367, 0.453, 0.411)),
|
||||
(-59, (_c(_t('NPC')) + _c(_t('Invasion')) + _t('Amarr EDENCOM Entities'), 0.360, 0.310, 0.441, 0.602)),
|
||||
(-60, (_c(_t('NPC')) + _c(_t('Invasion')) + _t('Caldari EDENCOM Entities'), 0.303, 0.610, 0.487, 0.401)),
|
||||
(-61, (_c(_t('NPC')) + _c(_t('Invasion')) + _t('Gallente EDENCOM Entities'), 0.383, 0.414, 0.578, 0.513)),
|
||||
(-62, (_c(_t('NPC')) + _c(_t('Invasion')) + _t('Minmatar EDENCOM Entities'), 0.620, 0.422, 0.355, 0.399)),
|
||||
(-63, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Dark Matter All Tiers')) + _t('Drones'), 0.439, 0.522, 0.529, 0.435)),
|
||||
(-64, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Dark Matter All Tiers')) + _t('Overmind'), 0.643, 0.593, 0.624, 0.639)),
|
||||
(-65, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Dark Matter All Tiers')) + _t('Seeker'), 0.082, 0.082, 0.082, 0.082)),
|
||||
(-66, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Dark Matter All Tiers')) + _t('Triglavian'), 0.494, 0.41, 0.464, 0.376)),
|
||||
(-67, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Dark Matter All Tiers')) + _t('Drifter'), 0.415, 0.415, 0.415, 0.415)),
|
||||
(-68, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Dark Matter All Tiers')) + _t('Sleeper'), 0.435, 0.435, 0.435, 0.435)),
|
||||
(-69, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Dark Matter All Tiers')) + _t('All'), 0.508, 0.474, 0.495, 0.488)),
|
||||
(-70, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T0/T1/T2')) + _t('Drones'), 0.323, 0.522, 0.529, 0.435)),
|
||||
(-71, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T0/T1/T2')) + _t('Overmind'), 0.542, 0.593, 0.624, 0.639)),
|
||||
(-72, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T0/T1/T2')) + _t('Seeker'), 0, 0.082, 0.082, 0.082)),
|
||||
(-73, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T0/T1/T2')) + _t('Triglavian'), 0.356, 0.41, 0.464, 0.376)),
|
||||
(-74, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T0/T1/T2')) + _t('Drifter'), 0.277, 0.415, 0.415, 0.415)),
|
||||
(-75, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T0/T1/T2')) + _t('Sleeper'), 0.329, 0.435, 0.435, 0.435)),
|
||||
(-76, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T0/T1/T2')) + _t('All'), 0.381, 0.474, 0.495, 0.488)),
|
||||
(-77, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T3 (Some T5 Rooms)')) + _t('Drones'), 0.255, 0.522, 0.529, 0.435)),
|
||||
(-78, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T3 (Some T5 Rooms)')) + _t('Overmind'), 0.48, 0.593, 0.624, 0.639)),
|
||||
(-79, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T3 (Some T5 Rooms)')) + _t('Seeker'), 0, 0.082, 0.082, 0.0822)),
|
||||
(-80, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T3 (Some T5 Rooms)')) + _t('Triglavian'), 0.268, 0.41, 0.464, 0.376)),
|
||||
(-81, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T3 (Some T5 Rooms)')) + _t('Drifter'), 0.191, 0.415, 0.415, 0.415)),
|
||||
(-82, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T3 (Some T5 Rooms)')) + _t('Sleeper'), 0.268, 0.435, 0.435, 0.435)),
|
||||
(-83, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T3 (Some T5 Rooms)')) + _t('All'), 0.308, 0.474, 0.495, 0.488)),
|
||||
(-84, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T4/T5/T6')) + _t('Drones'), 0.193, 0.522, 0.529, 0.435)),
|
||||
(-85, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T4/T5/T6')) + _t('Overmind'), 0.423, 0.593, 0.624, 0.639)),
|
||||
(-86, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T4/T5/T6')) + _t('Seeker'), 0, 0.082, 0.082, 0.082)),
|
||||
(-87, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T4/T5/T6')) + _t('Triglavian'), 0.206, 0.41, 0.464, 0.376)),
|
||||
(-88, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T4/T5/T6')) + _t('Drifter'), 0.111, 0.415, 0.415, 0.415)),
|
||||
(-89, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T4/T5/T6')) + _t('Sleeper'), 0.215, 0.435, 0.435, 0.435)),
|
||||
(-90, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T4/T5/T6')) + _t('All'), 0.247, 0.474, 0.495, 0.488)),
|
||||
(-91, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T0/T1/T2')) + _t('Drones'), 0.461, 0.425, 0.541, 0.443)),
|
||||
(-92, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T0/T1/T2')) + _t('Overmind'), 0.666, 0.489, 0.634, 0.646)),
|
||||
(-93, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T0/T1/T2')) + _t('Seeker'), 0.084, 0, 0.084, 0.084)),
|
||||
(-94, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T0/T1/T2')) + _t('Triglavian'), 0.537, 0.269, 0.489, 0.371)),
|
||||
(-95, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T0/T1/T2')) + _t('Drifter'), 0.43, 0.289, 0.43, 0.43)),
|
||||
(-96, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T0/T1/T2')) + _t('Sleeper'), 0.512, 0.402, 0.512, 0.512)),
|
||||
(-97, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T0/T1/T2')) + _t('All'), 0.537, 0.352, 0.512, 0.495)),
|
||||
(-98, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T3 (Some T5 Rooms)')) + _t('Drones'), 0.461, 0.36, 0.541, 0.443)),
|
||||
(-99, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T3 (Some T5 Rooms)')) + _t('Overmind'), 0.666, 0.413, 0.634, 0.646)),
|
||||
(-100, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T3 (Some T5 Rooms)')) + _t('Seeker'), 0.084, 0, 0.084, 0.084)),
|
||||
(-101, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T3 (Some T5 Rooms)')) + _t('Triglavian'), 0.537, 0.166, 0.489, 0.371)),
|
||||
(-102, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T3 (Some T5 Rooms)')) + _t('Drifter'), 0.43, 0.201, 0.43, 0.43)),
|
||||
(-103, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T3 (Some T5 Rooms)')) + _t('Sleeper'), 0.512, 0.337, 0.512, 0.512)),
|
||||
(-104, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T3 (Some T5 Rooms)')) + _t('All'), 0.537, 0.269, 0.512, 0.495)),
|
||||
(-105, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T4/T5/T6')) + _t('Drones'), 0.461, 0.305, 0.541, 0.443)),
|
||||
(-106, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T4/T5/T6')) + _t('Overmind'), 0.666, 0.345, 0.634, 0.646)),
|
||||
(-107, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T4/T5/T6')) + _t('Seeker'), 0.084, 0, 0.084, 0.084)),
|
||||
(-108, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T4/T5/T6')) + _t('Triglavian'), 0.537, 0.085, 0.489, 0.371)),
|
||||
(-109, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T4/T5/T6')) + _t('Drifter'), 0.43, 0.117, 0.43, 0.43)),
|
||||
(-110, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T4/T5/T6')) + _t('Sleeper'), 0.512, 0.276, 0.512, 0.512)),
|
||||
(-111, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T4/T5/T6')) + _t('All'), 0.537, 0.201, 0.512, 0.495)),
|
||||
(-112, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T0/T1/T2')) + _t('Drones'), 0.439, 0.522, 0.417, 0.435)),
|
||||
(-113, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T0/T1/T2')) + _t('Overmind'), 0.643, 0.593, 0.511, 0.639)),
|
||||
(-114, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T0/T1/T2')) + _t('Seeker'), 0.082, 0.082, 0, 0.082)),
|
||||
(-115, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T0/T1/T2')) + _t('Triglavian'), 0.494, 0.41, 0.304, 0.376)),
|
||||
(-116, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T0/T1/T2')) + _t('Drifter'), 0.415, 0.415, 0.277, 0.415)),
|
||||
(-117, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T0/T1/T2')) + _t('Sleeper'), 0.435, 0.435, 0.329, 0.435)),
|
||||
(-118, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T0/T1/T2')) + _t('All'), 0.508, 0.474, 0.359, 0.488)),
|
||||
(-119, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T3 (Some T5 Rooms)')) + _t('Drones'), 0.439, 0.522, 0.351, 0.435)),
|
||||
(-120, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T3 (Some T5 Rooms)')) + _t('Overmind'), 0.643, 0.593, 0.435, 0.639)),
|
||||
(-121, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T3 (Some T5 Rooms)')) + _t('Seeker'), 0.082, 0.082, 0, 0.082)),
|
||||
(-122, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T3 (Some T5 Rooms)')) + _t('Triglavian'), 0.494, 0.41, 0.198, 0.376)),
|
||||
(-123, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T3 (Some T5 Rooms)')) + _t('Drifter'), 0.415, 0.415, 0.191, 0.415)),
|
||||
(-124, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T3 (Some T5 Rooms)')) + _t('Sleeper'), 0.435, 0.435, 0.268, 0.435)),
|
||||
(-125, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T3 (Some T5 Rooms)')) + _t('All'), 0.508, 0.474, 0.276, 0.488)),
|
||||
(-126, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T4/T5/T6')) + _t('Drones'), 0.439, 0.522, 0.293, 0.435)),
|
||||
(-127, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T4/T5/T6')) + _t('Overmind'), 0.643, 0.593, 0.362, 0.639)),
|
||||
(-128, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T4/T5/T6')) + _t('Seeker'), 0.082, 0.082, 0, 0.082)),
|
||||
(-129, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T4/T5/T6')) + _t('Triglavian'), 0.494, 0.41, 0.122, 0.376)),
|
||||
(-130, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T4/T5/T6')) + _t('Drifter'), 0.415, 0.415, 0.111, 0.415)),
|
||||
(-131, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T4/T5/T6')) + _t('Sleeper'), 0.435, 0.435, 0.215, 0.435)),
|
||||
(-132, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T4/T5/T6')) + _t('All'), 0.508, 0.474, 0.208, 0.488)),
|
||||
(-133, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T0/T1/T2')) + _t('Drones'), 0.449, 0.54, 0.549, 0.336)),
|
||||
(-134, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T0/T1/T2')) + _t('Overmind'), 0.619, 0.574, 0.612, 0.522)),
|
||||
(-135, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T0/T1/T2')) + _t('Seeker'), 0.085, 0.085, 0.085, 0)),
|
||||
(-136, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T0/T1/T2')) + _t('Triglavian'), 0.477, 0.4, 0.461, 0.202)),
|
||||
(-137, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T0/T1/T2')) + _t('Drifter'), 0.437, 0.437, 0.437, 0.295)),
|
||||
(-138, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T0/T1/T2')) + _t('Sleeper'), 0.435, 0.435, 0.435, 0.329)),
|
||||
(-139, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T0/T1/T2')) + _t('All'), 0.493, 0.468, 0.492, 0.35)),
|
||||
(-140, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T3 (Some T5 Rooms)')) + _t('Drones'), 0.449, 0.54, 0.549, 0.264)),
|
||||
(-141, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T3 (Some T5 Rooms)')) + _t('Overmind'), 0.619, 0.574, 0.612, 0.449)),
|
||||
(-142, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T3 (Some T5 Rooms)')) + _t('Seeker'), 0.085, 0.085, 0.085, 0)),
|
||||
(-143, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T3 (Some T5 Rooms)')) + _t('Triglavian'), 0.477, 0.4, 0.461, 0.081)),
|
||||
(-144, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T3 (Some T5 Rooms)')) + _t('Drifter'), 0.437, 0.437, 0.437, 0.206)),
|
||||
(-145, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T3 (Some T5 Rooms)')) + _t('Sleeper'), 0.435, 0.435, 0.435, 0.268)),
|
||||
(-146, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T3 (Some T5 Rooms)')) + _t('All'), 0.493, 0.468, 0.492, 0.264)),
|
||||
(-147, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T4/T5/T6')) + _t('Drones'), 0.449, 0.54, 0.549, 0.197)),
|
||||
(-148, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T4/T5/T6')) + _t('Overmind'), 0.619, 0.574, 0.612, 0.379)),
|
||||
(-149, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T4/T5/T6')) + _t('Seeker'), 0.085, 0.085, 0.085, 0)),
|
||||
(-150, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T4/T5/T6')) + _t('Triglavian'), 0.477, 0.4, 0.461, 0.034)),
|
||||
(-151, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T4/T5/T6')) + _t('Drifter'), 0.437, 0.437, 0.437, 0.121)),
|
||||
(-152, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T4/T5/T6')) + _t('Sleeper'), 0.435, 0.435, 0.435, 0.215)),
|
||||
(-153, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T4/T5/T6')) + _t('All'), 0.493, 0.468, 0.492, 0.196)),
|
||||
# Source: ticket #2265
|
||||
(-148, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Dark Matter All Tiers')) + _t('Concord'), 0.324, 0.318, 0.369, 0.372)),
|
||||
(-149, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Dark Matter All Tiers')) + _t('Sansha'), 0.137, 0.331, 0.332, 0.322)),
|
||||
(-150, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Dark Matter All Tiers')) + _t('Angel'), 0.582, 0.508, 0.457, 0.416)),
|
||||
(-151, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T0/T1/T2')) + _t('Concord'), 0.121, 0.318, 0.369, 0.372)),
|
||||
(-152, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T0/T1/T2')) + _t('Sansha'), 0.034, 0.331, 0.332, 0.322)),
|
||||
(-153, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T0/T1/T2')) + _t('Angel'), 0.456, 0.508, 0.457, 0.416)),
|
||||
(-154, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T3 (Some T5 Rooms)')) + _t('Concord'), 0.025, 0.318, 0.369, 0.372)),
|
||||
(-155, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T3 (Some T5 Rooms)')) + _t('Sansha'), 0.018, 0.331, 0.332, 0.322)),
|
||||
(-156, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T3 (Some T5 Rooms)')) + _t('Angel'), 0.373, 0.508, 0.457, 0.416)),
|
||||
(-157, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T4/T5/T6')) + _t('Concord'), 0.008, 0.318, 0.369, 0.372)),
|
||||
(-158, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T4/T5/T6')) + _t('Sansha'), 0.009, 0.331, 0.332, 0.322)),
|
||||
(-159, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T4/T5/T6')) + _t('Angel'), 0.3, 0.508, 0.457, 0.416)),
|
||||
(-160, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T0/T1/T2')) + _t('Concord'), 0.324, 0.107, 0.369, 0.372)),
|
||||
(-161, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T0/T1/T2')) + _t('Sansha'), 0.148, 0.181, 0.329, 0.328)),
|
||||
(-162, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T0/T1/T2')) + _t('Angel'), 0.587, 0.342, 0.439, 0.39)),
|
||||
(-163, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T3 (Some T5 Rooms)')) + _t('Concord'), 0.324, 0.016, 0.369, 0.372)),
|
||||
(-164, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T3 (Some T5 Rooms)')) + _t('Sansha'), 0.148, 0.14, 0.329, 0.328)),
|
||||
(-165, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T3 (Some T5 Rooms)')) + _t('Angel'), 0.587, 0.241, 0.439, 0.39)),
|
||||
(-166, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T4/T5/T6')) + _t('Concord'), 0.324, 0.004, 0.369, 0.372)),
|
||||
(-167, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T4/T5/T6')) + _t('Sansha'), 0.148, 0.106, 0.329, 0.328)),
|
||||
(-168, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T4/T5/T6')) + _t('Angel'), 0.587, 0.172, 0.439, 0.39)),
|
||||
(-169, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T0/T1/T2')) + _t('Concord'), 0.324, 0.318, 0.18, 0.372)),
|
||||
(-170, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T0/T1/T2')) + _t('Sansha'), 0.137, 0.331, 0.166, 0.322)),
|
||||
(-171, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T0/T1/T2')) + _t('Angel'), 0.582, 0.508, 0.295, 0.416)),
|
||||
(-172, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T3 (Some T5 Rooms)')) + _t('Concord'), 0.324, 0.318, 0.089, 0.372)),
|
||||
(-173, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T3 (Some T5 Rooms)')) + _t('Sansha'), 0.137, 0.331, 0.108, 0.322)),
|
||||
(-174, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T3 (Some T5 Rooms)')) + _t('Angel'), 0.582, 0.508, 0.203, 0.416)),
|
||||
(-175, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T4/T5/T6')) + _t('Concord'), 0.324, 0.318, 0.068, 0.372)),
|
||||
(-176, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T4/T5/T6')) + _t('Sansha'), 0.137, 0.331, 0.073, 0.322)),
|
||||
(-177, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T4/T5/T6')) + _t('Angel'), 0.582, 0.508, 0.14, 0.416)),
|
||||
(-178, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T0/T1/T2')) + _t('Concord'), 0.324, 0.318, 0.369, 0.203)),
|
||||
(-179, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T0/T1/T2')) + _t('Sansha'), 0.137, 0.355, 0.352, 0.16)),
|
||||
(-180, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T0/T1/T2')) + _t('Angel'), 0.59, 0.528, 0.477, 0.286)),
|
||||
(-181, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T3 (Some T5 Rooms)')) + _t('Concord'), 0.324, 0.318, 0.369, 0.112)),
|
||||
(-182, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T3 (Some T5 Rooms)')) + _t('Sansha'), 0.137, 0.355, 0.352, 0.05)),
|
||||
(-183, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T3 (Some T5 Rooms)')) + _t('Angel'), 0.59, 0.528, 0.477, 0.197)),
|
||||
(-184, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T4/T5/T6')) + _t('Concord'), 0.324, 0.318, 0.369, 0.086)),
|
||||
(-185, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T4/T5/T6')) + _t('Sansha'), 0.137, 0.355, 0.352, 0)),
|
||||
(-186, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T4/T5/T6')) + _t('Angel'), 0.59, 0.528, 0.477, 0.126)),
|
||||
(-187, (_c(_t('NPC')) + _c(_t('Invasion')) + _t('Drifter Entities'), 0.128, 0.375, 0.383, 0.383)),
|
||||
(-188, (_c(_t('NPC')) + _c(_t('Invasion')) + _t('Rogue Drone Entities'), 0.104, 0.147, 0.147, 0.102)),
|
||||
(-189, (_c(_t('NPC')) + _c(_t('Invasion')) + _t('Sleeper Entities'), 0.563, 0.563, 0.563, 0.563))])
|
||||
(-154, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Dark Matter All Tiers')) + _t('Concord'), 0.324, 0.318, 0.369, 0.372)),
|
||||
(-155, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Dark Matter All Tiers')) + _t('Sansha'), 0.137, 0.331, 0.332, 0.322)),
|
||||
(-156, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Dark Matter All Tiers')) + _t('Angel'), 0.582, 0.508, 0.457, 0.416)),
|
||||
(-157, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T0/T1/T2')) + _t('Concord'), 0.121, 0.318, 0.369, 0.372)),
|
||||
(-158, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T0/T1/T2')) + _t('Sansha'), 0.034, 0.331, 0.332, 0.322)),
|
||||
(-159, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T0/T1/T2')) + _t('Angel'), 0.456, 0.508, 0.457, 0.416)),
|
||||
(-160, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T3 (Some T5 Rooms)')) + _t('Concord'), 0.025, 0.318, 0.369, 0.372)),
|
||||
(-161, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T3 (Some T5 Rooms)')) + _t('Sansha'), 0.018, 0.331, 0.332, 0.322)),
|
||||
(-162, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T3 (Some T5 Rooms)')) + _t('Angel'), 0.373, 0.508, 0.457, 0.416)),
|
||||
(-163, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T4/T5/T6')) + _t('Concord'), 0.008, 0.318, 0.369, 0.372)),
|
||||
(-164, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T4/T5/T6')) + _t('Sansha'), 0.009, 0.331, 0.332, 0.322)),
|
||||
(-165, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T4/T5/T6')) + _t('Angel'), 0.3, 0.508, 0.457, 0.416)),
|
||||
(-166, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T0/T1/T2')) + _t('Concord'), 0.324, 0.107, 0.369, 0.372)),
|
||||
(-167, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T0/T1/T2')) + _t('Sansha'), 0.148, 0.181, 0.329, 0.328)),
|
||||
(-168, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T0/T1/T2')) + _t('Angel'), 0.587, 0.342, 0.439, 0.39)),
|
||||
(-169, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T3 (Some T5 Rooms)')) + _t('Concord'), 0.324, 0.016, 0.369, 0.372)),
|
||||
(-170, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T3 (Some T5 Rooms)')) + _t('Sansha'), 0.148, 0.14, 0.329, 0.328)),
|
||||
(-171, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T3 (Some T5 Rooms)')) + _t('Angel'), 0.587, 0.241, 0.439, 0.39)),
|
||||
(-172, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T4/T5/T6')) + _t('Concord'), 0.324, 0.004, 0.369, 0.372)),
|
||||
(-173, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T4/T5/T6')) + _t('Sansha'), 0.148, 0.106, 0.329, 0.328)),
|
||||
(-174, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T4/T5/T6')) + _t('Angel'), 0.587, 0.172, 0.439, 0.39)),
|
||||
(-175, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T0/T1/T2')) + _t('Concord'), 0.324, 0.318, 0.18, 0.372)),
|
||||
(-176, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T0/T1/T2')) + _t('Sansha'), 0.137, 0.331, 0.166, 0.322)),
|
||||
(-177, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T0/T1/T2')) + _t('Angel'), 0.582, 0.508, 0.295, 0.416)),
|
||||
(-178, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T3 (Some T5 Rooms)')) + _t('Concord'), 0.324, 0.318, 0.089, 0.372)),
|
||||
(-179, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T3 (Some T5 Rooms)')) + _t('Sansha'), 0.137, 0.331, 0.108, 0.322)),
|
||||
(-180, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T3 (Some T5 Rooms)')) + _t('Angel'), 0.582, 0.508, 0.203, 0.416)),
|
||||
(-181, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T4/T5/T6')) + _t('Concord'), 0.324, 0.318, 0.068, 0.372)),
|
||||
(-182, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T4/T5/T6')) + _t('Sansha'), 0.137, 0.331, 0.073, 0.322)),
|
||||
(-183, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T4/T5/T6')) + _t('Angel'), 0.582, 0.508, 0.14, 0.416)),
|
||||
(-184, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T0/T1/T2')) + _t('Concord'), 0.324, 0.318, 0.369, 0.203)),
|
||||
(-185, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T0/T1/T2')) + _t('Sansha'), 0.137, 0.355, 0.352, 0.16)),
|
||||
(-186, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T0/T1/T2')) + _t('Angel'), 0.59, 0.528, 0.477, 0.286)),
|
||||
(-187, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T3 (Some T5 Rooms)')) + _t('Concord'), 0.324, 0.318, 0.369, 0.112)),
|
||||
(-188, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T3 (Some T5 Rooms)')) + _t('Sansha'), 0.137, 0.355, 0.352, 0.05)),
|
||||
(-189, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T3 (Some T5 Rooms)')) + _t('Angel'), 0.59, 0.528, 0.477, 0.197)),
|
||||
(-190, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T4/T5/T6')) + _t('Concord'), 0.324, 0.318, 0.369, 0.086)),
|
||||
(-191, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T4/T5/T6')) + _t('Sansha'), 0.137, 0.355, 0.352, 0)),
|
||||
(-192, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T4/T5/T6')) + _t('Angel'), 0.59, 0.528, 0.477, 0.126)),
|
||||
(-193, (_c(_t('NPC')) + _c(_t('Invasion')) + _t('Drifter Entities'), 0.128, 0.375, 0.383, 0.383)),
|
||||
(-194, (_c(_t('NPC')) + _c(_t('Invasion')) + _t('Rogue Drone Entities'), 0.104, 0.147, 0.147, 0.102)),
|
||||
(-195, (_c(_t('NPC')) + _c(_t('Invasion')) + _t('Sleeper Entities'), 0.563, 0.563, 0.563, 0.563))])
|
||||
|
||||
|
||||
class TargetProfile:
|
||||
@@ -246,7 +254,7 @@ class TargetProfile:
|
||||
def init(self):
|
||||
self.builtin = False
|
||||
|
||||
def update(self, emAmount=0, thermalAmount=0, kineticAmount=0, explosiveAmount=0, maxVelocity=None, signatureRadius=None, radius=None):
|
||||
def update(self, emAmount=0, thermalAmount=0, kineticAmount=0, explosiveAmount=0, maxVelocity=None, signatureRadius=None, radius=None, hp=None):
|
||||
self.emAmount = emAmount
|
||||
self.thermalAmount = thermalAmount
|
||||
self.kineticAmount = kineticAmount
|
||||
@@ -254,6 +262,7 @@ class TargetProfile:
|
||||
self._maxVelocity = maxVelocity
|
||||
self._signatureRadius = signatureRadius
|
||||
self._radius = radius
|
||||
self._hp = hp
|
||||
|
||||
@classmethod
|
||||
def getBuiltinList(cls):
|
||||
@@ -323,6 +332,18 @@ class TargetProfile:
|
||||
def radius(self, val):
|
||||
self._radius = val
|
||||
|
||||
@property
|
||||
def hp(self):
|
||||
if self._hp is None or self._hp == -1:
|
||||
return math.inf
|
||||
return self._hp
|
||||
|
||||
@hp.setter
|
||||
def hp(self, val):
|
||||
if val is not None and math.isinf(val):
|
||||
val = None
|
||||
self._hp = val
|
||||
|
||||
@classmethod
|
||||
def importPatterns(cls, text):
|
||||
lines = re.split('[\n\r]+', text)
|
||||
|
||||
@@ -18,6 +18,9 @@
|
||||
# ===============================================================================
|
||||
|
||||
|
||||
import math
|
||||
from collections import defaultdict
|
||||
|
||||
from eos.utils.float import floatUnerr
|
||||
from utils.repr import makeReprStr
|
||||
|
||||
@@ -26,15 +29,133 @@ def _t(x):
|
||||
return x
|
||||
|
||||
|
||||
class BreacherInfo:
|
||||
|
||||
def __init__(self, absolute, relative):
|
||||
self.absolute = absolute
|
||||
self.relative = relative
|
||||
|
||||
def __mul__(self, mul):
|
||||
return type(self)(absolute=self.absolute * mul, relative=self.relative * mul)
|
||||
|
||||
def __imul__(self, mul):
|
||||
if mul == 1:
|
||||
return self
|
||||
self.absolute *= mul
|
||||
self.relative *= mul
|
||||
return self
|
||||
|
||||
def __truediv__(self, div):
|
||||
return type(self)(absolute=self.absolute / div, relative=self.relative / div)
|
||||
|
||||
|
||||
class DmgTypes:
|
||||
"""Container for damage data stats."""
|
||||
"""
|
||||
Container for volley stats, which stores breacher pod data
|
||||
in raw form, before application of it to target profile.
|
||||
"""
|
||||
|
||||
def __init__(self, em, thermal, kinetic, explosive):
|
||||
self.em = em
|
||||
self.thermal = thermal
|
||||
self.kinetic = kinetic
|
||||
self.explosive = explosive
|
||||
self._calcTotal()
|
||||
self._em = em
|
||||
self._thermal = thermal
|
||||
self._kinetic = kinetic
|
||||
self._explosive = explosive
|
||||
self._breachers = defaultdict(lambda: [])
|
||||
self.__profile = None
|
||||
# Cached data
|
||||
self.__cached_em = None
|
||||
self.__cached_thermal = None
|
||||
self.__cached_kinetic = None
|
||||
self.__cached_explosive = None
|
||||
self.__cached_pure = None
|
||||
self.__cached_total = None
|
||||
|
||||
@classmethod
|
||||
def default(cls):
|
||||
return cls(0, 0, 0, 0)
|
||||
|
||||
def _clear_cached(self):
|
||||
self.__cached_em = None
|
||||
self.__cached_thermal = None
|
||||
self.__cached_kinetic = None
|
||||
self.__cached_explosive = None
|
||||
self.__cached_pure = None
|
||||
self.__cached_total = None
|
||||
|
||||
def add_breacher(self, key, data):
|
||||
self._breachers[key].append(data)
|
||||
|
||||
@property
|
||||
def profile(self):
|
||||
return self.__profile
|
||||
|
||||
@profile.setter
|
||||
def profile(self, profile):
|
||||
self.__profile = profile
|
||||
self._clear_cached()
|
||||
|
||||
@property
|
||||
def em(self):
|
||||
if self.__cached_em is not None:
|
||||
return self.__cached_em
|
||||
dmg = self._em
|
||||
if self.profile is not None:
|
||||
dmg *= 1 - getattr(self.profile, "emAmount", 0)
|
||||
self.__cached_em = dmg
|
||||
return dmg
|
||||
|
||||
@property
|
||||
def thermal(self):
|
||||
if self.__cached_thermal is not None:
|
||||
return self.__cached_thermal
|
||||
dmg = self._thermal
|
||||
if self.profile is not None:
|
||||
dmg *= 1 - getattr(self.profile, "thermalAmount", 0)
|
||||
self.__cached_thermal = dmg
|
||||
return dmg
|
||||
|
||||
@property
|
||||
def kinetic(self):
|
||||
if self.__cached_kinetic is not None:
|
||||
return self.__cached_kinetic
|
||||
dmg = self._kinetic
|
||||
if self.profile is not None:
|
||||
dmg *= 1 - getattr(self.profile, "kineticAmount", 0)
|
||||
self.__cached_kinetic = dmg
|
||||
return dmg
|
||||
|
||||
@property
|
||||
def explosive(self):
|
||||
if self.__cached_explosive is not None:
|
||||
return self.__cached_explosive
|
||||
dmg = self._explosive
|
||||
if self.profile is not None:
|
||||
dmg *= 1 - getattr(self.profile, "explosiveAmount", 0)
|
||||
self.__cached_explosive = dmg
|
||||
return dmg
|
||||
|
||||
@property
|
||||
def pure(self):
|
||||
if self.__cached_pure is not None:
|
||||
return self.__cached_pure
|
||||
if self.profile is None:
|
||||
dmg = sum(
|
||||
max((b.absolute for b in bs), default=0)
|
||||
for bs in self._breachers.values())
|
||||
else:
|
||||
dmg = sum(
|
||||
max((min(b.absolute, b.relative * getattr(self.profile, "hp", math.inf)) for b in bs), default=0)
|
||||
for bs in self._breachers.values())
|
||||
self.__cached_pure = dmg
|
||||
return dmg
|
||||
|
||||
@property
|
||||
def total(self):
|
||||
if self.__cached_total is not None:
|
||||
return self.__cached_total
|
||||
dmg = self.em + self.thermal + self.kinetic + self.explosive + self.pure
|
||||
self.__cached_total = dmg
|
||||
return dmg
|
||||
|
||||
# Iterator is needed to support tuple-style unpacking
|
||||
def __iter__(self):
|
||||
@@ -42,6 +163,7 @@ class DmgTypes:
|
||||
yield self.thermal
|
||||
yield self.kinetic
|
||||
yield self.explosive
|
||||
yield self.pure
|
||||
yield self.total
|
||||
|
||||
def __eq__(self, other):
|
||||
@@ -50,77 +172,87 @@ class DmgTypes:
|
||||
# Round for comparison's sake because often damage profiles are
|
||||
# generated from data which includes float errors
|
||||
return (
|
||||
floatUnerr(self.em) == floatUnerr(other.em) and
|
||||
floatUnerr(self.thermal) == floatUnerr(other.thermal) and
|
||||
floatUnerr(self.kinetic) == floatUnerr(other.kinetic) and
|
||||
floatUnerr(self.explosive) == floatUnerr(other.explosive) and
|
||||
floatUnerr(self.total) == floatUnerr(other.total))
|
||||
|
||||
def __bool__(self):
|
||||
return any((
|
||||
self.em, self.thermal, self.kinetic,
|
||||
self.explosive, self.total))
|
||||
|
||||
def _calcTotal(self):
|
||||
self.total = self.em + self.thermal + self.kinetic + self.explosive
|
||||
floatUnerr(self._em) == floatUnerr(other._em) and
|
||||
floatUnerr(self._thermal) == floatUnerr(other._thermal) and
|
||||
floatUnerr(self._kinetic) == floatUnerr(other._kinetic) and
|
||||
floatUnerr(self._explosive) == floatUnerr(other._explosive) and
|
||||
sorted(self._breachers) == sorted(other._breachers) and
|
||||
self.profile == other.profile)
|
||||
|
||||
def __add__(self, other):
|
||||
return type(self)(
|
||||
em=self.em + other.em,
|
||||
thermal=self.thermal + other.thermal,
|
||||
kinetic=self.kinetic + other.kinetic,
|
||||
explosive=self.explosive + other.explosive)
|
||||
new = type(self)(
|
||||
em=self._em + other._em,
|
||||
thermal=self._thermal + other._thermal,
|
||||
kinetic=self._kinetic + other._kinetic,
|
||||
explosive=self._explosive + other._explosive)
|
||||
new.profile = self.profile
|
||||
for k, v in self._breachers.items():
|
||||
new._breachers[k].extend(v)
|
||||
for k, v in other._breachers.items():
|
||||
new._breachers[k].extend(v)
|
||||
return new
|
||||
|
||||
def __iadd__(self, other):
|
||||
self.em += other.em
|
||||
self.thermal += other.thermal
|
||||
self.kinetic += other.kinetic
|
||||
self.explosive += other.explosive
|
||||
self._calcTotal()
|
||||
self._em += other._em
|
||||
self._thermal += other._thermal
|
||||
self._kinetic += other._kinetic
|
||||
self._explosive += other._explosive
|
||||
for k, v in other._breachers.items():
|
||||
self._breachers[k].extend(v)
|
||||
self._clear_cached()
|
||||
return self
|
||||
|
||||
def __mul__(self, mul):
|
||||
return type(self)(
|
||||
em=self.em * mul,
|
||||
thermal=self.thermal * mul,
|
||||
kinetic=self.kinetic * mul,
|
||||
explosive=self.explosive * mul)
|
||||
new = type(self)(
|
||||
em=self._em * mul,
|
||||
thermal=self._thermal * mul,
|
||||
kinetic=self._kinetic * mul,
|
||||
explosive=self._explosive * mul)
|
||||
new.profile = self.profile
|
||||
for k, v in self._breachers.items():
|
||||
new._breachers[k] = [b * mul for b in v]
|
||||
return new
|
||||
|
||||
def __imul__(self, mul):
|
||||
if mul == 1:
|
||||
return
|
||||
self.em *= mul
|
||||
self.thermal *= mul
|
||||
self.kinetic *= mul
|
||||
self.explosive *= mul
|
||||
self._calcTotal()
|
||||
return self
|
||||
self._em *= mul
|
||||
self._thermal *= mul
|
||||
self._kinetic *= mul
|
||||
self._explosive *= mul
|
||||
for v in self._breachers.values():
|
||||
for b in v:
|
||||
b *= mul
|
||||
self._clear_cached()
|
||||
return self
|
||||
|
||||
def __truediv__(self, div):
|
||||
return type(self)(
|
||||
em=self.em / div,
|
||||
thermal=self.thermal / div,
|
||||
kinetic=self.kinetic / div,
|
||||
explosive=self.explosive / div)
|
||||
new = type(self)(
|
||||
em=self._em / div,
|
||||
thermal=self._thermal / div,
|
||||
kinetic=self._kinetic / div,
|
||||
explosive=self._explosive / div)
|
||||
new.profile = self.profile
|
||||
for k, v in self._breachers.items():
|
||||
new._breachers[k] = [b / div for b in v]
|
||||
return new
|
||||
|
||||
def __itruediv__(self, div):
|
||||
if div == 1:
|
||||
return
|
||||
self.em /= div
|
||||
self.thermal /= div
|
||||
self.kinetic /= div
|
||||
self.explosive /= div
|
||||
self._calcTotal()
|
||||
return self
|
||||
def __bool__(self):
|
||||
return any((
|
||||
self._em, self._thermal, self._kinetic, self._explosive,
|
||||
any(b.absolute or b.relative for b in self._breachers)))
|
||||
|
||||
def __repr__(self):
|
||||
spec = DmgTypes.names()
|
||||
spec.append('total')
|
||||
return makeReprStr(self, spec)
|
||||
class_name = type(self).__name__
|
||||
return (f'<{class_name}(em={self._em}, thermal={self._thermal}, kinetic={self._kinetic}, '
|
||||
f'explosive={self._explosive}, breachers={len(self._breachers)})>')
|
||||
|
||||
@staticmethod
|
||||
def names(short=None, postProcessor=None):
|
||||
def names(short=None, postProcessor=None, includePure=False):
|
||||
|
||||
value = [_t('em'), _t('th'), _t('kin'), _t('exp')] if short else [_t('em'), _t('thermal'), _t('kinetic'), _t('explosive')]
|
||||
if includePure:
|
||||
value += [_t('pure')]
|
||||
|
||||
if postProcessor:
|
||||
value = [postProcessor(x) for x in value]
|
||||
|
||||
24
graphs/data/fitDamageStats/cache/time.py
vendored
24
graphs/data/fitDamageStats/cache/time.py
vendored
@@ -117,7 +117,7 @@ class TimeCache(FitDataCache):
|
||||
pointData[timeStart] = (dps, volley)
|
||||
# Gap between items
|
||||
elif floatUnerr(prevTimeEnd) < floatUnerr(timeStart):
|
||||
pointData[prevTimeEnd] = (DmgTypes(0, 0, 0, 0), DmgTypes(0, 0, 0, 0))
|
||||
pointData[prevTimeEnd] = (DmgTypes.default(), DmgTypes.default())
|
||||
pointData[timeStart] = (dps, volley)
|
||||
# Changed value
|
||||
elif dps != prevDps or volley != prevVolley:
|
||||
@@ -157,7 +157,7 @@ class TimeCache(FitDataCache):
|
||||
def addDpsVolley(ddKey, addedTimeStart, addedTimeFinish, addedVolleys):
|
||||
if not addedVolleys:
|
||||
return
|
||||
volleySum = sum(addedVolleys, DmgTypes(0, 0, 0, 0))
|
||||
volleySum = sum(addedVolleys, DmgTypes.default())
|
||||
if volleySum.total > 0:
|
||||
addedDps = volleySum / (addedTimeFinish - addedTimeStart)
|
||||
# We can take "just best" volley, no matter target resistances, because all
|
||||
@@ -170,24 +170,38 @@ class TimeCache(FitDataCache):
|
||||
def addDmg(ddKey, addedTime, addedDmg):
|
||||
if addedDmg.total == 0:
|
||||
return
|
||||
addedDmg._breachers = {addedTime + k: v for k, v in addedDmg._breachers.items()}
|
||||
addedDmg._clear_cached()
|
||||
intCacheDmg.setdefault(ddKey, {})[addedTime] = addedDmg
|
||||
|
||||
# Modules
|
||||
for mod in src.item.activeModulesIter():
|
||||
if not mod.isDealingDamage():
|
||||
continue
|
||||
cycleParams = mod.getCycleParameters(reloadOverride=True)
|
||||
cycleParams = mod.getCycleParametersForDps(reloadOverride=True)
|
||||
if cycleParams is None:
|
||||
continue
|
||||
currentTime = 0
|
||||
nonstopCycles = 0
|
||||
isBreacher = mod.isBreacher
|
||||
for cycleTimeMs, inactiveTimeMs, isInactivityReload in cycleParams.iterCycles():
|
||||
cycleVolleys = []
|
||||
volleyParams = mod.getVolleyParameters(spoolOptions=SpoolOptions(SpoolType.CYCLES, nonstopCycles, True))
|
||||
|
||||
for volleyTimeMs, volley in volleyParams.items():
|
||||
cycleVolleys.append(volley)
|
||||
addDmg(mod, currentTime + volleyTimeMs / 1000, volley)
|
||||
addDpsVolley(mod, currentTime, currentTime + cycleTimeMs / 1000, cycleVolleys)
|
||||
time = currentTime + volleyTimeMs / 1000
|
||||
if isBreacher:
|
||||
time += 1
|
||||
addDmg(mod, time, volley)
|
||||
if isBreacher:
|
||||
break
|
||||
timeStart = currentTime
|
||||
timeFinish = currentTime + cycleTimeMs / 1000
|
||||
if isBreacher:
|
||||
timeStart += 1
|
||||
timeFinish += 1
|
||||
addDpsVolley(mod, timeStart, timeFinish, cycleVolleys)
|
||||
if inactiveTimeMs > 0:
|
||||
nonstopCycles = 0
|
||||
else:
|
||||
|
||||
@@ -58,7 +58,8 @@ def getApplicationPerKey(src, tgt, atkSpeed, atkAngle, distance, tgtSpeed, tgtAn
|
||||
tgtSigRadius=tgtSigRadius)
|
||||
else:
|
||||
applicationMap[mod] = 0
|
||||
elif mod.hardpoint == FittingHardpoint.MISSILE:
|
||||
# Missile launcher or civilian missile launcher
|
||||
elif mod.hardpoint == FittingHardpoint.MISSILE or mod.item.ID == 32461:
|
||||
# FoF missiles can shoot beyond lock range
|
||||
if inLockRange or (mod.charge is not None and 'fofMissileLaunching' in mod.charge.effects):
|
||||
applicationMap[mod] = getLauncherMult(
|
||||
@@ -98,6 +99,8 @@ def getApplicationPerKey(src, tgt, atkSpeed, atkAngle, distance, tgtSpeed, tgtAn
|
||||
tgt=tgt,
|
||||
distance=distance,
|
||||
tgtSigRadius=tgtSigRadius)
|
||||
elif mod.isBreacher:
|
||||
applicationMap[mod] = getBreacherMult(mod=mod, distance=distance) if inLockRange else 0
|
||||
for drone in src.item.activeDronesIter():
|
||||
if not drone.isDealingDamage():
|
||||
continue
|
||||
@@ -192,6 +195,21 @@ def getLauncherMult(mod, distance, tgtSpeed, tgtSigRadius):
|
||||
return distanceFactor * applicationFactor
|
||||
|
||||
|
||||
def getBreacherMult(mod, distance):
|
||||
missileMaxRangeData = mod.missileMaxRangeData
|
||||
if missileMaxRangeData is None:
|
||||
return 0
|
||||
# The ranges already consider ship radius
|
||||
lowerRange, higherRange, higherChance = missileMaxRangeData
|
||||
if distance is None or distance <= lowerRange:
|
||||
distanceFactor = 1
|
||||
elif lowerRange < distance <= higherRange:
|
||||
distanceFactor = higherChance
|
||||
else:
|
||||
distanceFactor = 0
|
||||
return distanceFactor
|
||||
|
||||
|
||||
def getSmartbombMult(mod, distance):
|
||||
modRange = mod.maxRange
|
||||
if modRange is None:
|
||||
@@ -211,7 +229,7 @@ def getDoomsdayMult(mod, tgt, distance, tgtSigRadius):
|
||||
# Disallow only against subcaps, allow against caps and tgt profiles
|
||||
if tgt.isFit and not tgt.item.ship.item.requiresSkill('Capital Ships'):
|
||||
return 0
|
||||
damageSig = mod.getModifiedItemAttr('doomsdayDamageRadius') or mod.getModifiedItemAttr('signatureRadius')
|
||||
damageSig = mod.getModifiedItemAttr('signatureRadius')
|
||||
if not damageSig:
|
||||
return 1
|
||||
return min(1, tgtSigRadius / damageSig)
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
|
||||
import eos.config
|
||||
from eos.saveddata.targetProfile import TargetProfile
|
||||
from eos.utils.spoolSupport import SpoolOptions, SpoolType
|
||||
from eos.utils.stats import DmgTypes
|
||||
from graphs.data.base import PointGetter, SmoothPointGetter
|
||||
@@ -27,17 +28,16 @@ from .calc.application import getApplicationPerKey
|
||||
from .calc.projected import getScramRange, getScrammables, getTackledSpeed, getSigRadiusMult
|
||||
|
||||
|
||||
def applyDamage(dmgMap, applicationMap, tgtResists):
|
||||
total = DmgTypes(em=0, thermal=0, kinetic=0, explosive=0)
|
||||
def applyDamage(dmgMap, applicationMap, tgtResists, tgtFullHp):
|
||||
total = DmgTypes.default()
|
||||
for key, dmg in dmgMap.items():
|
||||
total += dmg * applicationMap.get(key, 0)
|
||||
if not GraphSettings.getInstance().get('ignoreResists'):
|
||||
emRes, thermRes, kinRes, exploRes = tgtResists
|
||||
total = DmgTypes(
|
||||
em=total.em * (1 - emRes),
|
||||
thermal=total.thermal * (1 - thermRes),
|
||||
kinetic=total.kinetic * (1 - kinRes),
|
||||
explosive=total.explosive * (1 - exploRes))
|
||||
else:
|
||||
emRes = thermRes = kinRes = exploRes = 0
|
||||
total.profile = TargetProfile(
|
||||
emAmount=emRes, thermalAmount=thermRes, kineticAmount=kinRes, explosiveAmount=exploRes, hp=tgtFullHp)
|
||||
return total
|
||||
|
||||
|
||||
@@ -144,7 +144,8 @@ class XDistanceMixin(SmoothPointGetter):
|
||||
'srcScramRange': getScramRange(src=src) if applyProjected else None,
|
||||
'tgtScrammables': getScrammables(tgt=tgt) if applyProjected else (),
|
||||
'dmgMap': self._getDamagePerKey(src=src, time=miscParams['time']),
|
||||
'tgtResists': tgt.getResists()}
|
||||
'tgtResists': tgt.getResists(),
|
||||
'tgtFullHp': tgt.getFullHp()}
|
||||
|
||||
def _calculatePoint(self, x, miscParams, src, tgt, commonData):
|
||||
distance = x
|
||||
@@ -186,7 +187,8 @@ class XDistanceMixin(SmoothPointGetter):
|
||||
y = applyDamage(
|
||||
dmgMap=commonData['dmgMap'],
|
||||
applicationMap=applicationMap,
|
||||
tgtResists=commonData['tgtResists']).total
|
||||
tgtResists=commonData['tgtResists'],
|
||||
tgtFullHp=commonData['tgtFullHp']).total
|
||||
return y
|
||||
|
||||
|
||||
@@ -241,14 +243,17 @@ class XTimeMixin(PointGetter):
|
||||
self._prepareTimeCache(src=src, maxTime=maxTime)
|
||||
timeCache = self._getTimeCacheData(src=src)
|
||||
applicationMap = self._prepareApplicationMap(miscParams=miscParams, src=src, tgt=tgt)
|
||||
tgtResists = tgt.getResists()
|
||||
# Custom iteration for time graph to show all data points
|
||||
currentDmg = None
|
||||
currentTime = None
|
||||
for currentTime in sorted(timeCache):
|
||||
prevDmg = currentDmg
|
||||
currentDmgData = timeCache[currentTime]
|
||||
currentDmg = applyDamage(dmgMap=currentDmgData, applicationMap=applicationMap, tgtResists=tgtResists).total
|
||||
currentDmg = applyDamage(
|
||||
dmgMap=currentDmgData,
|
||||
applicationMap=applicationMap,
|
||||
tgtResists=tgt.getResists(),
|
||||
tgtFullHp=tgt.getFullHp()).total
|
||||
if currentTime < minTime:
|
||||
continue
|
||||
# First set of data points
|
||||
@@ -294,7 +299,11 @@ class XTimeMixin(PointGetter):
|
||||
self._prepareTimeCache(src=src, maxTime=time)
|
||||
dmgData = self._getTimeCacheDataPoint(src=src, time=time)
|
||||
applicationMap = self._prepareApplicationMap(miscParams=miscParams, src=src, tgt=tgt)
|
||||
y = applyDamage(dmgMap=dmgData, applicationMap=applicationMap, tgtResists=tgt.getResists()).total
|
||||
y = applyDamage(
|
||||
dmgMap=dmgData,
|
||||
applicationMap=applicationMap,
|
||||
tgtResists=tgt.getResists(),
|
||||
tgtFullHp=tgt.getFullHp()).total
|
||||
return y
|
||||
|
||||
|
||||
@@ -310,7 +319,8 @@ class XTgtSpeedMixin(SmoothPointGetter):
|
||||
return {
|
||||
'applyProjected': GraphSettings.getInstance().get('applyProjected'),
|
||||
'dmgMap': self._getDamagePerKey(src=src, time=miscParams['time']),
|
||||
'tgtResists': tgt.getResists()}
|
||||
'tgtResists': tgt.getResists(),
|
||||
'tgtFullHp': tgt.getFullHp()}
|
||||
|
||||
def _calculatePoint(self, x, miscParams, src, tgt, commonData):
|
||||
tgtSpeed = x
|
||||
@@ -353,7 +363,8 @@ class XTgtSpeedMixin(SmoothPointGetter):
|
||||
y = applyDamage(
|
||||
dmgMap=commonData['dmgMap'],
|
||||
applicationMap=applicationMap,
|
||||
tgtResists=commonData['tgtResists']).total
|
||||
tgtResists=commonData['tgtResists'],
|
||||
tgtFullHp=commonData['tgtFullHp']).total
|
||||
return y
|
||||
|
||||
|
||||
@@ -398,7 +409,8 @@ class XTgtSigRadiusMixin(SmoothPointGetter):
|
||||
'tgtSpeed': tgtSpeed,
|
||||
'tgtSigMult': tgtSigMult,
|
||||
'dmgMap': self._getDamagePerKey(src=src, time=miscParams['time']),
|
||||
'tgtResists': tgt.getResists()}
|
||||
'tgtResists': tgt.getResists(),
|
||||
'tgtFullHp': tgt.getFullHp()}
|
||||
|
||||
def _calculatePoint(self, x, miscParams, src, tgt, commonData):
|
||||
tgtSigRadius = x
|
||||
@@ -414,7 +426,8 @@ class XTgtSigRadiusMixin(SmoothPointGetter):
|
||||
y = applyDamage(
|
||||
dmgMap=commonData['dmgMap'],
|
||||
applicationMap=applicationMap,
|
||||
tgtResists=commonData['tgtResists']).total
|
||||
tgtResists=commonData['tgtResists'],
|
||||
tgtFullHp=commonData['tgtFullHp']).total
|
||||
return y
|
||||
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ class FitDamageStatsGraph(FitGraph):
|
||||
cols = []
|
||||
if not GraphSettings.getInstance().get('ignoreResists'):
|
||||
cols.append('Target Resists')
|
||||
cols.extend(('Speed', 'SigRadius', 'Radius'))
|
||||
cols.extend(('Speed', 'SigRadius', 'Radius', 'FullHP'))
|
||||
return cols
|
||||
|
||||
# Calculation stuff
|
||||
|
||||
@@ -66,7 +66,7 @@ class FitShieldRegenGraph(FitGraph):
|
||||
('shieldAmount', '%'): lambda v, src, tgt: v / 100 * src.item.ship.getModifiedItemAttr('shieldCapacity'),
|
||||
('shieldAmountT0', '%'): lambda v, src, tgt: None if v is None else v / 100 * src.item.ship.getModifiedItemAttr('shieldCapacity'),
|
||||
# Needed only for "x mark" support, to convert EHP x into normalized value
|
||||
('shieldAmount', 'EHP'): lambda v, src, tgt: v / src.item.damagePattern.effectivify(src.item, 1, 'shield')}
|
||||
('shieldAmount', 'EHP'): lambda v, src, tgt: v / src.item.damagePattern.effectivify(src.item.ship, 1, 'shield')}
|
||||
_limiters = {
|
||||
'shieldAmount': lambda src, tgt: (0, src.item.ship.getModifiedItemAttr('shieldCapacity')),
|
||||
'shieldAmountT0': lambda src, tgt: (0, src.item.ship.getModifiedItemAttr('shieldCapacity'))}
|
||||
@@ -77,5 +77,5 @@ class FitShieldRegenGraph(FitGraph):
|
||||
('shieldAmount', 'shieldRegen'): ShieldAmount2ShieldRegenGetter}
|
||||
_denormalizers = {
|
||||
('shieldAmount', '%'): lambda v, src, tgt: v * 100 / src.item.ship.getModifiedItemAttr('shieldCapacity'),
|
||||
('shieldAmount', 'EHP'): lambda v, src, tgt: src.item.damagePattern.effectivify(src.item, v, 'shield'),
|
||||
('shieldRegen', 'EHP/s'): lambda v, src, tgt: src.item.damagePattern.effectivify(src.item, v, 'shield')}
|
||||
('shieldAmount', 'EHP'): lambda v, src, tgt: src.item.damagePattern.effectivify(src.item.ship, v, 'shield'),
|
||||
('shieldRegen', 'EHP/s'): lambda v, src, tgt: src.item.damagePattern.effectivify(src.item.ship, v, 'shield')}
|
||||
|
||||
@@ -273,7 +273,7 @@ class GraphCanvasPanel(wx.Panel):
|
||||
legendLines = []
|
||||
for i, iData in enumerate(legendData):
|
||||
color, lineStyle, label = iData
|
||||
legendLines.append(Line2D([0], [0], color=color, linestyle=lineStyle, label=label.replace('$', '\$')))
|
||||
legendLines.append(Line2D([0], [0], color=color, linestyle=lineStyle, label=label.replace('$', r'\$')))
|
||||
|
||||
if len(legendLines) > 0 and self.graphFrame.ctrlPanel.showLegend:
|
||||
legend = self.subplot.legend(handles=legendLines)
|
||||
|
||||
@@ -114,7 +114,7 @@ class GraphFrame(AuxiliaryFrame):
|
||||
newW = max(curW, bestW)
|
||||
newH = max(curH, bestH)
|
||||
if newW > curW or newH > curH:
|
||||
newSize = wx.Size(newW, newH)
|
||||
newSize = wx.Size(round(newW), round(newH))
|
||||
self.SetSize(newSize)
|
||||
self.SetMinSize(newSize)
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ class VectorPicker(wx.Window):
|
||||
self._directionOnly = kwargs.pop('directionOnly', False)
|
||||
super().__init__(*args, **kwargs)
|
||||
self._fontsize = max(1, float(kwargs.pop('fontsize', 8 / self.GetContentScaleFactor())))
|
||||
self._font = wx.Font(self._fontsize, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False)
|
||||
self._font = wx.Font(round(self._fontsize), wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False)
|
||||
self._angle = 0
|
||||
self.__length = 1
|
||||
self._left = False
|
||||
@@ -76,7 +76,7 @@ class VectorPicker(wx.Window):
|
||||
self.__length = newLength
|
||||
|
||||
def DoGetBestSize(self):
|
||||
return wx.Size(self._size, self._size)
|
||||
return wx.Size(round(self._size), round(self._size))
|
||||
|
||||
def AcceptsFocusFromKeyboard(self):
|
||||
return False
|
||||
@@ -121,35 +121,37 @@ class VectorPicker(wx.Window):
|
||||
|
||||
radius = min(width, height) / 2 - 2
|
||||
dc.SetBrush(wx.WHITE_BRUSH)
|
||||
dc.DrawCircle(radius + 2, radius + 2, radius)
|
||||
dc.DrawCircle(round(radius + 2), round(radius + 2), round(radius))
|
||||
a = math.radians(self._angle + self._offset)
|
||||
x = math.cos(a) * radius
|
||||
y = math.sin(a) * radius
|
||||
# See PR #2260 on why this is needed
|
||||
pointRadius = 2 / self.GetContentScaleFactor() if 'wxGTK' in wx.PlatformInfo else 2
|
||||
dc.DrawLine(radius + 2, radius + 2, radius + 2 + x * self._length, radius + 2 - y * self._length)
|
||||
dc.DrawLine(
|
||||
round(radius + 2), round(radius + 2),
|
||||
round(radius + 2 + x * self._length), round(radius + 2 - y * self._length))
|
||||
dc.SetBrush(wx.BLACK_BRUSH)
|
||||
dc.DrawCircle(radius + 2 + x * self._length, radius + 2 - y * self._length, pointRadius)
|
||||
dc.DrawCircle(round(radius + 2 + x * self._length), round(radius + 2 - y * self._length), round(pointRadius))
|
||||
|
||||
if self._label:
|
||||
labelText = self._label
|
||||
labelTextW, labelTextH = dc.GetTextExtent(labelText)
|
||||
labelTextX = (radius * 2 + 4 - labelTextW) if (self._labelpos & 1) else 0
|
||||
labelTextY = (radius * 2 + 4 - labelTextH) if (self._labelpos & 2) else 0
|
||||
dc.DrawText(labelText, labelTextX, labelTextY)
|
||||
dc.DrawText(labelText, round(labelTextX), round(labelTextY))
|
||||
|
||||
if not self._directionOnly:
|
||||
lengthText = '%d%%' % (100 * self._length,)
|
||||
lengthTextW, lengthTextH = dc.GetTextExtent(lengthText)
|
||||
lengthTextX = radius + 2 + x / 2 - y / 3 - lengthTextW / 2
|
||||
lengthTextY = radius + 2 - y / 2 - x / 3 - lengthTextH / 2
|
||||
dc.DrawText(lengthText, lengthTextX, lengthTextY)
|
||||
dc.DrawText(lengthText, round(lengthTextX), round(lengthTextY))
|
||||
|
||||
angleText = '%d\u00B0' % (self._angle,)
|
||||
angleTextW, angleTextH = dc.GetTextExtent(angleText)
|
||||
angleTextX = radius + 2 - x / 2 - angleTextW / 2
|
||||
angleTextY = radius + 2 + y / 2 - angleTextH / 2
|
||||
dc.DrawText(angleText, angleTextX, angleTextY)
|
||||
dc.DrawText(angleText, round(angleTextX), round(angleTextY))
|
||||
|
||||
def OnEraseBackground(self, event):
|
||||
pass
|
||||
|
||||
@@ -145,6 +145,11 @@ class TargetWrapper(BaseWrapper):
|
||||
else:
|
||||
return em, therm, kin, explo
|
||||
|
||||
def getFullHp(self):
|
||||
if self.isProfile:
|
||||
return self.item.hp
|
||||
if self.isFit:
|
||||
return self.item.hp.get('shield', 0) + self.item.hp.get('armor', 0) + self.item.hp.get('hull', 0)
|
||||
|
||||
|
||||
def _getShieldResists(ship):
|
||||
@@ -199,7 +204,7 @@ def _getAutoResists(fit):
|
||||
armorHp = hpData['armor']
|
||||
hullHp = hpData['hull']
|
||||
uniformDamagePattern = DamagePattern(emAmount=25, thermalAmount=25, kineticAmount=25, explosiveAmount=25)
|
||||
ehpData = uniformDamagePattern.calculateEhp(fit)
|
||||
ehpData = uniformDamagePattern.calculateEhp(fit.ship)
|
||||
shieldEhp = ehpData['shield']
|
||||
armorEhp = ehpData['armor']
|
||||
hullEhp = ehpData['hull']
|
||||
|
||||
@@ -212,7 +212,7 @@ class AttributeGauge(wx.Window):
|
||||
for x in range(1, 20):
|
||||
dc.SetBrush(wx.Brush(wx.LIGHT_GREY))
|
||||
dc.SetPen(wx.Pen(wx.LIGHT_GREY))
|
||||
dc.DrawRectangle(x * 10, 1, 1, rect.height)
|
||||
dc.DrawRectangle(round(x * 10), 1, 1, round(rect.height))
|
||||
|
||||
dc.SetBrush(wx.Brush(colour))
|
||||
dc.SetPen(wx.Pen(colour))
|
||||
@@ -222,19 +222,19 @@ class AttributeGauge(wx.Window):
|
||||
|
||||
if value >= 0:
|
||||
padding = (half if is_even else math.ceil(half - 1)) + 1
|
||||
dc.DrawRectangle(padding, 1, w, rect.height)
|
||||
dc.DrawRectangle(round(padding), 1, round(w), round(rect.height))
|
||||
else:
|
||||
padding = half - w + 1 if is_even else math.ceil(half) - (w - 1)
|
||||
dc.DrawRectangle(padding, 1, w, rect.height)
|
||||
dc.DrawRectangle(round(padding), 1, round(w), round(rect.height))
|
||||
|
||||
if self.leading_edge and (self.edge_on_neutral or value != 0):
|
||||
dc.SetPen(wx.Pen(wx.WHITE))
|
||||
dc.SetBrush(wx.Brush(wx.WHITE))
|
||||
|
||||
if value > 0:
|
||||
dc.DrawRectangle(min(padding + w, rect.width), 1, 1, rect.height)
|
||||
dc.DrawRectangle(round(min(padding + w, rect.width)), 1, 1, round(rect.height))
|
||||
else:
|
||||
dc.DrawRectangle(max(padding - 1, 1), 1, 1, rect.height)
|
||||
dc.DrawRectangle(round(max(padding - 1, 1)), 1, 1, round(rect.height))
|
||||
|
||||
def OnTimer(self, event):
|
||||
old_value = self._old_percentage
|
||||
|
||||
@@ -103,10 +103,9 @@ class BitmapLoader:
|
||||
pyfalog.warning("Missing icon file: {0}/{1}".format(location, filename))
|
||||
return None
|
||||
|
||||
bmp: wx.Bitmap = img.ConvertToBitmap()
|
||||
if scale > 1:
|
||||
bmp.SetSize((bmp.GetWidth() // scale, bmp.GetHeight() // scale))
|
||||
return bmp
|
||||
return img.Scale(round(img.GetWidth() // scale), round(img.GetHeight() // scale)).ConvertToBitmap()
|
||||
return img.ConvertToBitmap()
|
||||
|
||||
@classmethod
|
||||
def loadScaledBitmap(cls, name, location, scale=0):
|
||||
|
||||
@@ -43,6 +43,10 @@ class BoosterViewDrop(wx.DropTarget):
|
||||
if self.GetData():
|
||||
dragged_data = DragDropHelper.data
|
||||
data = dragged_data.split(':')
|
||||
|
||||
if dragged_data is None:
|
||||
return t
|
||||
|
||||
self.dropFn(x, y, data)
|
||||
return t
|
||||
|
||||
|
||||
@@ -41,6 +41,10 @@ class CargoViewDrop(wx.DropTarget):
|
||||
def OnData(self, x, y, t):
|
||||
if self.GetData():
|
||||
dragged_data = DragDropHelper.data
|
||||
|
||||
if dragged_data is None:
|
||||
return t
|
||||
|
||||
data = dragged_data.split(':')
|
||||
self.dropFn(x, y, data)
|
||||
return t
|
||||
|
||||
@@ -56,6 +56,10 @@ class CommandViewDrop(wx.DropTarget):
|
||||
def OnData(self, x, y, t):
|
||||
if self.GetData():
|
||||
dragged_data = DragDropHelper.data
|
||||
|
||||
if dragged_data is None:
|
||||
return t
|
||||
|
||||
data = dragged_data.split(':')
|
||||
self.dropFn(x, y, data)
|
||||
return t
|
||||
|
||||
@@ -52,6 +52,10 @@ class DroneViewDrop(wx.DropTarget):
|
||||
def OnData(self, x, y, t):
|
||||
if self.GetData():
|
||||
dragged_data = DragDropHelper.data
|
||||
|
||||
if dragged_data is None:
|
||||
return t
|
||||
|
||||
data = dragged_data.split(':')
|
||||
self.dropFn(x, y, data)
|
||||
return t
|
||||
@@ -66,6 +70,8 @@ class DroneView(Display):
|
||||
"Max Range",
|
||||
"Miscellanea",
|
||||
"attr:maxVelocity",
|
||||
"Drone HP",
|
||||
"Drone Regen",
|
||||
"Price",
|
||||
]
|
||||
|
||||
@@ -193,7 +199,11 @@ class DroneView(Display):
|
||||
|
||||
@staticmethod
|
||||
def droneKey(drone):
|
||||
groupName = Market.getInstance().getMarketGroupByItem(drone.item).marketGroupName
|
||||
if drone.isMutated:
|
||||
item = drone.baseItem
|
||||
else:
|
||||
item = drone.item
|
||||
groupName = Market.getInstance().getMarketGroupByItem(item).marketGroupName
|
||||
return (DRONE_ORDER.index(groupName), drone.isMutated, drone.fullName)
|
||||
|
||||
def fitChanged(self, event):
|
||||
|
||||
@@ -34,7 +34,10 @@ from service.fit import Fit
|
||||
from service.market import Market
|
||||
|
||||
|
||||
FIGHTER_ORDER = ('Light Fighter', 'Heavy Fighter', 'Support Fighter')
|
||||
FIGHTER_ORDER = (
|
||||
'Light Fighter', 'Structure Light Fighter',
|
||||
'Heavy Fighter', 'Structure Heavy Fighter',
|
||||
'Support Fighter', 'Structure Support Fighter')
|
||||
_t = wx.GetTranslation
|
||||
|
||||
|
||||
@@ -49,6 +52,10 @@ class FighterViewDrop(wx.DropTarget):
|
||||
def OnData(self, x, y, t):
|
||||
if self.GetData():
|
||||
dragged_data = DragDropHelper.data
|
||||
|
||||
if dragged_data is None:
|
||||
return t
|
||||
|
||||
data = dragged_data.split(':')
|
||||
self.dropFn(x, y, data)
|
||||
return t
|
||||
@@ -151,6 +158,8 @@ class FighterDisplay(d.Display):
|
||||
# "Max Range",
|
||||
# "Miscellanea",
|
||||
"attr:maxVelocity",
|
||||
"Drone HP",
|
||||
"Drone Regen",
|
||||
"Fighter Abilities",
|
||||
"Price",
|
||||
]
|
||||
|
||||
@@ -46,6 +46,10 @@ class ImplantViewDrop(wx.DropTarget):
|
||||
def OnData(self, x, y, t):
|
||||
if self.GetData():
|
||||
dragged_data = DragDropHelper.data
|
||||
|
||||
if dragged_data is None:
|
||||
return t
|
||||
|
||||
data = dragged_data.split(':')
|
||||
self.dropFn(x, y, data)
|
||||
return t
|
||||
|
||||
@@ -65,6 +65,10 @@ class ProjectedViewDrop(wx.DropTarget):
|
||||
def OnData(self, x, y, t):
|
||||
if self.GetData():
|
||||
dragged_data = DragDropHelper.data
|
||||
|
||||
if dragged_data is None:
|
||||
return t
|
||||
|
||||
data = dragged_data.split(':')
|
||||
self.dropFn(x, y, data)
|
||||
return t
|
||||
|
||||
@@ -11,6 +11,7 @@ from gui.builtinContextMenus import graphFitAmmoPicker
|
||||
from gui.builtinContextMenus import shipModeChange
|
||||
from gui.builtinContextMenus import moduleAmmoChange
|
||||
from gui.builtinContextMenus import moduleSpool
|
||||
from gui.builtinContextMenus import moduleRahPattern
|
||||
from gui.builtinContextMenus import boosterSideEffects
|
||||
from gui.builtinContextMenus import fighterAbilities
|
||||
from gui.builtinContextMenus import resistMode
|
||||
@@ -19,6 +20,7 @@ from gui.builtinContextMenus.targetProfile import editor
|
||||
from gui.builtinContextMenus import itemStats
|
||||
from gui.builtinContextMenus import itemMarketJump
|
||||
from gui.builtinContextMenus import fitSystemSecurity # Not really an item info but want to keep it here
|
||||
from gui.builtinContextMenus import fitPilotSecurity # Not really an item info but want to keep it here
|
||||
from gui.builtinContextMenus import shipJump
|
||||
# Generic item manipulations
|
||||
from gui.builtinContextMenus import itemRemove
|
||||
@@ -34,6 +36,7 @@ from gui.builtinContextMenus import skillAffectors
|
||||
from gui.builtinContextMenus import itemFill
|
||||
from gui.builtinContextMenus import droneAddStack
|
||||
from gui.builtinContextMenus import cargoAdd
|
||||
from gui.builtinContextMenus import cargoFill
|
||||
from gui.builtinContextMenus import cargoAddAmmo
|
||||
from gui.builtinContextMenus import itemProject
|
||||
from gui.builtinContextMenus import ammoToDmgPattern
|
||||
|
||||
@@ -25,7 +25,7 @@ class AddToCargoAmmo(ContextMenuSingle):
|
||||
return True
|
||||
|
||||
def getText(self, callingWindow, itmContext, mainItem):
|
||||
if mainItem.marketGroup.name == "Scan Probes":
|
||||
if mainItem.marketGroup and mainItem.marketGroup.name == "Scan Probes":
|
||||
return _t("Add {0} to Cargo (x8)").format(itmContext)
|
||||
|
||||
return _t("Add {0} to Cargo (x1000)").format(itmContext)
|
||||
@@ -34,7 +34,7 @@ class AddToCargoAmmo(ContextMenuSingle):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
typeID = int(mainItem.ID)
|
||||
|
||||
if mainItem.marketGroup.name == "Scan Probes":
|
||||
if mainItem.marketGroup and mainItem.marketGroup.name == "Scan Probes":
|
||||
command = cmd.GuiAddCargoCommand(fitID=fitID, itemID=typeID, amount=8)
|
||||
else:
|
||||
command = cmd.GuiAddCargoCommand(fitID=fitID, itemID=typeID, amount=1000)
|
||||
|
||||
68
gui/builtinContextMenus/cargoFill.py
Normal file
68
gui/builtinContextMenus/cargoFill.py
Normal file
@@ -0,0 +1,68 @@
|
||||
import wx
|
||||
|
||||
import gui.fitCommands as cmd
|
||||
import gui.mainFrame
|
||||
from gui.contextMenu import ContextMenuSingle
|
||||
from service.fit import Fit
|
||||
from eos.saveddata.cargo import Cargo
|
||||
|
||||
_t = wx.GetTranslation
|
||||
|
||||
|
||||
class FillCargoWithItem(ContextMenuSingle):
|
||||
def __init__(self):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
|
||||
def display(self, callingWindow, srcContext, mainItem):
|
||||
if srcContext not in ("marketItemGroup", "marketItemMisc", "cargoItem"):
|
||||
return False
|
||||
|
||||
if mainItem is None:
|
||||
return False
|
||||
|
||||
if self.mainFrame.getActiveFit() is None:
|
||||
return False
|
||||
|
||||
if srcContext in ("marketItemGroup", "marketItemMisc"):
|
||||
if not (mainItem.isCharge or mainItem.isCommodity):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def getText(self, callingWindow, itmContext, mainItem):
|
||||
return _t("Fill Cargo With {0}").format(itmContext)
|
||||
|
||||
def activate(self, callingWindow, fullContext, mainItem, i):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
|
||||
if isinstance(mainItem, Cargo):
|
||||
itemVolume = mainItem.item.attributes['volume'].value
|
||||
itemID = mainItem.itemID
|
||||
else:
|
||||
itemVolume = mainItem.attributes['volume'].value
|
||||
itemID = int(mainItem.ID)
|
||||
|
||||
if itemVolume is None or itemVolume <= 0:
|
||||
return
|
||||
|
||||
# Calculate how many items can fit in the cargo
|
||||
cargoCapacity = fit.ship.getModifiedItemAttr("capacity")
|
||||
currentCargoVolume = fit.cargoBayUsed
|
||||
availableVolume = cargoCapacity - currentCargoVolume
|
||||
|
||||
if availableVolume <= 0:
|
||||
return
|
||||
|
||||
# Calculate maximum amount that can fit
|
||||
maxAmount = int(availableVolume / itemVolume)
|
||||
if maxAmount <= 0:
|
||||
return
|
||||
|
||||
# Add the items to cargo
|
||||
command = cmd.GuiAddCargoCommand(fitID=fitID, itemID=itemID, amount=maxAmount)
|
||||
if self.mainFrame.command.Submit(command):
|
||||
self.mainFrame.additionsPane.select("Cargo", focus=False)
|
||||
|
||||
|
||||
FillCargoWithItem.register()
|
||||
@@ -44,7 +44,6 @@ class AddCommandFit(ContextMenuUnconditional):
|
||||
def display(self, callingWindow, srcContext):
|
||||
if self.mainFrame.getActiveFit() is None or len(self.__class__.commandFits) == 0 or srcContext != "commandView":
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def getText(self, callingWindow, itmContext):
|
||||
@@ -52,6 +51,8 @@ class AddCommandFit(ContextMenuUnconditional):
|
||||
|
||||
def addFit(self, menu, fit, includeShip=False):
|
||||
label = fit.name if not includeShip else "({}) {}".format(fit.ship.item.name, fit.name)
|
||||
if not label:
|
||||
label = ' '
|
||||
id = ContextMenuUnconditional.nextID()
|
||||
self.fitMenuItemIds[id] = fit
|
||||
menuItem = wx.MenuItem(menu, id, label)
|
||||
|
||||
@@ -1,20 +1,16 @@
|
||||
from collections import OrderedDict
|
||||
from itertools import chain
|
||||
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
|
||||
import gui.globalEvents as GE
|
||||
import gui.mainFrame
|
||||
from gui.builtinContextMenus.shared.patterns import DamagePatternMixin
|
||||
from gui.contextMenu import ContextMenuUnconditional
|
||||
from gui.utils.sorter import smartSort
|
||||
from service.damagePattern import DamagePattern as DmgPatternSvc
|
||||
from service.fit import Fit
|
||||
|
||||
_t = wx.GetTranslation
|
||||
|
||||
|
||||
class ChangeDamagePattern(ContextMenuUnconditional):
|
||||
class ChangeDamagePattern(ContextMenuUnconditional, DamagePatternMixin):
|
||||
|
||||
def __init__(self):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
@@ -27,29 +23,12 @@ class ChangeDamagePattern(ContextMenuUnconditional):
|
||||
return self.mainFrame.getActiveFit() is not None
|
||||
|
||||
def getText(self, callingWindow, itmContext):
|
||||
sDP = DmgPatternSvc.getInstance()
|
||||
sFit = Fit.getInstance()
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
self.fit = sFit.getFit(fitID)
|
||||
|
||||
builtinPatterns = sDP.getBuiltinDamagePatternList()
|
||||
userPatterns = sorted(sDP.getUserDamagePatternList(), key=lambda p: smartSort(p.fullName))
|
||||
# Order here is important: patterns with duplicate names from the latter will overwrite
|
||||
# patterns from the former
|
||||
self.patterns = sorted(
|
||||
chain(builtinPatterns, userPatterns),
|
||||
key=lambda p: p.fullName not in ["Uniform", "Selected Ammo"])
|
||||
|
||||
self.patternEventMap = {}
|
||||
self.items = (OrderedDict(), OrderedDict())
|
||||
for pattern in self.patterns:
|
||||
container = self.items
|
||||
for categoryName in pattern.hierarchy:
|
||||
categoryName = _t(categoryName) if pattern.builtin else categoryName
|
||||
container = container[1].setdefault(categoryName, (OrderedDict(), OrderedDict()))
|
||||
shortName = _t(pattern.shortName) if pattern.builtin else pattern.shortName
|
||||
container[0][shortName] = pattern
|
||||
|
||||
self.patterns = self._getPatterns()
|
||||
self.items = self._getItems(self.patterns)
|
||||
return list(self.items[0].keys()) + list(self.items[1].keys())
|
||||
|
||||
def _addPattern(self, parentMenu, pattern, name):
|
||||
|
||||
@@ -65,7 +65,6 @@ class DroneStackSplit(wx.Dialog):
|
||||
|
||||
self.input = wx.TextCtrl(self, wx.ID_ANY, style=wx.TE_PROCESS_ENTER)
|
||||
self.input.SetValue(str(value))
|
||||
self.input.SelectAll()
|
||||
|
||||
bSizer1.Add(self.input, 0, wx.LEFT | wx.RIGHT | wx.EXPAND, 15)
|
||||
|
||||
@@ -75,12 +74,13 @@ class DroneStackSplit(wx.Dialog):
|
||||
bSizer3.Add(self.CreateStdDialogButtonSizer(wx.OK | wx.CANCEL), 0, wx.EXPAND)
|
||||
bSizer1.Add(bSizer3, 0, wx.ALL | wx.EXPAND, 10)
|
||||
|
||||
self.input.SetFocus()
|
||||
self.input.Bind(wx.EVT_CHAR, self.onChar)
|
||||
self.input.Bind(wx.EVT_TEXT_ENTER, self.processEnter)
|
||||
self.SetSizer(bSizer1)
|
||||
self.CenterOnParent()
|
||||
self.Fit()
|
||||
self.CenterOnParent()
|
||||
self.input.SetFocus()
|
||||
self.input.SelectAll()
|
||||
|
||||
def processEnter(self, evt):
|
||||
self.EndModal(wx.ID_OK)
|
||||
|
||||
@@ -123,9 +123,11 @@ class AddEnvironmentEffect(ContextMenuUnconditional):
|
||||
data.groups[_t('Abyssal Weather')] = self.getAbyssalWeather()
|
||||
data.groups[_t('Sansha Incursion')] = self.getEffectBeacons(
|
||||
_t('ContextMenu|ProjectedEffectManipulation|Sansha Incursion'))
|
||||
data.groups[_t('Triglavian Invasion')] = self.getEffectBeacons(
|
||||
_t('ContextMenu|ProjectedEffectManipulation|Triglavian Invasion'))
|
||||
data.groups[_t('Triglavian Invasion')].groups[_t('Destructible Beacons')] = self.getDestructibleBeacons()
|
||||
data.groups[_t('Drifter Incursion')] = self.getDrifterIncursion()
|
||||
data.groups[_t('Triglavian Invasion')] = self.getInvasionBeacons()
|
||||
data.groups[_t('Pirate Insurgency')] = self.getEffectBeacons(
|
||||
_t('ContextMenu|ProjectedEffectManipulation|Insurgency'),
|
||||
extra_garbage=(_t('ContextMenu|ProjectedEffectManipulation|Beacon'),))
|
||||
return data
|
||||
|
||||
def getEffectBeacons(self, *groups, extra_garbage=()):
|
||||
@@ -176,7 +178,6 @@ class AddEnvironmentEffect(ContextMenuUnconditional):
|
||||
container.append(Entry(beacon.ID, beaconname, shortname))
|
||||
# Break loop on 1st result
|
||||
break
|
||||
data.sort()
|
||||
return data
|
||||
|
||||
def getAbyssalWeather(self):
|
||||
@@ -233,5 +234,31 @@ class AddEnvironmentEffect(ContextMenuUnconditional):
|
||||
data.sort()
|
||||
return data
|
||||
|
||||
def getDrifterIncursion(self):
|
||||
data = self.getEffectBeacons(_t('ContextMenu|ProjectedEffectManipulation|Drifter Incursion'))
|
||||
# Drifter Crisis
|
||||
item = Market.getInstance().getItem(87294)
|
||||
data.items.append(Entry(item.ID, item.name, item.name))
|
||||
return data
|
||||
|
||||
def getInvasionBeacons(self):
|
||||
data = self.getDestructibleBeacons()
|
||||
# Trig Minor Victory
|
||||
item = Market.getInstance().getItem(87177)
|
||||
data.items.append(Entry(item.ID, item.name, item.name))
|
||||
# Trig Final Liminality
|
||||
item = Market.getInstance().getItem(87164)
|
||||
data.items.append(Entry(item.ID, item.name, item.name))
|
||||
# Turnur weather
|
||||
item = Market.getInstance().getItem(74002)
|
||||
data.items.append(Entry(item.ID, item.name, item.name))
|
||||
return data
|
||||
|
||||
def getInsurgencyBeacons(self):
|
||||
data = self.getDestructibleBeacons()
|
||||
# Suppression Interdiction Range Beacon
|
||||
item = Market.getInstance().getItem(79839)
|
||||
data.items.append(Entry(item.ID, item.name, item.name))
|
||||
return data
|
||||
|
||||
AddEnvironmentEffect.register()
|
||||
|
||||
157
gui/builtinContextMenus/fitPilotSecurity.py
Normal file
157
gui/builtinContextMenus/fitPilotSecurity.py
Normal file
@@ -0,0 +1,157 @@
|
||||
import re
|
||||
|
||||
import wx
|
||||
|
||||
import gui.fitCommands as cmd
|
||||
import gui.mainFrame
|
||||
from gui.contextMenu import ContextMenuUnconditional
|
||||
from service.fit import Fit
|
||||
|
||||
_t = wx.GetTranslation
|
||||
|
||||
|
||||
class FitPilotSecurityMenu(ContextMenuUnconditional):
|
||||
|
||||
def __init__(self):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
|
||||
def display(self, callingWindow, srcContext):
|
||||
if srcContext != "fittingShip":
|
||||
return False
|
||||
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
|
||||
if fit.ship.name not in ('Pacifier', 'Enforcer', 'Marshal', 'Sidewinder', 'Cobra', 'Python'):
|
||||
return
|
||||
|
||||
return True
|
||||
|
||||
def getText(self, callingWindow, itmContext):
|
||||
return _t("Pilot Security Status")
|
||||
|
||||
def addOption(self, menu, optionLabel, optionValue):
|
||||
id = ContextMenuUnconditional.nextID()
|
||||
self.optionIds[id] = optionValue
|
||||
menuItem = wx.MenuItem(menu, id, optionLabel, kind=wx.ITEM_CHECK)
|
||||
menu.Bind(wx.EVT_MENU, self.handleMode, menuItem)
|
||||
return menuItem
|
||||
|
||||
def addOptionCustom(self, menu, optionLabel):
|
||||
id = ContextMenuUnconditional.nextID()
|
||||
menuItem = wx.MenuItem(menu, id, optionLabel, kind=wx.ITEM_CHECK)
|
||||
menu.Bind(wx.EVT_MENU, self.handleModeCustom, menuItem)
|
||||
return menuItem
|
||||
|
||||
def getSubMenu(self, callingWindow, context, rootMenu, i, pitem):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
msw = True if "wxMSW" in wx.PlatformInfo else False
|
||||
self.optionIds = {}
|
||||
sub = wx.Menu()
|
||||
presets = (-10, -8, -6, -4, -2, 0, 1, 2, 3, 4, 5)
|
||||
# Inherit
|
||||
char_sec_status = round(fit.character.secStatus, 2)
|
||||
menuItem = self.addOption(rootMenu if msw else sub, _t('Character') + f' ({char_sec_status})', None)
|
||||
sub.Append(menuItem)
|
||||
menuItem.Check(fit.pilotSecurity is None)
|
||||
# Custom
|
||||
label = _t('Custom')
|
||||
is_checked = False
|
||||
if fit.pilotSecurity is not None and fit.pilotSecurity not in presets:
|
||||
sec_status = round(fit.getPilotSecurity(), 2)
|
||||
label += f' ({sec_status})'
|
||||
is_checked = True
|
||||
menuItem = self.addOptionCustom(rootMenu if msw else sub, label)
|
||||
sub.Append(menuItem)
|
||||
menuItem.Check(is_checked)
|
||||
sub.AppendSeparator()
|
||||
# Predefined options
|
||||
for sec_status in presets:
|
||||
menuItem = self.addOption(rootMenu if msw else sub, str(sec_status), sec_status)
|
||||
sub.Append(menuItem)
|
||||
menuItem.Check(fit.pilotSecurity == sec_status)
|
||||
return sub
|
||||
|
||||
def handleMode(self, event):
|
||||
optionValue = self.optionIds[event.Id]
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeFitPilotSecurityCommand(
|
||||
fitID=self.mainFrame.getActiveFit(),
|
||||
secStatus=optionValue))
|
||||
|
||||
def handleModeCustom(self, event):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
sec_status = fit.getPilotSecurity()
|
||||
|
||||
with SecStatusChanger(self.mainFrame, value=sec_status) as dlg:
|
||||
if dlg.ShowModal() == wx.ID_OK:
|
||||
cleanInput = re.sub(r'[^0-9.\-+]', '', dlg.input.GetLineText(0).strip())
|
||||
if cleanInput:
|
||||
try:
|
||||
cleanInputFloat = float(cleanInput)
|
||||
except ValueError:
|
||||
return
|
||||
else:
|
||||
return
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeFitPilotSecurityCommand(
|
||||
fitID=fitID, secStatus=max(-10.0, min(5.0, cleanInputFloat))))
|
||||
|
||||
|
||||
FitPilotSecurityMenu.register()
|
||||
|
||||
|
||||
class SecStatusChanger(wx.Dialog):
|
||||
|
||||
def __init__(self, parent, value):
|
||||
super().__init__(parent, title=_t('Change Security Status'), style=wx.DEFAULT_DIALOG_STYLE)
|
||||
self.SetMinSize((346, 156))
|
||||
|
||||
bSizer1 = wx.BoxSizer(wx.VERTICAL)
|
||||
|
||||
bSizer2 = wx.BoxSizer(wx.VERTICAL)
|
||||
text = wx.StaticText(self, wx.ID_ANY, _t('Security Status (min -10.0, max 5.0):'))
|
||||
bSizer2.Add(text, 0)
|
||||
|
||||
bSizer1.Add(bSizer2, 0, wx.ALL, 10)
|
||||
|
||||
self.input = wx.TextCtrl(self, wx.ID_ANY, style=wx.TE_PROCESS_ENTER)
|
||||
if value is None:
|
||||
value = '0.0'
|
||||
else:
|
||||
if value == int(value):
|
||||
value = int(value)
|
||||
value = str(value)
|
||||
self.input.SetValue(value)
|
||||
|
||||
bSizer1.Add(self.input, 0, wx.LEFT | wx.RIGHT | wx.EXPAND, 15)
|
||||
|
||||
bSizer3 = wx.BoxSizer(wx.VERTICAL)
|
||||
bSizer3.Add(wx.StaticLine(self, wx.ID_ANY), 0, wx.BOTTOM | wx.EXPAND, 15)
|
||||
|
||||
bSizer3.Add(self.CreateStdDialogButtonSizer(wx.OK | wx.CANCEL), 0, wx.EXPAND)
|
||||
bSizer1.Add(bSizer3, 0, wx.ALL | wx.EXPAND, 10)
|
||||
|
||||
self.input.Bind(wx.EVT_CHAR, self.onChar)
|
||||
self.input.Bind(wx.EVT_TEXT_ENTER, self.processEnter)
|
||||
self.SetSizer(bSizer1)
|
||||
self.Fit()
|
||||
self.CenterOnParent()
|
||||
self.input.SetFocus()
|
||||
self.input.SelectAll()
|
||||
|
||||
def processEnter(self, evt):
|
||||
self.EndModal(wx.ID_OK)
|
||||
|
||||
# checks to make sure it's valid number
|
||||
@staticmethod
|
||||
def onChar(event):
|
||||
key = event.GetKeyCode()
|
||||
|
||||
acceptable_characters = '1234567890.-+'
|
||||
acceptable_keycode = [3, 22, 13, 8, 127] # modifiers like delete, copy, paste
|
||||
if key in acceptable_keycode or key >= 255 or (key < 255 and chr(key) in acceptable_characters):
|
||||
event.Skip()
|
||||
return
|
||||
else:
|
||||
return False
|
||||
@@ -59,7 +59,6 @@ class NameDialog(wx.Dialog):
|
||||
else:
|
||||
value = str(value)
|
||||
self.input.SetValue(value)
|
||||
self.input.SelectAll()
|
||||
|
||||
bSizer1.Add(self.input, 0, wx.LEFT | wx.RIGHT | wx.EXPAND, 15)
|
||||
|
||||
@@ -69,11 +68,12 @@ class NameDialog(wx.Dialog):
|
||||
bSizer3.Add(self.CreateStdDialogButtonSizer(wx.OK | wx.CANCEL), 0, wx.EXPAND)
|
||||
bSizer1.Add(bSizer3, 0, wx.ALL | wx.EXPAND, 10)
|
||||
|
||||
self.input.SetFocus()
|
||||
self.input.Bind(wx.EVT_TEXT_ENTER, self.processEnter)
|
||||
self.SetSizer(bSizer1)
|
||||
self.CenterOnParent()
|
||||
self.Fit()
|
||||
self.CenterOnParent()
|
||||
self.input.SetFocus()
|
||||
self.input.SelectAll()
|
||||
|
||||
def processEnter(self, evt):
|
||||
self.EndModal(wx.ID_OK)
|
||||
|
||||
@@ -108,7 +108,6 @@ class AmountChanger(wx.Dialog):
|
||||
|
||||
self.input = wx.TextCtrl(self, wx.ID_ANY, style=wx.TE_PROCESS_ENTER)
|
||||
self.input.SetValue(str(value))
|
||||
self.input.SelectAll()
|
||||
|
||||
bSizer1.Add(self.input, 0, wx.LEFT | wx.RIGHT | wx.EXPAND, 15)
|
||||
|
||||
@@ -118,12 +117,13 @@ class AmountChanger(wx.Dialog):
|
||||
bSizer3.Add(self.CreateStdDialogButtonSizer(wx.OK | wx.CANCEL), 0, wx.EXPAND)
|
||||
bSizer1.Add(bSizer3, 0, wx.ALL | wx.EXPAND, 10)
|
||||
|
||||
self.input.SetFocus()
|
||||
self.input.Bind(wx.EVT_CHAR, self.onChar)
|
||||
self.input.Bind(wx.EVT_TEXT_ENTER, self.processEnter)
|
||||
self.SetSizer(bSizer1)
|
||||
self.CenterOnParent()
|
||||
self.Fit()
|
||||
self.CenterOnParent()
|
||||
self.input.SetFocus()
|
||||
self.input.SelectAll()
|
||||
|
||||
def processEnter(self, evt):
|
||||
self.EndModal(wx.ID_OK)
|
||||
|
||||
@@ -13,6 +13,16 @@ from service.fit import Fit
|
||||
_t = wx.GetTranslation
|
||||
|
||||
|
||||
GLORIFIED_PREFIX = 'Gl. '
|
||||
|
||||
|
||||
def nameSorter(mutaplasmid):
|
||||
name = mutaplasmid.shortName
|
||||
if name.startswith(GLORIFIED_PREFIX):
|
||||
return name[len(GLORIFIED_PREFIX):], True
|
||||
return name, False
|
||||
|
||||
|
||||
class ChangeItemMutation(ContextMenuSingle):
|
||||
|
||||
def __init__(self):
|
||||
@@ -45,7 +55,7 @@ class ChangeItemMutation(ContextMenuSingle):
|
||||
|
||||
menu = rootMenu if msw else sub
|
||||
|
||||
for mutaplasmid in mainItem.item.mutaplasmids:
|
||||
for mutaplasmid in sorted(mainItem.item.mutaplasmids, key=nameSorter):
|
||||
id = ContextMenuSingle.nextID()
|
||||
self.eventIDs[id] = (mutaplasmid, mainItem)
|
||||
mItem = wx.MenuItem(menu, id, mutaplasmid.shortName)
|
||||
|
||||
@@ -94,7 +94,6 @@ class RangeChanger(wx.Dialog):
|
||||
value = int(value)
|
||||
value = str(value)
|
||||
self.input.SetValue(value)
|
||||
self.input.SelectAll()
|
||||
|
||||
bSizer1.Add(self.input, 0, wx.LEFT | wx.RIGHT | wx.EXPAND, 15)
|
||||
|
||||
@@ -104,12 +103,13 @@ class RangeChanger(wx.Dialog):
|
||||
bSizer3.Add(self.CreateStdDialogButtonSizer(wx.OK | wx.CANCEL), 0, wx.EXPAND)
|
||||
bSizer1.Add(bSizer3, 0, wx.ALL | wx.EXPAND, 10)
|
||||
|
||||
self.input.SetFocus()
|
||||
self.input.Bind(wx.EVT_CHAR, self.onChar)
|
||||
self.input.Bind(wx.EVT_TEXT_ENTER, self.processEnter)
|
||||
self.SetSizer(bSizer1)
|
||||
self.CenterOnParent()
|
||||
self.Fit()
|
||||
self.CenterOnParent()
|
||||
self.input.SetFocus()
|
||||
self.input.SelectAll()
|
||||
|
||||
def processEnter(self, evt):
|
||||
self.EndModal(wx.ID_OK)
|
||||
|
||||
97
gui/builtinContextMenus/moduleRahPattern.py
Normal file
97
gui/builtinContextMenus/moduleRahPattern.py
Normal file
@@ -0,0 +1,97 @@
|
||||
from collections import OrderedDict
|
||||
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
|
||||
import gui.globalEvents as GE
|
||||
import gui.mainFrame
|
||||
from gui.builtinContextMenus.shared.patterns import DamagePatternMixin
|
||||
from gui.contextMenu import ContextMenuSingle
|
||||
from service.fit import Fit
|
||||
|
||||
_t = wx.GetTranslation
|
||||
|
||||
|
||||
class ChangeRahPattern(ContextMenuSingle, DamagePatternMixin):
|
||||
|
||||
def __init__(self):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
|
||||
def display(self, callingWindow, srcContext, mainItem):
|
||||
if srcContext != 'fittingModule':
|
||||
return False
|
||||
|
||||
if self.mainFrame.getActiveFit() is None:
|
||||
return False
|
||||
|
||||
if (mainItem is None or getattr(mainItem, "isEmpty", False)) and srcContext != "fittingShip":
|
||||
return False
|
||||
|
||||
if mainItem.item.group.name != 'Armor Resistance Shift Hardener':
|
||||
return False
|
||||
|
||||
self.module = mainItem
|
||||
self.patternEventMap = {}
|
||||
self.patterns = self._getPatterns()
|
||||
self.items = self._getItems(self.patterns)
|
||||
return True
|
||||
|
||||
def getText(self, callingWindow, itmContext, mainItem):
|
||||
return _t('RAH Damage Pattern')
|
||||
|
||||
def _addPattern(self, parentMenu, pattern, name):
|
||||
id = ContextMenuSingle.nextID()
|
||||
self.patternEventMap[id] = pattern
|
||||
menuItem = wx.MenuItem(parentMenu, id, name, kind=wx.ITEM_CHECK)
|
||||
parentMenu.Bind(wx.EVT_MENU, self.handlePatternSwitch, menuItem)
|
||||
|
||||
checked = self.module.rahPatternOverride is pattern
|
||||
return menuItem, checked
|
||||
|
||||
def _addCategory(self, parentMenu, name):
|
||||
id = ContextMenuSingle.nextID()
|
||||
menuItem = wx.MenuItem(parentMenu, id, name)
|
||||
parentMenu.Bind(wx.EVT_MENU, self.handlePatternSwitch, menuItem)
|
||||
return menuItem
|
||||
|
||||
def getSubMenu(self, callingWindow, context, mainItem, rootMenu, i, pitem):
|
||||
# Category as menu item - expands further
|
||||
msw = "wxMSW" in wx.PlatformInfo
|
||||
|
||||
def makeMenu(container, parentMenu, root=False):
|
||||
menu = wx.Menu()
|
||||
if root:
|
||||
menuItem, checked = self._addPattern(rootMenu if msw else parentMenu, None, 'Fit Pattern')
|
||||
menu.Append(menuItem)
|
||||
menuItem.Check(checked)
|
||||
menuItem, checked = self._addPattern(rootMenu if msw else parentMenu, 'disable', 'Do Not Adapt')
|
||||
menu.Append(menuItem)
|
||||
menuItem.Check(checked)
|
||||
menu.AppendSeparator()
|
||||
for name, subcontainer in container[1].items():
|
||||
menuItem = self._addCategory(rootMenu if msw else parentMenu, name)
|
||||
subMenu = makeMenu(subcontainer, menu)
|
||||
menuItem.SetSubMenu(subMenu)
|
||||
menu.Append(menuItem)
|
||||
for name, pattern in container[0].items():
|
||||
menuItem, checked = self._addPattern(rootMenu if msw else parentMenu, pattern, name)
|
||||
menu.Append(menuItem)
|
||||
menuItem.Check(checked)
|
||||
menu.Bind(wx.EVT_MENU, self.handlePatternSwitch)
|
||||
return menu
|
||||
|
||||
subMenu = makeMenu(self.items, rootMenu, root=True)
|
||||
return subMenu
|
||||
|
||||
def handlePatternSwitch(self, event):
|
||||
pattern = self.patternEventMap.get(event.Id, False)
|
||||
if pattern is False:
|
||||
event.Skip()
|
||||
return
|
||||
sFit = Fit.getInstance()
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
sFit.setRahPattern(fitID, self.module, pattern)
|
||||
wx.PostEvent(self.mainFrame, GE.FitChanged(fitIDs=(fitID,)))
|
||||
|
||||
|
||||
ChangeRahPattern.register()
|
||||
0
gui/builtinContextMenus/shared/__init__.py
Normal file
0
gui/builtinContextMenus/shared/__init__.py
Normal file
35
gui/builtinContextMenus/shared/patterns.py
Normal file
35
gui/builtinContextMenus/shared/patterns.py
Normal file
@@ -0,0 +1,35 @@
|
||||
from collections import OrderedDict
|
||||
from itertools import chain
|
||||
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
|
||||
from gui.utils.sorter import smartSort
|
||||
from service.damagePattern import DamagePattern as DmgPatternSvc
|
||||
|
||||
_t = wx.GetTranslation
|
||||
|
||||
|
||||
class DamagePatternMixin:
|
||||
|
||||
def _getPatterns(self):
|
||||
sDP = DmgPatternSvc.getInstance()
|
||||
builtinPatterns = sDP.getBuiltinDamagePatternList()
|
||||
userPatterns = sorted(sDP.getUserDamagePatternList(), key=lambda p: smartSort(p.fullName))
|
||||
# Order here is important: patterns with duplicate names from the latter will overwrite
|
||||
# patterns from the former
|
||||
patterns = sorted(
|
||||
chain(builtinPatterns, userPatterns),
|
||||
key=lambda p: p.fullName not in ["Uniform", "Selected Ammo"])
|
||||
return patterns
|
||||
|
||||
def _getItems(self, patterns):
|
||||
items = (OrderedDict(), OrderedDict())
|
||||
for pattern in patterns:
|
||||
container = items
|
||||
for categoryName in pattern.hierarchy:
|
||||
categoryName = _t(categoryName) if pattern.builtin else categoryName
|
||||
container = container[1].setdefault(categoryName, (OrderedDict(), OrderedDict()))
|
||||
shortName = _t(pattern.shortName) if pattern.builtin else pattern.shortName
|
||||
container[0][shortName] = pattern
|
||||
return items
|
||||
@@ -72,6 +72,7 @@ AttrGroupDict = {
|
||||
"specialAmmoHoldCapacity",
|
||||
"specialCommandCenterHoldCapacity",
|
||||
"specialPlanetaryCommoditiesHoldCapacity",
|
||||
"specialColonyResourcesHoldCapacity",
|
||||
"structureDamageLimit",
|
||||
"specialSubsystemHoldCapacity",
|
||||
"emDamageResonance",
|
||||
|
||||
@@ -4,13 +4,18 @@ import wx
|
||||
# noinspection PyPackageRequirements
|
||||
import wx.lib.mixins.listctrl as listmix
|
||||
|
||||
from gui.utils.dark import isDark
|
||||
|
||||
|
||||
class AutoListCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin, listmix.ListRowHighlighter):
|
||||
def __init__(self, parent, ID, pos=wx.DefaultPosition, size=wx.DefaultSize, style=0):
|
||||
wx.ListCtrl.__init__(self, parent, ID, pos, size, style)
|
||||
listmix.ListCtrlAutoWidthMixin.__init__(self)
|
||||
listmix.ListRowHighlighter.__init__(self)
|
||||
|
||||
if isDark():
|
||||
listcol = wx.SystemSettings.GetColour(wx.SYS_COLOUR_LISTBOX)
|
||||
highlight = listcol.ChangeLightness(110)
|
||||
listmix.ListRowHighlighter.SetHighlightColor(self, highlight)
|
||||
|
||||
class AutoListCtrlNoHighlight(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin, listmix.ListRowHighlighter):
|
||||
def __init__(self, parent, ID, pos=wx.DefaultPosition, size=wx.DefaultSize, style=0):
|
||||
|
||||
@@ -36,6 +36,8 @@ class ItemCompare(wx.Panel):
|
||||
self.item = item
|
||||
self.items = sorted(items, key=defaultSort)
|
||||
self.attrs = {}
|
||||
self.HighlightOn = wx.Colour(255, 255, 0, wx.ALPHA_OPAQUE)
|
||||
self.highlightedNames = []
|
||||
|
||||
# get a dict of attrName: attrInfo of all unique attributes across all items
|
||||
for item in self.items:
|
||||
@@ -88,6 +90,21 @@ class ItemCompare(wx.Panel):
|
||||
self.toggleViewBtn.Bind(wx.EVT_TOGGLEBUTTON, self.ToggleViewMode)
|
||||
self.Bind(wx.EVT_LIST_COL_CLICK, self.SortCompareCols)
|
||||
|
||||
self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.HighlightRow)
|
||||
|
||||
def HighlightRow(self, event):
|
||||
itemIdx = event.GetIndex()
|
||||
name = self.paramList.GetItem(itemIdx).Text
|
||||
if name in self.highlightedNames:
|
||||
self.highlightedNames.remove(name)
|
||||
else:
|
||||
self.highlightedNames.append(name)
|
||||
self.Freeze()
|
||||
self.paramList.ClearAll()
|
||||
self.PopulateList()
|
||||
self.Thaw()
|
||||
event.Skip()
|
||||
|
||||
def SortCompareCols(self, event):
|
||||
self.Freeze()
|
||||
self.paramList.ClearAll()
|
||||
@@ -155,6 +172,8 @@ class ItemCompare(wx.Panel):
|
||||
self.paramList.InsertColumn(len(self.attrs) + 1, _t("Price"))
|
||||
self.paramList.SetColumnWidth(len(self.attrs) + 1, 60)
|
||||
|
||||
toHighlight = []
|
||||
|
||||
for item in self.items:
|
||||
i = self.paramList.InsertItem(self.paramList.GetItemCount(), item.name)
|
||||
for x, attr in enumerate(self.attrs.keys()):
|
||||
@@ -172,10 +191,19 @@ class ItemCompare(wx.Panel):
|
||||
|
||||
# Add prices
|
||||
self.paramList.SetItem(i, len(self.attrs) + 1, formatAmount(item.price.price, 3, 3, 9, currency=True) if item.price.price else "")
|
||||
if item.name in self.highlightedNames:
|
||||
toHighlight.append(i)
|
||||
|
||||
self.paramList.RefreshRows()
|
||||
self.Layout()
|
||||
|
||||
# Highlight after layout, otherwise colors are getting overwritten
|
||||
for itemIdx in toHighlight:
|
||||
listItem = self.paramList.GetItem(itemIdx)
|
||||
listItem.SetBackgroundColour(self.HighlightOn)
|
||||
listItem.SetFont(listItem.GetFont().MakeBold())
|
||||
self.paramList.SetItem(listItem)
|
||||
|
||||
@staticmethod
|
||||
def TranslateValueUnit(value, unitName, unitDisplayName):
|
||||
def itemIDCallback():
|
||||
|
||||
@@ -22,9 +22,9 @@ class ItemDescription(wx.Panel):
|
||||
|
||||
desc = item.description.replace("\n", "<br>")
|
||||
# Strip font tags
|
||||
desc = re.sub("<( *)font( *)color( *)=(.*?)>(?P<inside>.*?)<( *)/( *)font( *)>", "\g<inside>", desc)
|
||||
desc = re.sub("<( *)font( *)color( *)=(.*?)>(?P<inside>.*?)<( *)/( *)font( *)>", r"\g<inside>", desc)
|
||||
# Strip URLs
|
||||
desc = re.sub("<( *)a(.*?)>(?P<inside>.*?)<( *)/( *)a( *)>", "\g<inside>", desc)
|
||||
desc = re.sub("<( *)a(.*?)>(?P<inside>.*?)<( *)/( *)a( *)>", r"\g<inside>", desc)
|
||||
desc = "<body bgcolor='{}' text='{}'>{}</body>".format(
|
||||
bgcolor.GetAsString(wx.C2S_HTML_SYNTAX),
|
||||
fgcolor.GetAsString(wx.C2S_HTML_SYNTAX),
|
||||
|
||||
@@ -5,3 +5,5 @@ import wx.lib.newevent
|
||||
ItemSelected, ITEM_SELECTED = wx.lib.newevent.NewEvent()
|
||||
|
||||
RECENTLY_USED_MODULES = -2
|
||||
|
||||
CHARGES_FOR_FIT = -3
|
||||
|
||||
@@ -2,14 +2,17 @@ import wx
|
||||
from logbook import Logger
|
||||
|
||||
import gui.builtinMarketBrowser.pfSearchBox as SBox
|
||||
from config import slotColourMap
|
||||
import gui.globalEvents as GE
|
||||
from config import slotColourMap, slotColourMapDark
|
||||
from eos.saveddata.module import Module
|
||||
from gui.builtinMarketBrowser.events import ItemSelected, RECENTLY_USED_MODULES
|
||||
from gui.builtinMarketBrowser.events import ItemSelected, RECENTLY_USED_MODULES, CHARGES_FOR_FIT
|
||||
from gui.contextMenu import ContextMenu
|
||||
from gui.display import Display
|
||||
from gui.utils.staticHelpers import DragDropHelper
|
||||
from gui.utils.dark import isDark
|
||||
from service.fit import Fit
|
||||
from service.market import Market
|
||||
from service.ammo import Ammo
|
||||
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
@@ -31,6 +34,7 @@ class ItemView(Display):
|
||||
self.filteredStore = set()
|
||||
self.sMkt = marketBrowser.sMkt
|
||||
self.sFit = Fit.getInstance()
|
||||
self.sAmmo = Ammo.getInstance()
|
||||
|
||||
self.marketBrowser = marketBrowser
|
||||
self.marketView = marketBrowser.marketView
|
||||
@@ -50,6 +54,9 @@ class ItemView(Display):
|
||||
self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.itemActivated)
|
||||
self.Bind(wx.EVT_LIST_BEGIN_DRAG, self.startDrag)
|
||||
|
||||
# the "charges for active fitting" needs to listen to fitting changes
|
||||
self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged)
|
||||
|
||||
self.active = []
|
||||
|
||||
def delaySearch(self, evt):
|
||||
@@ -90,7 +97,11 @@ class ItemView(Display):
|
||||
if sel.IsOk():
|
||||
# Get data field of the selected item (which is a marketGroup ID if anything was selected)
|
||||
seldata = self.marketView.GetItemData(sel)
|
||||
if seldata is not None and seldata != RECENTLY_USED_MODULES:
|
||||
if seldata == RECENTLY_USED_MODULES:
|
||||
items = self.sMkt.getRecentlyUsed()
|
||||
elif seldata == CHARGES_FOR_FIT:
|
||||
items = self.getChargesForActiveFit()
|
||||
elif seldata is not None:
|
||||
# If market group treeview item doesn't have children (other market groups or dummies),
|
||||
# then it should have items in it and we want to request them
|
||||
if self.marketView.ItemHasChildren(sel) is False:
|
||||
@@ -102,11 +113,7 @@ class ItemView(Display):
|
||||
else:
|
||||
items = set()
|
||||
else:
|
||||
# If method was called but selection wasn't actually made or we have a hit on recently used modules
|
||||
if seldata == RECENTLY_USED_MODULES:
|
||||
items = self.sMkt.getRecentlyUsed()
|
||||
else:
|
||||
items = set()
|
||||
items = set()
|
||||
|
||||
# Fill store
|
||||
self.updateItemStore(items)
|
||||
@@ -114,6 +121,9 @@ class ItemView(Display):
|
||||
# Set toggle buttons / use search mode flag if recently used modules category is selected (in order to have all modules listed and not filtered)
|
||||
if seldata == RECENTLY_USED_MODULES:
|
||||
self.marketBrowser.mode = 'recent'
|
||||
|
||||
if seldata == CHARGES_FOR_FIT:
|
||||
self.marketBrowser.mode = 'charges'
|
||||
|
||||
self.setToggles()
|
||||
if context == 'tree' and self.marketBrowser.settings.get('marketMGMarketSelectMode') == 1:
|
||||
@@ -122,6 +132,41 @@ class ItemView(Display):
|
||||
btn.setUserSelection(True)
|
||||
self.filterItemStore()
|
||||
|
||||
def getChargesForActiveFit(self):
|
||||
fitId = self.mainFrame.getActiveFit()
|
||||
|
||||
# no active fit => no charges
|
||||
if fitId is None:
|
||||
return set()
|
||||
|
||||
fit = self.sFit.getFit(fitId)
|
||||
|
||||
# use a set so we only add one entry for each charge
|
||||
items = set()
|
||||
for mod in fit.modules:
|
||||
charges = self.sAmmo.getModuleFlatAmmo(mod)
|
||||
for charge in charges:
|
||||
items.add(charge)
|
||||
return items
|
||||
|
||||
def fitChanged(self, event):
|
||||
# skip the event so the other handlers also get called
|
||||
event.Skip()
|
||||
|
||||
if self.marketBrowser.mode != 'charges':
|
||||
return
|
||||
|
||||
activeFitID = self.mainFrame.getActiveFit()
|
||||
# if it was not the active fitting that was changed, do not do anything
|
||||
if activeFitID is not None and activeFitID not in event.fitIDs:
|
||||
return
|
||||
|
||||
items = self.getChargesForActiveFit()
|
||||
|
||||
# update the UI
|
||||
self.updateItemStore(items)
|
||||
self.filterItemStore()
|
||||
|
||||
def updateItemStore(self, items):
|
||||
self.unfilteredStore = items
|
||||
|
||||
@@ -243,6 +288,7 @@ class ItemView(Display):
|
||||
|
||||
def columnBackground(self, colItem, item):
|
||||
if self.sFit.serviceFittingOptions["colorFitBySlot"]:
|
||||
return slotColourMap.get(Module.calculateSlot(item)) or self.GetBackgroundColour()
|
||||
colorMap = slotColourMapDark if isDark() else slotColourMap
|
||||
return colorMap.get(Module.calculateSlot(item)) or self.GetBackgroundColour()
|
||||
else:
|
||||
return self.GetBackgroundColour()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import wx
|
||||
|
||||
from gui.cachingImageList import CachingImageList
|
||||
from gui.builtinMarketBrowser.events import RECENTLY_USED_MODULES
|
||||
from gui.builtinMarketBrowser.events import RECENTLY_USED_MODULES, CHARGES_FOR_FIT
|
||||
|
||||
from logbook import Logger
|
||||
|
||||
@@ -35,6 +35,9 @@ class MarketTree(wx.TreeCtrl):
|
||||
# Add recently used modules node
|
||||
rumIconId = self.addImage("market_small", "gui")
|
||||
self.AppendItem(self.root, _t("Recently Used Items"), rumIconId, data=RECENTLY_USED_MODULES)
|
||||
# Add charges for active fitting node
|
||||
cffIconId = self.addImage("damagePattern_small", "gui")
|
||||
self.AppendItem(self.root, _t("Charges For Active Fit"), cffIconId, data=CHARGES_FOR_FIT)
|
||||
|
||||
# Bind our lookup method to when the tree gets expanded
|
||||
self.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.expandLookup)
|
||||
|
||||
@@ -253,8 +253,8 @@ class PFSearchBox(wx.Window):
|
||||
else:
|
||||
spad = 0
|
||||
|
||||
dc.DrawBitmap(self.searchBitmapShadow, self.searchButtonX + 1, self.searchButtonY + 1)
|
||||
dc.DrawBitmap(self.searchBitmap, self.searchButtonX + spad, self.searchButtonY + spad)
|
||||
dc.DrawBitmap(self.searchBitmapShadow, round(self.searchButtonX + 1), round(self.searchButtonY + 1))
|
||||
dc.DrawBitmap(self.searchBitmap, round(self.searchButtonX + spad), round(self.searchButtonY + spad))
|
||||
|
||||
if self.isCancelButtonVisible:
|
||||
if self.cancelBitmap:
|
||||
@@ -262,8 +262,8 @@ class PFSearchBox(wx.Window):
|
||||
cpad = 1
|
||||
else:
|
||||
cpad = 0
|
||||
dc.DrawBitmap(self.cancelBitmapShadow, self.cancelButtonX + 1, self.cancelButtonY + 1)
|
||||
dc.DrawBitmap(self.cancelBitmap, self.cancelButtonX + cpad, self.cancelButtonY + cpad)
|
||||
dc.DrawBitmap(self.cancelBitmapShadow, round(self.cancelButtonX + 1), round(self.cancelButtonY + 1))
|
||||
dc.DrawBitmap(self.cancelBitmap, round(self.cancelButtonX + cpad), round(self.cancelButtonY + cpad))
|
||||
|
||||
dc.SetPen(wx.Pen(sepColor, 1))
|
||||
dc.DrawLine(0, rect.height - 1, rect.width, rect.height - 1)
|
||||
|
||||
@@ -68,22 +68,27 @@ class PFContextMenuPref(PreferenceView):
|
||||
rbSizerRow2.Add(self.rbBox5, 1, wx.ALL, 5)
|
||||
self.rbBox5.Bind(wx.EVT_RADIOBOX, self.OnSetting5Change)
|
||||
|
||||
self.rbBox6 = wx.RadioBox(panel, -1, _t("Spoolup"), wx.DefaultPosition, wx.DefaultSize, [_t('Disabled'), _t('Enabled')], 1, wx.RA_SPECIFY_COLS)
|
||||
self.rbBox6.SetSelection(self.settings.get('spoolup'))
|
||||
rbSizerRow2.Add(self.rbBox6, 1, wx.ALL, 5)
|
||||
self.rbBox6.Bind(wx.EVT_RADIOBOX, self.OnSetting6Change)
|
||||
|
||||
mainSizer.Add(rbSizerRow2, 1, wx.ALL | wx.EXPAND, 0)
|
||||
|
||||
# Row 3
|
||||
rbSizerRow3 = wx.BoxSizer(wx.HORIZONTAL)
|
||||
|
||||
self.rbBox6 = wx.RadioBox(panel, -1, _t("Spoolup"), wx.DefaultPosition, wx.DefaultSize, [_t('Disabled'), _t('Enabled')], 1, wx.RA_SPECIFY_COLS)
|
||||
self.rbBox6.SetSelection(self.settings.get('spoolup'))
|
||||
rbSizerRow3.Add(self.rbBox6, 1, wx.ALL, 5)
|
||||
self.rbBox6.Bind(wx.EVT_RADIOBOX, self.OnSetting6Change)
|
||||
|
||||
self.rbBox7 = wx.RadioBox(panel, -1, _t("Additions Panel Copy/Paste"), wx.DefaultPosition, wx.DefaultSize, [_t('Disabled'), _t('Enabled')], 1,
|
||||
wx.RA_SPECIFY_COLS)
|
||||
self.rbBox7.SetSelection(self.settings.get('additionsCopyPaste'))
|
||||
rbSizerRow3.Add(self.rbBox7, 1, wx.ALL, 5)
|
||||
self.rbBox7.Bind(wx.EVT_RADIOBOX, self.OnSetting7Change)
|
||||
|
||||
self.rbBox8 = wx.RadioBox(panel, -1, _t("Fill cargo with"), wx.DefaultPosition, wx.DefaultSize, [_t('Disabled'), _t('Enabled')], 1, wx.RA_SPECIFY_COLS)
|
||||
self.rbBox8.SetSelection(self.settings.get('cargoFill'))
|
||||
rbSizerRow3.Add(self.rbBox8, 1, wx.ALL, 5)
|
||||
self.rbBox8.Bind(wx.EVT_RADIOBOX, self.OnSetting8Change)
|
||||
|
||||
mainSizer.Add(rbSizerRow3, 1, wx.ALL | wx.EXPAND, 0)
|
||||
|
||||
panel.SetSizer(mainSizer)
|
||||
@@ -110,6 +115,9 @@ class PFContextMenuPref(PreferenceView):
|
||||
def OnSetting7Change(self, event):
|
||||
self.settings.set('additionsCopyPaste', event.GetInt())
|
||||
|
||||
def OnSetting8Change(self, event):
|
||||
self.settings.set('cargoFill', event.GetInt())
|
||||
|
||||
def getImage(self):
|
||||
return BitmapLoader.getBitmap("settings_menu", "gui")
|
||||
|
||||
|
||||
@@ -57,12 +57,6 @@ class PFFittingEnginePref(PreferenceView):
|
||||
|
||||
mainSizer.Add(self.cbStrictSkillLevels, 0, wx.ALL | wx.EXPAND, 5)
|
||||
|
||||
self.cbUniversalAdaptiveArmorHardener = wx.CheckBox(panel, wx.ID_ANY,
|
||||
_t("When damage profile is Uniform, set Reactive Armor "
|
||||
"Hardener to match (old behavior)."),
|
||||
wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
mainSizer.Add(self.cbUniversalAdaptiveArmorHardener, 0, wx.ALL | wx.EXPAND, 5)
|
||||
|
||||
spoolup_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
|
||||
self.spool_up_label = wx.StaticText(panel, wx.ID_ANY, _t("Global Default Spoolup Percentage:"), wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
@@ -110,9 +104,6 @@ class PFFittingEnginePref(PreferenceView):
|
||||
self.cbStrictSkillLevels.SetValue(self.engine_settings.get("strictSkillLevels"))
|
||||
self.cbStrictSkillLevels.Bind(wx.EVT_CHECKBOX, self.OnCBStrictSkillLevelsChange)
|
||||
|
||||
self.cbUniversalAdaptiveArmorHardener.SetValue(self.engine_settings.get("useStaticAdaptiveArmorHardener"))
|
||||
self.cbUniversalAdaptiveArmorHardener.Bind(wx.EVT_CHECKBOX, self.OnCBUniversalAdaptiveArmorHardenerChange)
|
||||
|
||||
self.spoolup_value.SetValue(int(self.engine_settings.get("globalDefaultSpoolupPercentage") * 100))
|
||||
self.spoolup_value.Bind(wx.lib.intctrl.EVT_INT, self.OnSpoolupChange)
|
||||
|
||||
@@ -129,8 +120,6 @@ class PFFittingEnginePref(PreferenceView):
|
||||
def OnCBStrictSkillLevelsChange(self, event):
|
||||
self.engine_settings.set("strictSkillLevels", self.cbStrictSkillLevels.GetValue())
|
||||
|
||||
def OnCBUniversalAdaptiveArmorHardenerChange(self, event):
|
||||
self.engine_settings.set("useStaticAdaptiveArmorHardener", self.cbUniversalAdaptiveArmorHardener.GetValue())
|
||||
|
||||
def getImage(self):
|
||||
return BitmapLoader.getBitmap("settings_fitting", "gui")
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
|
||||
import config
|
||||
import gui.mainFrame
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
from gui.preferenceView import PreferenceView
|
||||
from service.esi import Esi
|
||||
from service.settings import EsiSettings
|
||||
|
||||
# noinspection PyPackageRequirements
|
||||
@@ -34,33 +36,75 @@ class PFEsiPref(PreferenceView):
|
||||
self.stInfo.Wrap(dlgWidth - 50)
|
||||
mainSizer.Add(self.stInfo, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5)
|
||||
|
||||
self.enforceJwtExpiration = wx.CheckBox(panel, wx.ID_ANY, _t("Enforce Token Expiration"), wx.DefaultPosition,
|
||||
wx.DefaultSize,
|
||||
0)
|
||||
self.enforceJwtExpiration.SetToolTip(wx.ToolTip(_t("This option is a workaround in case you cannot log into EVE SSO "
|
||||
"due to 'Signature has expired' error")))
|
||||
mainSizer.Add(self.enforceJwtExpiration, 0, wx.ALL | wx.EXPAND, 5)
|
||||
|
||||
self.ssoServer = wx.CheckBox(panel, wx.ID_ANY, _t("Auto-login (starts local server)"), wx.DefaultPosition,
|
||||
wx.DefaultSize,
|
||||
0)
|
||||
self.ssoServer.SetToolTip(wx.ToolTip(_t("This allows the EVE SSO to callback to your local pyfa instance and complete the authentication process without manual intervention.")))
|
||||
mainSizer.Add(self.ssoServer, 0, wx.ALL | wx.EXPAND, 5)
|
||||
|
||||
rbSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
self.rbMode = wx.RadioBox(panel, -1, _t("Login Authentication Method"), wx.DefaultPosition, wx.DefaultSize,
|
||||
[_t('Local Server'), _t('Manual')], 1, wx.RA_SPECIFY_COLS)
|
||||
self.rbMode.SetItemToolTip(0, _t("This option starts a local webserver that EVE SSO Server will call back to"
|
||||
" with information about the character login."))
|
||||
self.rbMode.SetItemToolTip(1, _t("This option prompts users to copy and paste information to allow for"
|
||||
" character login. Use this if having issues with the local server."))
|
||||
|
||||
self.rbMode.SetSelection(self.settings.get('loginMode'))
|
||||
self.enforceJwtExpiration.SetValue(self.settings.get("enforceJwtExpiration") or True)
|
||||
self.ssoServer.SetValue(True if self.settings.get("loginMode") == 0 else False)
|
||||
|
||||
rbSizer.Add(self.rbMode, 1, wx.TOP | wx.RIGHT, 5)
|
||||
mainSizer.Add(rbSizer, 0, wx.ALL | wx.EXPAND, 0)
|
||||
|
||||
self.rbMode.Bind(wx.EVT_RADIOBOX, self.OnModeChange)
|
||||
esiSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
|
||||
self.esiServer = wx.StaticText(panel, wx.ID_ANY, _t("Default SSO Server:"), wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
|
||||
mainSizer.Add(rbSizer, 1, wx.ALL | wx.EXPAND, 0)
|
||||
self.esiServer.Wrap(-1)
|
||||
|
||||
esiSizer.Add(self.esiServer, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
|
||||
|
||||
self.esiServer.SetToolTip(wx.ToolTip(_t('The source you choose will be used on connection.')))
|
||||
|
||||
self.chESIserver = wx.Choice(panel, choices=list(self.settings.keys()))
|
||||
|
||||
self.chESIserver.SetStringSelection(self.settings.get("server"))
|
||||
|
||||
esiSizer.Add(self.chESIserver, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 10)
|
||||
|
||||
mainSizer.Add(esiSizer, 0, wx.TOP | wx.RIGHT, 10)
|
||||
|
||||
self.chESIserver.Bind(wx.EVT_CHOICE, self.OnServerChange)
|
||||
self.enforceJwtExpiration.Bind(wx.EVT_CHECKBOX, self.OnEnforceChange)
|
||||
self.ssoServer.Bind(wx.EVT_CHECKBOX, self.OnModeChange)
|
||||
|
||||
panel.SetSizer(mainSizer)
|
||||
|
||||
panel.Layout()
|
||||
|
||||
def OnTimeoutChange(self, event):
|
||||
self.settings.set('timeout', event.GetEventObject().GetValue())
|
||||
event.Skip()
|
||||
|
||||
def OnModeChange(self, event):
|
||||
self.settings.set('loginMode', event.GetInt())
|
||||
self.settings.set('loginMode', 0 if self.ssoServer.GetValue() else 1)
|
||||
event.Skip()
|
||||
|
||||
def OnEnforceChange(self, event):
|
||||
self.settings.set('enforceJwtExpiration', self.enforceJwtExpiration.GetValue())
|
||||
event.Skip()
|
||||
|
||||
def OnServerChange(self, event):
|
||||
# pass
|
||||
source = self.chESIserver.GetString(self.chESIserver.GetSelection())
|
||||
esiService = Esi.getInstance()
|
||||
# init servers
|
||||
esiService.init(config.supported_servers[source])
|
||||
self.settings.set("server", source)
|
||||
event.Skip()
|
||||
|
||||
def getImage(self):
|
||||
return BitmapLoader.getBitmap("eve", "gui")
|
||||
|
||||
|
||||
PFEsiPref.register()
|
||||
PFEsiPref.register()
|
||||
@@ -40,7 +40,7 @@ class PFGeneralPref(PreferenceView):
|
||||
|
||||
langSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
|
||||
self.langChoices = sorted([langInfo for lang, langInfo in LocaleSettings.supported_langauges().items()], key=lambda x: x.Description)
|
||||
self.langChoices = sorted([langInfo for lang, langInfo in LocaleSettings.supported_languages().items()], key=lambda x: x.Description)
|
||||
pyfaLangsEnabled = bool(self.langChoices)
|
||||
|
||||
if pyfaLangsEnabled:
|
||||
@@ -64,7 +64,7 @@ class PFGeneralPref(PreferenceView):
|
||||
langBox.Add(hl.HyperLinkCtrl(panel, -1,
|
||||
_t("Interested in helping with translations?"),
|
||||
URL="https://github.com/pyfa-org/Pyfa/blob/master/locale/README.md"
|
||||
), 0, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 15)
|
||||
), 0, wx.LEFT, 15)
|
||||
else:
|
||||
self.stLangLabel = wx.StaticText(panel, wx.ID_ANY, _t("Pyfa language selection disabled. Please check if .mo files have been generated.\nRefer to locale/README.md for info."), wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
self.stLangLabel.Wrap(-1)
|
||||
@@ -93,7 +93,7 @@ class PFGeneralPref(PreferenceView):
|
||||
langBox.Add(wx.StaticText(panel, wx.ID_ANY,
|
||||
_t("Auto will use the same language pyfa uses if available, otherwise English"),
|
||||
wx.DefaultPosition,
|
||||
wx.DefaultSize, 0), 0, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 15)
|
||||
wx.DefaultSize, 0), 0, wx.LEFT, 15)
|
||||
|
||||
self.cbGlobalChar = wx.CheckBox(panel, wx.ID_ANY, _t("Use global character"), wx.DefaultPosition, wx.DefaultSize,
|
||||
0)
|
||||
|
||||
@@ -106,6 +106,14 @@ class PFStatViewPref(PreferenceView):
|
||||
rbSizerRow3.Add(self.rbOutgoing, 1, wx.TOP | wx.RIGHT, 5)
|
||||
self.rbOutgoing.Bind(wx.EVT_RADIOBOX, self.OnOutgoingChange)
|
||||
|
||||
self.rbBombing = wx.RadioBox(panel, -1, _t("Bombing"), wx.DefaultPosition, wx.DefaultSize, [_t('None'), _t('Minimal'), _t('Full')], 1,
|
||||
wx.RA_SPECIFY_COLS)
|
||||
# Disable minimal as we don't have a view for this yet
|
||||
self.rbBombing.EnableItem(1, False)
|
||||
self.rbBombing.SetSelection(self.settings.get('bombing'))
|
||||
rbSizerRow3.Add(self.rbBombing, 1, wx.TOP | wx.RIGHT, 5)
|
||||
self.rbBombing.Bind(wx.EVT_RADIOBOX, self.OnBombingChange)
|
||||
|
||||
mainSizer.Add(rbSizerRow3, 1, wx.ALL | wx.EXPAND, 0)
|
||||
|
||||
panel.SetSizer(mainSizer)
|
||||
@@ -144,5 +152,7 @@ class PFStatViewPref(PreferenceView):
|
||||
def getImage(self):
|
||||
return BitmapLoader.getBitmap("settings_stats", "gui")
|
||||
|
||||
def OnBombingChange(self, event):
|
||||
self.settings.set('bombing', event.GetInt())
|
||||
|
||||
PFStatViewPref.register()
|
||||
|
||||
@@ -104,14 +104,14 @@ class CategoryItem(SFBrowserItem):
|
||||
textColor = colorUtils.GetSuitable(windowColor, 1)
|
||||
|
||||
mdc.SetTextForeground(textColor)
|
||||
mdc.DrawBitmap(self.dropShadowBitmap, self.shipBmpx + 1, self.shipBmpy + 1)
|
||||
mdc.DrawBitmap(self.shipBmp, self.shipBmpx, self.shipBmpy, 0)
|
||||
mdc.DrawBitmap(self.dropShadowBitmap, round(self.shipBmpx + 1), round(self.shipBmpy + 1))
|
||||
mdc.DrawBitmap(self.shipBmp, round(self.shipBmpx), round(self.shipBmpy), 0)
|
||||
|
||||
mdc.SetFont(self.fontBig)
|
||||
|
||||
categoryName, fittings = self.fittingInfo
|
||||
|
||||
mdc.DrawText(categoryName, self.catx, self.caty)
|
||||
mdc.DrawText(categoryName, round(self.catx), round(self.caty))
|
||||
|
||||
|
||||
# =============================================================================
|
||||
|
||||
@@ -416,9 +416,18 @@ class FitItem(SFItem.SFBrowserItem):
|
||||
if self.dragging:
|
||||
if not self.dragged:
|
||||
if self.dragMotionTrigger < 0:
|
||||
if not self.dragTLFBmp:
|
||||
tdc = wx.MemoryDC()
|
||||
bmpWidth = self.toolbarx if self.toolbarx < 200 else 200
|
||||
self.dragTLFBmp = wx.Bitmap(round(bmpWidth), round(self.GetRect().height))
|
||||
tdc.SelectObject(self.dragTLFBmp)
|
||||
tdc.SetBrush(wx.Brush(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW)))
|
||||
tdc.DrawRectangle(0, 0, bmpWidth, self.GetRect().height)
|
||||
self.DrawItem(tdc)
|
||||
tdc.SelectObject(wx.NullBitmap)
|
||||
if not self.HasCapture():
|
||||
self.CaptureMouse()
|
||||
self.dragWindow = PFBitmapFrame(self, pos, self.dragTLFBmp)
|
||||
self.dragWindow = PFBitmapFrame(self, pos, self.dragTLFBmp)
|
||||
self.dragWindow.Show()
|
||||
self.dragged = True
|
||||
self.dragMotionTrigger = self.dragMotionTrail
|
||||
@@ -493,9 +502,9 @@ class FitItem(SFItem.SFBrowserItem):
|
||||
else:
|
||||
shipEffBk = self.shipEffBk
|
||||
|
||||
mdc.DrawBitmap(shipEffBk, self.shipEffx, self.shipEffy, 0)
|
||||
mdc.DrawBitmap(shipEffBk, round(self.shipEffx), round(self.shipEffy), 0)
|
||||
|
||||
mdc.DrawBitmap(self.shipBmp, self.shipBmpx, self.shipBmpy, 0)
|
||||
mdc.DrawBitmap(self.shipBmp, round(self.shipBmpx), round(self.shipBmpy), 0)
|
||||
|
||||
mdc.SetFont(self.fontNormal)
|
||||
|
||||
@@ -504,26 +513,21 @@ class FitItem(SFItem.SFBrowserItem):
|
||||
pfdate = drawUtils.GetPartialText(mdc, fitLocalDate,
|
||||
self.toolbarx - self.textStartx - self.padding * 2 - self.thoverw)
|
||||
|
||||
mdc.DrawText(pfdate, self.textStartx, self.timestampy)
|
||||
mdc.DrawText(pfdate, round(self.textStartx), round(self.timestampy))
|
||||
|
||||
mdc.SetFont(self.fontSmall)
|
||||
mdc.DrawText(self.toolbar.hoverLabel, self.thoverx, self.thovery)
|
||||
mdc.DrawText(self.toolbar.hoverLabel, round(self.thoverx), round(self.thovery))
|
||||
|
||||
mdc.SetFont(self.fontBig)
|
||||
|
||||
psname = drawUtils.GetPartialText(mdc, self.fitName,
|
||||
self.toolbarx - self.textStartx - self.padding * 2 - self.thoverw)
|
||||
|
||||
mdc.DrawText(psname, self.textStartx, self.fitNamey)
|
||||
mdc.DrawText(psname, round(self.textStartx), round(self.fitNamey))
|
||||
|
||||
if self.tcFitName.IsShown():
|
||||
self.AdjustControlSizePos(self.tcFitName, self.textStartx, self.toolbarx - self.editWidth - self.padding)
|
||||
|
||||
tdc = wx.MemoryDC()
|
||||
self.dragTLFBmp = wx.Bitmap((self.toolbarx if self.toolbarx < 200 else 200), rect.height, 24)
|
||||
tdc.SelectObject(self.dragTLFBmp)
|
||||
tdc.Blit(0, 0, (self.toolbarx if self.toolbarx < 200 else 200), rect.height, mdc, 0, 0, wx.COPY)
|
||||
tdc.SelectObject(wx.NullBitmap)
|
||||
|
||||
def AdjustControlSizePos(self, editCtl, start, end):
|
||||
fnEditSize = editCtl.GetSize()
|
||||
|
||||
@@ -231,7 +231,7 @@ class NavigationPanel(SFItem.SFBrowserItem):
|
||||
|
||||
self.toolbar.SetPosition((self.toolbarx, self.toolbary))
|
||||
mdc.SetFont(self.fontSmall)
|
||||
mdc.DrawText(self.toolbar.hoverLabel, self.thoverx, self.thovery)
|
||||
mdc.DrawText(self.toolbar.hoverLabel, round(self.thoverx), round(self.thovery))
|
||||
mdc.SetPen(wx.Pen(sepColor, 1))
|
||||
mdc.DrawLine(0, rect.height - 1, rect.width, rect.height - 1)
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ class PFBitmapFrame(wx.Frame):
|
||||
# todo: evaluate wx.DragImage, might make this class obsolete, however might also lose our customizations
|
||||
# (like the sexy fade-in animation)
|
||||
rect = self.GetRect()
|
||||
canvas = wx.Bitmap(rect.width, rect.height)
|
||||
canvas = wx.Bitmap(round(rect.width), round(rect.height))
|
||||
# todo: convert to context manager after updating to wxPython >v4.0.1 (4.0.1 has a bug, see #1421)
|
||||
# See #1418 for discussion
|
||||
mdc = wx.BufferedPaintDC(self)
|
||||
@@ -63,4 +63,4 @@ class PFBitmapFrame(wx.Frame):
|
||||
mdc.DrawBitmap(self.bitmap, 0, 0)
|
||||
mdc.SetPen(wx.Pen("#000000", width=1))
|
||||
mdc.SetBrush(wx.TRANSPARENT_BRUSH)
|
||||
mdc.DrawRectangle(0, 0, rect.width, rect.height)
|
||||
mdc.DrawRectangle(0, 0, round(rect.width), round(rect.height))
|
||||
|
||||
@@ -57,7 +57,7 @@ class PFListPane(wx.ScrolledWindow):
|
||||
|
||||
posy = self.GetScrollPos(wx.VERTICAL)
|
||||
posy -= self.itemsHeight
|
||||
self.Scroll(0, posy)
|
||||
self.Scroll(0, round(posy))
|
||||
|
||||
event.Skip()
|
||||
|
||||
@@ -65,7 +65,7 @@ class PFListPane(wx.ScrolledWindow):
|
||||
|
||||
posy = self.GetScrollPos(wx.VERTICAL)
|
||||
posy += self.itemsHeight
|
||||
self.Scroll(0, posy)
|
||||
self.Scroll(0, round(posy))
|
||||
|
||||
event.Skip()
|
||||
|
||||
@@ -109,7 +109,7 @@ class PFListPane(wx.ScrolledWindow):
|
||||
|
||||
# if we need to adjust
|
||||
if new_vs_x != -1 or new_vs_y != -1:
|
||||
self.Scroll(new_vs_x, new_vs_y)
|
||||
self.Scroll(round(new_vs_x), round(new_vs_y))
|
||||
|
||||
def AddWidget(self, widget):
|
||||
widget.Reparent(self)
|
||||
@@ -163,6 +163,10 @@ class PFListPane(wx.ScrolledWindow):
|
||||
def RemoveAllChildren(self):
|
||||
for widget in self._wList:
|
||||
widget.Destroy()
|
||||
# this forces the garbage collector to work properly by removing dangling references to objects which are still alive, otherwise widget cannot be gc-ed eventually causing GDI id exhaustion and crash
|
||||
for i in widget.__dict__.keys():
|
||||
widget.__dict__[i] =None
|
||||
del widget
|
||||
|
||||
self.Scroll(0, 0)
|
||||
self._wList = []
|
||||
|
||||
@@ -68,7 +68,7 @@ class RaceSelector(wx.Window):
|
||||
img = img.Rotate90(False)
|
||||
img.Replace(0, 0, 0, sysTextColour[0], sysTextColour[1], sysTextColour[2])
|
||||
if layout == wx.VERTICAL:
|
||||
img = img.Scale(self.minWidth, 8, wx.IMAGE_QUALITY_HIGH)
|
||||
img = img.Scale(round(self.minWidth), 8, wx.IMAGE_QUALITY_HIGH)
|
||||
|
||||
self.bmpArrow = wx.Bitmap(img)
|
||||
|
||||
@@ -194,25 +194,25 @@ class RaceSelector(wx.Window):
|
||||
bmp = wx.Bitmap(img)
|
||||
|
||||
if self.layout == wx.VERTICAL:
|
||||
mdc.DrawBitmap(dropShadow, rect.width - self.buttonsPadding - bmp.GetWidth() + 1, y + 1)
|
||||
mdc.DrawBitmap(bmp, rect.width - self.buttonsPadding - bmp.GetWidth(), y)
|
||||
mdc.DrawBitmap(dropShadow, round(rect.width - self.buttonsPadding - bmp.GetWidth() + 1), round(y + 1))
|
||||
mdc.DrawBitmap(bmp, round(rect.width - self.buttonsPadding - bmp.GetWidth()), round(y))
|
||||
y += raceBmp.GetHeight() + self.buttonsPadding
|
||||
mdc.SetPen(wx.Pen(sepColor, 1))
|
||||
mdc.DrawLine(rect.width - 1, 0, rect.width - 1, rect.height)
|
||||
else:
|
||||
mdc.DrawBitmap(dropShadow, x + 1, self.buttonsPadding + 1)
|
||||
mdc.DrawBitmap(bmp, x, self.buttonsPadding)
|
||||
mdc.DrawBitmap(dropShadow, round(x + 1), round(self.buttonsPadding + 1))
|
||||
mdc.DrawBitmap(bmp, round(x), round(self.buttonsPadding))
|
||||
x += raceBmp.GetWidth() + self.buttonsPadding
|
||||
mdc.SetPen(wx.Pen(sepColor, 1))
|
||||
mdc.DrawLine(0, 0, rect.width, 0)
|
||||
|
||||
if self.direction < 1:
|
||||
if self.layout == wx.VERTICAL:
|
||||
mdc.DrawBitmap(self.bmpArrow, -2, (rect.height - self.bmpArrow.GetHeight()) / 2)
|
||||
mdc.DrawBitmap(self.bmpArrow, -2, round((rect.height - self.bmpArrow.GetHeight()) / 2))
|
||||
else:
|
||||
mdc.SetPen(wx.Pen(sepColor, 1))
|
||||
mdc.DrawLine(0, 0, rect.width, 0)
|
||||
mdc.DrawBitmap(self.bmpArrow, (rect.width - self.bmpArrow.GetWidth()) / 2, -2)
|
||||
mdc.DrawBitmap(self.bmpArrow, round((rect.width - self.bmpArrow.GetWidth()) / 2), -2)
|
||||
|
||||
def OnTimer(self, event):
|
||||
if event.GetId() == self.animTimerID:
|
||||
|
||||
@@ -233,8 +233,8 @@ class PFToolbar:
|
||||
|
||||
bmpWidth = bmp.GetWidth()
|
||||
|
||||
pdc.DrawBitmap(dropShadowBmp, bx + self.padding / 2, self.toolbarY + self.padding / 2)
|
||||
pdc.DrawBitmap(bmp, tbx, by)
|
||||
pdc.DrawBitmap(dropShadowBmp, round(bx + self.padding / 2), round(self.toolbarY + self.padding / 2))
|
||||
pdc.DrawBitmap(bmp, round(tbx), round(by))
|
||||
|
||||
bx += bmpWidth + self.padding
|
||||
|
||||
|
||||
@@ -247,12 +247,12 @@ class ShipItem(SFItem.SFBrowserItem):
|
||||
else:
|
||||
shipEffBk = self.shipEffBk
|
||||
|
||||
mdc.DrawBitmap(shipEffBk, self.shipEffx, self.shipEffy, 0)
|
||||
mdc.DrawBitmap(shipEffBk, round(self.shipEffx), round(self.shipEffy), 0)
|
||||
|
||||
mdc.DrawBitmap(self.shipBmp, self.shipBmpx, self.shipBmpy, 0)
|
||||
mdc.DrawBitmap(self.shipBmp, round(self.shipBmpx), round(self.shipBmpy), 0)
|
||||
|
||||
mdc.DrawBitmap(self.raceDropShadowBmp, self.raceBmpx + 1, self.raceBmpy + 1)
|
||||
mdc.DrawBitmap(self.raceBmp, self.raceBmpx, self.raceBmpy)
|
||||
mdc.DrawBitmap(self.raceDropShadowBmp, round(self.raceBmpx + 1), round(self.raceBmpy + 1))
|
||||
mdc.DrawBitmap(self.raceBmp, round(self.raceBmpx), round(self.raceBmpy))
|
||||
|
||||
shipName, shipTrait, fittings = self.shipFittingInfo
|
||||
|
||||
@@ -264,17 +264,17 @@ class ShipItem(SFItem.SFBrowserItem):
|
||||
fformat = "%d fits"
|
||||
|
||||
mdc.SetFont(self.fontNormal)
|
||||
mdc.DrawText(fformat % fittings if fittings > 0 else fformat, self.textStartx, self.fittingsy)
|
||||
mdc.DrawText(fformat % fittings if fittings > 0 else fformat, round(self.textStartx), round(self.fittingsy))
|
||||
|
||||
mdc.SetFont(self.fontSmall)
|
||||
mdc.DrawText(self.toolbar.hoverLabel, self.thoverx, self.thovery)
|
||||
mdc.DrawText(self.toolbar.hoverLabel, round(self.thoverx), round(self.thovery))
|
||||
|
||||
mdc.SetFont(self.fontBig)
|
||||
|
||||
psname = drawUtils.GetPartialText(mdc, shipName,
|
||||
self.toolbarx - self.textStartx - self.padding * 2 - self.thoverw)
|
||||
|
||||
mdc.DrawText(psname, self.textStartx, self.shipNamey)
|
||||
mdc.DrawText(psname, round(self.textStartx), round(self.shipNamey))
|
||||
|
||||
if self.tcFitName.IsShown():
|
||||
self.AdjustControlSizePos(self.tcFitName, self.textStartx, self.toolbarx - self.editWidth - self.padding)
|
||||
|
||||
@@ -7,6 +7,7 @@ __all__ = [
|
||||
"outgoingViewFull",
|
||||
"outgoingViewMinimal",
|
||||
"targetingMiscViewMinimal",
|
||||
"bombingViewFull",
|
||||
"priceViewFull",
|
||||
"priceViewMinimal",
|
||||
]
|
||||
|
||||
165
gui/builtinStatsViews/bombingViewFull.py
Normal file
165
gui/builtinStatsViews/bombingViewFull.py
Normal file
@@ -0,0 +1,165 @@
|
||||
# =============================================================================
|
||||
# Copyright (C) 2010 Diego Duclos
|
||||
#
|
||||
# This file is part of pyfa.
|
||||
#
|
||||
# pyfa is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# pyfa is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with pyfa. If not, see <http://www.gnu.org/licenses/>.
|
||||
# =============================================================================
|
||||
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
import math
|
||||
|
||||
import gui.mainFrame
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
from gui.statsView import StatsView
|
||||
from eos.const import FittingModuleState
|
||||
from service.market import Market
|
||||
|
||||
_t = wx.GetTranslation
|
||||
|
||||
|
||||
class BombingViewFull(StatsView):
|
||||
name = "bombingViewFull"
|
||||
|
||||
def __init__(self, parent):
|
||||
StatsView.__init__(self)
|
||||
self.parent = parent
|
||||
self._cachedValues = []
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
|
||||
def getHeaderText(self, fit):
|
||||
return _t("Bombing")
|
||||
|
||||
def getTextExtentW(self, text):
|
||||
width, height = self.parent.GetTextExtent(text)
|
||||
return width
|
||||
|
||||
def populatePanel(self, contentPanel, headerPanel):
|
||||
contentSizer = contentPanel.GetSizer()
|
||||
self.panel = contentPanel
|
||||
|
||||
self.headerPanel = headerPanel
|
||||
|
||||
# Display table
|
||||
sizerBombing = wx.FlexGridSizer(7, 5, 0, 0)
|
||||
for i in range(4):
|
||||
sizerBombing.AddGrowableCol(i + 1)
|
||||
contentSizer.Add(sizerBombing, 0, wx.EXPAND, 0)
|
||||
|
||||
# first row is for icons
|
||||
bitmap = BitmapLoader.getStaticBitmap("skill_big", contentPanel, "gui")
|
||||
tooltip = wx.ToolTip(_t("Covert Ops level"))
|
||||
bitmap.SetToolTip(tooltip)
|
||||
sizerBombing.Add(bitmap, 0, wx.ALIGN_CENTER)
|
||||
toolTipText = {
|
||||
"em": _t("Electron Bomb"),
|
||||
"thermal": _t("Scorch Bomb"),
|
||||
"kinetic": _t("Concussion Bomb"),
|
||||
"explosive": _t("Shrapnel Bomb")
|
||||
}
|
||||
for damageType in ("em", "thermal", "kinetic", "explosive"):
|
||||
bitmap = BitmapLoader.getStaticBitmap("%s_big" % damageType, contentPanel, "gui")
|
||||
tooltip = wx.ToolTip(toolTipText[damageType])
|
||||
bitmap.SetToolTip(tooltip)
|
||||
sizerBombing.Add(bitmap, 0, wx.ALIGN_CENTER)
|
||||
|
||||
# the other rows are for each possible level of Covert Ops skill
|
||||
for covertLevel in ("0", "1", "2", "3", "4", "5"):
|
||||
label = wx.StaticText(contentPanel, wx.ID_ANY, "%s" % covertLevel)
|
||||
tooltip = wx.ToolTip(_t("Covert Ops level"))
|
||||
label.SetToolTip(tooltip)
|
||||
sizerBombing.Add(label, 0, wx.ALIGN_CENTER)
|
||||
|
||||
for damageType in ("em", "thermal", "kinetic", "explosive"):
|
||||
label = wx.StaticText(contentPanel, wx.ID_ANY, "0.0")
|
||||
setattr(self, "labelDamagetypeCovertlevel%s%s" % (damageType.capitalize(), covertLevel), label)
|
||||
sizerBombing.Add(label, 0, wx.ALIGN_CENTER)
|
||||
|
||||
def refreshPanel(self, fit):
|
||||
# If we did anything interesting, we'd update our labels to reflect the new fit's stats here
|
||||
if fit is None:
|
||||
return
|
||||
|
||||
mkt = Market.getInstance()
|
||||
emBomb = mkt.getItem(27920)
|
||||
thermalBomb = mkt.getItem(27916)
|
||||
kineticBomb = mkt.getItem(27912)
|
||||
explosiveBomb = mkt.getItem(27918)
|
||||
environementBombDamageModifier = 1.0
|
||||
|
||||
# list all environmental effects affecting bomb damage
|
||||
relevantEffects = [
|
||||
'Class 6 Red Giant Effects',
|
||||
'Class 5 Red Giant Effects',
|
||||
'Class 4 Red Giant Effects',
|
||||
'Class 3 Red Giant Effects',
|
||||
'Class 2 Red Giant Effects',
|
||||
'Class 1 Red Giant Effects',
|
||||
]
|
||||
for effect in fit.projectedModules:
|
||||
if effect.state == FittingModuleState.ONLINE and effect.fullName in relevantEffects:
|
||||
# note: despite the name, smartbombDamageMultiplier applies to the damage of launched bombs
|
||||
environementBombDamageModifier = environementBombDamageModifier *\
|
||||
effect.item.attributes['smartbombDamageMultiplier'].value
|
||||
|
||||
# signature radius of the current fit to calculate the application of bombs
|
||||
shipSigRadius = fit.ship.getModifiedItemAttr('signatureRadius')
|
||||
|
||||
# get the raw values for all hp layers
|
||||
hullHP = fit.ship.getModifiedItemAttr('hp')
|
||||
armorHP = fit.ship.getModifiedItemAttr('armorHP')
|
||||
shieldHP = fit.ship.getModifiedItemAttr('shieldCapacity')
|
||||
|
||||
# we calculate the total ehp for pure damage of all types based on raw hp and resonance (resonance= 1-resistance)
|
||||
emEhp = hullHP / fit.ship.getModifiedItemAttr('emDamageResonance') +\
|
||||
armorHP / fit.ship.getModifiedItemAttr('armorEmDamageResonance') +\
|
||||
shieldHP / fit.ship.getModifiedItemAttr('shieldEmDamageResonance')
|
||||
thermalEhp = hullHP / fit.ship.getModifiedItemAttr('thermalDamageResonance') +\
|
||||
armorHP / fit.ship.getModifiedItemAttr('armorThermalDamageResonance') +\
|
||||
shieldHP / fit.ship.getModifiedItemAttr('shieldThermalDamageResonance')
|
||||
kineticEhp = hullHP / fit.ship.getModifiedItemAttr('kineticDamageResonance') +\
|
||||
armorHP / fit.ship.getModifiedItemAttr('armorKineticDamageResonance') +\
|
||||
shieldHP / fit.ship.getModifiedItemAttr('shieldKineticDamageResonance')
|
||||
explosiveEhp = hullHP / fit.ship.getModifiedItemAttr('explosiveDamageResonance') +\
|
||||
armorHP / fit.ship.getModifiedItemAttr('armorExplosiveDamageResonance') +\
|
||||
shieldHP / fit.ship.getModifiedItemAttr('shieldExplosiveDamageResonance')
|
||||
|
||||
# updates the labels for each combination of covert op level and damage type
|
||||
for covertLevel in ("0", "1", "2", "3", "4", "5"):
|
||||
covertOpsBombDamageModifier = 1 + 0.05 * int(covertLevel)
|
||||
for damageType, ehp, bomber, bomb in (("em", emEhp, "Purifier", emBomb),
|
||||
("thermal", thermalEhp, "Nemesis", thermalBomb),
|
||||
("kinetic", kineticEhp, "Manticore", kineticBomb),
|
||||
("explosive", explosiveEhp, "Hound", explosiveBomb)):
|
||||
baseBombDamage = (bomb.attributes['emDamage'].value + bomb.attributes['thermalDamage'].value +
|
||||
bomb.attributes['kineticDamage'].value + bomb.attributes['explosiveDamage'].value)
|
||||
appliedBombDamage = baseBombDamage * covertOpsBombDamageModifier * environementBombDamageModifier * \
|
||||
(min(bomb.attributes['signatureRadius'].value, shipSigRadius) /
|
||||
bomb.attributes['signatureRadius'].value)
|
||||
label = getattr(self, "labelDamagetypeCovertlevel%s%s" % (damageType.capitalize(), covertLevel))
|
||||
label.SetLabel("{:.1f}".format(math.ceil((ehp / appliedBombDamage) * 10) / 10))
|
||||
if covertLevel != "0":
|
||||
label.SetToolTip("Number of %s to kill a %s using a %s "
|
||||
"with Covert Ops level %s" % (bomb.customName, fit.name, bomber, covertLevel))
|
||||
else:
|
||||
label.SetToolTip("Number of %s to kill a %s with Covert Ops level %s" %
|
||||
(bomb.customName, fit.name, covertLevel))
|
||||
|
||||
|
||||
self.panel.Layout()
|
||||
self.headerPanel.Layout()
|
||||
|
||||
|
||||
BombingViewFull.register()
|
||||
@@ -173,7 +173,7 @@ class FirepowerViewFull(StatsView):
|
||||
if hasSpool:
|
||||
lines.append("")
|
||||
lines.append(_t("Current") + ": {}".format(formatAmount(normal.total, prec, lowest, highest)))
|
||||
for dmgType in normal.names():
|
||||
for dmgType in normal.names(includePure=True):
|
||||
val = getattr(normal, dmgType, None)
|
||||
if val:
|
||||
lines.append("{}{}: {}%".format(
|
||||
@@ -215,13 +215,13 @@ class FirepowerViewFull(StatsView):
|
||||
val = val() if fit is not None else None
|
||||
preSpoolVal = preSpoolVal() if fit is not None else None
|
||||
fullSpoolVal = fullSpoolVal() if fit is not None else None
|
||||
if self._cachedValues[counter] != val:
|
||||
if self._cachedValues[counter] != getattr(val, 'total', None):
|
||||
tooltipText = dpsToolTip(val, preSpoolVal, fullSpoolVal, prec, lowest, highest)
|
||||
label.SetLabel(valueFormat.format(
|
||||
formatAmount(0 if val is None else val.total, prec, lowest, highest),
|
||||
"\u02e2" if hasSpoolUp(preSpoolVal, fullSpoolVal) else ""))
|
||||
label.SetToolTip(wx.ToolTip(tooltipText))
|
||||
self._cachedValues[counter] = val
|
||||
self._cachedValues[counter] = getattr(val, 'total', None)
|
||||
counter += 1
|
||||
|
||||
self.panel.Layout()
|
||||
|
||||
@@ -146,8 +146,8 @@ class ResistancesViewFull(StatsView):
|
||||
|
||||
lbl = PyGauge(contentPanel, font, 100)
|
||||
lbl.SetMinSize((48, 16))
|
||||
lbl.SetBackgroundColour(wx.Colour(bc[0], bc[1], bc[2]))
|
||||
lbl.SetBarColour(wx.Colour(fc[0], fc[1], fc[2]))
|
||||
lbl.SetBackgroundColour(wx.Colour(round(bc[0]), round(bc[1]), round(bc[2])))
|
||||
lbl.SetBarColour(wx.Colour(round(fc[0]), round(fc[1]), round(fc[2])))
|
||||
lbl.SetBarGradient()
|
||||
lbl.SetFractionDigits(1)
|
||||
|
||||
|
||||
@@ -112,6 +112,7 @@ class TargetingMiscViewMinimal(StatsView):
|
||||
cargoNamesOrder = OrderedDict((
|
||||
("fleetHangarCapacity", _t("Fleet hangar")),
|
||||
("shipMaintenanceBayCapacity", _t("Maintenance bay")),
|
||||
("specialColonyResourcesHoldCapacity", _t("Infrastructure hold")),
|
||||
("specialAmmoHoldCapacity", _t("Ammo hold")),
|
||||
("specialFuelBayCapacity", _t("Fuel bay")),
|
||||
("specialShipHoldCapacity", _t("Ship hold")),
|
||||
@@ -127,11 +128,14 @@ class TargetingMiscViewMinimal(StatsView):
|
||||
("specialSalvageHoldCapacity", _t("Salvage hold")),
|
||||
("specialCommandCenterHoldCapacity", _t("Command center hold")),
|
||||
("specialPlanetaryCommoditiesHoldCapacity", _t("Planetary goods hold")),
|
||||
("specialQuafeHoldCapacity", _t("Quafe hold"))))
|
||||
("specialQuafeHoldCapacity", _t("Quafe hold")),
|
||||
("specialMobileDepotHoldCapacity", _t("Mobile depot hold")),
|
||||
))
|
||||
|
||||
cargoValues = {
|
||||
"main": lambda: fit.ship.getModifiedItemAttr("capacity"),
|
||||
"fleetHangarCapacity": lambda: fit.ship.getModifiedItemAttr("fleetHangarCapacity"),
|
||||
"specialColonyResourcesHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialColonyResourcesHoldCapacity"),
|
||||
"shipMaintenanceBayCapacity": lambda: fit.ship.getModifiedItemAttr("shipMaintenanceBayCapacity"),
|
||||
"specialAmmoHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialAmmoHoldCapacity"),
|
||||
"specialFuelBayCapacity": lambda: fit.ship.getModifiedItemAttr("specialFuelBayCapacity"),
|
||||
@@ -148,7 +152,8 @@ class TargetingMiscViewMinimal(StatsView):
|
||||
"specialSalvageHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialSalvageHoldCapacity"),
|
||||
"specialCommandCenterHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialCommandCenterHoldCapacity"),
|
||||
"specialPlanetaryCommoditiesHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialPlanetaryCommoditiesHoldCapacity"),
|
||||
"specialQuafeHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialQuafeHoldCapacity")
|
||||
"specialQuafeHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialQuafeHoldCapacity"),
|
||||
"specialMobileDepotHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialMobileDepotHoldCapacity"),
|
||||
}
|
||||
|
||||
stats = (("labelTargets", {"main": lambda: fit.maxTargets}, 3, 0, 0, ""),
|
||||
|
||||
@@ -197,6 +197,30 @@ class SignatureRadiusColumn(GraphColumn):
|
||||
SignatureRadiusColumn.register()
|
||||
|
||||
|
||||
class FullHpColumn(GraphColumn):
|
||||
|
||||
name = 'FullHP'
|
||||
stickPrefixToValue = True
|
||||
|
||||
def __init__(self, fittingView, params):
|
||||
super().__init__(fittingView, 68)
|
||||
|
||||
def _getValue(self, stuff):
|
||||
if isinstance(stuff, Fit):
|
||||
full_hp = stuff.hp.get('shield', 0) + stuff.hp.get('armor', 0) + stuff.hp.get('hull', 0)
|
||||
elif isinstance(stuff, TargetProfile):
|
||||
full_hp = stuff.hp
|
||||
else:
|
||||
full_hp = 0
|
||||
return full_hp, 'hp'
|
||||
|
||||
def _getFitTooltip(self):
|
||||
return 'Total raw HP'
|
||||
|
||||
|
||||
FullHpColumn.register()
|
||||
|
||||
|
||||
class ShieldAmountColumn(GraphColumn):
|
||||
|
||||
name = 'ShieldAmount'
|
||||
|
||||
87
gui/builtinViewColumns/droneEhp.py
Normal file
87
gui/builtinViewColumns/droneEhp.py
Normal file
@@ -0,0 +1,87 @@
|
||||
# =============================================================================
|
||||
# Copyright (C) 2010 Diego Duclos
|
||||
#
|
||||
# This file is part of pyfa.
|
||||
#
|
||||
# pyfa is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# pyfa is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with pyfa. If not, see <http://www.gnu.org/licenses/>.
|
||||
# =============================================================================
|
||||
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
|
||||
import gui.mainFrame
|
||||
from eos.saveddata.drone import Drone
|
||||
from eos.saveddata.fighter import Fighter
|
||||
from service.attribute import Attribute
|
||||
from gui.viewColumn import ViewColumn
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
from gui.utils.numberFormatter import formatAmount
|
||||
|
||||
|
||||
_t = wx.GetTranslation
|
||||
|
||||
|
||||
class DroneEhpColumn(ViewColumn):
|
||||
name = "Drone HP"
|
||||
|
||||
def __init__(self, fittingView, params=None):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
if params is None:
|
||||
params = {"showIcon": True, "displayName": False}
|
||||
|
||||
ViewColumn.__init__(self, fittingView)
|
||||
|
||||
sAttr = Attribute.getInstance()
|
||||
info = sAttr.getAttributeInfo("shieldCapacity")
|
||||
self.info = info
|
||||
if params["showIcon"]:
|
||||
iconFile = info.iconID
|
||||
if iconFile:
|
||||
self.imageId = fittingView.imageList.GetImageIndex(iconFile, "icons")
|
||||
self.bitmap = BitmapLoader.getBitmap(iconFile, "icons")
|
||||
else:
|
||||
self.imageId = -1
|
||||
self.mask = wx.LIST_MASK_IMAGE
|
||||
else:
|
||||
self.imageId = -1
|
||||
|
||||
if params["displayName"] or self.imageId == -1:
|
||||
self.columnText = info.displayName if info.displayName != "" else info.name
|
||||
self.mask |= wx.LIST_MASK_TEXT
|
||||
|
||||
def getText(self, stuff):
|
||||
if not isinstance(stuff, (Drone, Fighter)):
|
||||
return ""
|
||||
if self.mainFrame.statsPane.nameViewMap["resistancesViewFull"].showEffective:
|
||||
ehp = sum(stuff.ehp.values())
|
||||
else:
|
||||
ehp = sum(stuff.hp.values())
|
||||
return formatAmount(ehp, 3, 0, 9)
|
||||
|
||||
def getImageId(self, mod):
|
||||
return -1
|
||||
|
||||
def getParameters(self):
|
||||
return ("displayName", bool, False), ("showIcon", bool, True)
|
||||
|
||||
def getToolTip(self, stuff):
|
||||
if not isinstance(stuff, (Drone, Fighter)):
|
||||
return ""
|
||||
if self.mainFrame.statsPane.nameViewMap["resistancesViewFull"].showEffective:
|
||||
return _t("Effective HP")
|
||||
else:
|
||||
return _t("HP")
|
||||
|
||||
|
||||
DroneEhpColumn.register()
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user