Compare commits
1838 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
57c2d37e3c | ||
|
|
5558cc8a51 | ||
|
|
76e6dd86e1 | ||
|
|
83973dda73 | ||
|
|
e68530d08c | ||
|
|
750ed8b6fa | ||
|
|
8225dc8f49 | ||
|
|
ec4a0743b8 | ||
|
|
2201dde8ed | ||
|
|
3a324b9f9f | ||
|
|
11dcc31554 | ||
|
|
074048890c | ||
|
|
4ac1627b33 | ||
|
|
84f532284e | ||
|
|
ccbe88d3bf | ||
|
|
c7074f499f | ||
|
|
f6f3a69be4 | ||
|
|
23e09729f7 | ||
|
|
0aca05704f | ||
|
|
b08894e984 | ||
|
|
a1bc8742c9 | ||
|
|
6472cabc05 | ||
|
|
56bb8217d3 | ||
|
|
17f9071317 | ||
|
|
50eda1f4db | ||
|
|
84fbc0a46c | ||
|
|
9551195078 | ||
|
|
f01949d892 | ||
|
|
26b4c05b6f | ||
|
|
6ecab03fd8 | ||
|
|
edc0418d9a | ||
|
|
1d413595b9 | ||
|
|
ce5a593f7b | ||
|
|
faea6a97f0 | ||
|
|
b92913cbf9 | ||
|
|
1af2e7f94b | ||
|
|
dbb61a8a37 | ||
|
|
b12adcae3d | ||
|
|
72567a7155 | ||
|
|
0d4c2551c1 | ||
|
|
ce10aeb55e | ||
|
|
7b14266f0d | ||
|
|
b436f6ec89 | ||
|
|
e7e3f4e626 | ||
|
|
ae8405d132 | ||
|
|
977cf61ff5 | ||
|
|
7cc4f6ec27 | ||
|
|
0e6b9b48f1 | ||
|
|
a00a80b4e4 | ||
|
|
6843373283 | ||
|
|
408da2e344 | ||
|
|
dc8ecae0f1 | ||
|
|
50e6ed516f | ||
|
|
42a6bb92a7 | ||
|
|
521a58d77b | ||
|
|
6546f32971 | ||
|
|
ad3019debe | ||
|
|
fe9fa8b4fe | ||
|
|
92fce5be96 | ||
|
|
d271515060 | ||
|
|
343e52e556 | ||
|
|
7949bc60dc | ||
|
|
bfbd3526cd | ||
|
|
c363f7359d | ||
|
|
3a7ece6a5b | ||
|
|
e0e1c1ce03 | ||
|
|
edec81f4b8 | ||
|
|
405cf78341 | ||
|
|
e29374f681 | ||
|
|
8c2b09f90b | ||
|
|
f91012fe9f | ||
|
|
8d84299a66 | ||
|
|
9ed60f37a9 | ||
|
|
88d2e9deef | ||
|
|
36cc4de2d0 | ||
|
|
e792bd62d3 | ||
|
|
d71c69ea64 | ||
|
|
967a625292 | ||
|
|
e09215caab | ||
|
|
b24a3c7220 | ||
|
|
52d6c452c7 | ||
|
|
fb3721ace5 | ||
|
|
b1f048ac93 | ||
|
|
73b3e347fb | ||
|
|
6090d748d2 | ||
|
|
5e37b5ef77 | ||
|
|
78bb5e7cc0 | ||
|
|
96c248f477 | ||
|
|
696b195bb5 | ||
|
|
b0fa8f1b8c | ||
|
|
6e173551ed | ||
|
|
2fccc7487b | ||
|
|
2f5b674b75 | ||
|
|
80ced86ace | ||
|
|
de93777f99 | ||
|
|
f23d1e4921 | ||
|
|
8afb2139b3 | ||
|
|
c7412a5174 | ||
|
|
2494ad8cef | ||
|
|
a2f17e3205 | ||
|
|
f4d50792ea | ||
|
|
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 | ||
|
|
532268d4e6 | ||
|
|
6445d4219a | ||
|
|
9e7e994e1d | ||
|
|
209120a0dd | ||
|
|
0e0d820cb5 | ||
|
|
8cd87929b9 | ||
|
|
acd853bdde | ||
|
|
94b4bed1fe | ||
|
|
01bed745a2 | ||
|
|
40022d5d18 | ||
|
|
2ad4040ec7 | ||
|
|
8cbe094ee6 | ||
|
|
2e11cbc5ae | ||
|
|
12f191d6d1 | ||
|
|
3918c4e3f9 | ||
|
|
779f1b476c | ||
|
|
61bb1ac225 | ||
|
|
c459cbbc92 | ||
|
|
66018642f7 | ||
|
|
4c6d171793 | ||
|
|
aa1ecd6bea | ||
|
|
be73ffd929 | ||
|
|
3024ccd176 | ||
|
|
d12128bc58 | ||
|
|
c295482055 | ||
|
|
b62260cd1f | ||
|
|
a61e3ebaad | ||
|
|
9ae78aab0d | ||
|
|
06b9f8fe08 | ||
|
|
60ab34f6a4 | ||
|
|
18255d324c | ||
|
|
51e43f6927 | ||
|
|
1208253110 | ||
|
|
af4ed86324 | ||
|
|
8b34566d72 | ||
|
|
5ab149d9f6 | ||
|
|
bb38ba6564 | ||
|
|
3f6aa91966 | ||
|
|
e86e5933b5 | ||
|
|
000c1bc38f | ||
|
|
feaa52c36b | ||
|
|
a8e595b470 | ||
|
|
157e709208 | ||
|
|
ae4ec99308 | ||
|
|
aa7d3a1b7b | ||
|
|
a7c2f700b4 | ||
|
|
ce4b7f4f20 | ||
|
|
6c86aa95a3 | ||
|
|
b52df842d5 | ||
|
|
4ccc64c7c7 | ||
|
|
a0d2b13dd6 | ||
|
|
7873853f97 | ||
|
|
877990d486 | ||
|
|
1019c5f0cd | ||
|
|
b1b4284b6e | ||
|
|
e0377a084f | ||
|
|
dbf9875d25 | ||
|
|
5d66454e52 | ||
|
|
f5d18fd606 | ||
|
|
145e92b458 | ||
|
|
ca8a8f0785 | ||
|
|
dfdcf0a804 | ||
|
|
087284e048 | ||
|
|
65741ed03b | ||
|
|
ae3d7ade0f | ||
|
|
789df97017 | ||
|
|
7992ba43b2 | ||
|
|
3595e24ad8 | ||
|
|
6677435474 | ||
|
|
1561d9f581 | ||
|
|
1dee8ae648 | ||
|
|
51ea0c4f31 | ||
|
|
9d8e338136 | ||
|
|
48818c9709 | ||
|
|
ca356b413b | ||
|
|
a9ef5caf2d | ||
|
|
22be97f901 | ||
|
|
06f515ec0b | ||
|
|
c878f4c155 | ||
|
|
5cf433a3eb | ||
|
|
15191e5752 | ||
|
|
9d833d9cac | ||
|
|
5767c92985 | ||
|
|
3246e58278 | ||
|
|
30546c6e45 | ||
|
|
37fe587d90 | ||
|
|
09b1b50257 | ||
|
|
91a507eadd | ||
|
|
dd92c3742c | ||
|
|
181170d7b0 | ||
|
|
1156f06db2 | ||
|
|
90baffe267 | ||
|
|
100a1fa01a | ||
|
|
d8d854da52 | ||
|
|
418339d000 | ||
|
|
24a0f4f53e | ||
|
|
94784bf1e0 | ||
|
|
d894df9819 | ||
|
|
a5e1f3be40 | ||
|
|
dcb6439444 | ||
|
|
5918d6371f | ||
|
|
8b1553df03 | ||
|
|
98fa16d921 | ||
|
|
d5795ed5f5 | ||
|
|
fe809ed86b | ||
|
|
d09db832c3 | ||
|
|
de4466b037 | ||
|
|
8ebf478cf7 | ||
|
|
530a12ea4f | ||
|
|
b94f6e8d0a | ||
|
|
4b83169070 | ||
|
|
a7da9e20a5 | ||
|
|
0020dcdf0a | ||
|
|
066f697186 | ||
|
|
b2217bb6cd | ||
|
|
4669b4813c | ||
|
|
a71701932c | ||
|
|
1be0d306bb | ||
|
|
85f63e237d | ||
|
|
acab787a7b | ||
|
|
da50c7dcfc | ||
|
|
b0d3469f01 | ||
|
|
148093f6ad | ||
|
|
fab58bdb57 | ||
|
|
34b2fdbd1b | ||
|
|
ea7a5b3c70 | ||
|
|
056685ded5 | ||
|
|
60f9e96b19 | ||
|
|
341950cb42 | ||
|
|
641e982614 | ||
|
|
1e534e087a | ||
|
|
0b7b80d0f6 | ||
|
|
27bd9fc55b | ||
|
|
533f6f3b24 | ||
|
|
c18cbe5e59 | ||
|
|
adba55eb7d | ||
|
|
03c6f7c894 | ||
|
|
478b2bfe4c | ||
|
|
7e4d8a3074 | ||
|
|
8497d1f1bb | ||
|
|
1b5acc36d3 | ||
|
|
2a94dcebf8 | ||
|
|
2731e9962b | ||
|
|
7b9e196ca8 | ||
|
|
f3f7d688ab | ||
|
|
17391f119c | ||
|
|
abd138a015 | ||
|
|
89d0ee4c77 | ||
|
|
33aa208513 | ||
|
|
1874cbe0c5 | ||
|
|
76536a5dcc | ||
|
|
41d6828024 | ||
|
|
bf4b4ab3c0 | ||
|
|
028fb42e18 | ||
|
|
6b444bfc63 | ||
|
|
1753d79bc1 | ||
|
|
d9b0d2e72a | ||
|
|
e0234c343e | ||
|
|
d7d4643a33 | ||
|
|
166635d0a1 | ||
|
|
b5816a312c | ||
|
|
076e1999af | ||
|
|
69caff50c3 | ||
|
|
0db72f7a8c | ||
|
|
43ac76ac1f | ||
|
|
279c912703 | ||
|
|
e54cec47f2 | ||
|
|
50293081d1 | ||
|
|
4f526d7cc4 | ||
|
|
86cc025812 | ||
|
|
8072f7e618 | ||
|
|
c34b12d806 | ||
|
|
67760fa2ab | ||
|
|
9a28dd17be | ||
|
|
6e4d1876ad | ||
|
|
40b0b27176 | ||
|
|
b238422f29 | ||
|
|
4e20583b81 | ||
|
|
f730cdb86a | ||
|
|
e05471fa6f | ||
|
|
45b517e097 | ||
|
|
f27274c9e5 | ||
|
|
d32a8f0b61 | ||
|
|
6a01086cf5 | ||
|
|
e0f65c2370 | ||
|
|
e7c2f4cff7 | ||
|
|
93cce50d00 | ||
|
|
d1a2cdc586 | ||
|
|
9dab6b0f92 | ||
|
|
33e3ea6030 | ||
|
|
9d97b27c3a | ||
|
|
05d156c7f4 | ||
|
|
2ddc4924cc | ||
|
|
e3fef6f1a7 | ||
|
|
fbd8d04f68 | ||
|
|
23916165bb | ||
|
|
3cca6a6c95 | ||
|
|
a52172a3c6 | ||
|
|
8a2bfb48ce | ||
|
|
b42254cb61 | ||
|
|
91fbcd4eb4 | ||
|
|
bdc52da05d | ||
|
|
2bbe17722b | ||
|
|
3ef07b9153 | ||
|
|
5af904ca18 | ||
|
|
d3cdc67472 | ||
|
|
0991124d5f | ||
|
|
83ea548c4d | ||
|
|
4752eee09d | ||
|
|
53d2d1fabe | ||
|
|
f600868eb2 | ||
|
|
f6a489fa22 | ||
|
|
2962a89919 | ||
|
|
c3d53f56a9 | ||
|
|
3257abbeff | ||
|
|
5858d96deb | ||
|
|
94d1eae464 | ||
|
|
b7e2b782c9 | ||
|
|
05e5958d36 | ||
|
|
e9364fe65a | ||
|
|
b3b71896ae | ||
|
|
00d20f53d0 | ||
|
|
88e4f7a77e | ||
|
|
5763954f51 | ||
|
|
4fbfbcc84e | ||
|
|
1f2c20e95b | ||
|
|
d7aa744bc1 | ||
|
|
1117c786ff | ||
|
|
6a715f6678 | ||
|
|
5e93235b02 | ||
|
|
271b915ce6 | ||
|
|
52667da64a | ||
|
|
82f0fea1bf | ||
|
|
751823177b | ||
|
|
ac7e8a1efa | ||
|
|
066a12e912 | ||
|
|
ea64657fba | ||
|
|
17b94001e0 | ||
|
|
cc481f9f29 | ||
|
|
f73bb14990 | ||
|
|
a5ed992cd2 | ||
|
|
f7e792411a | ||
|
|
fc71d7b706 | ||
|
|
162055d5ce | ||
|
|
5cc87a439f | ||
|
|
d2d3a42adf | ||
|
|
e43cb74906 | ||
|
|
ace00d495a | ||
|
|
729b39e351 | ||
|
|
5ae1e64a76 | ||
|
|
88a9ce03ba | ||
|
|
cbd3d8cc1b | ||
|
|
65a126b4c5 | ||
|
|
241c744d40 | ||
|
|
8ee900a90e | ||
|
|
1c415fbe06 | ||
|
|
304aebbccf | ||
|
|
18d9f6a542 | ||
|
|
cdc6e046b0 | ||
|
|
59bb9670b6 | ||
|
|
9321a78a61 | ||
|
|
53191714f2 | ||
|
|
b17347156c | ||
|
|
66ace06194 | ||
|
|
e8697c6131 | ||
|
|
7a6e87330d | ||
|
|
2acf92160e | ||
|
|
6e49f6fd7a | ||
|
|
6e56230a0e | ||
|
|
01282d103a | ||
|
|
a0317d5b3c | ||
|
|
a697c1890a | ||
|
|
aaf719437f | ||
|
|
3d6e703b7c | ||
|
|
ce26b22752 | ||
|
|
6b30b2859e | ||
|
|
961258618a | ||
|
|
f07684875f | ||
|
|
538954f4a6 | ||
|
|
30e73ed795 | ||
|
|
d216eb3e55 | ||
|
|
c25fa0f632 | ||
|
|
8a115be0bd | ||
|
|
b897ee5146 | ||
|
|
7b3bda5816 | ||
|
|
f62e16288d | ||
|
|
9aad8860a9 | ||
|
|
f8dd1f27a9 | ||
|
|
09bc2da863 | ||
|
|
f531c144af | ||
|
|
dfd08fa8e2 | ||
|
|
e430848be9 | ||
|
|
2d645b8f91 | ||
|
|
e33cb80608 | ||
|
|
fcc8c3eeb9 | ||
|
|
c29d567255 | ||
|
|
e85b618cbc | ||
|
|
008b6a887d | ||
|
|
05ab2d47c7 | ||
|
|
75b611c94a | ||
|
|
356d594637 | ||
|
|
03f4bc1d75 | ||
|
|
226c5d7fad | ||
|
|
0c3998b640 | ||
|
|
942f9a35ee | ||
|
|
031a3c2d3e | ||
|
|
ed7df2f1a7 | ||
|
|
fb2ad2cc10 | ||
|
|
34bce4e083 | ||
|
|
fc45b43295 | ||
|
|
4160b08388 | ||
|
|
95c25b274c | ||
|
|
690e5caf52 | ||
|
|
95ada5d61d | ||
|
|
976820a7c0 | ||
|
|
8ab14bcfcc | ||
|
|
6e8798eb08 | ||
|
|
15af830dca | ||
|
|
ce728243b0 | ||
|
|
2696b480fa | ||
|
|
ca7a3cae9c | ||
|
|
10613c9070 | ||
|
|
a692b1fe74 | ||
|
|
0592d2c3f4 | ||
|
|
301a3473cc | ||
|
|
4887852de4 | ||
|
|
6b3bf1f7a8 | ||
|
|
a4d163fc89 | ||
|
|
e88e3996a8 | ||
|
|
a36dd20890 | ||
|
|
c9f37457ab | ||
|
|
4709cd78f3 | ||
|
|
5ac5b3f5e4 | ||
|
|
da78c24d9b | ||
|
|
566c87fa59 | ||
|
|
e6d7a140cf | ||
|
|
89e496f3af | ||
|
|
d650284ae1 | ||
|
|
b41d7d2603 | ||
|
|
33c20bfc3b | ||
|
|
6f0778ad94 | ||
|
|
7ce39a0dac | ||
|
|
7de7a17bae | ||
|
|
50b8c3fc61 | ||
|
|
dab5d1f211 | ||
|
|
5141a30e22 | ||
|
|
1172e5011e | ||
|
|
ea2141b719 | ||
|
|
9f7fdfc800 | ||
|
|
de63b11a80 | ||
|
|
514d11e6f2 | ||
|
|
9d759054ca | ||
|
|
c162d96cc0 | ||
|
|
3c22397377 | ||
|
|
20e605c30f | ||
|
|
9601887855 | ||
|
|
0a7f5b6092 | ||
|
|
e27ea79bc6 | ||
|
|
adb0609eb8 | ||
|
|
39b08baff0 | ||
|
|
e4388bf835 | ||
|
|
b9cae5165b | ||
|
|
06e6589209 | ||
|
|
9f21b251ff | ||
|
|
7d7a499070 | ||
|
|
e18a3bfab5 | ||
|
|
a80b7c098a | ||
|
|
1580a8ddc9 | ||
|
|
385215b717 | ||
|
|
d09913d61b | ||
|
|
b9059835ce | ||
|
|
7af28b497c | ||
|
|
5cf555b8ce | ||
|
|
a25bb1aaa3 | ||
|
|
24a54c9cec | ||
|
|
0b9e6bf55d | ||
|
|
41070ce22e | ||
|
|
7b5aab8a35 | ||
|
|
c9fe6c959d | ||
|
|
67cf3a3a26 | ||
|
|
c644d528e9 | ||
|
|
c1302ce7ee | ||
|
|
e4ea9c2ab9 | ||
|
|
cca5781ca8 | ||
|
|
9760d2c4a5 | ||
|
|
4efa9a6961 | ||
|
|
b27d4bcbbd | ||
|
|
7bff295012 | ||
|
|
01249dfd9c | ||
|
|
0cc2668c1c | ||
|
|
dfad734a0a | ||
|
|
66c901a2b0 | ||
|
|
3673e7f39e | ||
|
|
124b35d108 | ||
|
|
5181e8e95a | ||
|
|
48cc4ce9d4 | ||
|
|
07ff43b576 | ||
|
|
70969cc9fb | ||
|
|
d23bf2fa4c | ||
|
|
04c8659922 | ||
|
|
5e3b92699b | ||
|
|
1755ab4c3f | ||
|
|
26ec741094 | ||
|
|
530f7510d5 | ||
|
|
37ab704cd7 | ||
|
|
683ad3802f | ||
|
|
c882351886 | ||
|
|
9d71215c52 | ||
|
|
d2a0605abe | ||
|
|
bee6652cc6 | ||
|
|
047ef75960 | ||
|
|
32e57f8138 | ||
|
|
e891804b64 | ||
|
|
7d42b89726 | ||
|
|
6561996327 | ||
|
|
0d2ad6eac2 | ||
|
|
3b2bfd01c5 | ||
|
|
a317dab9a8 | ||
|
|
db6d8b93e9 | ||
|
|
7d585c1a62 | ||
|
|
c45e84470e | ||
|
|
ab3b40e136 | ||
|
|
b566a8bfa6 | ||
|
|
ccb395d592 | ||
|
|
3d039724c9 | ||
|
|
d0e7e7eed5 | ||
|
|
a387bc8d09 | ||
|
|
94344bd432 | ||
|
|
a976fb33f0 | ||
|
|
f850fdf0d5 | ||
|
|
0fcbedba45 | ||
|
|
5707bddacd | ||
|
|
8ed9257dfa | ||
|
|
056ae590cf | ||
|
|
7733fd38c2 | ||
|
|
fef78c971f | ||
|
|
e52ceacdb9 | ||
|
|
85b2d7af8d | ||
|
|
3e658a31bb | ||
|
|
0d2a4d4d44 | ||
|
|
c08216252e | ||
|
|
02219846a7 | ||
|
|
ff0af7cce7 | ||
|
|
0e8316192b | ||
|
|
c0f8099f49 | ||
|
|
32a03dedaa | ||
|
|
5ae43fc648 | ||
|
|
d1f26dd4db | ||
|
|
c595e4426e | ||
|
|
abe7fa4ac1 | ||
|
|
fd5d6ffca5 | ||
|
|
abaa7f395d | ||
|
|
8e3893d6c6 | ||
|
|
5de808456e | ||
|
|
362d081338 | ||
|
|
bc2cdcdea7 | ||
|
|
30ad554cc0 | ||
|
|
4a85fd5d1b | ||
|
|
4d8dbe74bd | ||
|
|
016f2b44ff | ||
|
|
676794baed | ||
|
|
ca488089fd | ||
|
|
96f9b9a719 | ||
|
|
508572e08b | ||
|
|
f91e0b2e23 | ||
|
|
93ae9e0891 | ||
|
|
2ed5dbc3c7 | ||
|
|
bca8ba3114 | ||
|
|
174ac97682 | ||
|
|
2d9e873d42 | ||
|
|
7bc4e5b34c | ||
|
|
9450f4a915 | ||
|
|
1a9ebf0772 | ||
|
|
1c7036e612 | ||
|
|
87cb9713f9 | ||
|
|
d3f0d9d41a | ||
|
|
b0d088ab31 | ||
|
|
47bda45516 | ||
|
|
95d18dc3ab | ||
|
|
be5f8ce37d | ||
|
|
33377357f6 | ||
|
|
107856ab0e | ||
|
|
c1f9b1d0d7 | ||
|
|
469c255bbf | ||
|
|
bec9eb2224 | ||
|
|
7dc362fef9 | ||
|
|
80781c7eff | ||
|
|
fca78a72e4 | ||
|
|
7438fb72bc | ||
|
|
b6f24ec4c2 | ||
|
|
db2bd22ddc | ||
|
|
bbcedbf2cb | ||
|
|
937adb68d7 | ||
|
|
1fe83ddcdf | ||
|
|
ac1e6fe5b7 | ||
|
|
cb99151f69 | ||
|
|
0b850808cb | ||
|
|
ffb14a2393 | ||
|
|
f1feb8cebe | ||
|
|
507cc09a2f | ||
|
|
6990a71d14 | ||
|
|
2239428b71 | ||
|
|
ce755e393c | ||
|
|
d07e46099b | ||
|
|
d11e9c52c0 | ||
|
|
3ee0ee7e40 | ||
|
|
57aaed4140 | ||
|
|
af76103957 | ||
|
|
fdaf682dbd | ||
|
|
26e1ab47f2 | ||
|
|
f1ffd369d0 | ||
|
|
9ac8969d43 | ||
|
|
7bc4a1d334 | ||
|
|
bb9866e175 | ||
|
|
d81e25fc50 | ||
|
|
6b11fd0a91 | ||
|
|
2f02747b29 | ||
|
|
78b176a135 | ||
|
|
52063beea9 | ||
|
|
03a55d94e9 | ||
|
|
db256f57d1 | ||
|
|
8de6d78be0 | ||
|
|
902a00d37d | ||
|
|
523cb1467e | ||
|
|
f75de70d79 | ||
|
|
4b635f4d21 | ||
|
|
63632e09b3 | ||
|
|
8a5b2b3e48 | ||
|
|
99d1655c20 | ||
|
|
91c30832b3 | ||
|
|
390f198310 | ||
|
|
316265e19d | ||
|
|
8754326446 | ||
|
|
443769badd | ||
|
|
0cee45e33d | ||
|
|
9d02845875 | ||
|
|
5f97ba6931 | ||
|
|
ca7358d2fc | ||
|
|
dcdd50a91b | ||
|
|
9895339c13 | ||
|
|
04f9c1c9f8 | ||
|
|
e536aa5f1c | ||
|
|
b82030090c | ||
|
|
9868b219d7 | ||
|
|
f9b3defd4b | ||
|
|
7451ce147e | ||
|
|
2203767fde | ||
|
|
067f60bfcd | ||
|
|
d4c9423c77 | ||
|
|
ffbae4826d | ||
|
|
da2ec4d759 | ||
|
|
5a45863432 | ||
|
|
605addd0d6 | ||
|
|
5f45709118 | ||
|
|
76674435c9 | ||
|
|
dcdf69d658 | ||
|
|
47e428d57e | ||
|
|
95af9660b6 | ||
|
|
6e7b8cca24 | ||
|
|
af62a7273f | ||
|
|
6cdb4a7dc1 | ||
|
|
469f0a9be7 | ||
|
|
832ad5bc6b | ||
|
|
bc77f24471 | ||
|
|
5d95877d2c | ||
|
|
3642ff8cee | ||
|
|
8cc770467e | ||
|
|
85e779469f | ||
|
|
29f6ac0d99 | ||
|
|
4d49512a7e | ||
|
|
dae13f934e | ||
|
|
bf99132f2f | ||
|
|
25a694bd69 | ||
|
|
038b7ce931 | ||
|
|
8625dea833 | ||
|
|
6f3049f39b | ||
|
|
52b74d9950 | ||
|
|
dc60c3d68b | ||
|
|
6612724beb | ||
|
|
c242a18a34 | ||
|
|
732634fefa | ||
|
|
10a4f62b78 | ||
|
|
df78eb5781 | ||
|
|
c39ae080fb | ||
|
|
e5e8a570db | ||
|
|
c1a5828d6b | ||
|
|
eaea671f4e | ||
|
|
1145a27e04 | ||
|
|
b18205a184 | ||
|
|
9703ce70d9 | ||
|
|
47dd6f7bba | ||
|
|
3db9c637e0 | ||
|
|
77d233ca1c | ||
|
|
b225093b1f | ||
|
|
4a6a3fc6ea | ||
|
|
2fd4168ab7 | ||
|
|
22498e5605 | ||
|
|
f515536d1a | ||
|
|
5c7e3fe782 | ||
|
|
546a3b6e01 | ||
|
|
1d0c890c23 | ||
|
|
5377210b89 | ||
|
|
1936255f2c | ||
|
|
b3e5763cfc | ||
|
|
7442f315c9 | ||
|
|
427b1189f7 | ||
|
|
19da89a467 | ||
|
|
be4f6241a9 | ||
|
|
03e29e02b1 | ||
|
|
853b65d6aa | ||
|
|
45dae7a031 | ||
|
|
4130852e76 | ||
|
|
3d238a1cc1 | ||
|
|
977ae98ea6 | ||
|
|
dac35597ea | ||
|
|
0a1489719b | ||
|
|
4ff63e5fc4 | ||
|
|
c3389fb19b | ||
|
|
42706f35c6 | ||
|
|
c7ec87b979 | ||
|
|
66140f092b | ||
|
|
8be4b0dd91 | ||
|
|
a2f65e9d46 | ||
|
|
c040ff26c0 | ||
|
|
92ecd8c844 | ||
|
|
60c8a66bea | ||
|
|
b1ff13e370 | ||
|
|
50b0df7d6d | ||
|
|
281e30f78a | ||
|
|
821378e2c3 | ||
|
|
433c9555bf | ||
|
|
d1345fc71e | ||
|
|
d4106a7229 | ||
|
|
079256a99f | ||
|
|
bcd7e6d56d | ||
|
|
e56db0d129 | ||
|
|
6035d26dd0 | ||
|
|
61127dc4d3 | ||
|
|
f4f0dc775c | ||
|
|
d33976a2ad | ||
|
|
1c4de8b259 | ||
|
|
1079371ecf | ||
|
|
85c3158e98 | ||
|
|
2d97f0952e | ||
|
|
396640e5b1 | ||
|
|
aa8a03b18b | ||
|
|
889e901cbd | ||
|
|
07696ce0ed | ||
|
|
ffdbab87e9 | ||
|
|
4eda1a1d66 | ||
|
|
29ec297acb | ||
|
|
7cd7d475db | ||
|
|
02aa4eb2b4 | ||
|
|
7cf9326311 | ||
|
|
2214269cca | ||
|
|
9b73f1c221 | ||
|
|
339367f730 | ||
|
|
68f7e6c709 | ||
|
|
eb657e804c | ||
|
|
413bc5d46c | ||
|
|
e4e49cef54 | ||
|
|
49cf03759b | ||
|
|
a05e7dbad1 | ||
|
|
d28665f172 | ||
|
|
1848acc1ba | ||
|
|
d9e5349edd | ||
|
|
d939b0e565 | ||
|
|
c290e9e23a | ||
|
|
ce0d8b1247 | ||
|
|
834e4a3d6e | ||
|
|
3d9b4c11d4 | ||
|
|
2d3a661442 | ||
|
|
343f4a5196 | ||
|
|
31fd480fb0 | ||
|
|
8e83adf7db | ||
|
|
7fa638f62b | ||
|
|
31cae0e54b | ||
|
|
b2cc3ae600 | ||
|
|
62d1d6a06d | ||
|
|
83fa567321 | ||
|
|
9b315b5870 | ||
|
|
3094fd32fc | ||
|
|
c3f1824a84 | ||
|
|
b558ae3810 | ||
|
|
c38f05902a | ||
|
|
181e1e1e30 | ||
|
|
3fc26254ae | ||
|
|
ca8822c455 | ||
|
|
4c55d32086 | ||
|
|
c13817708e | ||
|
|
39cab4be72 | ||
|
|
f80473d073 | ||
|
|
0d9be1b174 | ||
|
|
83b0b3ff0e | ||
|
|
c4b490907d | ||
|
|
f89fe4d5d7 | ||
|
|
81dced03bf | ||
|
|
27d3bea8d8 | ||
|
|
7eaeea325d | ||
|
|
9695526352 | ||
|
|
4f35bc38db | ||
|
|
2490d3de92 | ||
|
|
d9c7c1a0fd | ||
|
|
a94e66e5ed | ||
|
|
e9c41c6439 | ||
|
|
b24ceb95f5 | ||
|
|
6ba6e0f69b | ||
|
|
9aca49e539 | ||
|
|
01f1cdb175 | ||
|
|
c1356906bb | ||
|
|
8229537e5f | ||
|
|
6cfa2161ec | ||
|
|
fa3cf46786 | ||
|
|
ac40dfd018 | ||
|
|
4c32d559bb | ||
|
|
02ee12586f | ||
|
|
496f15a83e | ||
|
|
acf6af8f95 | ||
|
|
225b00b9ff | ||
|
|
c3ceebcec7 | ||
|
|
5177768962 | ||
|
|
fdd06ed3d6 | ||
|
|
7f2b7415ea | ||
|
|
d1d9ae4dac | ||
|
|
ac7ccfb6a3 | ||
|
|
05c2c41762 | ||
|
|
10f5133282 | ||
|
|
42658a8167 | ||
|
|
6dbf38dbb4 | ||
|
|
64306e2366 | ||
|
|
f259df85cd | ||
|
|
1090589cb2 | ||
|
|
29e09fcdda | ||
|
|
36fd4aeaff | ||
|
|
c46a59e3b1 | ||
|
|
8a3a21972a | ||
|
|
6839669e04 | ||
|
|
5645e20505 | ||
|
|
15fe8f6261 | ||
|
|
855fafa94d | ||
|
|
4e10335ae7 | ||
|
|
21ea9ce579 | ||
|
|
ea07bbf4f9 | ||
|
|
8eed6fbe21 | ||
|
|
0859f2fbe9 | ||
|
|
71ba33edeb | ||
|
|
ce80d92b35 | ||
|
|
f17cf9b736 | ||
|
|
98579c774b | ||
|
|
509fa279e7 | ||
|
|
091ee87761 | ||
|
|
c0c20cc92e | ||
|
|
1341f7bca1 | ||
|
|
fe93db1d4b | ||
|
|
5db97ea773 | ||
|
|
1758e4f320 | ||
|
|
1a897c0419 | ||
|
|
32db3e3179 | ||
|
|
d830a8957a | ||
|
|
652ea48223 | ||
|
|
8c25b2b8f5 | ||
|
|
db4c56be8e | ||
|
|
f3bcffe2f9 | ||
|
|
bc5786d099 | ||
|
|
5959fe5daf | ||
|
|
649d338bb1 | ||
|
|
dcb058a718 | ||
|
|
1772bb5e7f | ||
|
|
30bd0adb06 | ||
|
|
44dfcf771c | ||
|
|
a1f8a7a930 | ||
|
|
b22887dfad | ||
|
|
28137fa3f4 | ||
|
|
9cbdc6055d | ||
|
|
fc93c61fcf | ||
|
|
3fa2e7ebd1 | ||
|
|
818628da0c | ||
|
|
adf90a8263 | ||
|
|
362923ac64 | ||
|
|
7d73838ce1 | ||
|
|
b3278ca9ec | ||
|
|
5707914ad5 | ||
|
|
9b697b24d8 | ||
|
|
098b088da6 | ||
|
|
14d62f31e1 | ||
|
|
e7b1f55d08 | ||
|
|
8f501896a1 | ||
|
|
befcb9b874 | ||
|
|
c70afa9a4c | ||
|
|
029c7dd4c2 | ||
|
|
1f83dba1ac | ||
|
|
6eae3405fd | ||
|
|
c0e1d9e4de | ||
|
|
7c0bd7aa88 | ||
|
|
394583584c | ||
|
|
4112e2aa6b | ||
|
|
aa19e0da72 | ||
|
|
bba9be1598 | ||
|
|
17998916b4 | ||
|
|
543089bcd9 | ||
|
|
7f35c78a65 | ||
|
|
b25798dd83 | ||
|
|
744b9ff78a | ||
|
|
8dc1457ebb | ||
|
|
982ad54fab | ||
|
|
912192cd7d | ||
|
|
74cd6d48da | ||
|
|
3467a7fe3f | ||
|
|
eb269a05ed | ||
|
|
c63cf4b3b0 | ||
|
|
11ed94454d | ||
|
|
8df07645da | ||
|
|
5666fdd250 | ||
|
|
0b55ae40fc | ||
|
|
3726e84697 | ||
|
|
a0c4341102 | ||
|
|
cd6b1038e8 | ||
|
|
10583fd506 | ||
|
|
713694be56 | ||
|
|
f50293cf77 | ||
|
|
7c88fa477f | ||
|
|
f8df540fad | ||
|
|
61a01805cc | ||
|
|
a93915cf04 | ||
|
|
0f74c97fbf | ||
|
|
29713b69dc | ||
|
|
cc3c2cb9c8 | ||
|
|
b36a3959da | ||
|
|
3b26bfc0e9 | ||
|
|
c59b621963 | ||
|
|
977a8fa329 | ||
|
|
a5226fee83 | ||
|
|
aec5a46452 | ||
|
|
588f9a4490 | ||
|
|
4ba948c6f9 | ||
|
|
e4069a3988 | ||
|
|
6527f9e11e | ||
|
|
9ddfcc894f | ||
|
|
f22a4f13e5 | ||
|
|
fc9337df67 | ||
|
|
98a8332cfa | ||
|
|
a78eedad48 | ||
|
|
85e1a1bd06 | ||
|
|
25b2c04309 | ||
|
|
54a84d0e42 | ||
|
|
af0a4d1def | ||
|
|
97cdde84ce | ||
|
|
a65129b277 | ||
|
|
3b40a49918 | ||
|
|
d95345b476 | ||
|
|
2ba2d95017 | ||
|
|
4d0ffedcc8 | ||
|
|
84abde4fc5 | ||
|
|
8897f1e4b1 | ||
|
|
6f33cacb7a | ||
|
|
b1a8c0ad09 | ||
|
|
13ed635803 | ||
|
|
5a44909ebf | ||
|
|
6cbb80693d | ||
|
|
0b90d254f9 | ||
|
|
641d36205c | ||
|
|
6e38b6ea4d | ||
|
|
84b4d8fabb | ||
|
|
c0e1f7e746 | ||
|
|
d734c12168 | ||
|
|
340d94eb3e | ||
|
|
9b0b31648c | ||
|
|
8346358c37 | ||
|
|
b935700a12 | ||
|
|
2f4a5a4830 | ||
|
|
e951ce8e9d | ||
|
|
fc6d42f483 | ||
|
|
7df1431ad2 | ||
|
|
4d4680961e | ||
|
|
1cae99b812 | ||
|
|
3727d19311 | ||
|
|
c45c093f4f | ||
|
|
b613d3e312 | ||
|
|
4cff9247b0 | ||
|
|
b9a26ec28d | ||
|
|
245f81e888 | ||
|
|
204c1ba9f2 | ||
|
|
a7a4be133c | ||
|
|
14f9a46d7a | ||
|
|
003dd040dc | ||
|
|
97cf53217e | ||
|
|
7c1805826d | ||
|
|
d6a2b4dfc4 | ||
|
|
d70952520a | ||
|
|
a57867b806 | ||
|
|
17d7583cfc | ||
|
|
55ad95081b | ||
|
|
da8da1759f | ||
|
|
b4e758b9ee | ||
|
|
3ba41db699 | ||
|
|
cfc47cf483 | ||
|
|
38c8be995b | ||
|
|
492207700b | ||
|
|
b69adefbf8 | ||
|
|
361299f51e | ||
|
|
025091c3f6 | ||
|
|
a54d70036b | ||
|
|
9e67b5962c | ||
|
|
ed0c080163 | ||
|
|
eacc97160b | ||
|
|
21389d84fc | ||
|
|
0fe54631ad | ||
|
|
36ea5200f2 | ||
|
|
d9934160a6 | ||
|
|
b9c92c48d3 | ||
|
|
6d23df6156 | ||
|
|
a913ab72c2 | ||
|
|
f6d33a2ac1 | ||
|
|
5a3fec33a7 | ||
|
|
95734aca2c | ||
|
|
377254be9d | ||
|
|
ad9905a5e0 | ||
|
|
6fc5a9699e | ||
|
|
0ee86f4836 | ||
|
|
85593367f7 | ||
|
|
465ea61b06 | ||
|
|
2d5507e951 | ||
|
|
84e20be153 | ||
|
|
60080c02b0 | ||
|
|
2a0d4179d0 | ||
|
|
6dd55e0cd4 | ||
|
|
0adc6e0c6f | ||
|
|
e155356701 | ||
|
|
ef44b50980 | ||
|
|
6785d16c87 | ||
|
|
76c670b7d5 | ||
|
|
717303a72f | ||
|
|
e13125b174 | ||
|
|
8aa7964b81 | ||
|
|
5e5f23d296 | ||
|
|
05837f99ff | ||
|
|
70f620c2c3 | ||
|
|
10ffb987ea | ||
|
|
13ed785551 | ||
|
|
69d2e38647 | ||
|
|
c45abbdbcf | ||
|
|
2655b001a9 | ||
|
|
3c7f4edb1b | ||
|
|
663cbab401 | ||
|
|
897ca9ca43 | ||
|
|
b31a071859 | ||
|
|
8c2788fd78 | ||
|
|
dee8fa400c | ||
|
|
2339327473 | ||
|
|
f6f9239dd5 | ||
|
|
cd4c8c8c10 | ||
|
|
c25eda8b64 | ||
|
|
b6edf0e034 | ||
|
|
af4277fc7e | ||
|
|
acd774abe5 | ||
|
|
9081353634 | ||
|
|
9c7fa37a72 | ||
|
|
d92e11893a | ||
|
|
f55ab00bf5 | ||
|
|
36265aa2a3 | ||
|
|
bfedcdbffd | ||
|
|
a5d10c4a76 | ||
|
|
78423f67dd | ||
|
|
2962ce1945 | ||
|
|
0063d2955e | ||
|
|
9787a18666 | ||
|
|
d8cd3197b5 | ||
|
|
e07c4f65ab | ||
|
|
c3108f773a | ||
|
|
a400821268 | ||
|
|
ca4bac07da | ||
|
|
0038eacc3f | ||
|
|
4bd633a0d4 | ||
|
|
ee837f0b04 | ||
|
|
016e18cc89 | ||
|
|
1d6ac53183 | ||
|
|
1e32dfa463 | ||
|
|
4431753570 | ||
|
|
6fdb57318c | ||
|
|
649db019de | ||
|
|
1d528be0ef | ||
|
|
2d6f6df83d | ||
|
|
7fa998f276 | ||
|
|
6a3157a4c8 | ||
|
|
74efa50576 | ||
|
|
61e32d0b1b | ||
|
|
bf04d26a7b | ||
|
|
e99be78c54 | ||
|
|
e386232de1 | ||
|
|
a15896044d | ||
|
|
72c74513ce | ||
|
|
34d6d13cb2 | ||
|
|
323bb8e064 | ||
|
|
b628f8ef9b | ||
|
|
1900216d36 | ||
|
|
c045ed81c1 | ||
|
|
39edec60e3 | ||
|
|
259214e907 | ||
|
|
2e1c53392d | ||
|
|
7a77d12686 | ||
|
|
763a7714ed | ||
|
|
e89f35d654 | ||
|
|
3196751b7a | ||
|
|
f221024974 | ||
|
|
66cab8a0d4 | ||
|
|
daa49a8cd4 | ||
|
|
b8601ff240 | ||
|
|
5df3eeca64 | ||
|
|
274e4ac2ca | ||
|
|
f6485251ca | ||
|
|
c03fb70def | ||
|
|
2217aff5ab | ||
|
|
400d0aaa22 | ||
|
|
bfc928934c | ||
|
|
395c17270e | ||
|
|
8652a2891b | ||
|
|
a09af0a9eb | ||
|
|
c7d4b93fba | ||
|
|
745c0db530 | ||
|
|
aa56ab8d6c | ||
|
|
043c430221 | ||
|
|
dc5cc5855e | ||
|
|
59d6266e2b | ||
|
|
7495ba67f8 | ||
|
|
e649683a4d | ||
|
|
bec58a5772 | ||
|
|
e8f9ae8a9c | ||
|
|
679382e220 | ||
|
|
1b40467775 | ||
|
|
7f86782f54 | ||
|
|
f80b7d972f | ||
|
|
c0bd489c1b | ||
|
|
41e4c2107d | ||
|
|
cfc95c272a | ||
|
|
27e3e53868 | ||
|
|
f778f9ceae | ||
|
|
7fb6170bcb | ||
|
|
00b9884c68 | ||
|
|
b05bd04801 | ||
|
|
7ba5585b83 | ||
|
|
9386ba3fb9 | ||
|
|
1321e70035 | ||
|
|
e2d943b0b0 | ||
|
|
424b769ba9 | ||
|
|
3671f10c9c | ||
|
|
935ecd0ea7 | ||
|
|
92e15ece72 | ||
|
|
a5c1875a29 | ||
|
|
9146c0f2c6 | ||
|
|
a917207e07 | ||
|
|
c21b92945f | ||
|
|
fa37428cd3 | ||
|
|
2a2d9d3456 | ||
|
|
a91efb681f | ||
|
|
386e05be8f | ||
|
|
65e7bf609d | ||
|
|
7a58d97652 | ||
|
|
c5118da417 | ||
|
|
13b505525d | ||
|
|
b682dec363 | ||
|
|
6aa98e2214 | ||
|
|
8fba988222 | ||
|
|
24a82efe50 | ||
|
|
8054fa9267 | ||
|
|
a0e39a3725 | ||
|
|
185d6d0c51 | ||
|
|
1975e96848 | ||
|
|
ab37d228ea | ||
|
|
049bd10797 | ||
|
|
f48483d754 | ||
|
|
e6cfd33435 | ||
|
|
c29126ce1d | ||
|
|
c52170b731 | ||
|
|
6607dd31bf | ||
|
|
c6c74be38d | ||
|
|
41c6062ff9 | ||
|
|
bbb96a73b7 | ||
|
|
9eb3b9e017 | ||
|
|
6b3e94729c | ||
|
|
b486276167 | ||
|
|
fb48f2b5d4 | ||
|
|
cfffc77777 | ||
|
|
f7089f358d | ||
|
|
06c4f2ce46 | ||
|
|
83eb0abd92 | ||
|
|
4199b33c47 | ||
|
|
23cd4bff5a | ||
|
|
b65f95fe77 | ||
|
|
32160c94e1 | ||
|
|
ac02fba98b | ||
|
|
cde0108cba | ||
|
|
39dc7e4a46 | ||
|
|
9943f784a8 | ||
|
|
88ce45f29e | ||
|
|
7157e876ca | ||
|
|
0cf88cf7ca | ||
|
|
10dfdc3627 | ||
|
|
76bdefcda6 | ||
|
|
1c2c8cc5f9 | ||
|
|
58f853de5b | ||
|
|
c052297bf7 | ||
|
|
9e78cd1076 | ||
|
|
79f4deacea | ||
|
|
ff42c4c711 | ||
|
|
02d31d49d8 | ||
|
|
64f47fcc24 | ||
|
|
0ceb8acd64 | ||
|
|
78579e2e13 | ||
|
|
cf4e1d3935 | ||
|
|
d1be0bb680 | ||
|
|
e70ebad3f7 | ||
|
|
4dfe5c3abf | ||
|
|
384d9f4614 | ||
|
|
47434c68f9 | ||
|
|
af88afb6b5 | ||
|
|
e4df215e47 | ||
|
|
33cb332978 | ||
|
|
7c8f4d51bb | ||
|
|
66bf7b3dc6 | ||
|
|
536eb1efa5 | ||
|
|
c4c763089e | ||
|
|
cdfd4c0d8e | ||
|
|
f9bb8836e5 | ||
|
|
58b2634c8c | ||
|
|
093ae008ce | ||
|
|
5f62fc0cdc | ||
|
|
e7a4b4ac26 | ||
|
|
66e9944cb5 | ||
|
|
ad8528c248 | ||
|
|
07d22cd8e4 | ||
|
|
2ca50a4658 | ||
|
|
09ff4fd128 | ||
|
|
8bb1d43d0c | ||
|
|
3d70ca941c | ||
|
|
d4bdf47d62 | ||
|
|
660ee7c4bf | ||
|
|
04a1e95730 | ||
|
|
9fddb64ef9 | ||
|
|
e883035120 | ||
|
|
4d2da8e52e | ||
|
|
1db1f3070b | ||
|
|
3dba82c497 | ||
|
|
c1df335f08 | ||
|
|
b2c718d614 |
306
.appveyor.yml
306
.appveyor.yml
@@ -1,135 +1,187 @@
|
||||
environment:
|
||||
global:
|
||||
# SDK v7.0 MSVC Express 2008's SetEnv.cmd script will fail if the
|
||||
# /E:ON and /V:ON options are not enabled in the batch script intepreter
|
||||
# See: http://stackoverflow.com/a/13751649/163740
|
||||
CMD_IN_ENV: "cmd /E:ON /V:ON /C .\\appveyor\\run_with_env.cmd"
|
||||
image:
|
||||
- Ubuntu2204
|
||||
- Visual Studio 2022
|
||||
- macos-monterey
|
||||
for:
|
||||
-
|
||||
matrix:
|
||||
only:
|
||||
- image: Ubuntu2204
|
||||
environment:
|
||||
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 --fail-with-body -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"
|
||||
|
||||
matrix:
|
||||
- ps: echo("Filesystem - root:")
|
||||
- ps: "ls \"C:\\\""
|
||||
|
||||
- PYTHON: "C:\\Python36"
|
||||
PYTHON_VERSION: "3.6.x"
|
||||
PYTHON_ARCH: "32"
|
||||
init:
|
||||
- ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
|
||||
install:
|
||||
# If there is a newer build queued for the same PR, cancel this one.
|
||||
# The AppVeyor 'rollout builds' option is supposed to serve the same
|
||||
# purpose but it is problematic because it tends to cancel builds pushed
|
||||
# directly to master instead of just PR builds (or the converse).
|
||||
# credits: JuliaLang developers.
|
||||
- ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_NUMBER -ne ((Invoke-RestMethod `
|
||||
https://ci.appveyor.com/api/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/history?recordsNumber=50).builds | `
|
||||
Where-Object pullRequestId -eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) { `
|
||||
throw "There are newer queued builds for this pull request, failing early." }
|
||||
- ps: echo("Filesystem - projects root:")
|
||||
- ps: "ls \"C:\\projects\\\""
|
||||
|
||||
- ECHO "Filesystem root:"
|
||||
- ps: "ls \"C:/\""
|
||||
- ps: echo("Filesystem - pyfa root:")
|
||||
- ps: "ls \"C:\\projects\\$env:APPVEYOR_PROJECT_SLUG\\\""
|
||||
|
||||
- ECHO "Filesystem projects root:"
|
||||
- ps: "ls \"C:\\projects\\\""
|
||||
- ps: echo("Filesystem - installed SDKs:")
|
||||
- ps: "ls \"C:\\Program Files (x86)\\Windows Kits\\\""
|
||||
|
||||
- ECHO "Filesystem pyfa root:"
|
||||
- ps: "ls \"C:\\projects\\$env:APPVEYOR_PROJECT_SLUG\""
|
||||
# Prepend newly installed Python to the PATH of this build (this cannot be
|
||||
# done from inside the powershell script as it would require to restart
|
||||
# the parent CMD process).
|
||||
- cmd: "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
|
||||
- cmd: "appveyor DownloadFile https://github.com/mlocati/gettext-iconv-windows/releases/download/v0.20.2-v1.16/gettext0.20.2-iconv1.16-shared-64.zip"
|
||||
- cmd: "7z x gettext0.20.2-iconv1.16-shared-64.zip -ogettext"
|
||||
- cmd: "SET PATH=gettext;%PATH%"
|
||||
|
||||
- ECHO "Installed SDKs:"
|
||||
- ps: "ls \"C:/Program Files/Microsoft SDKs/Windows\""
|
||||
- cmd: "python --version"
|
||||
- cmd: "python -c \"import struct; print(struct.calcsize('P') * 8)\""
|
||||
|
||||
# Prepend newly installed Python to the PATH of this build (this cannot be
|
||||
# done from inside the powershell script as it would require to restart
|
||||
# the parent CMD process).
|
||||
- "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
|
||||
# Upgrade to the latest version of pip to avoid it displaying warnings
|
||||
# about it being out of date.
|
||||
- cmd: "python -m pip install --upgrade pip"
|
||||
|
||||
- "python --version"
|
||||
- "python -c \"import struct; print(struct.calcsize('P') * 8)\""
|
||||
|
||||
# Upgrade to the latest version of pip to avoid it displaying warnings
|
||||
# about it being out of date.
|
||||
- "pip install --disable-pip-version-check --user --upgrade pip"
|
||||
|
||||
# Install the build dependencies of the project. If some dependencies contain
|
||||
# compiled extensions and are not provided as pre-built wheel packages,
|
||||
# pip will build them from source using the MSVC compiler matching the
|
||||
# target Python version and architecture
|
||||
- ECHO "Install pip requirements:"
|
||||
- "python -m pip install -r requirements.txt"
|
||||
- "python -m pip install PyInstaller"
|
||||
|
||||
before_build:
|
||||
# directory that will contain the built files
|
||||
- ps: $env:PYFA_DIST_DIR = "c:\projects\$env:APPVEYOR_PROJECT_SLUG\dist"
|
||||
- ps: $env:PYFA_VERSION = (python ./scripts/dump_version.py)
|
||||
- ps: echo("pyfa version ")
|
||||
- ps: echo ($env:PYFA_VERSION)
|
||||
|
||||
build_script:
|
||||
- ECHO "Build pyfa:"
|
||||
|
||||
##########
|
||||
# PyInstaller - create binaries for pyfa
|
||||
##########
|
||||
# Build command for PyInstaller
|
||||
- "python -m PyInstaller --noupx --clean --windowed --noconsole -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\
|
||||
# Not really sure if this is needed, but why not
|
||||
- ps: xcopy /y dist_assets\win\Microsoft.VC90.CRT.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...
|
||||
##########
|
||||
- "python dist_assets/win/dist.py"
|
||||
- ps: dir $env:PYFA_DIST_DIR/
|
||||
#- ECHO "Build pyfa (Debug):"
|
||||
#- copy C:\projects\pyfa\dist_assets\win\pyfa_debug.spec C:\projects\pyfa\pyfa_debug.spec
|
||||
#- "pyinstaller.exe --clean --noconfirm --windowed --upx-dir=C:\\projects\\pyfa\\scripts\\upx.exe C:\\projects\\pyfa\\pyfa_debug.spec"
|
||||
|
||||
build: on
|
||||
|
||||
after_build:
|
||||
- ps: "ls \"./\""
|
||||
#- ps: "ls \"C:\\projects\\pyfa\\build\\pyfa\\\""
|
||||
# - ps: "ls \"C:\\projects\\$env:APPVEYOR_PROJECT_SLUG\\build\\exe.win32-2.7\\\""
|
||||
# Zip
|
||||
# APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_NUMBER
|
||||
#- 7z a build.zip -r C:\projects\pyfa\build\pyfa\*.*
|
||||
- ps: 7z a "pyfa-$env:PYFA_VERSION-win.zip" -r "$env:PYFA_DIST_DIR\pyfa\*.*"
|
||||
#- 7z a pyfa_debug.zip -r C:\projects\pyfa\dist\pyfa_debug\*.*
|
||||
|
||||
on_success:
|
||||
# Do nothing right now
|
||||
|
||||
test_script:
|
||||
#- tox
|
||||
#- "py.test --cov=./"
|
||||
# Run the project tests
|
||||
# - "%CMD_IN_ENV% python C:/projects/eve-gnosis/setup.py nosetests"
|
||||
|
||||
after_test:
|
||||
# If tests are successful, create binary packages for the project.
|
||||
# - "%CMD_IN_ENV% python setup.py bdist_wheel"
|
||||
# - "%CMD_IN_ENV% python setup.py bdist_wininst"
|
||||
# - "%CMD_IN_ENV% python setup.py bdist_msi"
|
||||
# - ps: "ls dist"
|
||||
|
||||
artifacts:
|
||||
# Archive the generated packages in the ci.appveyor.com build report.
|
||||
- path: pyfa*-win.zip
|
||||
- path: pyfa*-win.exe
|
||||
#- path: pyfa_debug.zip
|
||||
# name: Pyfa_debug
|
||||
|
||||
deploy:
|
||||
tag: $(pyfa_version)
|
||||
release: pyfa $(pyfa_version)
|
||||
description: 'Release description'
|
||||
provider: GitHub
|
||||
auth_token:
|
||||
secure: BfNHO66ff5hVx2O2ORbl49X0U/5h2V2T0IuRZDwm7fd1HvsVluF0wRCbl29oRp1M
|
||||
draft: true
|
||||
on:
|
||||
APPVEYOR_REPO_TAG: true # deploy on tag push only
|
||||
#on_success:
|
||||
# - TODO: upload the content of dist/*.whl to a public wheelhouse
|
||||
#
|
||||
# Install the build dependencies of the project. If some dependencies contain
|
||||
# compiled extensions and are not provided as pre-built wheel packages,
|
||||
# pip will build them from source using the MSVC compiler matching the
|
||||
# target Python version and architecture
|
||||
- ps: echo("Install pip requirements:")
|
||||
- cmd: "python -m pip install -r requirements.txt"
|
||||
- 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"
|
||||
- ps: $env:PYFA_VERSION = (python ./scripts/dump_version.py)
|
||||
- ps: echo("pyfa version $env:PYFA_VERSION")
|
||||
build_script:
|
||||
- ps: echo("Build pyfa:")
|
||||
- ps: Get-ChildItem locale/*.po -Recurse -File| Foreach {msgen $_.fullname -o $_.fullname}
|
||||
# Build language files
|
||||
- cmd: "python scripts/compile_lang.py"
|
||||
# Dump language progress
|
||||
- cmd: "python scripts/dump_crowdin_progress.py"
|
||||
# Build gamedata DB
|
||||
- cmd: "python db_update.py"
|
||||
# Build command for PyInstaller
|
||||
- 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...
|
||||
- cmd: "python dist_assets/win/dist.py"
|
||||
- ps: dir $env:PYFA_DIST_DIR/
|
||||
after_build:
|
||||
- ps: "ls \"./\""
|
||||
- ps: 7z a "pyfa-$env:PYFA_VERSION-win.zip" -r "$env:PYFA_DIST_DIR\pyfa\*"
|
||||
artifacts:
|
||||
- path: pyfa*-win.zip
|
||||
- path: pyfa*-win.exe
|
||||
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: macos-monterey
|
||||
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.11/bin/activate
|
||||
install:
|
||||
- sh: bash scripts/osx-setup.sh
|
||||
build_script:
|
||||
- sh: bash scripts/osx-translations.sh
|
||||
- sh: python3 scripts/compile_lang.py
|
||||
- sh: python3 scripts/dump_crowdin_progress.py
|
||||
- sh: python3 db_update.py
|
||||
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:
|
||||
- sh: export RELEASE_PKG_FILE=$(ls *.deb)
|
||||
- sh: echo "deploying $RELEASE_PKG_FILE to GitHub releases"
|
||||
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
|
||||
|
||||
6
.gitattributes
vendored
6
.gitattributes
vendored
@@ -1,11 +1,9 @@
|
||||
# Set the default behavior, in case people don't have core.autocrlf set.
|
||||
* text=auto
|
||||
|
||||
# Explicitly declare text files you want to always be normalized and converted
|
||||
# to native line endings on checkout.
|
||||
# *.c text
|
||||
# *.h text
|
||||
|
||||
# Declare files that will always have CRLF line endings on checkout.
|
||||
# Source files
|
||||
# ============
|
||||
@@ -15,7 +13,6 @@
|
||||
*.pyw text eol=crlf
|
||||
*.pyx text eol=crlf
|
||||
pyfa.py text eol=lf
|
||||
|
||||
# Denote all files that are truly binary and should not be modified.
|
||||
# Binary files
|
||||
# ============
|
||||
@@ -25,12 +22,10 @@ pyfa.py text eol=lf
|
||||
*.pyc binary
|
||||
*.pyd binary
|
||||
*.pyo binary
|
||||
|
||||
# Note: .db, .p, and .pkl files are associated
|
||||
# with the python modules ``pickle``, ``dbm.*``,
|
||||
# ``shelve``, ``marshal``, ``anydbm``, & ``bsddb``
|
||||
# (among others).
|
||||
|
||||
# Denote all files that are truly binary and should not be modified.
|
||||
# Image files
|
||||
# ============
|
||||
@@ -38,3 +33,4 @@ pyfa.py text eol=lf
|
||||
*.jpg binary
|
||||
*.icns binary
|
||||
*.ico binary
|
||||
|
||||
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -67,7 +67,6 @@ coverage.xml
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
@@ -91,6 +90,7 @@ target/
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
PyfaEnv/
|
||||
|
||||
# celery beat schedule file
|
||||
celerybeat-schedule
|
||||
@@ -123,3 +123,7 @@ gitversion
|
||||
*.swp
|
||||
|
||||
*.fsdbinary
|
||||
/locale/progress.json
|
||||
|
||||
# vscode settings
|
||||
.vscode
|
||||
31
.travis.yml
31
.travis.yml
@@ -1,31 +0,0 @@
|
||||
os: linux
|
||||
language: python
|
||||
git:
|
||||
depth: 400
|
||||
python:
|
||||
- 3.6
|
||||
matrix:
|
||||
include:
|
||||
- os: osx
|
||||
osx_image: xcode7.3
|
||||
language: generic
|
||||
env: PYTHON=3.6.1
|
||||
before_install:
|
||||
- bash scripts/setup-osx.sh
|
||||
install:
|
||||
- export PYFA_VERSION="$(python3 scripts/dump_version.py)"
|
||||
- bash scripts/package-osx.sh
|
||||
before_deploy:
|
||||
- export RELEASE_PKG_FILE=$(ls *.deb)
|
||||
- echo "deploying $RELEASE_PKG_FILE to GitHub releases"
|
||||
deploy:
|
||||
provider: releases
|
||||
api_key:
|
||||
secure: Xfu0xApoB0zUPLXl29aYUulVC3iA4/3bXQwwADKCfAKZwxgNon4dLbO7Rie5/7Ukf2POL0KwmRaQGN3kOr+XSoIVTE4M5sXxnhiaaLGKQ+48hDizLE6JuXcZGJvkxUaghaTzIdCwHsG7VGBsPfQgfGsjJcfBp8tFNLmRyM/Jpsr8T6BR2MxtBIEUVy8zrOWFNZqnmWrY2pWMsB9fYt3JFNdpqeIgRAYqbBsBcZQ1MngLTi3ztuYS5IaF+lk06RrnBlHmUsJu/5nCvIpvPvD0i2BLZ3Uu0+Fn+8QWUgjJEL9MNseXZMXynu05xd8YRk7Ajc9CUrzQIIbAktyteYp85kE3pUJHmrMLcXhh7nqkwttR5/47Zwa3OLJLJFKBxMx6wY5jFkJjkV08850B7aWrmTFl/Eqc3Q5nZMuiEt3wFRbjxHi9h1mTN/fkxfRRHg8u3ENGPR+ZPiFC3J18qtks/B/hsKjjHvZP1i79OYlET4V/zyLyyQkCbpDaARQANuotLYJyZ7tH+KWEyRsvTi0M9Yev9mNNw6aI4vzh4HfkEhvcvnWnYwckPj1dnjQ573Qpw0Z9wsconoWfHAn+hBDt3+YLMrrFZl++mCRskHH1mZChX3aGMDi49zD0kfxBUkYPOAhguc6PwudBxHUZP+O6T/SoHylff6EizCE/k5dGeAk=
|
||||
file_glob: true
|
||||
file: "dist/pyfa-*.zip"
|
||||
skip_cleanup: true
|
||||
draft: true
|
||||
on:
|
||||
tags: true
|
||||
repo: pyfa-org/Pyfa
|
||||
111
CONTRIBUTING.md
Normal file
111
CONTRIBUTING.md
Normal file
@@ -0,0 +1,111 @@
|
||||
# Contribution
|
||||
|
||||
## Requirements
|
||||
|
||||
- Python 3.11 (later versions may not work)
|
||||
- Git CLI installed
|
||||
- Python, pip and git are all available as command-line commands (add to the path if needed)
|
||||
|
||||
Virtual environment will be created in *PyfaEnv* folder. Project will be cloned and run from the *PyfaDEV* folder. Separate virtual environment will be created so required libraries won't clutter the main python installation.
|
||||
|
||||
> Commands and screens were created on Windows 10. Please, update all the paths according to your OS.
|
||||
|
||||
## Setting up the project manually
|
||||
|
||||
Clone the repository
|
||||
```
|
||||
git clone <repo> PyfaDEV
|
||||
```
|
||||
|
||||
Create the virtual environment
|
||||
```
|
||||
python -m venv PyfaEnv
|
||||
```
|
||||
|
||||
Activate the virtual environment
|
||||
|
||||
```
|
||||
For cmd.exe: PyfaEnv\scripts\activate.bat
|
||||
For PowerShell: PyfaEnv\Scripts\Activate.ps1
|
||||
For bash: source <venv>/Scripts/activate
|
||||
```
|
||||
> For other OS check [Python documentation](https://docs.python.org/3/library/venv.html)
|
||||
|
||||
Install requirements for the project from *requirements.txt*
|
||||
```
|
||||
pip install -r PyfaDEV\requirements.txt
|
||||
```
|
||||
> For some Linux distributions, you may need to install separate wxPython bindings, such as `python-matplotlib-wx`
|
||||
|
||||
> You may need to [install a C compiler](https://mesonbuild.com/SimpleStart.html#installing-a-compiler-toolchain)
|
||||
|
||||
Check that the libs from *requirements.txt* are installed
|
||||
```
|
||||
pip list
|
||||
```
|
||||
|
||||
Build translations and database:
|
||||
```
|
||||
python PyfaDEV\scripts\compile_lang.py
|
||||
python PyfaDEV\db_update.py
|
||||
```
|
||||
|
||||
Test that the project is starting properly
|
||||
```
|
||||
python PyfaDEV\pyfa.py
|
||||
```
|
||||
|
||||
|
||||
## Setting up the project with PyCharm/IntelliJ
|
||||
|
||||
Install PyCharm / Other IntelliJ product with Python plugin
|
||||
|
||||
After launching - select *Check out from Version Control* -> *GIt*
|
||||
|
||||

|
||||
|
||||
Login to GitHub, paste the repo URL and select the folder to which to clone the project into, press *Clone*.
|
||||
|
||||

|
||||
|
||||
After process is complete, open *File* -> *Settings* -> *Project* -> *Project Interpreter*.
|
||||
|
||||

|
||||
|
||||
Press on options and add new virtual environment.
|
||||
|
||||

|
||||
|
||||
Open project tree view and double-click on the *requirements.txt*. Press *Install requirements*. Install all requirements.
|
||||
|
||||

|
||||
|
||||
Create new *Run Configuration*. Set correct *Script path* and *Python interpreter*.
|
||||
|
||||

|
||||
|
||||
Check that the project is starting properly.
|
||||
|
||||
## Running tests
|
||||
|
||||
Switch to the proper virtual environment
|
||||
```
|
||||
For cmd.exe: PyfaEnv\scripts\activate.bat
|
||||
For PowerShell: PyfaEnv\Scripts\Activate.ps1
|
||||
For bash: source <venv>/Scripts/activate
|
||||
```
|
||||
|
||||
Install pytest
|
||||
```
|
||||
pip install pytest
|
||||
```
|
||||
|
||||
Switch to pyfa directory.
|
||||
|
||||
Run tests (any will do)
|
||||
```
|
||||
python -m pytest
|
||||
py.test
|
||||
```
|
||||
|
||||
More information on tests can be found on appropriate [Wiki page](https://github.com/pyfa-org/Pyfa/wiki/Developers:-Writing-Tests-for-Pyfa).
|
||||
25
README.md
25
README.md
@@ -1,37 +1,36 @@
|
||||
# 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
|
||||
### Third Party Packages
|
||||
Please note that these packages are maintained by third-parties and are not evaluated by the pyfa developers.
|
||||
|
||||
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:
|
||||
#### 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
|
||||
|
||||
### Dependencies
|
||||
If you wish to help with development or simply need to run pyfa through a Python interpreter, the following software is required:
|
||||
|
||||
* Python 3.6
|
||||
* Requirements as listed in `requirements.txt`
|
||||
## Contribution
|
||||
If you wish to help with development or you need to run pyfa through a Python interpreter, check out [the instructions](https://github.com/pyfa-org/Pyfa/blob/master/CONTRIBUTING.md).
|
||||
|
||||
## Bug Reporting
|
||||
The preferred method of reporting bugs is through the project's [GitHub Issues interface](https://github.com/pyfa-org/Pyfa/issues). Alternatively, posting a report in the [pyfa thread](https://forums.eveonline.com/t/27156) on the official EVE Online forums is acceptable. Guidelines for bug reporting can be found on [this wiki page](https://github.com/pyfa-org/Pyfa/wiki/Bug-Reporting).
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
<option name="RIGHT_MARGIN" value="165" />
|
||||
<Python>
|
||||
<option name="NEW_LINE_AFTER_COLON" value="true" />
|
||||
<option name="DICT_ALIGNMENT" value="2" />
|
||||
<option name="DICT_NEW_LINE_AFTER_LEFT_BRACE" value="true" />
|
||||
<option name="DICT_NEW_LINE_BEFORE_RIGHT_BRACE" value="true" />
|
||||
<option name="USE_CONTINUATION_INDENT_FOR_ARGUMENTS" value="true" />
|
||||
|
||||
@@ -1,54 +1,61 @@
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Pyfa" />
|
||||
<inspection_tool class="IgnoreUnusedEntry" enabled="false" level="UNUSED ENTRY" enabled_by_default="false" />
|
||||
<inspection_tool class="InconsistentLineSeparators" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
<inspection_tool class="ProblematicWhitespace" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PyBehaveInspection" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PyClassicStyleClassInspection" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PyCompatibilityInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ourVersions">
|
||||
<value>
|
||||
<list size="1">
|
||||
<item index="0" class="java.lang.String" itemvalue="2.7" />
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Pyfa" />
|
||||
<inspection_tool class="IgnoreUnusedEntry" enabled="false" level="UNUSED ENTRY" enabled_by_default="false" />
|
||||
<inspection_tool class="InconsistentLineSeparators" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
<inspection_tool class="ProblematicWhitespace" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PyBehaveInspection" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PyClassicStyleClassInspection" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PyCompatibilityInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ourVersions">
|
||||
<value>
|
||||
<list size="1">
|
||||
<item index="0" class="java.lang.String" itemvalue="2.7" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PyMissingTypeHintsInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ignoredPackages">
|
||||
<value>
|
||||
<list size="1">
|
||||
<item index="0" class="java.lang.String" itemvalue="wxPython" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PyPep8Inspection" enabled="true" level="TYPO" enabled_by_default="true">
|
||||
<option name="ignoredErrors">
|
||||
<list>
|
||||
<option value="E203" />
|
||||
<option value="E127" />
|
||||
<option value="E128" />
|
||||
<option value="E126" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PyMissingTypeHintsInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ignoredPackages">
|
||||
<value>
|
||||
<list size="1">
|
||||
<item index="0" class="java.lang.String" itemvalue="wxPython" />
|
||||
</option>
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PyPep8NamingInspection" enabled="true" level="TYPO" enabled_by_default="true">
|
||||
<option name="ignoredErrors">
|
||||
<list>
|
||||
<option value="N802" />
|
||||
<option value="N806" />
|
||||
<option value="N803" />
|
||||
<option value="N814" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PyPep8Inspection" enabled="true" level="TYPO" enabled_by_default="true">
|
||||
<option name="ignoredErrors">
|
||||
<list>
|
||||
<option value="E203" />
|
||||
<option value="E127" />
|
||||
<option value="E128" />
|
||||
<option value="E126" />
|
||||
</list>
|
||||
</option>
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PyPep8NamingInspection" enabled="true" level="TYPO" enabled_by_default="true">
|
||||
<option name="ignoredErrors">
|
||||
<list>
|
||||
<option value="N802" />
|
||||
<option value="N806" />
|
||||
<option value="N803" />
|
||||
<option value="N814" />
|
||||
</list>
|
||||
</option>
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PyShadowingBuiltinsInspection" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PyShadowingNamesInspection" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
|
||||
<option name="processCode" value="true" />
|
||||
<option name="processLiterals" value="true" />
|
||||
<option name="processComments" value="true" />
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</option>
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PyShadowingBuiltinsInspection" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PyShadowingNamesInspection" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PyUnresolvedReferencesInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ignoredIdentifiers">
|
||||
<list>
|
||||
<option value="_" />
|
||||
</list>
|
||||
</option>
|
||||
</inspection_tool>
|
||||
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
|
||||
<option name="processCode" value="true" />
|
||||
<option name="processLiterals" value="true" />
|
||||
<option name="processComments" value="true" />
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
|
||||
@@ -49,6 +49,8 @@ def DBInMemory_test():
|
||||
gamedata_version = gamedata_session.execute(
|
||||
"SELECT `field_value` FROM `metadata` WHERE `field_name` LIKE 'client_build'"
|
||||
).fetchone()[0]
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
raise
|
||||
except Exception as e:
|
||||
print("Missing gamedata version.")
|
||||
gamedata_version = None
|
||||
@@ -72,7 +74,7 @@ def DBInMemory_test():
|
||||
# noinspection PyPep8
|
||||
#from eos.db.gamedata import alphaClones, attribute, category, effect, group, icon, item, marketGroup, metaData, metaGroup, queries, traits, unit
|
||||
# noinspection PyPep8
|
||||
#from eos.db.saveddata import booster, cargo, character, crest, damagePattern, databaseRepair, drone, fighter, fit, implant, implantSet, loadDefaultDatabaseValues, miscData, module, override, price, queries, skill, targetProfile, user
|
||||
#from eos.db.saveddata import booster, cargo, character, crest, damagePattern, databaseRepair, drone, fighter, fit, implant, implantSet, miscData, module, override, price, queries, skill, targetProfile, user
|
||||
|
||||
# If using in memory saveddata, you'll want to reflect it so the data structure is good.
|
||||
if saveddata_connectionstring == "sqlite:///:memory:":
|
||||
|
||||
45
config.py
45
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
|
||||
@@ -39,7 +41,19 @@ loggingLevel = None
|
||||
logging_setup = None
|
||||
cipher = None
|
||||
clientHash = None
|
||||
experimentalFeatures = None
|
||||
version = None
|
||||
language = None
|
||||
|
||||
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'
|
||||
|
||||
LOGLEVEL_MAP = {
|
||||
@@ -50,13 +64,24 @@ LOGLEVEL_MAP = {
|
||||
"debug": DEBUG,
|
||||
}
|
||||
|
||||
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
|
||||
@@ -103,6 +128,8 @@ def defPaths(customSavePath=None):
|
||||
global cipher
|
||||
global clientHash
|
||||
global version
|
||||
global experimentalFeatures
|
||||
global language
|
||||
|
||||
pyfalog.debug("Configuring Pyfa")
|
||||
|
||||
@@ -168,6 +195,10 @@ def defPaths(customSavePath=None):
|
||||
|
||||
logPath = os.path.join(savePath, logFile)
|
||||
|
||||
experimentalFeatures = getattr(configforced, "experimentalFeatures", experimentalFeatures)
|
||||
if experimentalFeatures is None:
|
||||
experimentalFeatures = False
|
||||
|
||||
# DON'T MODIFY ANYTHING BELOW
|
||||
import eos.config
|
||||
|
||||
@@ -178,9 +209,15 @@ def defPaths(customSavePath=None):
|
||||
eos.config.gamedata_connectionstring = "sqlite:///" + gameDB + "?check_same_thread=False"
|
||||
|
||||
# initialize the settings
|
||||
from service.settings import EOSSettings
|
||||
from service.settings import EOSSettings, LocaleSettings
|
||||
eos.config.settings = EOSSettings.getInstance().EOSSettings # this is kind of confusing, but whatever
|
||||
|
||||
# set langauge, taking the passed argument or falling back to what's saved in the settings
|
||||
localeSettings = LocaleSettings.getInstance()
|
||||
language = language or localeSettings.get('locale')
|
||||
|
||||
# sets the lang for eos, using the mapped langauge.
|
||||
eos.config.set_lang(localeSettings.get_eos_locale())
|
||||
|
||||
def defLogging():
|
||||
global debug
|
||||
@@ -226,6 +263,8 @@ def defLogging():
|
||||
# reset=False,
|
||||
)
|
||||
])
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
raise
|
||||
except:
|
||||
print("Critical error attempting to setup logging. Falling back to console only.")
|
||||
logging_setup = NestedSetup([
|
||||
|
||||
3
crowdin.yml
Normal file
3
crowdin.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
files:
|
||||
- source: /locale/*.pot
|
||||
translation: /locale/%locale_with_underscore%/LC_MESSAGES/lang.po
|
||||
859
db_update.py
Normal file
859
db_update.py
Normal file
@@ -0,0 +1,859 @@
|
||||
#!/usr/bin/env python3
|
||||
#======================================================================
|
||||
# Copyright (C) 2012 Diego Duclos
|
||||
#
|
||||
# This file is part of eos.
|
||||
#
|
||||
# eos is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of
|
||||
# the License, or (at your option) any later version.
|
||||
#
|
||||
# eos 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 Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with eos. If not, see <http://www.gnu.org/licenses/>.
|
||||
#======================================================================
|
||||
|
||||
|
||||
import functools
|
||||
import itertools
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sqlite3
|
||||
import sys
|
||||
|
||||
import sqlalchemy.orm
|
||||
from sqlalchemy import or_, and_
|
||||
|
||||
|
||||
# todo: need to set the EOS language to en, becasuse this assumes it's being run within an English context
|
||||
# Need to know what that would do if called from pyfa
|
||||
ROOT_DIR = os.path.realpath(os.path.dirname(__file__))
|
||||
DB_PATH = os.path.join(ROOT_DIR, 'eve.db')
|
||||
JSON_DIR = os.path.join(ROOT_DIR, 'staticdata')
|
||||
if ROOT_DIR not in sys.path:
|
||||
sys.path.insert(0, ROOT_DIR)
|
||||
GAMEDATA_SCHEMA_VERSION = 4
|
||||
|
||||
|
||||
def db_needs_update():
|
||||
"""True if needs, false if it does not, none if we cannot check it."""
|
||||
try:
|
||||
with open(os.path.join(JSON_DIR, 'phobos', 'metadata.0.json')) as f:
|
||||
data_version = next((r['field_value'] for r in json.load(f) if r['field_name'] == 'client_build'))
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
raise
|
||||
# If we have no source data - return None; should not update in this case
|
||||
except:
|
||||
return None
|
||||
if not os.path.isfile(DB_PATH):
|
||||
print('Gamedata DB not found')
|
||||
return True
|
||||
db_data_version = None
|
||||
db_schema_version = None
|
||||
try:
|
||||
db = sqlite3.connect(DB_PATH)
|
||||
cursor = db.cursor()
|
||||
cursor.execute('SELECT field_value FROM metadata WHERE field_name = \'client_build\'')
|
||||
for row in cursor:
|
||||
db_data_version = int(row[0])
|
||||
cursor.execute('SELECT field_value FROM metadata WHERE field_name = \'schema_version\'')
|
||||
for row in cursor:
|
||||
db_schema_version = int(row[0])
|
||||
cursor.close()
|
||||
db.close()
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
raise
|
||||
except:
|
||||
print('Error when fetching gamedata DB metadata')
|
||||
return True
|
||||
if data_version != db_data_version:
|
||||
print('Gamedata DB data version mismatch: needed {}, DB has {}'.format(data_version, db_data_version))
|
||||
return True
|
||||
if GAMEDATA_SCHEMA_VERSION != db_schema_version:
|
||||
print('Gamedata DB schema version mismatch: needed {}, DB has {}'.format(GAMEDATA_SCHEMA_VERSION, db_schema_version))
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def update_db():
|
||||
|
||||
print('Building gamedata DB...')
|
||||
|
||||
if os.path.isfile(DB_PATH):
|
||||
os.remove(DB_PATH)
|
||||
|
||||
import eos.db
|
||||
import eos.gamedata
|
||||
import eos.config
|
||||
|
||||
# Create the database tables
|
||||
eos.db.gamedata_meta.create_all()
|
||||
|
||||
def _readData(minerName, jsonName, keyIdName=None):
|
||||
compiled_data = None
|
||||
for i in itertools.count(0):
|
||||
try:
|
||||
with open(os.path.join(JSON_DIR, minerName, '{}.{}.json'.format(jsonName, i)), encoding='utf-8') as f:
|
||||
rawData = json.load(f)
|
||||
if i == 0:
|
||||
compiled_data = {} if type(rawData) == dict else []
|
||||
if type(rawData) == dict:
|
||||
compiled_data.update(rawData)
|
||||
else:
|
||||
compiled_data.extend(rawData)
|
||||
except FileNotFoundError:
|
||||
break
|
||||
|
||||
if not keyIdName:
|
||||
return compiled_data
|
||||
# IDs in keys, rows in values
|
||||
data = []
|
||||
for k, v in compiled_data.items():
|
||||
row = {}
|
||||
row.update(v)
|
||||
row[keyIdName] = int(k)
|
||||
data.append(row)
|
||||
return data
|
||||
|
||||
def _addRows(data, cls, fieldMap=None):
|
||||
if fieldMap is None:
|
||||
fieldMap = {}
|
||||
for row in data:
|
||||
instance = cls()
|
||||
for k, v in row.items():
|
||||
if isinstance(v, str):
|
||||
v = v.strip()
|
||||
setattr(instance, fieldMap.get(k, k), v)
|
||||
eos.db.gamedata_session.add(instance)
|
||||
|
||||
def processEveTypes():
|
||||
print('processing evetypes')
|
||||
data = _readData('fsd_built', 'types', keyIdName='typeID')
|
||||
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
|
||||
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 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
|
||||
"'Extraction'" not in row['typeName_en-us'] and
|
||||
"'Krai Veles'" not in row['typeName_en-us'] and
|
||||
"'Krai Perun'" not in row['typeName_en-us'] and
|
||||
"'Krai Svarog'" not in row['typeName_en-us']
|
||||
)
|
||||
):
|
||||
row['published'] = False
|
||||
|
||||
newData = []
|
||||
for row in data:
|
||||
if (
|
||||
row['published'] or
|
||||
# group Ship Modifiers, for items like tactical t3 ship modes
|
||||
row['groupID'] == 1306 or
|
||||
# Micro Bombs (Fighters)
|
||||
row['typeID'] in (41549, 41548, 41551, 41550) or
|
||||
# Abyssal weather (environment)
|
||||
row['groupID'] in (
|
||||
1882,
|
||||
1975,
|
||||
1971,
|
||||
1983) # the "container" for the abyssal environments
|
||||
):
|
||||
newData.append(row)
|
||||
map = {'typeName_en-us': 'typeName', 'description_en-us': '_description'}
|
||||
map.update({'description'+v: '_description'+v for (k, v) in eos.config.translation_mapping.items() if k != 'en'})
|
||||
_addRows(newData, eos.gamedata.Item, fieldMap=map)
|
||||
return newData
|
||||
|
||||
def processEveGroups():
|
||||
print('processing evegroups')
|
||||
data = _readData('fsd_built', 'groups', keyIdName='groupID')
|
||||
map = {'groupName_en-us': 'name'}
|
||||
map.update({'groupName'+v: 'name'+v for (k, v) in eos.config.translation_mapping.items() if k != 'en'})
|
||||
_addRows(data, eos.gamedata.Group, fieldMap=map)
|
||||
return data
|
||||
|
||||
def processEveCategories():
|
||||
print('processing evecategories')
|
||||
data = _readData('fsd_built', 'categories', keyIdName='categoryID')
|
||||
map = { 'categoryName_en-us': 'name' }
|
||||
map.update({'categoryName'+v: 'name'+v for (k, v) in eos.config.translation_mapping.items() if k != 'en'})
|
||||
_addRows(data, eos.gamedata.Category, fieldMap=map)
|
||||
|
||||
def processDogmaAttributes():
|
||||
print('processing dogmaattributes')
|
||||
data = _readData('fsd_built', 'dogmaattributes', keyIdName='attributeID')
|
||||
map = {
|
||||
'displayName_en-us': 'displayName',
|
||||
# 'tooltipDescription_en-us': 'tooltipDescription'
|
||||
}
|
||||
_addRows(data, eos.gamedata.AttributeInfo, fieldMap=map)
|
||||
|
||||
def processDogmaTypeAttributes(eveTypesData):
|
||||
print('processing dogmatypeattributes')
|
||||
data = _readData('fsd_built', 'typedogma', keyIdName='typeID')
|
||||
eveTypeIds = set(r['typeID'] for r in eveTypesData)
|
||||
newData = []
|
||||
seenKeys = set()
|
||||
|
||||
def checkKey(key):
|
||||
if key in seenKeys:
|
||||
return False
|
||||
seenKeys.add(key)
|
||||
return True
|
||||
|
||||
for typeData in data:
|
||||
if typeData['typeID'] not in eveTypeIds:
|
||||
continue
|
||||
for row in typeData.get('dogmaAttributes', ()):
|
||||
row['typeID'] = typeData['typeID']
|
||||
if checkKey((row['typeID'], row['attributeID'])):
|
||||
newData.append(row)
|
||||
for row in eveTypesData:
|
||||
for attrId, attrName in {4: 'mass', 38: 'capacity', 161: 'volume', 162: 'radius'}.items():
|
||||
if attrName in row and checkKey((row['typeID'], attrId)):
|
||||
newData.append({'typeID': row['typeID'], 'attributeID': attrId, 'value': row[attrName]})
|
||||
|
||||
_addRows(newData, eos.gamedata.Attribute)
|
||||
return newData
|
||||
|
||||
def processDynamicItemAttributes():
|
||||
print('processing dynamicitemattributes')
|
||||
data = _readData('fsd_built', 'dynamicitemattributes')
|
||||
for mutaID, mutaData in data.items():
|
||||
muta = eos.gamedata.DynamicItem()
|
||||
muta.typeID = mutaID
|
||||
muta.resultingTypeID = mutaData['inputOutputMapping'][0]['resultingType']
|
||||
eos.db.gamedata_session.add(muta)
|
||||
|
||||
for x in mutaData['inputOutputMapping'][0]['applicableTypes']:
|
||||
item = eos.gamedata.DynamicItemItem()
|
||||
item.typeID = mutaID
|
||||
item.applicableTypeID = x
|
||||
eos.db.gamedata_session.add(item)
|
||||
|
||||
for attrID, attrData in mutaData['attributeIDs'].items():
|
||||
attr = eos.gamedata.DynamicItemAttribute()
|
||||
attr.typeID = mutaID
|
||||
attr.attributeID = attrID
|
||||
attr.min = attrData['min']
|
||||
attr.max = attrData['max']
|
||||
eos.db.gamedata_session.add(attr)
|
||||
|
||||
def processDogmaEffects():
|
||||
print('processing dogmaeffects')
|
||||
data = _readData('fsd_built', 'dogmaeffects', keyIdName='effectID')
|
||||
_addRows(data, eos.gamedata.Effect, fieldMap={'resistanceAttributeID': 'resistanceID'})
|
||||
|
||||
def processDogmaTypeEffects(eveTypesData):
|
||||
print('processing dogmatypeeffects')
|
||||
data = _readData('fsd_built', 'typedogma', keyIdName='typeID')
|
||||
eveTypeIds = set(r['typeID'] for r in eveTypesData)
|
||||
newData = []
|
||||
for typeData in data:
|
||||
if typeData['typeID'] not in eveTypeIds:
|
||||
continue
|
||||
for row in typeData.get('dogmaEffects', ()):
|
||||
row['typeID'] = typeData['typeID']
|
||||
newData.append(row)
|
||||
_addRows(newData, eos.gamedata.ItemEffect)
|
||||
return newData
|
||||
|
||||
def processDogmaUnits():
|
||||
print('processing dogmaunits')
|
||||
data = _readData('fsd_built', 'dogmaunits', keyIdName='unitID')
|
||||
_addRows(data, eos.gamedata.Unit, fieldMap={
|
||||
'name': 'unitName',
|
||||
'displayName_en-us': 'displayName'
|
||||
})
|
||||
|
||||
def processMarketGroups():
|
||||
print('processing marketgroups')
|
||||
data = _readData('fsd_built', 'marketgroups', keyIdName='marketGroupID')
|
||||
map = {
|
||||
'name_en-us': 'marketGroupName',
|
||||
'description_en-us': '_description',
|
||||
}
|
||||
map.update({'name'+v: 'marketGroupName'+v for (k, v) in eos.config.translation_mapping.items() if k != 'en'})
|
||||
map.update({'description' + v: '_description' + v for (k, v) in eos.config.translation_mapping.items() if k != 'en'})
|
||||
_addRows(data, eos.gamedata.MarketGroup, fieldMap=map)
|
||||
|
||||
def processMetaGroups():
|
||||
print('processing metagroups')
|
||||
data = _readData('fsd_built', 'metagroups', keyIdName='metaGroupID')
|
||||
map = {'name_en-us': 'metaGroupName'}
|
||||
map.update({'name' + v: 'metaGroupName' + v for (k, v) in eos.config.translation_mapping.items() if k != 'en'})
|
||||
_addRows(data, eos.gamedata.MetaGroup, fieldMap=map)
|
||||
|
||||
def processCloneGrades():
|
||||
print('processing clonegrades')
|
||||
data = _readData('fsd_lite', 'clonegrades')
|
||||
|
||||
newData = []
|
||||
# December, 2017 - CCP decided to use only one set of skill levels for alpha clones. However, this is still
|
||||
# represented in the data as a skillset per race. To ensure that all skills are the same, we store them in a way
|
||||
# that we can check to make sure all races have the same skills, as well as skill levels
|
||||
check = {}
|
||||
for ID in data:
|
||||
for skill in data[ID]['skills']:
|
||||
newData.append({
|
||||
'alphaCloneID': int(ID),
|
||||
'alphaCloneName': 'Alpha Clone',
|
||||
'typeID': skill['typeID'],
|
||||
'level': skill['level']})
|
||||
if ID not in check:
|
||||
check[ID] = {}
|
||||
check[ID][int(skill['typeID'])] = int(skill['level'])
|
||||
if not functools.reduce(lambda a, b: a if a == b else False, [v for _, v in check.items()]):
|
||||
raise Exception('Alpha Clones not all equal')
|
||||
newData = [x for x in newData if x['alphaCloneID'] == 1]
|
||||
if len(newData) == 0:
|
||||
raise Exception('Alpha Clone processing failed')
|
||||
|
||||
tmp = []
|
||||
for row in newData:
|
||||
if row['alphaCloneID'] not in tmp:
|
||||
cloneParent = eos.gamedata.AlphaClone()
|
||||
setattr(cloneParent, 'alphaCloneID', row['alphaCloneID'])
|
||||
setattr(cloneParent, 'alphaCloneName', row['alphaCloneName'])
|
||||
eos.db.gamedata_session.add(cloneParent)
|
||||
tmp.append(row['alphaCloneID'])
|
||||
_addRows(newData, eos.gamedata.AlphaCloneSkill)
|
||||
|
||||
def processTraits():
|
||||
print('processing traits')
|
||||
data = _readData('phobos', 'traits')
|
||||
|
||||
def convertSection(sectionData):
|
||||
sectionLines = []
|
||||
headerText = '<b>{}</b>'.format(sectionData['header'])
|
||||
sectionLines.append(headerText)
|
||||
for bonusData in sectionData['bonuses']:
|
||||
prefix = '{} '.format(bonusData['number']) if 'number' in bonusData else ''
|
||||
bonusText = '{}{}'.format(prefix, bonusData['text'].replace('\u00B7', '\u2022 '))
|
||||
sectionLines.append(bonusText)
|
||||
sectionLine = '<br />\n'.join(sectionLines)
|
||||
return sectionLine
|
||||
|
||||
newData = []
|
||||
for row in data:
|
||||
try:
|
||||
newRow = {
|
||||
'typeID': row['typeID'],
|
||||
}
|
||||
for (k, v) in eos.config.translation_mapping.items():
|
||||
if v == '':
|
||||
v = '_en-us'
|
||||
typeLines = []
|
||||
traitData = row['traits{}'.format(v)]
|
||||
for skillData in sorted(traitData.get('skills', ()), key=lambda i: i['header']):
|
||||
typeLines.append(convertSection(skillData))
|
||||
if 'role' in traitData:
|
||||
typeLines.append(convertSection(traitData['role']))
|
||||
if 'misc' in traitData:
|
||||
typeLines.append(convertSection(traitData['misc']))
|
||||
traitLine = '<br />\n<br />\n'.join(typeLines)
|
||||
newRow['traitText{}'.format(v)] = traitLine
|
||||
|
||||
newData.append(newRow)
|
||||
except:
|
||||
pass
|
||||
_addRows(newData, eos.gamedata.Traits, fieldMap={'traitText_en-us': 'traitText'})
|
||||
|
||||
def processMetadata():
|
||||
print('processing metadata')
|
||||
data = _readData('phobos', 'metadata')
|
||||
_addRows(data, eos.gamedata.MetaData)
|
||||
|
||||
def processReqSkills(eveTypesData):
|
||||
print('processing requiredskillsfortypes')
|
||||
|
||||
def composeReqSkills(raw):
|
||||
reqSkills = {}
|
||||
for skillTypeID, skillLevel in raw.items():
|
||||
reqSkills[int(skillTypeID)] = skillLevel
|
||||
return reqSkills
|
||||
|
||||
eveTypeIds = set(r['typeID'] for r in eveTypesData)
|
||||
data = _readData('fsd_built', 'requiredskillsfortypes')
|
||||
reqsByItem = {}
|
||||
itemsByReq = {}
|
||||
for typeID, skillreqData in data.items():
|
||||
typeID = int(typeID)
|
||||
if typeID not in eveTypeIds:
|
||||
continue
|
||||
for skillTypeID, skillLevel in composeReqSkills(skillreqData).items():
|
||||
reqsByItem.setdefault(typeID, {})[skillTypeID] = skillLevel
|
||||
itemsByReq.setdefault(skillTypeID, {})[typeID] = skillLevel
|
||||
for item in eos.db.gamedata_session.query(eos.gamedata.Item).all():
|
||||
if item.typeID in reqsByItem:
|
||||
item.reqskills = json.dumps(reqsByItem[item.typeID])
|
||||
if item.typeID in itemsByReq:
|
||||
item.requiredfor = json.dumps(itemsByReq[item.typeID])
|
||||
|
||||
def processReplacements(eveTypesData, eveGroupsData, dogmaTypeAttributesData, dogmaTypeEffectsData):
|
||||
print('finding item replacements')
|
||||
|
||||
def compareAttrs(attrs1, attrs2):
|
||||
# Consider items as different if they have no attrs
|
||||
if len(attrs1) == 0 and len(attrs2) == 0:
|
||||
return False
|
||||
if set(attrs1) != set(attrs2):
|
||||
return False
|
||||
if all(attrs1[aid] == attrs2[aid] for aid in attrs1):
|
||||
return True
|
||||
return False
|
||||
|
||||
skillReqAttribs = {
|
||||
182: 277,
|
||||
183: 278,
|
||||
184: 279,
|
||||
1285: 1286,
|
||||
1289: 1287,
|
||||
1290: 1288}
|
||||
skillReqAttribsFlat = set(skillReqAttribs.keys()).union(skillReqAttribs.values())
|
||||
# Get data on type groups
|
||||
# Format: {type ID: group ID}
|
||||
typesGroups = {}
|
||||
for row in eveTypesData:
|
||||
typesGroups[row['typeID']] = row['groupID']
|
||||
# Get data on item effects
|
||||
# Format: {type ID: set(effect, IDs)}
|
||||
typesEffects = {}
|
||||
for row in dogmaTypeEffectsData:
|
||||
typesEffects.setdefault(row['typeID'], set()).add(row['effectID'])
|
||||
# Get data on type attributes
|
||||
# Format: {type ID: {attribute ID: attribute value}}
|
||||
typesNormalAttribs = {}
|
||||
typesSkillAttribs = {}
|
||||
for row in dogmaTypeAttributesData:
|
||||
attributeID = row['attributeID']
|
||||
if attributeID in skillReqAttribsFlat:
|
||||
typeSkillAttribs = typesSkillAttribs.setdefault(row['typeID'], {})
|
||||
typeSkillAttribs[row['attributeID']] = row['value']
|
||||
# Ignore these attributes for comparison purposes
|
||||
elif attributeID in (
|
||||
# We do not need mass as it affects final ship stats only when carried by ship itself
|
||||
# (and we're not going to replace ships), but it's wildly inconsistent for other items,
|
||||
# which otherwise would be the same
|
||||
4, # mass
|
||||
124, # mainColor
|
||||
162, # radius
|
||||
422, # techLevel
|
||||
633, # metaLevel
|
||||
1692, # metaGroupID
|
||||
1768 # typeColorScheme
|
||||
):
|
||||
continue
|
||||
else:
|
||||
typeNormalAttribs = typesNormalAttribs.setdefault(row['typeID'], {})
|
||||
typeNormalAttribs[row['attributeID']] = row['value']
|
||||
# Get data on skill requirements
|
||||
# Format: {type ID: {skill type ID: skill level}}
|
||||
typesSkillReqs = {}
|
||||
for typeID, typeAttribs in typesSkillAttribs.items():
|
||||
typeSkillAttribs = typesSkillAttribs.get(typeID, {})
|
||||
if not typeSkillAttribs:
|
||||
continue
|
||||
typeSkillReqs = typesSkillReqs.setdefault(typeID, {})
|
||||
for skillreqTypeAttr, skillreqLevelAttr in skillReqAttribs.items():
|
||||
try:
|
||||
skillType = int(typeSkillAttribs[skillreqTypeAttr])
|
||||
skillLevel = int(typeSkillAttribs[skillreqLevelAttr])
|
||||
except (KeyError, ValueError):
|
||||
continue
|
||||
typeSkillReqs[skillType] = skillLevel
|
||||
# Format: {group ID: category ID}
|
||||
groupCategories = {}
|
||||
for row in eveGroupsData:
|
||||
groupCategories[row['groupID']] = row['categoryID']
|
||||
# As EVE affects various types mostly depending on their group or skill requirements,
|
||||
# we're going to group various types up this way
|
||||
# Format: {(group ID, frozenset(skillreq, type, IDs), frozenset(type, effect, IDs): [type ID, {attribute ID: attribute value}]}
|
||||
groupedData = {}
|
||||
for row in eveTypesData:
|
||||
typeID = row['typeID']
|
||||
# Ignore items outside of categories we need
|
||||
if groupCategories[typesGroups[typeID]] not in (
|
||||
6, # Ship
|
||||
7, # Module
|
||||
8, # Charge
|
||||
18, # Drone
|
||||
20, # Implant
|
||||
22, # Deployable
|
||||
23, # Starbase
|
||||
32, # Subsystem
|
||||
35, # Decryptors
|
||||
65, # Structure
|
||||
66, # Structure Module
|
||||
87, # Fighter
|
||||
):
|
||||
continue
|
||||
typeAttribs = typesNormalAttribs.get(typeID, {})
|
||||
# Ignore items w/o attributes
|
||||
if not typeAttribs:
|
||||
continue
|
||||
# We need only skill types, not levels for keys
|
||||
typeSkillreqs = frozenset(typesSkillReqs.get(typeID, {}))
|
||||
typeGroup = typesGroups[typeID]
|
||||
typeEffects = frozenset(typesEffects.get(typeID, ()))
|
||||
groupData = groupedData.setdefault((typeGroup, typeSkillreqs, typeEffects), [])
|
||||
groupData.append((typeID, typeAttribs))
|
||||
# Format: {type ID: set(type IDs)}
|
||||
replacements = {}
|
||||
# Now, go through composed groups and for every item within it
|
||||
# find items which are the same
|
||||
for groupData in groupedData.values():
|
||||
for type1, type2 in itertools.combinations(groupData, 2):
|
||||
if compareAttrs(type1[1], type2[1]):
|
||||
replacements.setdefault(type1[0], set()).add(type2[0])
|
||||
replacements.setdefault(type2[0], set()).add(type1[0])
|
||||
# Update DB session with data we generated
|
||||
for item in eos.db.gamedata_session.query(eos.gamedata.Item).all():
|
||||
itemReplacements = replacements.get(item.typeID)
|
||||
if itemReplacements is not None:
|
||||
item.replacements = ','.join('{}'.format(tid) for tid in sorted(itemReplacements))
|
||||
|
||||
def processImplantSets(eveTypesData):
|
||||
print('composing implant sets')
|
||||
# Includes only implants which can be considered part of sets, not all implants
|
||||
implant_groups = (300, 1730)
|
||||
specials = {'Genolution': ('Genolution Core Augmentation', r'CA-\d+')}
|
||||
implantSets = {}
|
||||
for row in eveTypesData:
|
||||
if not row.get('published'):
|
||||
continue
|
||||
if row.get('groupID') not in implant_groups:
|
||||
continue
|
||||
typeName = row.get('typeName_en-us', '')
|
||||
# Regular sets matching
|
||||
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
|
||||
for setHandle, (setName, implantPattern) in specials.items():
|
||||
pattern = '(?P<set>{}) (?P<implant>{})'.format(setName, implantPattern)
|
||||
m = re.match(pattern, typeName)
|
||||
if m:
|
||||
implantSets.setdefault((None, setHandle), set()).add(row['typeID'])
|
||||
break
|
||||
data = []
|
||||
for (gradeName, setName), implants in implantSets.items():
|
||||
if len(implants) < 2:
|
||||
continue
|
||||
implants = ','.join('{}'.format(tid) for tid in sorted(implants))
|
||||
row = {'setName': setName, 'gradeName': gradeName, 'implants': implants}
|
||||
data.append(row)
|
||||
_addRows(data, eos.gamedata.ImplantSet)
|
||||
|
||||
eveTypesData = processEveTypes()
|
||||
eveGroupsData = processEveGroups()
|
||||
processEveCategories()
|
||||
processDogmaAttributes()
|
||||
dogmaTypeAttributesData = processDogmaTypeAttributes(eveTypesData)
|
||||
processDynamicItemAttributes()
|
||||
processDogmaEffects()
|
||||
dogmaTypeEffectsData = processDogmaTypeEffects(eveTypesData)
|
||||
processDogmaUnits()
|
||||
processMarketGroups()
|
||||
processMetaGroups()
|
||||
processCloneGrades()
|
||||
processTraits()
|
||||
processMetadata()
|
||||
|
||||
eos.db.gamedata_session.flush()
|
||||
processReqSkills(eveTypesData)
|
||||
processReplacements(eveTypesData, eveGroupsData, dogmaTypeAttributesData, dogmaTypeEffectsData)
|
||||
processImplantSets(eveTypesData)
|
||||
|
||||
# Add schema version to prevent further updates
|
||||
metadata_schema_version = eos.gamedata.MetaData()
|
||||
metadata_schema_version.field_name = 'schema_version'
|
||||
metadata_schema_version.field_value = GAMEDATA_SCHEMA_VERSION
|
||||
eos.db.gamedata_session.add(metadata_schema_version)
|
||||
|
||||
eos.db.gamedata_session.flush()
|
||||
|
||||
# CCP still has 5 subsystems assigned to T3Cs, even though only 4 are available / usable. They probably have some
|
||||
# old legacy requirement or assumption that makes it difficult for them to change this value in the data. But for
|
||||
# pyfa, we can do it here as a post-processing step
|
||||
for attr in eos.db.gamedata_session.query(eos.gamedata.Attribute).filter(eos.gamedata.Attribute.ID == 1367).all():
|
||||
attr.value = 4.0
|
||||
for item in eos.db.gamedata_session.query(eos.gamedata.Item).filter(or_(
|
||||
eos.gamedata.Item.name.like('%abyssal%'),
|
||||
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', 'Lux Ballistra', 'Lux Kopis'))
|
||||
)).all():
|
||||
if 'Asteroid Mining Crystal' in item.name:
|
||||
continue
|
||||
if 'Mutated Drone Specialization' in item.name:
|
||||
continue
|
||||
item.published = False
|
||||
|
||||
for x in [
|
||||
30 # Apparel
|
||||
]:
|
||||
cat = eos.db.gamedata_session.query(eos.gamedata.Category).filter(eos.gamedata.Category.ID == x).first()
|
||||
print ('Removing Category: {}'.format(cat.name))
|
||||
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:
|
||||
attr = eos.db.gamedata_session.query(eos.gamedata.Attribute).filter(and_(
|
||||
eos.gamedata.Attribute.name == attrName, eos.gamedata.Attribute.typeID == typeID)).one()
|
||||
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.attributeID = attrInfo.ID
|
||||
attr.typeID = typeID
|
||||
attr.value = value
|
||||
eos.db.gamedata_session.add(attr)
|
||||
else:
|
||||
attr.value = value
|
||||
|
||||
def _hardcodeEffects(typeID, effectMap, clearEffects=True):
|
||||
item = eos.db.gamedata_session.query(eos.gamedata.Item).filter(eos.gamedata.Item.ID == typeID).one()
|
||||
if clearEffects:
|
||||
item.effects.clear()
|
||||
for effectID, effectName in effectMap.items():
|
||||
try:
|
||||
effect = eos.db.gamedata_session.query(eos.gamedata.Effect).filter(eos.gamedata.Effect.ID == effectID).one()
|
||||
except sqlalchemy.orm.exc.NoResultFound:
|
||||
effect = eos.gamedata.Effect()
|
||||
effect.effectID = effectID
|
||||
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)
|
||||
eos.db.gamedata_session.flush()
|
||||
|
||||
def hardcodeSovUpgradeBuffs():
|
||||
typeBuffMap = {
|
||||
# Gamma
|
||||
87815: {
|
||||
'warfareBuff1ID': 2433,
|
||||
'warfareBuff1Value': 5,
|
||||
'warfareBuff2ID': 2434,
|
||||
'warfareBuff2Value': 10,
|
||||
'warfareBuff3ID': 2441,
|
||||
'warfareBuff3Value': 5},
|
||||
# Plasma
|
||||
87949: {
|
||||
'warfareBuff1ID': 2442,
|
||||
'warfareBuff1Value': 5,
|
||||
'warfareBuff2ID': 2435,
|
||||
'warfareBuff2Value': 5,
|
||||
'warfareBuff3ID': 2436,
|
||||
'warfareBuff3Value': 10},
|
||||
# Electric
|
||||
87950: {
|
||||
'warfareBuff1ID': 2437,
|
||||
'warfareBuff1Value': -25,
|
||||
'warfareBuff2ID': 2438,
|
||||
'warfareBuff2Value': 25},
|
||||
# Exotic
|
||||
87951: {
|
||||
'warfareBuff1ID': 2440,
|
||||
'warfareBuff1Value': 2,
|
||||
'warfareBuff2ID': 2439,
|
||||
'warfareBuff2Value': 25}}
|
||||
effectMap = {100001: 'pyfaCustomSovUpgradeBuffEffect'}
|
||||
for typeID, attrMap in typeBuffMap.items():
|
||||
_hardcodeAttribs(typeID, attrMap)
|
||||
_hardcodeEffects(typeID, effectMap, clearEffects=False)
|
||||
eos.db.gamedata_session.flush()
|
||||
|
||||
|
||||
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()
|
||||
hardcodeSovUpgradeBuffs()
|
||||
|
||||
eos.db.gamedata_session.commit()
|
||||
eos.db.gamedata_engine.execute('VACUUM')
|
||||
|
||||
print('done')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
update_db()
|
||||
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')
|
||||
@@ -20,6 +20,7 @@ added_files = [
|
||||
('../../imgs/renders/*.png', 'imgs/renders'),
|
||||
('../../dist_assets/win/pyfa.ico', '.'),
|
||||
('../../service/jargon/*.yaml', 'service/jargon'),
|
||||
('../../locale', 'locale'),
|
||||
(requests.certs.where(), '.'), # is this needed anymore?
|
||||
('../../eve.db', '.'),
|
||||
('../../README.md', '.'),
|
||||
@@ -30,7 +31,8 @@ added_files = [
|
||||
|
||||
import_these = [
|
||||
'numpy.core._dtype_ctypes', # https://github.com/pyinstaller/pyinstaller/issues/3982
|
||||
'sqlalchemy.ext.baked' # windows build doesn't launch without if when using sqlalchemy 1.3.x
|
||||
'sqlalchemy.ext.baked', # windows build doesn't launch without if when using sqlalchemy 1.3.x
|
||||
'pkg_resources.py2_warn' # issue 2156
|
||||
]
|
||||
|
||||
icon = os.path.join(os.getcwd(), "dist_assets", "mac", "pyfa.icns")
|
||||
@@ -78,6 +80,7 @@ exe = EXE(pyz,
|
||||
app = BUNDLE(
|
||||
exe,
|
||||
name='pyfa.app',
|
||||
version=os.getenv('PYFA_VERSION'),
|
||||
icon=icon,
|
||||
bundle_identifier=None,
|
||||
info_plist={
|
||||
@@ -86,5 +89,7 @@ app = BUNDLE(
|
||||
'CFBundleName': 'pyfa',
|
||||
'CFBundleDisplayName': 'pyfa',
|
||||
'CFBundleIdentifier': 'org.pyfaorg.pyfa',
|
||||
'CFBundleVersion': os.getenv('PYFA_VERSION'),
|
||||
'CFBundleShortVersionString': os.getenv('PYFA_VERSION'),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
# This apes hook-matplotlib.backends.py, but REMOVES backends, all but
|
||||
# the ones in the list below.
|
||||
# Courtesy of https://github.com/bpteague/cytoflow/blob/70f9291/packaging/hook-matplotlib.backends.py
|
||||
|
||||
KEEP = ["WXAgg", "WX", "agg"]
|
||||
|
||||
from PyInstaller.compat import is_darwin
|
||||
from PyInstaller.utils.hooks import (
|
||||
eval_statement, exec_statement, logger)
|
||||
|
||||
|
||||
def get_matplotlib_backend_module_names():
|
||||
"""
|
||||
List the names of all matplotlib backend modules importable under the
|
||||
current Python installation.
|
||||
Returns
|
||||
----------
|
||||
list
|
||||
List of the fully-qualified names of all such modules.
|
||||
"""
|
||||
# Statement safely importing a single backend module.
|
||||
import_statement = """
|
||||
import os, sys
|
||||
# Preserve stdout.
|
||||
sys_stdout = sys.stdout
|
||||
try:
|
||||
# Redirect output printed by this importation to "/dev/null", preventing
|
||||
# such output from being erroneously interpreted as an error.
|
||||
with open(os.devnull, 'w') as dev_null:
|
||||
sys.stdout = dev_null
|
||||
__import__('%s')
|
||||
# If this is an ImportError, print this exception's message without a traceback.
|
||||
# ImportError messages are human-readable and require no additional context.
|
||||
except ImportError as exc:
|
||||
sys.stdout = sys_stdout
|
||||
print(exc)
|
||||
# Else, print this exception preceded by a traceback. traceback.print_exc()
|
||||
# prints to stderr rather than stdout and must not be called here!
|
||||
except Exception:
|
||||
sys.stdout = sys_stdout
|
||||
import traceback
|
||||
print(traceback.format_exc())
|
||||
"""
|
||||
|
||||
# List of the human-readable names of all available backends.
|
||||
backend_names = eval_statement(
|
||||
'import matplotlib; print(matplotlib.rcsetup.all_backends)')
|
||||
|
||||
# List of the fully-qualified names of all importable backend modules.
|
||||
module_names = []
|
||||
|
||||
# If the current system is not OS X and the "CocoaAgg" backend is available,
|
||||
# remove this backend from consideration. Attempting to import this backend
|
||||
# on non-OS X systems halts the current subprocess without printing output
|
||||
# or raising exceptions, preventing its reliable detection.
|
||||
if not is_darwin and 'CocoaAgg' in backend_names:
|
||||
backend_names.remove('CocoaAgg')
|
||||
|
||||
# For safety, attempt to import each backend in a unique subprocess.
|
||||
for backend_name in backend_names:
|
||||
if backend_name in KEEP:
|
||||
continue
|
||||
|
||||
module_name = 'matplotlib.backends.backend_%s' % backend_name.lower()
|
||||
stdout = exec_statement(import_statement % module_name)
|
||||
|
||||
# If no output was printed, this backend is importable.
|
||||
if not stdout:
|
||||
module_names.append(module_name)
|
||||
logger.info(' Matplotlib backend "%s": removed' % backend_name)
|
||||
|
||||
return module_names
|
||||
|
||||
# Freeze all importable backends, as PyInstaller is unable to determine exactly
|
||||
# which backends are required by the current program.
|
||||
e=get_matplotlib_backend_module_names()
|
||||
print(e)
|
||||
excludedimports = e
|
||||
@@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<noInheritable/>
|
||||
<assemblyIdentity
|
||||
type="win32"
|
||||
name="Microsoft.VC90.CRT"
|
||||
version="9.0.21022.8"
|
||||
processorArchitecture="x86"
|
||||
publicKeyToken="1fc8b3b9a1e18e3b"/>
|
||||
<file name="MSVCR90.DLL"/>
|
||||
<file name="MSVCM90.DLL"/>
|
||||
<file name="MSVCP90.DLL"/>
|
||||
</assembly>
|
||||
@@ -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 5\ISCC.exe" # inno script location via wine
|
||||
iscc = r"C:\Program Files (x86)\Inno Setup 6\ISCC.exe"
|
||||
|
||||
source = os.path.join(os.environ["PYFA_DIST_DIR"], "pyfa")
|
||||
|
||||
|
||||
@@ -15,10 +15,6 @@
|
||||
#define MyAppURL "https://github.com/pyfa-org/Pyfa/"
|
||||
#define MyAppExeName "pyfa.exe"
|
||||
|
||||
; What version starts with the new structure (1.x.0). This is used to determine if we run directory structure cleanup
|
||||
#define MajorVersionFlag 2
|
||||
#define MinorVersionFlag 0
|
||||
|
||||
#ifndef MyOutputFile
|
||||
#define MyOutputFile LowerCase(StringChange(MyAppName+'-'+MyAppVersion+'-win', " ", "-"))
|
||||
#endif
|
||||
@@ -30,7 +26,6 @@
|
||||
#endif
|
||||
|
||||
[Setup]
|
||||
|
||||
; NOTE: The value of AppId uniquely identifies this application.
|
||||
; Do not use the same AppId value in installers for other applications.
|
||||
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
|
||||
@@ -41,15 +36,17 @@ AppPublisher={#MyAppPublisher}
|
||||
AppPublisherURL={#MyAppURL}
|
||||
AppSupportURL={#MyAppURL}
|
||||
AppUpdatesURL={#MyAppURL}
|
||||
ArchitecturesAllowed=x64
|
||||
ArchitecturesInstallIn64BitMode=x64
|
||||
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
|
||||
CloseApplications=yes
|
||||
|
||||
[Languages]
|
||||
Name: "english"; MessagesFile: "compiler:Default.isl"
|
||||
@@ -83,6 +80,7 @@ Type: files; Name: "{app}\*.pyc"
|
||||
|
||||
[Code]
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
function IsAppRunning(const FileName : string): Boolean;
|
||||
var
|
||||
FSWbemLocator: Variant;
|
||||
@@ -99,6 +97,7 @@ begin
|
||||
FSWbemLocator := Unassigned;
|
||||
end;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
procedure RemoveFromVirtualStore;
|
||||
var
|
||||
VirtualStore,FileName,FilePath:String;
|
||||
@@ -115,11 +114,12 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
function PrepareToInstall(var NeedsRestart: Boolean): String;
|
||||
begin
|
||||
if(IsAppRunning( 'pyfa.exe' )) then
|
||||
begin
|
||||
Result := 'Please close pyfa before continuing. When closed, please go back to the previous step and continue.';
|
||||
Result := 'Please close pyfa before continuing. When closed, please go back to the previous step and continue. If you have named this installer pyfa.exe, please rename it and restart installation';
|
||||
end
|
||||
else
|
||||
begin
|
||||
@@ -127,54 +127,61 @@ begin
|
||||
end
|
||||
end;
|
||||
|
||||
function GetUninstallString: string;
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
function GetUninstallString(): String;
|
||||
var
|
||||
sUnInstPath: string;
|
||||
sUnInstPath: String;
|
||||
sUnInstallString: String;
|
||||
begin
|
||||
Result := '';
|
||||
sUnInstPath := ExpandConstant('Software\Microsoft\Windows\CurrentVersion\Uninstall\{{3DA39096-C08D-49CD-90E0-1D177F32C8AA}_is1'); //Your App GUID/ID
|
||||
sUnInstallString := '';
|
||||
if not RegQueryStringValue(HKLM, sUnInstPath, 'UninstallString', sUnInstallString) then
|
||||
RegQueryStringValue(HKCU, sUnInstPath, 'UninstallString', sUnInstallString);
|
||||
if not RegQueryStringValue(HKCU, sUnInstPath, 'UninstallString', sUnInstallString) then
|
||||
if not RegQueryStringValue(HKLM32, sUnInstPath, 'UninstallString', sUnInstallString) then
|
||||
RegQueryStringValue(HKCU32, sUnInstPath, 'UninstallString', sUnInstallString);
|
||||
Result := sUnInstallString;
|
||||
end;
|
||||
|
||||
function IsUpgrade: Boolean;
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
function UnInstallOldVersion(): Integer;
|
||||
var
|
||||
sUnInstallString: String;
|
||||
iResultCode: Integer;
|
||||
begin
|
||||
// Return Values:
|
||||
// 1 - uninstall string is empty
|
||||
// 2 - error executing the UnInstallString
|
||||
// 3 - successfully executed the UnInstallString
|
||||
|
||||
// default return value
|
||||
Result := 0;
|
||||
|
||||
// get the uninstall string of the old app
|
||||
sUnInstallString := GetUninstallString();
|
||||
if sUnInstallString <> '' then begin
|
||||
sUnInstallString := RemoveQuotes(sUnInstallString);
|
||||
if Exec(sUnInstallString, '/SILENT /NORESTART /SUPPRESSMSGBOXES','', SW_HIDE, ewWaitUntilTerminated, iResultCode) then
|
||||
Result := 3
|
||||
else
|
||||
Result := 2;
|
||||
end else
|
||||
Result := 1;
|
||||
end;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
function IsUpgrade(): Boolean;
|
||||
begin
|
||||
Result := (GetUninstallString() <> '');
|
||||
end;
|
||||
|
||||
function InitializeSetup: Boolean;
|
||||
var
|
||||
V: Integer;
|
||||
iResultCode: Integer;
|
||||
sUnInstallString: string;
|
||||
iOldVersionMajor: Cardinal;
|
||||
iOldVersionMinor: Cardinal;
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
procedure CurStepChanged(CurStep: TSetupStep);
|
||||
begin
|
||||
Result := True; // in case when no previous version is found
|
||||
if RegValueExists(HKEY_LOCAL_MACHINE,'Software\Microsoft\Windows\CurrentVersion\Uninstall\{3DA39096-C08D-49CD-90E0-1D177F32C8AA}_is1', 'UninstallString') then //Your App GUID/ID
|
||||
if (CurStep=ssInstall) then
|
||||
begin
|
||||
RegQueryDWordValue(HKEY_LOCAL_MACHINE,
|
||||
'Software\Microsoft\Windows\CurrentVersion\Uninstall\{3DA39096-C08D-49CD-90E0-1D177F32C8AA}_is1',
|
||||
'MajorVersion', iOldVersionMajor);
|
||||
RegQueryDWordValue(HKEY_LOCAL_MACHINE,
|
||||
'Software\Microsoft\Windows\CurrentVersion\Uninstall\{3DA39096-C08D-49CD-90E0-1D177F32C8AA}_is1',
|
||||
'MinorVersion', iOldVersionMinor);
|
||||
if (iOldVersionMajor < {#MajorVersionFlag}) or ((iOldVersionMajor = {#MajorVersionFlag}) and (iOldVersionMinor < {#MinorVersionFlag})) then // If old version with old structure is installed.
|
||||
if (IsUpgrade()) then
|
||||
begin
|
||||
V := MsgBox(ExpandConstant('An old version of pyfa was detected. Due to recent changes in the application structure, you must uninstall the previous version first. This will not affect your user data (saved fittings, characters, etc.). Do you want to uninstall now?'), mbInformation, MB_YESNO); //Custom Message if App installed
|
||||
if V = IDYES then
|
||||
begin
|
||||
sUnInstallString := GetUninstallString();
|
||||
sUnInstallString := RemoveQuotes(sUnInstallString);
|
||||
Exec(ExpandConstant(sUnInstallString), '', '', SW_SHOW, ewWaitUntilTerminated, iResultCode);
|
||||
Result := True; //if you want to proceed after uninstall
|
||||
//Exit; //if you want to quit after uninstall
|
||||
end
|
||||
else
|
||||
Result := False; //when older version present and not uninstalled
|
||||
UnInstallOldVersion();
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
@@ -8,17 +8,17 @@
|
||||
</requestedPrivileges>
|
||||
</security>
|
||||
</trustInfo>
|
||||
<dependency>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity type="win32" name="Microsoft.VC90.CRT" version="9.0.21022.8" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
<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>
|
||||
|
||||
@@ -20,7 +20,6 @@ added_files = [
|
||||
('../../service/jargon/*.yaml', 'service/jargon'),
|
||||
('../../dist_assets/win/pyfa.ico', '.'),
|
||||
('../../dist_assets/win/pyfa.exe.manifest', '.'),
|
||||
('../../dist_assets/win/Microsoft.VC90.CRT.manifest', '.'),
|
||||
(requests.certs.where(), '.'), # is this needed anymore?
|
||||
('../../eve.db', '.'),
|
||||
('../../README.md', '.'),
|
||||
@@ -30,7 +29,8 @@ added_files = [
|
||||
|
||||
import_these = [
|
||||
'numpy.core._dtype_ctypes', # https://github.com/pyinstaller/pyinstaller/issues/3982
|
||||
'sqlalchemy.ext.baked' # windows build doesn't launch without if when using sqlalchemy 1.3.x
|
||||
'sqlalchemy.ext.baked', # windows build doesn't launch without if when using sqlalchemy 1.3.x
|
||||
'pkg_resources.py2_warn' # issue 2156
|
||||
]
|
||||
|
||||
# Walk directories that do dynamic importing
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
# -*- mode: python -*-
|
||||
|
||||
# Note: This script is provided AS-IS for those that may be interested.
|
||||
# pyfa does not currently support pyInstaller (or any other build process) 100% at the moment
|
||||
|
||||
# Command line to build:
|
||||
# (Run from directory where pyfa.py and pyfa.spec lives.)
|
||||
# c:\Python27\scripts\pyinstaller.exe --clean --noconfirm --windowed --upx-dir=.\scripts\upx.exe pyfa.spec
|
||||
|
||||
# Don't forget to change the path to where your pyfa.py and pyfa.spec lives
|
||||
# pathex=['C:\\Users\\Ebag333\\Documents\\GitHub\\Ebag333\\Pyfa'],
|
||||
|
||||
import os
|
||||
|
||||
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', '.' ),
|
||||
]
|
||||
|
||||
import_these = []
|
||||
|
||||
# Walk eos.effects and add all effects so we can import them properly
|
||||
for root, folders, files in os.walk("eos/effects"):
|
||||
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=['C:\\projects\\pyfa\\'],
|
||||
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,
|
||||
debug=True,
|
||||
console=True,
|
||||
strip=False,
|
||||
upx=True,
|
||||
name='pyfa_debug',
|
||||
icon='dist_assets/win/pyfa.ico',
|
||||
onefile=False,
|
||||
)
|
||||
|
||||
coll = COLLECT(
|
||||
exe,
|
||||
a.binaries,
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
strip=False,
|
||||
upx=True,
|
||||
onefile=False,
|
||||
name='pyfa_debug',
|
||||
icon='dist_assets/win/pyfa.ico',
|
||||
)
|
||||
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.
|
||||
38
eos/calc.py
38
eos/calc.py
@@ -18,6 +18,38 @@
|
||||
# =============================================================================
|
||||
|
||||
|
||||
import math
|
||||
|
||||
|
||||
# Just copy-paste penalization chain calculation code (with some modifications,
|
||||
# as multipliers arrive in different form) in here to not make actual attribute
|
||||
# calculations slower than they already are due to extra function calls
|
||||
def calculateMultiplier(multipliers):
|
||||
"""
|
||||
multipliers: dictionary in format:
|
||||
{stacking group name: [(mult, resist attr ID), (mult, resist attr ID)]}
|
||||
"""
|
||||
val = 1
|
||||
for penalizedMultipliers in multipliers.values():
|
||||
# A quick explanation of how this works:
|
||||
# 1: Bonuses and penalties are calculated seperately, so we'll have to filter each of them
|
||||
l1 = [v[0] for v in penalizedMultipliers if v[0] > 1]
|
||||
l2 = [v[0] for v in penalizedMultipliers if v[0] < 1]
|
||||
# 2: The most significant bonuses take the smallest penalty,
|
||||
# This means we'll have to sort
|
||||
abssort = lambda _val: -abs(_val - 1)
|
||||
l1.sort(key=abssort)
|
||||
l2.sort(key=abssort)
|
||||
# 3: The first module doesn't get penalized at all
|
||||
# Any module after the first takes penalties according to:
|
||||
# 1 + (multiplier - 1) * math.exp(- math.pow(i, 2) / 7.1289)
|
||||
for l in (l1, l2):
|
||||
for i in range(len(l)):
|
||||
bonus = l[i]
|
||||
val *= 1 + (bonus - 1) * math.exp(- i ** 2 / 7.1289)
|
||||
return val
|
||||
|
||||
|
||||
def calculateRangeFactor(srcOptimalRange, srcFalloffRange, distance, restrictedRange=True):
|
||||
"""Range strength/chance factor, applicable to guns, ewar, RRs, etc."""
|
||||
if distance is None:
|
||||
@@ -31,3 +63,9 @@ def calculateRangeFactor(srcOptimalRange, srcFalloffRange, distance, restrictedR
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
|
||||
def calculateLockTime(srcScanRes, tgtSigRadius):
|
||||
if not srcScanRes or not tgtSigRadius:
|
||||
return None
|
||||
return min(40000 / srcScanRes / math.asinh(tgtSigRadius) ** 2, 30 * 60)
|
||||
|
||||
@@ -13,6 +13,24 @@ saveddataCache = True
|
||||
gamedata_version = ""
|
||||
gamedata_date = ""
|
||||
gamedata_connectionstring = 'sqlite:///' + realpath(join(dirname(abspath(__file__)), "..", "eve.db"))
|
||||
|
||||
lang = ""
|
||||
|
||||
# Maps supported langauges to their suffix in the database
|
||||
translation_mapping = {
|
||||
"en": "",
|
||||
"fr": "_fr",
|
||||
# "it": "_it",
|
||||
"ja": "_ja",
|
||||
"ko": "_ko",
|
||||
"ru": "_ru",
|
||||
"zh": "_zh",
|
||||
}
|
||||
|
||||
def set_lang(i18n_lang):
|
||||
global lang
|
||||
lang = translation_mapping.get(i18n_lang, translation_mapping.get("en"))
|
||||
|
||||
pyfalog.debug("Gamedata connection string: {0}", gamedata_connectionstring)
|
||||
|
||||
if istravis is True or hasattr(sys, '_called_from_test'):
|
||||
|
||||
@@ -17,24 +17,37 @@
|
||||
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
||||
# ===============================================================================
|
||||
|
||||
import re
|
||||
import threading
|
||||
|
||||
from sqlalchemy import MetaData, create_engine
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from sqlalchemy import MetaData, create_engine, event
|
||||
from sqlalchemy.orm import sessionmaker, scoped_session
|
||||
|
||||
from . import migration
|
||||
from eos import config
|
||||
from logbook import Logger
|
||||
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
pyfalog.info("Initializing database")
|
||||
pyfalog.info("Gamedata connection: {0}", config.gamedata_connectionstring)
|
||||
pyfalog.info("Saveddata connection: {0}", config.saveddata_connectionstring)
|
||||
|
||||
|
||||
class ReadOnlyException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def re_fn(expr, item):
|
||||
try:
|
||||
reg = re.compile(expr, re.IGNORECASE)
|
||||
except (SystemExit, KeyboardInterrupt):
|
||||
raise
|
||||
except:
|
||||
return False
|
||||
return reg.search(item) is not None
|
||||
|
||||
|
||||
pyfalog.debug('Initializing gamedata')
|
||||
gamedata_connectionstring = config.gamedata_connectionstring
|
||||
if callable(gamedata_connectionstring):
|
||||
@@ -42,9 +55,26 @@ if callable(gamedata_connectionstring):
|
||||
else:
|
||||
gamedata_engine = create_engine(gamedata_connectionstring, echo=config.debug)
|
||||
|
||||
|
||||
@event.listens_for(gamedata_engine, 'connect')
|
||||
def create_functions(dbapi_connection, connection_record):
|
||||
dbapi_connection.create_function('regexp', 2, re_fn)
|
||||
|
||||
|
||||
gamedata_meta = MetaData()
|
||||
gamedata_meta.bind = gamedata_engine
|
||||
gamedata_session = sessionmaker(bind=gamedata_engine, autoflush=False, expire_on_commit=False)()
|
||||
GamedataSession = scoped_session(sessionmaker(bind=gamedata_engine, autoflush=False, expire_on_commit=False))
|
||||
gamedata_session = GamedataSession()
|
||||
|
||||
gamedata_sessions = {threading.get_ident(): gamedata_session}
|
||||
|
||||
|
||||
def get_gamedata_session():
|
||||
thread_id = threading.get_ident()
|
||||
if thread_id not in gamedata_sessions:
|
||||
gamedata_sessions[thread_id] = GamedataSession()
|
||||
return gamedata_sessions[thread_id]
|
||||
|
||||
|
||||
pyfalog.debug('Getting gamedata version')
|
||||
# This should be moved elsewhere, maybe as an actual query. Current, without try-except, it breaks when making a new
|
||||
@@ -56,6 +86,8 @@ try:
|
||||
config.gamedata_date = gamedata_session.execute(
|
||||
"SELECT `field_value` FROM `metadata` WHERE `field_name` LIKE 'dump_time'"
|
||||
).fetchone()[0]
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
raise
|
||||
except Exception as e:
|
||||
pyfalog.warning("Missing gamedata version.")
|
||||
pyfalog.critical(e)
|
||||
@@ -82,11 +114,11 @@ sd_lock = threading.RLock()
|
||||
pyfalog.debug('Importing gamedata DB scheme')
|
||||
# Import all the definitions for all our database stuff
|
||||
# noinspection PyPep8
|
||||
from eos.db.gamedata import alphaClones, attribute, category, effect, group, item, marketGroup, metaData, metaGroup, queries, traits, unit, dynamicAttributes
|
||||
from eos.db.gamedata import alphaClones, attribute, category, effect, group, item, marketGroup, metaData, metaGroup, queries, traits, unit, dynamicAttributes, implantSet
|
||||
pyfalog.debug('Importing saveddata DB scheme')
|
||||
# noinspection PyPep8
|
||||
from eos.db.saveddata import booster, cargo, character, damagePattern, databaseRepair, drone, fighter, fit, implant, implantSet, loadDefaultDatabaseValues, \
|
||||
miscData, mutator, module, override, price, queries, skill, targetProfile, user
|
||||
from eos.db.saveddata import booster, cargo, character, damagePattern, databaseRepair, drone, fighter, fit, implant, implantSet, \
|
||||
miscData, mutatorMod, mutatorDrone, module, override, price, queries, skill, targetProfile, user
|
||||
|
||||
pyfalog.debug('Importing gamedata queries')
|
||||
# noinspection PyPep8
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
__all__ = ["attribute", "category", "effect", "group", "metaData", "dynamicAttributes",
|
||||
"item", "marketGroup", "metaGroup", "unit", "alphaClones"]
|
||||
"item", "marketGroup", "metaGroup", "unit", "alphaClones", "implantSet"]
|
||||
|
||||
@@ -23,7 +23,7 @@ from sqlalchemy.orm import relation, mapper, synonym, deferred
|
||||
|
||||
from eos.db import gamedata_meta
|
||||
from eos.gamedata import Attribute, AttributeInfo, Unit
|
||||
|
||||
import eos.config
|
||||
typeattributes_table = Table("dgmtypeattribs", gamedata_meta,
|
||||
Column("value", Float),
|
||||
Column("typeID", Integer, ForeignKey("invtypes.typeID"), primary_key=True, index=True),
|
||||
@@ -36,11 +36,11 @@ attributes_table = Table("dgmattribs", gamedata_meta,
|
||||
Column("maxAttributeID", Integer, ForeignKey("dgmattribs.attributeID")),
|
||||
Column("description", Unicode),
|
||||
Column("published", Boolean),
|
||||
Column("displayName", String),
|
||||
*[Column("displayName{}".format(lang), String) for lang in eos.config.translation_mapping.values()],
|
||||
Column("highIsGood", Boolean),
|
||||
Column("iconID", Integer),
|
||||
Column("attributeCategory", Integer),
|
||||
Column("tooltipDescription", Integer),
|
||||
# Column("tooltipDescription", Integer), # deprecated...?
|
||||
Column("unitID", Integer, ForeignKey("dgmunits.unitID")))
|
||||
|
||||
mapper(Attribute, typeattributes_table,
|
||||
@@ -51,14 +51,14 @@ mapper(AttributeInfo, attributes_table,
|
||||
"unit" : relation(Unit),
|
||||
"ID" : synonym("attributeID"),
|
||||
"name" : synonym("attributeName"),
|
||||
"description": deferred(attributes_table.c.description)
|
||||
"description": deferred(attributes_table.c.description),
|
||||
})
|
||||
|
||||
Attribute.ID = association_proxy("info", "attributeID")
|
||||
Attribute.name = association_proxy("info", "attributeName")
|
||||
Attribute.description = association_proxy("info", "description")
|
||||
Attribute.published = association_proxy("info", "published")
|
||||
Attribute.displayName = association_proxy("info", "displayName")
|
||||
Attribute.displayName = association_proxy("info", "displayName{}".format(eos.config.lang))
|
||||
Attribute.highIsGood = association_proxy("info", "highIsGood")
|
||||
Attribute.iconID = association_proxy("info", "iconID")
|
||||
Attribute.icon = association_proxy("info", "icon")
|
||||
|
||||
@@ -22,17 +22,18 @@ from sqlalchemy.orm import deferred, mapper, synonym
|
||||
|
||||
from eos.db import gamedata_meta
|
||||
from eos.gamedata import Category
|
||||
import eos.config
|
||||
|
||||
categories_table = Table("invcategories", gamedata_meta,
|
||||
Column("categoryID", Integer, primary_key=True),
|
||||
Column("categoryName", String),
|
||||
Column("description", String),
|
||||
*[Column("name{}".format(lang), String) for lang in eos.config.translation_mapping.values()],
|
||||
# Column("description", String), # deprecated
|
||||
Column("published", Boolean),
|
||||
Column("iconID", Integer))
|
||||
|
||||
mapper(Category, categories_table,
|
||||
properties={
|
||||
"ID" : synonym("categoryID"),
|
||||
"name" : synonym("categoryName"),
|
||||
"description": deferred(categories_table.c.description)
|
||||
"displayName": synonym("name{}".format(eos.config.lang)),
|
||||
# "description": deferred(categories_table.c.description) # deprecated
|
||||
})
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -22,11 +22,12 @@ from sqlalchemy.orm import relation, mapper, synonym, deferred, backref
|
||||
|
||||
from eos.db import gamedata_meta
|
||||
from eos.gamedata import Category, Group
|
||||
import eos.config
|
||||
|
||||
groups_table = Table("invgroups", gamedata_meta,
|
||||
Column("groupID", Integer, primary_key=True),
|
||||
Column("groupName", String),
|
||||
Column("description", String),
|
||||
*[Column("name{}".format(lang), String) for lang in eos.config.translation_mapping.values()],
|
||||
# Column("description", String), # deprecated
|
||||
Column("published", Boolean),
|
||||
Column("categoryID", Integer, ForeignKey("invcategories.categoryID")),
|
||||
Column("iconID", Integer))
|
||||
@@ -35,6 +36,6 @@ mapper(Group, groups_table,
|
||||
properties={
|
||||
"category" : relation(Category, backref=backref("groups", cascade="all,delete")),
|
||||
"ID" : synonym("groupID"),
|
||||
"name" : synonym("groupName"),
|
||||
"description": deferred(groups_table.c.description)
|
||||
"displayName" : synonym("name{}".format(eos.config.lang)),
|
||||
# "description": deferred(groups_table.c.description) # deprecated
|
||||
})
|
||||
|
||||
33
eos/db/gamedata/implantSet.py
Normal file
33
eos/db/gamedata/implantSet.py
Normal file
@@ -0,0 +1,33 @@
|
||||
# ===============================================================================
|
||||
# Copyright (C) 2010 Diego Duclos
|
||||
#
|
||||
# This file is part of eos.
|
||||
#
|
||||
# eos is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# eos 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 Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
||||
# ===============================================================================
|
||||
|
||||
from sqlalchemy import Column, String, Integer, Table
|
||||
from sqlalchemy.orm import mapper, synonym
|
||||
|
||||
from eos.db import gamedata_meta
|
||||
from eos.gamedata import ImplantSet
|
||||
|
||||
implant_set_table = Table("implantsets", gamedata_meta,
|
||||
Column("setID", Integer, primary_key=True),
|
||||
Column("setName", String),
|
||||
Column("gradeName", String),
|
||||
Column("implants", String))
|
||||
|
||||
mapper(ImplantSet, implant_set_table,
|
||||
properties={"ID": synonym("setID")})
|
||||
@@ -17,7 +17,7 @@
|
||||
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
||||
# ===============================================================================
|
||||
|
||||
from sqlalchemy import Boolean, Column, Float, ForeignKey, Integer, String, Table
|
||||
from sqlalchemy import Boolean, Column, ForeignKey, Integer, String, Table
|
||||
from sqlalchemy.ext.associationproxy import association_proxy
|
||||
from sqlalchemy.orm import backref, deferred, mapper, relation, synonym
|
||||
from sqlalchemy.orm.collections import attribute_mapped_collection
|
||||
@@ -27,15 +27,14 @@ from eos.db.gamedata.dynamicAttributes import dynamicApplicable_table
|
||||
from eos.db.gamedata.effect import typeeffects_table
|
||||
from eos.gamedata import Attribute, DynamicItem, Effect, Group, Item, Traits, MetaGroup
|
||||
|
||||
import eos.config
|
||||
|
||||
items_table = Table("invtypes", gamedata_meta,
|
||||
Column("typeID", Integer, primary_key=True),
|
||||
Column("typeName", String, index=True),
|
||||
Column("description", String),
|
||||
*[Column("typeName{}".format(lang), String, index=True) for lang in eos.config.translation_mapping.values()],
|
||||
*[Column("typeDescription{}".format(lang), String) for lang in eos.config.translation_mapping.values()],
|
||||
Column("raceID", Integer),
|
||||
Column("factionID", Integer),
|
||||
Column("volume", Float),
|
||||
Column("mass", Float),
|
||||
Column("capacity", Float),
|
||||
Column("published", Boolean),
|
||||
Column("marketGroupID", Integer, ForeignKey("invmarketgroups.marketGroupID")),
|
||||
Column("iconID", Integer),
|
||||
@@ -44,27 +43,41 @@ items_table = Table("invtypes", gamedata_meta,
|
||||
Column("metaLevel", Integer),
|
||||
Column("metaGroupID", Integer, ForeignKey("invmetagroups.metaGroupID"), index=True),
|
||||
Column("variationParentTypeID", Integer, ForeignKey("invtypes.typeID"), index=True),
|
||||
Column("replacements", String))
|
||||
Column("replacements", String),
|
||||
Column("reqskills", String),
|
||||
Column("requiredfor", String),
|
||||
)
|
||||
|
||||
from .traits import traits_table # noqa
|
||||
|
||||
mapper(Item, items_table,
|
||||
properties={
|
||||
"group" : relation(Group, backref=backref("items", cascade="all,delete")),
|
||||
props = {
|
||||
"group": relation(Group, backref=backref("items", cascade="all,delete")),
|
||||
"_Item__attributes": relation(Attribute, cascade='all, delete, delete-orphan', collection_class=attribute_mapped_collection('name')),
|
||||
"effects": relation(Effect, secondary=typeeffects_table, collection_class=attribute_mapped_collection('name')),
|
||||
"metaGroup" : relation(MetaGroup, backref=backref("items", cascade="all,delete")),
|
||||
"varParent" : relation(Item, backref=backref("varChildren", cascade="all,delete"), remote_side=items_table.c.typeID),
|
||||
"ID" : synonym("typeID"),
|
||||
"name" : synonym("typeName"),
|
||||
"description" : deferred(items_table.c.description),
|
||||
"traits" : relation(Traits,
|
||||
primaryjoin=traits_table.c.typeID == items_table.c.typeID,
|
||||
uselist=False),
|
||||
"mutaplasmids": relation(DynamicItem,
|
||||
primaryjoin=dynamicApplicable_table.c.applicableTypeID == items_table.c.typeID,
|
||||
secondaryjoin=dynamicApplicable_table.c.typeID == DynamicItem.typeID,
|
||||
secondary=dynamicApplicable_table,
|
||||
backref="applicableItems")})
|
||||
"metaGroup": relation(MetaGroup, backref=backref("items", cascade="all,delete")),
|
||||
"varParent": relation(Item, backref=backref("varChildren", cascade="all,delete"), remote_side=items_table.c.typeID),
|
||||
"ID": synonym("typeID"),
|
||||
"name": synonym("typeName{}".format(eos.config.lang)),
|
||||
"description" : synonym("_description{}".format(eos.config.lang)),
|
||||
"traits": relation(
|
||||
Traits,
|
||||
primaryjoin=traits_table.c.typeID == items_table.c.typeID,
|
||||
uselist=False
|
||||
),
|
||||
"mutaplasmids": relation(
|
||||
DynamicItem,
|
||||
primaryjoin=dynamicApplicable_table.c.applicableTypeID == items_table.c.typeID,
|
||||
secondaryjoin=dynamicApplicable_table.c.typeID == DynamicItem.typeID,
|
||||
secondary=dynamicApplicable_table,
|
||||
backref="applicableItems",
|
||||
viewonly=True
|
||||
)
|
||||
}
|
||||
|
||||
# Create deferred columns shadowing all the description fields. The literal `description` property will dynamically
|
||||
# be assigned as synonym to one of these
|
||||
props.update({'_description' + v: deferred(items_table.c['typeDescription' + v]) for (k, v) in eos.config.translation_mapping.items()})
|
||||
|
||||
mapper(Item, items_table, properties=props)
|
||||
|
||||
Item.category = association_proxy("group", "category")
|
||||
|
||||
@@ -22,22 +22,34 @@ from sqlalchemy.orm import relation, mapper, synonym, deferred
|
||||
|
||||
from eos.db import gamedata_meta
|
||||
from eos.gamedata import Item, MarketGroup
|
||||
import eos.config
|
||||
|
||||
marketgroups_table = Table("invmarketgroups", gamedata_meta,
|
||||
Column("marketGroupID", Integer, primary_key=True),
|
||||
Column("marketGroupName", String),
|
||||
Column("description", String),
|
||||
*[Column("marketGroupName{}".format(lang), String) for lang in eos.config.translation_mapping.values()],
|
||||
*[Column("marketGroupDescription{}".format(lang), String) for lang in eos.config.translation_mapping.values()],
|
||||
Column("hasTypes", Boolean),
|
||||
Column("parentGroupID", Integer,
|
||||
ForeignKey("invmarketgroups.marketGroupID", initially="DEFERRED", deferrable=True)),
|
||||
ForeignKey("invmarketgroups.marketGroupID", initially="DEFERRED", deferrable=True)),
|
||||
Column("iconID", Integer))
|
||||
|
||||
mapper(MarketGroup, marketgroups_table,
|
||||
properties={
|
||||
"items" : relation(Item, backref="marketGroup"),
|
||||
"parent" : relation(MarketGroup, backref="children",
|
||||
remote_side=[marketgroups_table.c.marketGroupID]),
|
||||
"ID" : synonym("marketGroupID"),
|
||||
"name" : synonym("marketGroupName"),
|
||||
"description": deferred(marketgroups_table.c.description)
|
||||
})
|
||||
props = {
|
||||
"items": relation(Item, backref="marketGroup"),
|
||||
"parent": relation(MarketGroup, backref="children", remote_side=[marketgroups_table.c.marketGroupID]),
|
||||
"ID": synonym("marketGroupID"),
|
||||
"name": synonym("marketGroupName{}".format(eos.config.lang)),
|
||||
"description": synonym("_description{}".format(eos.config.lang)),
|
||||
}
|
||||
|
||||
# Create deferred columns shadowing all the description fields. The literal `description` property will dynamically
|
||||
# be assigned as synonym to one of these
|
||||
# this is mostly here to allow the db_update to be language-agnostic
|
||||
# todo: determine if we ever use market group descriptions... can we just get with of these?
|
||||
props.update({'_description' + v: deferred(marketgroups_table.c['marketGroupDescription' + v]) for (k, v) in eos.config.translation_mapping.items()})
|
||||
|
||||
mapper(
|
||||
MarketGroup,
|
||||
marketgroups_table,
|
||||
properties=props
|
||||
)
|
||||
|
||||
|
||||
@@ -22,12 +22,20 @@ from sqlalchemy.orm import mapper, synonym
|
||||
|
||||
from eos.db import gamedata_meta
|
||||
from eos.gamedata import MetaGroup
|
||||
import eos.config
|
||||
|
||||
metagroups_table = Table("invmetagroups", gamedata_meta,
|
||||
Column("metaGroupID", Integer, primary_key=True),
|
||||
Column("metaGroupName", String))
|
||||
metagroups_table = Table(
|
||||
"invmetagroups",
|
||||
gamedata_meta,
|
||||
Column("metaGroupID", Integer, primary_key=True),
|
||||
*[Column("metaGroupName{}".format(lang), String) for lang in eos.config.translation_mapping.values()],
|
||||
)
|
||||
|
||||
mapper(MetaGroup, metagroups_table,
|
||||
properties={
|
||||
"ID" : synonym("metaGroupID"),
|
||||
"name": synonym("metaGroupName")})
|
||||
mapper(
|
||||
MetaGroup,
|
||||
metagroups_table,
|
||||
properties={
|
||||
"ID" : synonym("metaGroupID"),
|
||||
"name": synonym("metaGroupName{}".format(eos.config.lang))
|
||||
}
|
||||
)
|
||||
|
||||
@@ -22,11 +22,11 @@ from sqlalchemy.orm import aliased, exc, join
|
||||
from sqlalchemy.sql import and_, or_, select
|
||||
|
||||
import eos.config
|
||||
from eos.db import gamedata_session
|
||||
from eos.db import get_gamedata_session
|
||||
from eos.db.gamedata.item import items_table
|
||||
from eos.db.gamedata.group import groups_table
|
||||
from eos.db.util import processEager, processWhere
|
||||
from eos.gamedata import AlphaClone, Attribute, AttributeInfo, Category, DynamicItem, Group, Item, MarketGroup, MetaData, MetaGroup
|
||||
from eos.gamedata import AlphaClone, Attribute, AttributeInfo, Category, DynamicItem, Group, Item, MarketGroup, MetaData, MetaGroup, ImplantSet
|
||||
|
||||
cache = {}
|
||||
configVal = getattr(eos.config, "gamedataCache", None)
|
||||
@@ -64,7 +64,7 @@ else:
|
||||
return deco
|
||||
|
||||
|
||||
def sqlizeString(line):
|
||||
def sqlizeNormalString(line):
|
||||
# Escape backslashes first, as they will be as escape symbol in queries
|
||||
# Then escape percent and underscore signs
|
||||
# Finally, replace generic wildcards with sql-style wildcards
|
||||
@@ -79,28 +79,39 @@ itemNameMap = {}
|
||||
def getItem(lookfor, eager=None):
|
||||
if isinstance(lookfor, int):
|
||||
if eager is None:
|
||||
item = gamedata_session.query(Item).get(lookfor)
|
||||
item = get_gamedata_session().query(Item).get(lookfor)
|
||||
else:
|
||||
item = gamedata_session.query(Item).options(*processEager(eager)).filter(Item.ID == lookfor).first()
|
||||
item = get_gamedata_session().query(Item).options(*processEager(eager)).filter(Item.ID == lookfor).first()
|
||||
elif isinstance(lookfor, str):
|
||||
if lookfor in itemNameMap:
|
||||
id = itemNameMap[lookfor]
|
||||
if eager is None:
|
||||
item = gamedata_session.query(Item).get(id)
|
||||
item = get_gamedata_session().query(Item).get(id)
|
||||
else:
|
||||
item = gamedata_session.query(Item).options(*processEager(eager)).filter(Item.ID == id).first()
|
||||
item = get_gamedata_session().query(Item).options(*processEager(eager)).filter(Item.ID == id).first()
|
||||
else:
|
||||
# Item names are unique, so we can use first() instead of one()
|
||||
item = gamedata_session.query(Item).options(*processEager(eager)).filter(Item.name == lookfor).first()
|
||||
itemNameMap[lookfor] = item.ID
|
||||
item = get_gamedata_session().query(Item).options(*processEager(eager)).filter(Item.typeName == lookfor).first()
|
||||
if item is not None:
|
||||
itemNameMap[lookfor] = item.ID
|
||||
else:
|
||||
raise TypeError("Need integer or string as argument")
|
||||
return item
|
||||
|
||||
@cachedQuery(1, "itemIDs")
|
||||
def getItems(itemIDs, eager=None):
|
||||
if not isinstance(itemIDs, (tuple, list, set)) or not all(isinstance(t, int) for t in itemIDs):
|
||||
raise TypeError("Need iterable of integers as argument")
|
||||
if eager is None:
|
||||
items = get_gamedata_session().query(Item).filter(Item.ID.in_(itemIDs)).all()
|
||||
else:
|
||||
items = get_gamedata_session().query(Item).options(*processEager(eager)).filter(Item.ID.in_(itemIDs)).all()
|
||||
return items
|
||||
|
||||
|
||||
def getMutaplasmid(lookfor, eager=None):
|
||||
if isinstance(lookfor, int):
|
||||
item = gamedata_session.query(DynamicItem).filter(DynamicItem.ID == lookfor).first()
|
||||
item = get_gamedata_session().query(DynamicItem).filter(DynamicItem.ID == lookfor).first()
|
||||
else:
|
||||
raise TypeError("Need integer as argument")
|
||||
return item
|
||||
@@ -108,7 +119,7 @@ def getMutaplasmid(lookfor, eager=None):
|
||||
|
||||
def getItemWithBaseItemAttribute(lookfor, baseItemID, eager=None):
|
||||
# A lot of this is described in more detail in #1597
|
||||
item = gamedata_session.query(Item).get(lookfor)
|
||||
item = get_gamedata_session().query(Item).get(lookfor)
|
||||
base = getItem(baseItemID)
|
||||
|
||||
# we have to load all attributes for this object, otherwise we'll lose access to them when we expunge.
|
||||
@@ -124,7 +135,7 @@ def getItemWithBaseItemAttribute(lookfor, baseItemID, eager=None):
|
||||
# Expunge the item form the session. This is required to have different Abyssal / Base combinations loaded in memory.
|
||||
# Without expunging it, once one Abyssal Web is created, SQLAlchmey will use it for all others. We don't want this,
|
||||
# we want to generate a completely new object to work with
|
||||
gamedata_session.expunge(item)
|
||||
get_gamedata_session().expunge(item)
|
||||
return item
|
||||
|
||||
|
||||
@@ -147,7 +158,7 @@ def getItems(lookfor, eager=None):
|
||||
|
||||
if len(toGet) > 0:
|
||||
# Get items that aren't currently cached, and store them in the cache
|
||||
items = gamedata_session.query(Item).filter(Item.ID.in_(toGet)).all()
|
||||
items = get_gamedata_session().query(Item).filter(Item.ID.in_(toGet)).all()
|
||||
for item in items:
|
||||
cache[(item.ID, None)] = item
|
||||
results += items
|
||||
@@ -161,9 +172,9 @@ def getItems(lookfor, eager=None):
|
||||
def getAlphaClone(lookfor, eager=None):
|
||||
if isinstance(lookfor, int):
|
||||
if eager is None:
|
||||
item = gamedata_session.query(AlphaClone).get(lookfor)
|
||||
item = get_gamedata_session().query(AlphaClone).get(lookfor)
|
||||
else:
|
||||
item = gamedata_session.query(AlphaClone).options(*processEager(eager)).filter(AlphaClone.ID == lookfor).first()
|
||||
item = get_gamedata_session().query(AlphaClone).options(*processEager(eager)).filter(AlphaClone.ID == lookfor).first()
|
||||
else:
|
||||
raise TypeError("Need integer as argument")
|
||||
return item
|
||||
@@ -171,7 +182,7 @@ def getAlphaClone(lookfor, eager=None):
|
||||
|
||||
def getAlphaCloneList(eager=None):
|
||||
eager = processEager(eager)
|
||||
clones = gamedata_session.query(AlphaClone).options(*eager).all()
|
||||
clones = get_gamedata_session().query(AlphaClone).options(*eager).all()
|
||||
return clones
|
||||
|
||||
|
||||
@@ -182,20 +193,21 @@ groupNameMap = {}
|
||||
def getGroup(lookfor, eager=None):
|
||||
if isinstance(lookfor, int):
|
||||
if eager is None:
|
||||
group = gamedata_session.query(Group).get(lookfor)
|
||||
group = get_gamedata_session().query(Group).get(lookfor)
|
||||
else:
|
||||
group = gamedata_session.query(Group).options(*processEager(eager)).filter(Group.ID == lookfor).first()
|
||||
group = get_gamedata_session().query(Group).options(*processEager(eager)).filter(Group.ID == lookfor).first()
|
||||
elif isinstance(lookfor, str):
|
||||
if lookfor in groupNameMap:
|
||||
id = groupNameMap[lookfor]
|
||||
if eager is None:
|
||||
group = gamedata_session.query(Group).get(id)
|
||||
group = get_gamedata_session().query(Group).get(id)
|
||||
else:
|
||||
group = gamedata_session.query(Group).options(*processEager(eager)).filter(Group.ID == id).first()
|
||||
group = get_gamedata_session().query(Group).options(*processEager(eager)).filter(Group.ID == id).first()
|
||||
else:
|
||||
# Group names are unique, so we can use first() instead of one()
|
||||
group = gamedata_session.query(Group).options(*processEager(eager)).filter(Group.name == lookfor).first()
|
||||
groupNameMap[lookfor] = group.ID
|
||||
group = get_gamedata_session().query(Group).options(*processEager(eager)).filter(Group.name == lookfor).first()
|
||||
if group is not None:
|
||||
groupNameMap[lookfor] = group.ID
|
||||
else:
|
||||
raise TypeError("Need integer or string as argument")
|
||||
return group
|
||||
@@ -208,23 +220,24 @@ categoryNameMap = {}
|
||||
def getCategory(lookfor, eager=None):
|
||||
if isinstance(lookfor, int):
|
||||
if eager is None:
|
||||
category = gamedata_session.query(Category).get(lookfor)
|
||||
category = get_gamedata_session().query(Category).get(lookfor)
|
||||
else:
|
||||
category = gamedata_session.query(Category).options(*processEager(eager)).filter(
|
||||
category = get_gamedata_session().query(Category).options(*processEager(eager)).filter(
|
||||
Category.ID == lookfor).first()
|
||||
elif isinstance(lookfor, str):
|
||||
if lookfor in categoryNameMap:
|
||||
id = categoryNameMap[lookfor]
|
||||
if eager is None:
|
||||
category = gamedata_session.query(Category).get(id)
|
||||
category = get_gamedata_session().query(Category).get(id)
|
||||
else:
|
||||
category = gamedata_session.query(Category).options(*processEager(eager)).filter(
|
||||
category = get_gamedata_session().query(Category).options(*processEager(eager)).filter(
|
||||
Category.ID == id).first()
|
||||
else:
|
||||
# Category names are unique, so we can use first() instead of one()
|
||||
category = gamedata_session.query(Category).options(*processEager(eager)).filter(
|
||||
category = get_gamedata_session().query(Category).options(*processEager(eager)).filter(
|
||||
Category.name == lookfor).first()
|
||||
categoryNameMap[lookfor] = category.ID
|
||||
if category is not None:
|
||||
categoryNameMap[lookfor] = category.ID
|
||||
else:
|
||||
raise TypeError("Need integer or string as argument")
|
||||
return category
|
||||
@@ -237,39 +250,40 @@ metaGroupNameMap = {}
|
||||
def getMetaGroup(lookfor, eager=None):
|
||||
if isinstance(lookfor, int):
|
||||
if eager is None:
|
||||
metaGroup = gamedata_session.query(MetaGroup).get(lookfor)
|
||||
metaGroup = get_gamedata_session().query(MetaGroup).get(lookfor)
|
||||
else:
|
||||
metaGroup = gamedata_session.query(MetaGroup).options(*processEager(eager)).filter(
|
||||
metaGroup = get_gamedata_session().query(MetaGroup).options(*processEager(eager)).filter(
|
||||
MetaGroup.ID == lookfor).first()
|
||||
elif isinstance(lookfor, str):
|
||||
if lookfor in metaGroupNameMap:
|
||||
id = metaGroupNameMap[lookfor]
|
||||
if eager is None:
|
||||
metaGroup = gamedata_session.query(MetaGroup).get(id)
|
||||
metaGroup = get_gamedata_session().query(MetaGroup).get(id)
|
||||
else:
|
||||
metaGroup = gamedata_session.query(MetaGroup).options(*processEager(eager)).filter(
|
||||
metaGroup = get_gamedata_session().query(MetaGroup).options(*processEager(eager)).filter(
|
||||
MetaGroup.ID == id).first()
|
||||
else:
|
||||
# MetaGroup names are unique, so we can use first() instead of one()
|
||||
metaGroup = gamedata_session.query(MetaGroup).options(*processEager(eager)).filter(
|
||||
MetaGroup.name == lookfor).first()
|
||||
metaGroupNameMap[lookfor] = metaGroup.ID
|
||||
metaGroup = get_gamedata_session().query(MetaGroup).options(*processEager(eager)).filter(
|
||||
MetaGroup.metaGroupName == lookfor).first()
|
||||
if metaGroup is not None:
|
||||
metaGroupNameMap[lookfor] = metaGroup.ID
|
||||
else:
|
||||
raise TypeError("Need integer or string as argument")
|
||||
return metaGroup
|
||||
|
||||
|
||||
def getMetaGroups():
|
||||
return gamedata_session.query(MetaGroup).all()
|
||||
return get_gamedata_session().query(MetaGroup).all()
|
||||
|
||||
|
||||
@cachedQuery(1, "lookfor")
|
||||
def getMarketGroup(lookfor, eager=None):
|
||||
if isinstance(lookfor, int):
|
||||
if eager is None:
|
||||
marketGroup = gamedata_session.query(MarketGroup).get(lookfor)
|
||||
marketGroup = get_gamedata_session().query(MarketGroup).get(lookfor)
|
||||
else:
|
||||
marketGroup = gamedata_session.query(MarketGroup).options(*processEager(eager)).filter(
|
||||
marketGroup = get_gamedata_session().query(MarketGroup).options(*processEager(eager)).filter(
|
||||
MarketGroup.ID == lookfor).first()
|
||||
else:
|
||||
raise TypeError("Need integer as argument")
|
||||
@@ -281,7 +295,7 @@ def getMarketTreeNodeIds(rootNodeIds):
|
||||
addedIds = set(rootNodeIds)
|
||||
while addedIds:
|
||||
allIds.update(addedIds)
|
||||
addedIds = {mg.ID for mg in gamedata_session.query(MarketGroup).filter(MarketGroup.parentGroupID.in_(addedIds))}
|
||||
addedIds = {mg.ID for mg in get_gamedata_session().query(MarketGroup).filter(MarketGroup.parentGroupID.in_(addedIds))}
|
||||
return allIds
|
||||
|
||||
|
||||
@@ -295,7 +309,7 @@ def getItemsByCategory(filter, where=None, eager=None):
|
||||
raise TypeError("Need integer or string as argument")
|
||||
|
||||
filter = processWhere(filter, where)
|
||||
return gamedata_session.query(Item).options(*processEager(eager)).join(Item.group, Group.category).filter(
|
||||
return get_gamedata_session().query(Item).options(*processEager(eager)).join(Item.group, Group.category).filter(
|
||||
filter).all()
|
||||
|
||||
|
||||
@@ -310,9 +324,9 @@ def searchItems(nameLike, where=None, join=None, eager=None):
|
||||
if not hasattr(join, "__iter__"):
|
||||
join = (join,)
|
||||
|
||||
items = gamedata_session.query(Item).options(*processEager(eager)).join(*join)
|
||||
items = get_gamedata_session().query(Item).options(*processEager(eager)).join(*join)
|
||||
for token in nameLike.split(' '):
|
||||
token_safe = "%{0}%".format(sqlizeString(token))
|
||||
token_safe = "%{0}%".format(sqlizeNormalString(token))
|
||||
if where is not None:
|
||||
items = items.filter(and_(Item.name.like(token_safe, escape="\\"), where))
|
||||
else:
|
||||
@@ -321,14 +335,35 @@ def searchItems(nameLike, where=None, join=None, eager=None):
|
||||
return items
|
||||
|
||||
|
||||
@cachedQuery(3, "tokens", "where", "join")
|
||||
def searchItemsRegex(tokens, where=None, join=None, eager=None):
|
||||
if not isinstance(tokens, (tuple, list)) or not all(isinstance(t, str) for t in tokens):
|
||||
raise TypeError("Need tuple or list of strings as argument")
|
||||
|
||||
if join is None:
|
||||
join = tuple()
|
||||
|
||||
if not hasattr(join, "__iter__"):
|
||||
join = (join,)
|
||||
|
||||
items = get_gamedata_session().query(Item).options(*processEager(eager)).join(*join)
|
||||
for token in tokens:
|
||||
if where is not None:
|
||||
items = items.filter(and_(Item.name.op('regexp')(token), where))
|
||||
else:
|
||||
items = items.filter(Item.name.op('regexp')(token))
|
||||
items = items.limit(100).all()
|
||||
return items
|
||||
|
||||
|
||||
@cachedQuery(3, "where", "nameLike", "join")
|
||||
def searchSkills(nameLike, where=None, eager=None):
|
||||
if not isinstance(nameLike, str):
|
||||
raise TypeError("Need string as argument")
|
||||
|
||||
items = gamedata_session.query(Item).options(*processEager(eager)).join(Item.group, Group.category)
|
||||
items = get_gamedata_session().query(Item).options(*processEager(eager)).join(Item.group, Group.category)
|
||||
for token in nameLike.split(' '):
|
||||
token_safe = "%{0}%".format(sqlizeString(token))
|
||||
token_safe = "%{0}%".format(sqlizeNormalString(token))
|
||||
if where is not None:
|
||||
items = items.filter(and_(Item.name.like(token_safe, escape="\\"), Category.ID == 16, where))
|
||||
else:
|
||||
@@ -348,7 +383,7 @@ def getVariations(itemids, groupIDs=None, where=None, eager=None):
|
||||
|
||||
itemfilter = or_(*(items_table.c.variationParentTypeID == itemid for itemid in itemids))
|
||||
filter = processWhere(itemfilter, where)
|
||||
vars = gamedata_session.query(Item).options(*processEager(eager)).filter(filter).all()
|
||||
vars = get_gamedata_session().query(Item).options(*processEager(eager)).filter(filter).all()
|
||||
|
||||
if vars:
|
||||
return vars
|
||||
@@ -356,7 +391,7 @@ def getVariations(itemids, groupIDs=None, where=None, eager=None):
|
||||
itemfilter = or_(*(groups_table.c.groupID == groupID for groupID in groupIDs))
|
||||
filter = processWhere(itemfilter, where)
|
||||
joinon = items_table.c.groupID == groups_table.c.groupID
|
||||
vars = gamedata_session.query(Item).options(*processEager(eager)).join((groups_table, joinon)).filter(
|
||||
vars = get_gamedata_session().query(Item).options(*processEager(eager)).join((groups_table, joinon)).filter(
|
||||
filter).all()
|
||||
|
||||
return vars
|
||||
@@ -371,7 +406,7 @@ def getAttributeInfo(attr, eager=None):
|
||||
else:
|
||||
raise TypeError("Need integer or string as argument")
|
||||
try:
|
||||
result = gamedata_session.query(AttributeInfo).options(*processEager(eager)).filter(filter).one()
|
||||
result = get_gamedata_session().query(AttributeInfo).options(*processEager(eager)).filter(filter).one()
|
||||
except exc.NoResultFound:
|
||||
result = None
|
||||
return result
|
||||
@@ -380,7 +415,7 @@ def getAttributeInfo(attr, eager=None):
|
||||
@cachedQuery(1, "field")
|
||||
def getMetaData(field):
|
||||
if isinstance(field, str):
|
||||
data = gamedata_session.query(MetaData).get(field)
|
||||
data = get_gamedata_session().query(MetaData).get(field)
|
||||
else:
|
||||
raise TypeError("Need string as argument")
|
||||
return data
|
||||
@@ -399,12 +434,12 @@ def directAttributeRequest(itemIDs, attrIDs):
|
||||
and_(Attribute.attributeID.in_(attrIDs), Item.typeID.in_(itemIDs)),
|
||||
from_obj=[join(Attribute, Item)])
|
||||
|
||||
result = gamedata_session.execute(q).fetchall()
|
||||
result = get_gamedata_session().execute(q).fetchall()
|
||||
return result
|
||||
|
||||
|
||||
def getAbyssalTypes():
|
||||
return set([r.resultingTypeID for r in gamedata_session.query(DynamicItem.resultingTypeID).distinct()])
|
||||
return set([r.resultingTypeID for r in get_gamedata_session().query(DynamicItem.resultingTypeID).distinct()])
|
||||
|
||||
|
||||
@cachedQuery(1, "itemID")
|
||||
@@ -412,9 +447,9 @@ def getDynamicItem(itemID, eager=None):
|
||||
try:
|
||||
if isinstance(itemID, int):
|
||||
if eager is None:
|
||||
result = gamedata_session.query(DynamicItem).filter(DynamicItem.ID == itemID).one()
|
||||
result = get_gamedata_session().query(DynamicItem).filter(DynamicItem.ID == itemID).one()
|
||||
else:
|
||||
result = gamedata_session.query(DynamicItem).options(*processEager(eager)).filter(DynamicItem.ID == itemID).one()
|
||||
result = get_gamedata_session().query(DynamicItem).options(*processEager(eager)).filter(DynamicItem.ID == itemID).one()
|
||||
else:
|
||||
raise TypeError("Need integer as argument")
|
||||
except exc.NoResultFound:
|
||||
@@ -422,23 +457,7 @@ def getDynamicItem(itemID, eager=None):
|
||||
return result
|
||||
|
||||
|
||||
def getRequiredFor(itemID, attrMapping):
|
||||
Attribute1 = aliased(Attribute)
|
||||
Attribute2 = aliased(Attribute)
|
||||
|
||||
skillToLevelClauses = []
|
||||
|
||||
for attrSkill, attrLevel in attrMapping.items():
|
||||
skillToLevelClauses.append(and_(Attribute1.attributeID == attrSkill, Attribute2.attributeID == attrLevel))
|
||||
|
||||
queryOr = or_(*skillToLevelClauses)
|
||||
|
||||
q = select((Attribute2.typeID, Attribute2.value),
|
||||
and_(Attribute1.value == itemID, queryOr),
|
||||
from_obj=[
|
||||
join(Attribute1, Attribute2, Attribute1.typeID == Attribute2.typeID)
|
||||
])
|
||||
|
||||
result = gamedata_session.execute(q).fetchall()
|
||||
|
||||
return result
|
||||
@cachedQuery(1, "lookfor")
|
||||
def getAllImplantSets():
|
||||
implantSets = get_gamedata_session().query(ImplantSet).all()
|
||||
return implantSets
|
||||
|
||||
@@ -1,11 +1,21 @@
|
||||
from sqlalchemy import Column, Table, Integer, String, ForeignKey
|
||||
from sqlalchemy.orm import mapper
|
||||
from sqlalchemy.orm import mapper, synonym
|
||||
|
||||
from eos.db import gamedata_meta
|
||||
from eos.gamedata import Traits
|
||||
import eos.config
|
||||
|
||||
traits_table = Table("invtraits", gamedata_meta,
|
||||
Column("typeID", Integer, ForeignKey("invtypes.typeID"), primary_key=True),
|
||||
Column("traitText", String))
|
||||
traits_table = Table(
|
||||
"invtraits",
|
||||
gamedata_meta,
|
||||
Column("typeID", Integer, ForeignKey("invtypes.typeID"), primary_key=True),
|
||||
*[Column("traitText{}".format(lang), String) for lang in eos.config.translation_mapping.values()],
|
||||
)
|
||||
|
||||
mapper(Traits, traits_table)
|
||||
mapper(
|
||||
Traits,
|
||||
traits_table,
|
||||
properties={
|
||||
"display": synonym("traitText{}".format(eos.config.lang)),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -22,11 +22,13 @@ from sqlalchemy.orm import mapper, synonym
|
||||
|
||||
from eos.db import gamedata_meta
|
||||
from eos.gamedata import Unit
|
||||
import eos.config
|
||||
|
||||
groups_table = Table("dgmunits", gamedata_meta,
|
||||
Column("unitID", Integer, primary_key=True),
|
||||
Column("unitName", String),
|
||||
Column("displayName", String))
|
||||
*[Column("displayName{}".format(lang), String) for lang in eos.config.translation_mapping.values()],
|
||||
)
|
||||
|
||||
mapper(Unit, groups_table,
|
||||
properties={
|
||||
|
||||
@@ -8,43 +8,25 @@ many upgrade files as there are database versions (version 5 would include
|
||||
upgrade files 1-5)
|
||||
"""
|
||||
|
||||
import pkgutil
|
||||
import re
|
||||
|
||||
from eos.utils.pyinst_support import iterNamespace
|
||||
|
||||
updates = {}
|
||||
appVersion = 0
|
||||
|
||||
prefix = __name__ + "."
|
||||
|
||||
# load modules to work based with and without pyinstaller
|
||||
# from: https://github.com/webcomics/dosage/blob/master/dosagelib/loader.py
|
||||
# see: https://github.com/pyinstaller/pyinstaller/issues/1905
|
||||
|
||||
# load modules using iter_modules()
|
||||
# (should find all filters in normal build, but not pyinstaller)
|
||||
module_names = [m[1] for m in pkgutil.iter_modules(__path__, prefix)]
|
||||
|
||||
# special handling for PyInstaller
|
||||
importers = map(pkgutil.get_importer, __path__)
|
||||
toc = set()
|
||||
for i in importers:
|
||||
if hasattr(i, 'toc'):
|
||||
toc |= i.toc
|
||||
|
||||
for elm in toc:
|
||||
if elm.startswith(prefix):
|
||||
module_names.append(elm)
|
||||
|
||||
for modname in module_names:
|
||||
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]
|
||||
module = __import__(modname, fromlist=True)
|
||||
m = re.match("^upgrade(?P<index>\d+)$", modname_tail)
|
||||
modname_tail = modName.rsplit('.', 1)[-1]
|
||||
m = re.match(r"^upgrade(?P<index>\d+)$", modname_tail)
|
||||
if not m:
|
||||
continue
|
||||
index = int(m.group("index"))
|
||||
appVersion = max(appVersion, index)
|
||||
module = __import__(modName, fromlist=True)
|
||||
upgrade = getattr(module, "upgrade", False)
|
||||
if upgrade:
|
||||
updates[index] = upgrade
|
||||
|
||||
@@ -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
|
||||
"""
|
||||
|
||||
|
||||
@@ -33,9 +33,13 @@ def upgrade(saveddata_engine):
|
||||
try:
|
||||
saveddata_session.execute(commandFits_table.insert(),
|
||||
{"boosterID": value, "boostedID": boosted, "active": 1})
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
raise
|
||||
except Exception:
|
||||
pass
|
||||
saveddata_session.commit()
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
raise
|
||||
except:
|
||||
# Shouldn't fail unless you have updated database without the old fleet schema and manually modify the database version
|
||||
# If it does, simply fail. Fleet data migration isn't critically important here
|
||||
|
||||
@@ -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,13 +4228,15 @@ 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]))
|
||||
|
||||
# And last but not least, delete the last subsystem
|
||||
saveddata_engine.execute("DELETE FROM modules WHERE ID = ?", (oldModules[4][0],))
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
raise
|
||||
except:
|
||||
# if something fails, fuck it, we tried. It'll default to the generic conversion below
|
||||
continue
|
||||
|
||||
@@ -6,16 +6,16 @@ Allow use of floats in damage pattern values
|
||||
|
||||
tmpTable = """
|
||||
CREATE TABLE "damagePatternsTemp" (
|
||||
"ID" INTEGER NOT NULL,
|
||||
"name" VARCHAR,
|
||||
"emAmount" FLOAT,
|
||||
"thermalAmount" FLOAT,
|
||||
"kineticAmount" FLOAT,
|
||||
"explosiveAmount" FLOAT,
|
||||
"ownerID" INTEGER,
|
||||
"created" DATETIME,
|
||||
"modified" DATETIME,
|
||||
PRIMARY KEY ("ID"),
|
||||
"ID" INTEGER NOT NULL,
|
||||
"name" VARCHAR,
|
||||
"emAmount" FLOAT,
|
||||
"thermalAmount" FLOAT,
|
||||
"kineticAmount" FLOAT,
|
||||
"explosiveAmount" FLOAT,
|
||||
"ownerID" INTEGER,
|
||||
"created" DATETIME,
|
||||
"modified" DATETIME,
|
||||
PRIMARY KEY ("ID"),
|
||||
FOREIGN KEY("ownerID") REFERENCES users ("ID")
|
||||
)
|
||||
"""
|
||||
|
||||
166
eos/db/migrations/upgrade35.py
Normal file
166
eos/db/migrations/upgrade35.py
Normal file
@@ -0,0 +1,166 @@
|
||||
"""
|
||||
Migration 35
|
||||
|
||||
- Remove builtin damage patterns and target profiles from the database
|
||||
"""
|
||||
|
||||
import sqlalchemy
|
||||
|
||||
|
||||
dmgPatterns = (
|
||||
'Uniform',
|
||||
'[Bombs]Concussion Bomb',
|
||||
'[Bombs]Electron Bomb',
|
||||
'[Bombs]Scorch Bomb',
|
||||
'[Bombs]Shrapnel Bomb',
|
||||
'[Exotic Plasma]Baryon',
|
||||
'[Exotic Plasma]Meson',
|
||||
'[Exotic Plasma]Tetryon',
|
||||
'[Exotic Plasma][T2] Mystic',
|
||||
'[Exotic Plasma][T2] Occult',
|
||||
'[Frequency Crystals]Gamma',
|
||||
'[Frequency Crystals]Infrared',
|
||||
'[Frequency Crystals]Microwave',
|
||||
'[Frequency Crystals]Multifrequency',
|
||||
'[Frequency Crystals]Radio',
|
||||
'[Frequency Crystals]Standard',
|
||||
'[Frequency Crystals]Ultraviolet',
|
||||
'[Frequency Crystals]Xray',
|
||||
'[Frequency Crystals][T2] Aurora',
|
||||
'[Frequency Crystals][T2] Conflagration',
|
||||
'[Frequency Crystals][T2] Gleam',
|
||||
'[Frequency Crystals][T2] Scorch',
|
||||
'[Generic]EM',
|
||||
'[Generic]Explosive',
|
||||
'[Generic]Kinetic',
|
||||
'[Generic]Thermal',
|
||||
'[Hybrid Charges]Antimatter',
|
||||
'[Hybrid Charges]Iridium',
|
||||
'[Hybrid Charges]Iron',
|
||||
'[Hybrid Charges]Lead',
|
||||
'[Hybrid Charges]Plutonium',
|
||||
'[Hybrid Charges]Thorium',
|
||||
'[Hybrid Charges]Tungsten',
|
||||
'[Hybrid Charges]Uranium',
|
||||
'[Hybrid Charges][T2] Javelin',
|
||||
'[Hybrid Charges][T2] Null',
|
||||
'[Hybrid Charges][T2] Spike',
|
||||
'[Hybrid Charges][T2] Void',
|
||||
'[Missiles]Inferno',
|
||||
'[Missiles]Mjolnir',
|
||||
'[Missiles]Nova',
|
||||
'[Missiles]Scourge',
|
||||
'[Missiles][Structure) Standup Missile',
|
||||
'[Missiles][Structure] Standup Missile',
|
||||
'[NPC][Asteroid] Angel Cartel',
|
||||
'[NPC][Asteroid] Blood Raiders',
|
||||
'[NPC][Asteroid] Guristas',
|
||||
'[NPC][Asteroid] Rogue Drone',
|
||||
'[NPC][Asteroid] Sanshas Nation',
|
||||
'[NPC][Asteroid] Serpentis',
|
||||
'[NPC][Burner] Ashimmu (Blood Raiders)',
|
||||
'[NPC][Burner] Cruor (Blood Raiders)',
|
||||
'[NPC][Burner] Daredevil (Serpentis)',
|
||||
'[NPC][Burner] Dramiel (Angel)',
|
||||
'[NPC][Burner] Enyo',
|
||||
'[NPC][Burner] Hawk',
|
||||
'[NPC][Burner] Jaguar',
|
||||
'[NPC][Burner] Sentinel',
|
||||
'[NPC][Burner] Succubus (Sanshas Nation)',
|
||||
'[NPC][Burner] Talos',
|
||||
'[NPC][Burner] Vengeance',
|
||||
'[NPC][Burner] Worm (Guristas)',
|
||||
'[NPC][Deadspace] Angel Cartel',
|
||||
'[NPC][Deadspace] Blood Raiders',
|
||||
'[NPC][Deadspace] Guristas',
|
||||
'[NPC][Deadspace] Rogue Drone',
|
||||
'[NPC][Deadspace] Sanshas Nation',
|
||||
'[NPC][Deadspace] Serpentis',
|
||||
'[NPC][Mission] Amarr Empire',
|
||||
'[NPC][Mission] CONCORD',
|
||||
'[NPC][Mission] Caldari State',
|
||||
'[NPC][Mission] Gallente Federation',
|
||||
'[NPC][Mission] Khanid',
|
||||
'[NPC][Mission] Minmatar Republic',
|
||||
'[NPC][Mission] Mordus Legion',
|
||||
'[NPC][Mission] Thukker',
|
||||
'[NPC][Other] Sansha Incursion',
|
||||
'[NPC][Other] Sleepers',
|
||||
'[Projectile Ammo]Carbonized Lead',
|
||||
'[Projectile Ammo]Depleted Uranium',
|
||||
'[Projectile Ammo]EMP',
|
||||
'[Projectile Ammo]Fusion',
|
||||
'[Projectile Ammo]Nuclear',
|
||||
'[Projectile Ammo]Phased Plasma',
|
||||
'[Projectile Ammo]Proton',
|
||||
'[Projectile Ammo]Titanium Sabot',
|
||||
'[Projectile Ammo][T2] Barrage',
|
||||
'[Projectile Ammo][T2] Hail',
|
||||
'[Projectile Ammo][T2] Quake',
|
||||
'[Projectile Ammo][T2] Tremor')
|
||||
|
||||
tgtProfiles = (
|
||||
'Uniform (25%)',
|
||||
'Uniform (50%)',
|
||||
'Uniform (75%)',
|
||||
'Uniform (90%)',
|
||||
'[NPC][Asteroid] Angel Cartel',
|
||||
'[NPC][Asteroid] Blood Raiders',
|
||||
'[NPC][Asteroid] Guristas',
|
||||
'[NPC][Asteroid] Rogue Drones',
|
||||
'[NPC][Asteroid] Sanshas Nation',
|
||||
'[NPC][Asteroid] Serpentis',
|
||||
'[NPC][Burner] Ashimmu (Blood Raiders)',
|
||||
'[NPC][Burner] Cruor (Blood Raiders)',
|
||||
'[NPC][Burner] Daredevil (Serpentis)',
|
||||
'[NPC][Burner] Dramiel (Angel)',
|
||||
'[NPC][Burner] Enyo',
|
||||
'[NPC][Burner] Hawk',
|
||||
'[NPC][Burner] Jaguar',
|
||||
'[NPC][Burner] Sentinel',
|
||||
'[NPC][Burner] Succubus (Sanshas Nation)',
|
||||
'[NPC][Burner] Talos',
|
||||
'[NPC][Burner] Vengeance',
|
||||
'[NPC][Burner] Worm (Guristas)',
|
||||
'[NPC][Deadspace] Angel Cartel',
|
||||
'[NPC][Deadspace] Blood Raiders',
|
||||
'[NPC][Deadspace] Guristas',
|
||||
'[NPC][Deadspace] Rogue Drones',
|
||||
'[NPC][Deadspace] Sanshas Nation',
|
||||
'[NPC][Deadspace] Serpentis',
|
||||
'[NPC][Mission] Amarr Empire',
|
||||
'[NPC][Mission] CONCORD',
|
||||
'[NPC][Mission] Caldari State',
|
||||
'[NPC][Mission] Gallente Federation',
|
||||
'[NPC][Mission] Khanid',
|
||||
'[NPC][Mission] Minmatar Republic',
|
||||
'[NPC][Mission] Mordus Legion',
|
||||
'[NPC][Other] Sansha Incursion',
|
||||
'[NPC][Other] Sleeper',
|
||||
'[T1 Resist]Armor',
|
||||
'[T1 Resist]Armor (+T2 DCU)',
|
||||
'[T1 Resist]Hull',
|
||||
'[T1 Resist]Hull (+T2 DCU)',
|
||||
'[T1 Resist]Shield',
|
||||
'[T1 Resist]Shield (+T2 DCU)',
|
||||
'[T2 Resist]Amarr (Armor)',
|
||||
'[T2 Resist]Amarr (Shield)',
|
||||
'[T2 Resist]Caldari (Armor)',
|
||||
'[T2 Resist]Caldari (Shield)',
|
||||
'[T2 Resist]Gallente (Armor)',
|
||||
'[T2 Resist]Gallente (Shield)',
|
||||
'[T2 Resist]Minmatar (Armor)',
|
||||
'[T2 Resist]Minmatar (Shield)')
|
||||
|
||||
|
||||
def upgrade(saveddata_engine):
|
||||
saveddata_engine.execute('DELETE FROM damagePatterns WHERE name in ({});'.format(', '.join('\'{}\''.format(n) for n in dmgPatterns)))
|
||||
saveddata_engine.execute('DELETE FROM targetResists WHERE name in ({});'.format(', '.join('\'{}\''.format(n) for n in tgtProfiles)))
|
||||
try:
|
||||
saveddata_engine.execute("SELECT builtinDamagePatternID FROM fits LIMIT 1")
|
||||
except sqlalchemy.exc.DatabaseError:
|
||||
saveddata_engine.execute("ALTER TABLE fits ADD COLUMN builtinDamagePatternID INT;")
|
||||
try:
|
||||
saveddata_engine.execute("SELECT builtinTargetResistsID FROM fits LIMIT 1")
|
||||
except sqlalchemy.exc.DatabaseError:
|
||||
saveddata_engine.execute("ALTER TABLE fits ADD COLUMN builtinTargetResistsID INT;")
|
||||
84
eos/db/migrations/upgrade36.py
Normal file
84
eos/db/migrations/upgrade36.py
Normal file
@@ -0,0 +1,84 @@
|
||||
"""
|
||||
Migration 36
|
||||
|
||||
- Shield Booster, Armor Repairer and Capacitor Transfer tiericide
|
||||
"""
|
||||
|
||||
CONVERSIONS = {
|
||||
6441: ( # Small Clarity Ward Enduring Shield Booster
|
||||
6443, # Small Converse Deflection Catalyzer
|
||||
),
|
||||
6437: ( # Small C5-L Compact Shield Booster
|
||||
6439, # Small Neutron Saturation Injector I
|
||||
),
|
||||
10868: ( # Medium Clarity Ward Enduring Shield Booster
|
||||
10870, # Medium Converse Deflection Catalyzer
|
||||
),
|
||||
10872: ( # Medium C5-L Compact Shield Booster
|
||||
10866, # Medium Neutron Saturation Injector I
|
||||
),
|
||||
10876: ( # Large Clarity Ward Enduring Shield Booster
|
||||
10878, # Large Converse Deflection Catalyzer
|
||||
),
|
||||
10880: ( # Large C5-L Compact Shield Booster
|
||||
10874, # Large Neutron Saturation Injector I
|
||||
),
|
||||
10884: ( # X-Large Clarity Ward Enduring Shield Booster
|
||||
10886, # X-Large Converse Deflection Catalyzer
|
||||
),
|
||||
10888: ( # X-Large C5-L Compact Shield Booster
|
||||
10882, # X-Large Neutron Saturation Injector I
|
||||
),
|
||||
4533: ( # Small ACM Compact Armor Repairer
|
||||
4531, # Small Inefficient Armor Repair Unit
|
||||
),
|
||||
4529: ( # Small I-a Enduring Armor Repairer
|
||||
4535, # Small Automated Carapace Restoration
|
||||
),
|
||||
4573: ( # Medium ACM Compact Armor Repairer
|
||||
4571, # Medium Inefficient Armor Repair Unit
|
||||
),
|
||||
4569: ( # Medium I-a Enduring Armor Repairer
|
||||
4575, # Medium Automated Carapace Restoration
|
||||
),
|
||||
22889: ( # 'Meditation' Medium Armor Repairer I
|
||||
4579, # Medium Nano Armor Repair Unit I
|
||||
),
|
||||
4613: ( # Large ACM Compact Armor Repairer
|
||||
4611, # Large Inefficient Armor Repair Unit
|
||||
),
|
||||
4609: ( # Large I-a Enduring Armor Repairer
|
||||
4615, # Large Automated Carapace Restoration
|
||||
),
|
||||
22891: ( # 'Protest' Large Armor Repairer I
|
||||
4621, # Large 'Reprieve' Vestment Reconstructer I
|
||||
),
|
||||
5093: ( # Small Radiative Scoped Remote Capacitor Transmitter
|
||||
5087, # Small Partial E95a Remote Capacitor Transmitter
|
||||
),
|
||||
5091: ( # Small Inductive Compact Remote Capacitor Transmitter
|
||||
5089, # Small Murky Remote Capacitor Transmitter
|
||||
),
|
||||
16489: ( # Medium Radiative Scoped Remote Capacitor Transmitter
|
||||
16493, # Medium Partial E95b Remote Capacitor Transmitter
|
||||
),
|
||||
16495: ( # Medium Inductive Compact Remote Capacitor Transmitter
|
||||
16491, # Medium Murky Remote Capacitor Transmitter
|
||||
),
|
||||
16481: ( # Large Radiative Scoped Remote Capacitor Transmitter
|
||||
16485, # Large Partial E95c Remote Capacitor Transmitter
|
||||
),
|
||||
16487: ( # Large Inductive Compact Remote Capacitor Transmitter
|
||||
16483, # Large Murky Remote Capacitor Transmitter
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
def upgrade(saveddata_engine):
|
||||
# Convert modules
|
||||
for replacement_item, list in CONVERSIONS.items():
|
||||
for retired_item in list:
|
||||
saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?',
|
||||
(replacement_item, retired_item))
|
||||
saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?',
|
||||
(replacement_item, retired_item))
|
||||
44
eos/db/migrations/upgrade37.py
Normal file
44
eos/db/migrations/upgrade37.py
Normal file
@@ -0,0 +1,44 @@
|
||||
"""
|
||||
Migration 37
|
||||
|
||||
- Capacitor Booster tiericide
|
||||
"""
|
||||
|
||||
CONVERSIONS = {
|
||||
4959: ( # 'Seed' Micro Capacitor Booster I
|
||||
4957, # Micro Brief Capacitor Overcharge I
|
||||
4961, # Micro Tapered Capacitor Infusion I
|
||||
4955, # Micro F-RX Prototype Capacitor Boost
|
||||
3556, # Micro Capacitor Booster I
|
||||
3558, # Micro Capacitor Booster II
|
||||
15774, # Ammatar Navy Micro Capacitor Booster
|
||||
14180, # Dark Blood Micro Capacitor Booster
|
||||
14182, # True Sansha Micro Capacitor Booster
|
||||
15782, # Imperial Navy Micro Capacitor Booster
|
||||
),
|
||||
5011: ( # Small F-RX Compact Capacitor Booster
|
||||
5009, # Small Brief Capacitor Overcharge I
|
||||
5013, # Small Tapered Capacitor Infusion I
|
||||
5007, # Small F-RX Prototype Capacitor Boost
|
||||
),
|
||||
4833: ( # Medium F-RX Compact Capacitor Booster
|
||||
4831, # Medium Brief Capacitor Overcharge I
|
||||
4835, # Medium Tapered Capacitor Infusion I
|
||||
4829, # Medium F-RX Prototype Capacitor Boost
|
||||
),
|
||||
5051: ( # Heavy F-RX Compact Capacitor Booster
|
||||
5049, # Heavy Brief Capacitor Overcharge I
|
||||
5053, # Heavy Tapered Capacitor Infusion I
|
||||
5047, # Heavy F-RX Prototype Capacitor Boost
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
def upgrade(saveddata_engine):
|
||||
# Convert modules
|
||||
for replacement_item, list in CONVERSIONS.items():
|
||||
for retired_item in list:
|
||||
saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?',
|
||||
(replacement_item, retired_item))
|
||||
saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?',
|
||||
(replacement_item, retired_item))
|
||||
42
eos/db/migrations/upgrade38.py
Normal file
42
eos/db/migrations/upgrade38.py
Normal file
@@ -0,0 +1,42 @@
|
||||
"""
|
||||
Migration 38
|
||||
|
||||
- Armor hardener tiericide
|
||||
"""
|
||||
|
||||
CONVERSIONS = {
|
||||
16357: ( # Experimental Enduring EM Armor Hardener I
|
||||
16353, # Upgraded Armor EM Hardener I
|
||||
),
|
||||
16365: ( # Experimental Enduring Explosive Armor Hardener I
|
||||
16361, # Upgraded Armor Explosive Hardener I
|
||||
),
|
||||
16373: ( # Experimental Enduring Kinetic Armor Hardener I
|
||||
16369, # Upgraded Armor Kinetic Hardener I
|
||||
),
|
||||
16381: ( # Experimental Enduring Thermal Armor Hardener I
|
||||
16377, # Upgraded Armor Thermal Hardener I
|
||||
),
|
||||
16359: ( # Prototype Compact EM Armor Hardener I
|
||||
16355, # Limited Armor EM Hardener I
|
||||
),
|
||||
16367: ( # Prototype Compact Explosive Armor Hardener I
|
||||
16363, # Limited Armor Explosive Hardener I
|
||||
),
|
||||
16375: ( # Prototype Compact Kinetic Armor Hardener I
|
||||
16371, # Limited Armor Kinetic Hardener I
|
||||
),
|
||||
16383: ( # Prototype Compact Thermal Armor Hardener I
|
||||
16379, # Limited Armor Thermal Hardener I
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
def upgrade(saveddata_engine):
|
||||
# Convert modules
|
||||
for replacement_item, list in CONVERSIONS.items():
|
||||
for retired_item in list:
|
||||
saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?',
|
||||
(replacement_item, retired_item))
|
||||
saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?',
|
||||
(replacement_item, retired_item))
|
||||
34
eos/db/migrations/upgrade39.py
Normal file
34
eos/db/migrations/upgrade39.py
Normal file
@@ -0,0 +1,34 @@
|
||||
"""
|
||||
Migration 39
|
||||
|
||||
- Shield amplifier tiericide
|
||||
- CCP getting rid of DB TDs due to exploits
|
||||
"""
|
||||
|
||||
CONVERSIONS = {
|
||||
1798: ( # 'Basic' EM Shield Amplifier
|
||||
9562, # Supplemental EM Ward Amplifier
|
||||
),
|
||||
1804: ( # 'Basic' Explosive Shield Amplifier
|
||||
9574, # Supplemental Explosive Deflection Amplifier
|
||||
),
|
||||
1802: ( # 'Basic' Kinetic Shield Amplifier
|
||||
9570, # Supplemental Kinetic Deflection Amplifier
|
||||
),
|
||||
1800: ( # 'Basic' Thermal Shield Amplifier
|
||||
9566, # Supplemental Thermal Dissipation Amplifier
|
||||
),
|
||||
22933: ( # 'Investor' Tracking Disruptor I
|
||||
32416, # Dark Blood Tracking Disruptor
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
def upgrade(saveddata_engine):
|
||||
# Convert modules
|
||||
for replacement_item, list in CONVERSIONS.items():
|
||||
for retired_item in list:
|
||||
saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?',
|
||||
(replacement_item, retired_item))
|
||||
saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?',
|
||||
(replacement_item, retired_item))
|
||||
@@ -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
|
||||
"""
|
||||
|
||||
|
||||
18
eos/db/migrations/upgrade40.py
Normal file
18
eos/db/migrations/upgrade40.py
Normal file
@@ -0,0 +1,18 @@
|
||||
"""
|
||||
Migration 40
|
||||
|
||||
Imports all item conversions since Migration 28 and runs them against module.baseItemID. This column seems to have been
|
||||
forgotten about since it's been added.
|
||||
|
||||
"""
|
||||
from .upgrade36 import CONVERSIONS as u36
|
||||
from .upgrade37 import CONVERSIONS as u37
|
||||
from .upgrade38 import CONVERSIONS as u38
|
||||
from .upgrade39 import CONVERSIONS as u39
|
||||
|
||||
def upgrade(saveddata_engine):
|
||||
for conversions in [u36, u37, u38, u39]:
|
||||
for replacement_item, list in conversions.items():
|
||||
for retired_item in list:
|
||||
saveddata_engine.execute('UPDATE "modules" SET "baseItemID" = ? WHERE "baseItemID" = ?',
|
||||
(replacement_item, retired_item))
|
||||
50
eos/db/migrations/upgrade41.py
Normal file
50
eos/db/migrations/upgrade41.py
Normal file
@@ -0,0 +1,50 @@
|
||||
"""
|
||||
Migration 41
|
||||
|
||||
- Resistance plating tiericide
|
||||
"""
|
||||
|
||||
CONVERSIONS = {
|
||||
16345: ( # Upgraded Layered Coating I
|
||||
16347, # Limited Layered Plating I
|
||||
16349, # 'Scarab' Layered Plating I
|
||||
16351, # 'Grail' Layered Plating I
|
||||
),
|
||||
16305: ( # Upgraded Multispectrum Coating I
|
||||
16307, # Limited Adaptive Nano Plating I
|
||||
16309, # 'Collateral' Adaptive Nano Plating I
|
||||
16311, # 'Refuge' Adaptive Nano Plating I
|
||||
),
|
||||
16329: ( # Upgraded EM Coating I
|
||||
16331, # Limited EM Plating I
|
||||
16333, # 'Contour' EM Plating I
|
||||
16335, # 'Spiegel' EM Plating I
|
||||
),
|
||||
16321: ( # Upgraded Explosive Coating I
|
||||
16323, # Limited Explosive Plating I
|
||||
16325, # Experimental Explosive Plating I
|
||||
16319, # 'Aegis' Explosive Plating I
|
||||
),
|
||||
16313: ( # Upgraded Kinetic Coating I
|
||||
16315, # Limited Kinetic Plating I
|
||||
16317, # Experimental Kinetic Plating I
|
||||
16327, # 'Element' Kinetic Plating I
|
||||
),
|
||||
16337: ( # Upgraded Thermal Coating I
|
||||
16339, # Limited Thermal Plating I
|
||||
16341, # Experimental Thermal Plating I
|
||||
16343, # Prototype Thermal Plating I
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
def upgrade(saveddata_engine):
|
||||
# Convert modules
|
||||
for replacement_item, list in CONVERSIONS.items():
|
||||
for retired_item in list:
|
||||
saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?',
|
||||
(replacement_item, retired_item))
|
||||
saveddata_engine.execute('UPDATE "modules" SET "baseItemID" = ? WHERE "baseItemID" = ?',
|
||||
(replacement_item, retired_item))
|
||||
saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?',
|
||||
(replacement_item, retired_item))
|
||||
50
eos/db/migrations/upgrade42.py
Normal file
50
eos/db/migrations/upgrade42.py
Normal file
@@ -0,0 +1,50 @@
|
||||
"""
|
||||
Migration 42
|
||||
|
||||
- Resistance membrane tiericide
|
||||
"""
|
||||
|
||||
CONVERSIONS = {
|
||||
16391: ( # Compact Multispectrum Energized Membrane
|
||||
16389, # Experimental Energized Adaptive Nano Membrane I
|
||||
16387, # Limited Energized Adaptive Nano Membrane I
|
||||
16385, # Upgraded Energized Adaptive Nano Membrane I
|
||||
),
|
||||
16423: ( # Compact Layered Energized Membrane
|
||||
16421, # Experimental Energized Armor Layering Membrane I
|
||||
16419, # Limited Energized Armor Layering Membrane I
|
||||
16417, # Upgraded Energized Armor Layering Membrane I
|
||||
),
|
||||
16415: ( # Compact EM Energized Membrane
|
||||
16413, # Experimental Energized EM Membrane I
|
||||
16411, # Limited Energized EM Membrane I
|
||||
16409, # Upgraded Energized EM Membrane I
|
||||
),
|
||||
16407: ( # Compact Explosive Energized Membrane
|
||||
16405, # Experimental Energized Explosive Membrane I
|
||||
16403, # Limited Energized Explosive Membrane I
|
||||
16401, # Upgraded Energized Explosive Membrane I
|
||||
),
|
||||
16399: ( # Compact Kinetic Energized Membrane
|
||||
16397, # Experimental Energized Kinetic Membrane I
|
||||
16395, # Limited Energized Kinetic Membrane I
|
||||
16393, # Upgraded Energized Kinetic Membrane I
|
||||
),
|
||||
16431: ( # Compact Thermal Energized Membrane
|
||||
16429, # Experimental Energized Thermal Membrane I
|
||||
16427, # Limited Energized Thermal Membrane I
|
||||
16425, # Upgraded Energized Thermal Membrane I
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
def upgrade(saveddata_engine):
|
||||
# Convert modules
|
||||
for replacement_item, list in CONVERSIONS.items():
|
||||
for retired_item in list:
|
||||
saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?',
|
||||
(replacement_item, retired_item))
|
||||
saveddata_engine.execute('UPDATE "modules" SET "baseItemID" = ? WHERE "baseItemID" = ?',
|
||||
(replacement_item, retired_item))
|
||||
saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?',
|
||||
(replacement_item, retired_item))
|
||||
26
eos/db/migrations/upgrade43.py
Normal file
26
eos/db/migrations/upgrade43.py
Normal file
@@ -0,0 +1,26 @@
|
||||
"""
|
||||
Migration 43
|
||||
|
||||
- Shield booster amplifier tiericide
|
||||
"""
|
||||
|
||||
CONVERSIONS = {
|
||||
16533: ( # Stalwart Restrained Shield Boost Amplifier
|
||||
16531, # 5a Prototype Shield Support I
|
||||
),
|
||||
16535: ( # Copasetic Compact Shield Boost Amplifier
|
||||
16529, # Ionic Field Accelerator I
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def upgrade(saveddata_engine):
|
||||
# Convert modules
|
||||
for replacement_item, list in CONVERSIONS.items():
|
||||
for retired_item in list:
|
||||
saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?',
|
||||
(replacement_item, retired_item))
|
||||
saveddata_engine.execute('UPDATE "modules" SET "baseItemID" = ? WHERE "baseItemID" = ?',
|
||||
(replacement_item, retired_item))
|
||||
saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?',
|
||||
(replacement_item, retired_item))
|
||||
25
eos/db/migrations/upgrade44.py
Normal file
25
eos/db/migrations/upgrade44.py
Normal file
@@ -0,0 +1,25 @@
|
||||
"""
|
||||
Migration 44
|
||||
|
||||
- Signal distortion amplifier tiericide
|
||||
"""
|
||||
|
||||
CONVERSIONS = {
|
||||
25565: ( # Hypnos Compact Signal Distortion Amplifier I
|
||||
25571, # Initiated Signal Distortion Amplifier I
|
||||
25569, # Induced Signal Distortion Amplifier I
|
||||
25567, # Compulsive Signal Distortion Amplifier I
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def upgrade(saveddata_engine):
|
||||
# Convert modules
|
||||
for replacement_item, list in CONVERSIONS.items():
|
||||
for retired_item in list:
|
||||
saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?',
|
||||
(replacement_item, retired_item))
|
||||
saveddata_engine.execute('UPDATE "modules" SET "baseItemID" = ? WHERE "baseItemID" = ?',
|
||||
(replacement_item, retired_item))
|
||||
saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?',
|
||||
(replacement_item, retired_item))
|
||||
18
eos/db/migrations/upgrade45.py
Normal file
18
eos/db/migrations/upgrade45.py
Normal file
@@ -0,0 +1,18 @@
|
||||
"""
|
||||
Migration 45
|
||||
|
||||
- Drone mutaplasmid support
|
||||
"""
|
||||
|
||||
import sqlalchemy
|
||||
|
||||
|
||||
def upgrade(saveddata_engine):
|
||||
try:
|
||||
saveddata_engine.execute("SELECT baseItemID FROM drones LIMIT 1")
|
||||
except sqlalchemy.exc.DatabaseError:
|
||||
saveddata_engine.execute("ALTER TABLE drones ADD COLUMN baseItemID INTEGER;")
|
||||
try:
|
||||
saveddata_engine.execute("SELECT mutaplasmidID FROM drones LIMIT 1")
|
||||
except sqlalchemy.exc.DatabaseError:
|
||||
saveddata_engine.execute("ALTER TABLE drones ADD COLUMN mutaplasmidID INTEGER;")
|
||||
68
eos/db/migrations/upgrade46.py
Normal file
68
eos/db/migrations/upgrade46.py
Normal file
@@ -0,0 +1,68 @@
|
||||
"""
|
||||
Migration 46
|
||||
|
||||
- Mining crystal changes
|
||||
"""
|
||||
|
||||
CONVERSIONS = {
|
||||
60276: ( # Simple Asteroid Mining Crystal Type A I
|
||||
18066, # Veldspar Mining Crystal I
|
||||
18062, # Scordite Mining Crystal I
|
||||
18060, # Pyroxeres Mining Crystal I
|
||||
18058, # Plagioclase Mining Crystal I
|
||||
),
|
||||
60281: ( # Simple Asteroid Mining Crystal Type A II
|
||||
18618, # Veldspar Mining Crystal II
|
||||
18616, # Scordite Mining Crystal II
|
||||
18614, # Pyroxeres Mining Crystal II
|
||||
18612, # Plagioclase Mining Crystal II
|
||||
),
|
||||
60285: ( # Coherent Asteroid Mining Crystal Type A I
|
||||
18056, # Omber Mining Crystal I
|
||||
18052, # Kernite Mining Crystal I
|
||||
18050, # Jaspet Mining Crystal I
|
||||
18048, # Hemorphite Mining Crystal I
|
||||
18046, # Hedbergite Mining Crystal I
|
||||
),
|
||||
60288: ( # Coherent Asteroid Mining Crystal Type A II
|
||||
18610, # Omber Mining Crystal II
|
||||
18604, # Jaspet Mining Crystal II
|
||||
18606, # Kernite Mining Crystal II
|
||||
18600, # Hedbergite Mining Crystal II
|
||||
18602, # Hemorphite Mining Crystal II
|
||||
),
|
||||
60291: ( # Variegated Asteroid Mining Crystal Type A I
|
||||
18044, # Gneiss Mining Crystal I
|
||||
18042, # Dark Ochre Mining Crystal I
|
||||
18040, # Crokite Mining Crystal I
|
||||
),
|
||||
60294: ( # Variegated Asteroid Mining Crystal Type A II
|
||||
18598, # Gneiss Mining Crystal II
|
||||
18596, # Dark Ochre Mining Crystal II
|
||||
18594, # Crokite Mining Crystal II
|
||||
),
|
||||
60297: ( # Complex Asteroid Mining Crystal Type A I
|
||||
18038, # Bistot Mining Crystal I
|
||||
18036, # Arkonor Mining Crystal I
|
||||
18064, # Spodumain Mining Crystal I
|
||||
),
|
||||
60300: ( # Complex Asteroid Mining Crystal Type A II
|
||||
18592, # Bistot Mining Crystal II
|
||||
18590, # Arkonor Mining Crystal II
|
||||
18624, # Spodumain Mining Crystal II
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def upgrade(saveddata_engine):
|
||||
# Convert modules
|
||||
for replacement_item, list in CONVERSIONS.items():
|
||||
for retired_item in list:
|
||||
saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?',
|
||||
(replacement_item, retired_item))
|
||||
saveddata_engine.execute('UPDATE "modules" SET "baseItemID" = ? WHERE "baseItemID" = ?',
|
||||
(replacement_item, retired_item))
|
||||
saveddata_engine.execute('UPDATE "modules" SET "chargeID" = ? WHERE "chargeID" = ?',
|
||||
(replacement_item, retired_item))
|
||||
saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?',
|
||||
(replacement_item, retired_item))
|
||||
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;")
|
||||
@@ -1,7 +1,8 @@
|
||||
__all__ = [
|
||||
"character",
|
||||
"fit",
|
||||
"mutator",
|
||||
"mutatorMod",
|
||||
"mutatorDrone",
|
||||
"module",
|
||||
"user",
|
||||
"skill",
|
||||
@@ -13,6 +14,5 @@ __all__ = [
|
||||
"miscData",
|
||||
"targetProfile",
|
||||
"override",
|
||||
"implantSet",
|
||||
"loadDefaultDatabaseValues"
|
||||
"implantSet"
|
||||
]
|
||||
|
||||
@@ -24,11 +24,12 @@ import datetime
|
||||
from eos.db import saveddata_meta
|
||||
from eos.saveddata.booster import Booster
|
||||
from eos.saveddata.boosterSideEffect import BoosterSideEffect
|
||||
from eos.saveddata.fit import Fit
|
||||
|
||||
boosters_table = Table("boosters", saveddata_meta,
|
||||
Column("ID", Integer, primary_key=True),
|
||||
Column("itemID", Integer),
|
||||
Column("fitID", Integer, ForeignKey("fits.ID"), nullable=False),
|
||||
Column("fitID", Integer, ForeignKey("fits.ID"), nullable=False, index=True),
|
||||
Column("active", Boolean),
|
||||
Column("created", DateTime, nullable=True, default=datetime.datetime.now),
|
||||
Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now),
|
||||
@@ -44,6 +45,7 @@ booster_side_effect_table = Table("boosterSideEffects", saveddata_meta,
|
||||
|
||||
mapper(Booster, boosters_table,
|
||||
properties={
|
||||
"owner": relation(Fit),
|
||||
"_Booster__sideEffects": relation(
|
||||
BoosterSideEffect,
|
||||
backref="booster",
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -24,16 +24,20 @@ import datetime
|
||||
from eos.db import saveddata_meta
|
||||
from eos.saveddata.damagePattern import DamagePattern
|
||||
|
||||
damagePatterns_table = Table("damagePatterns", saveddata_meta,
|
||||
Column("ID", Integer, primary_key=True),
|
||||
Column("name", String),
|
||||
Column("emAmount", Float),
|
||||
Column("thermalAmount", Float),
|
||||
Column("kineticAmount", Float),
|
||||
Column("explosiveAmount", Float),
|
||||
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)
|
||||
)
|
||||
damagePatterns_table = Table(
|
||||
'damagePatterns',
|
||||
saveddata_meta,
|
||||
Column('ID', Integer, primary_key=True),
|
||||
Column('name', String),
|
||||
Column('emAmount', Float),
|
||||
Column('thermalAmount', Float),
|
||||
Column('kineticAmount', Float),
|
||||
Column('explosiveAmount', Float),
|
||||
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))
|
||||
|
||||
mapper(DamagePattern, damagePatterns_table)
|
||||
mapper(
|
||||
DamagePattern,
|
||||
damagePatterns_table,
|
||||
properties={'rawName': damagePatterns_table.c.name})
|
||||
|
||||
@@ -18,27 +18,35 @@
|
||||
# ===============================================================================
|
||||
|
||||
from sqlalchemy import Table, Column, Integer, Float, ForeignKey, Boolean, DateTime
|
||||
from sqlalchemy.orm import mapper, relation
|
||||
from sqlalchemy.orm import mapper, relation, synonym
|
||||
from sqlalchemy.orm.collections import attribute_mapped_collection
|
||||
import datetime
|
||||
|
||||
from eos.db import saveddata_meta
|
||||
from eos.saveddata.drone import Drone
|
||||
from eos.saveddata.fit import Fit
|
||||
from eos.saveddata.mutator import MutatorDrone
|
||||
|
||||
drones_table = Table("drones", saveddata_meta,
|
||||
Column("groupID", Integer, primary_key=True),
|
||||
Column("fitID", Integer, ForeignKey("fits.ID"), nullable=False, index=True),
|
||||
Column("itemID", Integer, nullable=False),
|
||||
Column("baseItemID", Integer, nullable=True),
|
||||
Column("mutaplasmidID", Integer, nullable=True),
|
||||
Column("amount", Integer, nullable=False),
|
||||
Column("amountActive", Integer, nullable=False),
|
||||
Column("projected", Boolean, default=False),
|
||||
Column("created", DateTime, nullable=True, default=datetime.datetime.now),
|
||||
Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now),
|
||||
Column("projectionRange", Float, nullable=True)
|
||||
)
|
||||
Column("projectionRange", Float, nullable=True))
|
||||
|
||||
|
||||
mapper(Drone, drones_table,
|
||||
properties={
|
||||
"owner": relation(Fit)
|
||||
}
|
||||
)
|
||||
"ID": synonym("groupID"),
|
||||
"owner": relation(Fit),
|
||||
"mutators": relation(
|
||||
MutatorDrone,
|
||||
backref="item",
|
||||
cascade="all,delete-orphan",
|
||||
collection_class=attribute_mapped_collection('attrID'))})
|
||||
|
||||
@@ -53,15 +53,18 @@ fits_table = Table("fits", saveddata_meta,
|
||||
Column("timestamp", Integer, nullable=False),
|
||||
Column("characterID", ForeignKey("characters.ID"), nullable=True),
|
||||
Column("damagePatternID", ForeignKey("damagePatterns.ID"), nullable=True),
|
||||
Column("builtinDamagePatternID", Integer, nullable=True),
|
||||
Column("booster", Boolean, nullable=False, index=True, default=0),
|
||||
Column("targetResistsID", ForeignKey("targetResists.ID"), nullable=True),
|
||||
Column("builtinTargetResistsID", Integer, nullable=True),
|
||||
Column("modeID", Integer, nullable=True),
|
||||
Column("implantLocation", Integer, nullable=False),
|
||||
Column("notes", String, nullable=True),
|
||||
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,
|
||||
@@ -173,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,
|
||||
@@ -188,38 +192,37 @@ mapper(es_Fit, fits_table,
|
||||
"_Fit__boosters": relation(
|
||||
Booster,
|
||||
collection_class=HandledBoosterList,
|
||||
cascade='all, delete, delete-orphan',
|
||||
backref='owner',
|
||||
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,
|
||||
@@ -233,8 +236,10 @@ mapper(es_Fit, fits_table,
|
||||
"_Fit__character": relation(
|
||||
Character,
|
||||
backref="fits"),
|
||||
"_Fit__damagePattern": relation(DamagePattern),
|
||||
"_Fit__targetProfile": relation(TargetProfile),
|
||||
"_Fit__userDamagePattern": relation(DamagePattern),
|
||||
"_Fit__builtinDamagePatternID": fits_table.c.builtinDamagePatternID,
|
||||
"_Fit__userTargetProfile": relation(TargetProfile),
|
||||
"_Fit__builtinTargetProfileID": fits_table.c.builtinTargetResistsID,
|
||||
"projectedOnto": projectedFitSourceRel,
|
||||
"victimOf": relationship(
|
||||
ProjectedFit,
|
||||
|
||||
@@ -1,248 +0,0 @@
|
||||
# ===============================================================================
|
||||
# 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/>.
|
||||
# ===============================================================================
|
||||
|
||||
|
||||
import eos.db
|
||||
from eos.saveddata.damagePattern import DamagePattern as es_DamagePattern
|
||||
from eos.saveddata.targetProfile import TargetProfile as es_TargetProfile
|
||||
|
||||
|
||||
class ImportError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class DefaultDatabaseValues:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
instance = None
|
||||
|
||||
@classmethod
|
||||
def importDamageProfileDefaults(cls):
|
||||
damageProfileList = [["Uniform", 25, 25, 25, 25],
|
||||
["[Generic]EM", 1, 0, 0, 0],
|
||||
["[Generic]Thermal", 0, 1, 0, 0],
|
||||
["[Generic]Kinetic", 0, 0, 1, 0],
|
||||
["[Generic]Explosive", 0, 0, 0, 1],
|
||||
["[NPC][Asteroid] Blood Raiders", 5067, 4214, 0, 0],
|
||||
["[Bombs]Electron Bomb", 6400, 0, 0, 0],
|
||||
["[Bombs]Scorch Bomb", 0, 6400, 0, 0],
|
||||
["[Bombs]Concussion Bomb", 0, 0, 6400, 0],
|
||||
["[Bombs]Shrapnel Bomb", 0, 0, 0, 6400],
|
||||
["[Frequency Crystals][T2] Conflagration", 7.7, 7.7, 0, 0],
|
||||
["[Frequency Crystals][T2] Scorch", 9, 2, 0, 0],
|
||||
["[Frequency Crystals][T2] Gleam", 7, 7, 0, 0],
|
||||
["[Frequency Crystals][T2] Aurora", 5, 3, 0, 0],
|
||||
["[Frequency Crystals]Multifrequency", 7, 5, 0, 0],
|
||||
["[Frequency Crystals]Gamma", 7, 4, 0, 0],
|
||||
["[Frequency Crystals]Xray", 6, 4, 0, 0],
|
||||
["[Frequency Crystals]Ultraviolet", 6, 3, 0, 0],
|
||||
["[Frequency Crystals]Standard", 5, 3, 0, 0],
|
||||
["[Frequency Crystals]Infrared", 5, 2, 0, 0],
|
||||
["[Frequency Crystals]Microwave", 4, 2, 0, 0],
|
||||
["[Frequency Crystals]Radio", 5, 0, 0, 0],
|
||||
["[Hybrid Charges][T2] Void", 0, 7.7, 7.7, 0],
|
||||
["[Hybrid Charges][T2] Null", 0, 6, 5, 0],
|
||||
["[Hybrid Charges][T2] Javelin", 0, 8, 6, 0],
|
||||
["[Hybrid Charges][T2] Spike", 0, 4, 4, 0],
|
||||
["[Hybrid Charges]Antimatter", 0, 5, 7, 0],
|
||||
["[Hybrid Charges]Plutonium", 0, 5, 6, 0],
|
||||
["[Hybrid Charges]Uranium", 0, 4, 6, 0],
|
||||
["[Hybrid Charges]Thorium", 0, 4, 5, 0],
|
||||
["[Hybrid Charges]Lead", 0, 3, 5, 0],
|
||||
["[Hybrid Charges]Iridium", 0, 3, 4, 0],
|
||||
["[Hybrid Charges]Tungsten", 0, 2, 4, 0],
|
||||
["[Hybrid Charges]Iron", 0, 2, 3, 0],
|
||||
["[Missiles]Mjolnir", 1, 0, 0, 0],
|
||||
["[Missiles]Inferno", 0, 1, 0, 0],
|
||||
["[Missiles]Scourge", 0, 0, 1, 0],
|
||||
["[Missiles]Nova", 0, 0, 0, 1],
|
||||
["[Missiles][Structure] Standup Missile", 1, 1, 1, 1],
|
||||
["[Projectile Ammo][T2] Hail", 0, 0, 3.3, 12.1],
|
||||
["[Projectile Ammo][T2] Barrage", 0, 0, 5, 6],
|
||||
["[Projectile Ammo][T2] Quake", 0, 0, 5, 9],
|
||||
["[Projectile Ammo][T2] Tremor", 0, 0, 3, 5],
|
||||
["[Projectile Ammo]EMP", 9, 0, 1, 2],
|
||||
["[Projectile Ammo]Phased Plasma", 0, 10, 2, 0],
|
||||
["[Projectile Ammo]Fusion", 0, 0, 2, 10],
|
||||
["[Projectile Ammo]Depleted Uranium", 0, 3, 2, 3],
|
||||
["[Projectile Ammo]Titanium Sabot", 0, 0, 6, 2],
|
||||
["[Projectile Ammo]Proton", 3, 0, 2, 0],
|
||||
["[Projectile Ammo]Carbonized Lead", 0, 0, 4, 1],
|
||||
["[Projectile Ammo]Nuclear", 0, 0, 1, 4],
|
||||
# Different sizes of plasma do different damage, the values here are
|
||||
# average of proportions across sizes
|
||||
["[Exotic Plasma][T2] Occult", 0, 55863, 0, 44137],
|
||||
["[Exotic Plasma][T2] Mystic", 0, 66319, 0, 33681],
|
||||
["[Exotic Plasma]Tetryon", 0, 69208, 0, 30792],
|
||||
["[Exotic Plasma]Baryon", 0, 59737, 0, 40263],
|
||||
["[Exotic Plasma]Meson", 0, 60519, 0, 39481],
|
||||
["[NPC][Burner] Cruor (Blood Raiders)", 90, 90, 0, 0],
|
||||
["[NPC][Burner] Dramiel (Angel)", 55, 0, 20, 96],
|
||||
["[NPC][Burner] Daredevil (Serpentis)", 0, 110, 154, 0],
|
||||
["[NPC][Burner] Succubus (Sanshas Nation)", 135, 30, 0, 0],
|
||||
["[NPC][Burner] Worm (Guristas)", 0, 0, 228, 0],
|
||||
["[NPC][Burner] Enyo", 0, 147, 147, 0],
|
||||
["[NPC][Burner] Hawk", 0, 0, 247, 0],
|
||||
["[NPC][Burner] Jaguar", 36, 0, 50, 182],
|
||||
["[NPC][Burner] Vengeance", 232, 0, 0, 0],
|
||||
["[NPC][Burner] Ashimmu (Blood Raiders)", 260, 100, 0, 0],
|
||||
["[NPC][Burner] Talos", 0, 413, 413, 0],
|
||||
["[NPC][Burner] Sentinel", 0, 75, 0, 90],
|
||||
["[NPC][Asteroid] Angel Cartel", 1838, 562, 2215, 3838],
|
||||
["[NPC][Deadspace] Angel Cartel", 369, 533, 1395, 3302],
|
||||
["[NPC][Deadspace] Blood Raiders", 6040, 5052, 10, 15],
|
||||
["[NPC][Asteroid] Guristas", 0, 1828, 7413, 0],
|
||||
["[NPC][Deadspace] Guristas", 0, 1531, 9680, 0],
|
||||
["[NPC][Asteroid] Rogue Drone", 394, 666, 1090, 1687],
|
||||
["[NPC][Deadspace] Rogue Drone", 276, 1071, 1069, 871],
|
||||
["[NPC][Asteroid] Sanshas Nation", 5586, 4112, 0, 0],
|
||||
["[NPC][Deadspace] Sanshas Nation", 3009, 2237, 0, 0],
|
||||
["[NPC][Asteroid] Serpentis", 0, 5373, 4813, 0],
|
||||
["[NPC][Deadspace] Serpentis", 0, 3110, 1929, 0],
|
||||
["[NPC][Mission] Amarr Empire", 4464, 3546, 97, 0],
|
||||
["[NPC][Mission] Caldari State", 0, 2139, 4867, 0],
|
||||
["[NPC][Mission] CONCORD", 336, 134, 212, 412],
|
||||
["[NPC][Mission] Gallente Federation", 9, 3712, 2758, 0],
|
||||
["[NPC][Mission] Khanid", 612, 483, 43, 6],
|
||||
["[NPC][Mission] Minmatar Republic", 1024, 388, 1655, 4285],
|
||||
["[NPC][Mission] Mordus Legion", 25, 262, 625, 0],
|
||||
["[NPC][Mission] Thukker", 0, 52, 10, 79],
|
||||
["[NPC][Other] Sleepers", 1472, 1472, 1384, 1384],
|
||||
["[NPC][Other] Sansha Incursion", 1682, 1347, 3678, 3678]]
|
||||
|
||||
for damageProfileRow in damageProfileList:
|
||||
name, em, therm, kin, exp = damageProfileRow
|
||||
damageProfile = eos.db.getDamagePattern(name)
|
||||
if damageProfile is None:
|
||||
damageProfile = es_DamagePattern(em, therm, kin, exp)
|
||||
damageProfile.name = name
|
||||
eos.db.add(damageProfile)
|
||||
else:
|
||||
damageProfile.emAmount = em
|
||||
damageProfile.thermalAmount = therm
|
||||
damageProfile.kineticAmount = kin
|
||||
damageProfile.explosiveAmount = exp
|
||||
eos.db.commit()
|
||||
|
||||
@classmethod
|
||||
def importTargetProfileDefaults(cls):
|
||||
targetProfileList = [["Uniform (25%)", 0.25, 0.25, 0.25, 0.25],
|
||||
["Uniform (50%)", 0.50, 0.50, 0.50, 0.50],
|
||||
["Uniform (75%)", 0.75, 0.75, 0.75, 0.75],
|
||||
["Uniform (90%)", 0.90, 0.90, 0.90, 0.90],
|
||||
["[T1 Resist]Shield", 0.0, 0.20, 0.40, 0.50],
|
||||
["[T1 Resist]Armor", 0.50, 0.45, 0.25, 0.10],
|
||||
["[T1 Resist]Hull", 0.33, 0.33, 0.33, 0.33],
|
||||
["[T1 Resist]Shield (+T2 DCU)", 0.125, 0.30, 0.475, 0.562],
|
||||
["[T1 Resist]Armor (+T2 DCU)", 0.575, 0.532, 0.363, 0.235],
|
||||
["[T1 Resist]Hull (+T2 DCU)", 0.598, 0.598, 0.598, 0.598],
|
||||
["[T2 Resist]Amarr (Shield)", 0.0, 0.20, 0.70, 0.875],
|
||||
["[T2 Resist]Amarr (Armor)", 0.50, 0.35, 0.625, 0.80],
|
||||
["[T2 Resist]Caldari (Shield)", 0.20, 0.84, 0.76, 0.60],
|
||||
["[T2 Resist]Caldari (Armor)", 0.50, 0.8625, 0.625, 0.10],
|
||||
["[T2 Resist]Gallente (Shield)", 0.0, 0.60, 0.85, 0.50],
|
||||
["[T2 Resist]Gallente (Armor)", 0.50, 0.675, 0.8375, 0.10],
|
||||
["[T2 Resist]Minmatar (Shield)", 0.75, 0.60, 0.40, 0.50],
|
||||
["[T2 Resist]Minmatar (Armor)", 0.90, 0.675, 0.25, 0.10],
|
||||
["[NPC][Asteroid] Angel Cartel", 0.54, 0.42, 0.37, 0.32],
|
||||
["[NPC][Asteroid] Blood Raiders", 0.34, 0.39, 0.45, 0.52],
|
||||
["[NPC][Asteroid] Guristas", 0.55, 0.35, 0.3, 0.48],
|
||||
["[NPC][Asteroid] Rogue Drones", 0.35, 0.38, 0.44, 0.49],
|
||||
["[NPC][Asteroid] Sanshas Nation", 0.35, 0.4, 0.47, 0.53],
|
||||
["[NPC][Asteroid] Serpentis", 0.49, 0.38, 0.29, 0.51],
|
||||
["[NPC][Deadspace] Angel Cartel", 0.59, 0.48, 0.4, 0.32],
|
||||
["[NPC][Deadspace] Blood Raiders", 0.31, 0.39, 0.47, 0.56],
|
||||
["[NPC][Deadspace] Guristas", 0.57, 0.39, 0.31, 0.5],
|
||||
["[NPC][Deadspace] Rogue Drones", 0.42, 0.42, 0.47, 0.49],
|
||||
["[NPC][Deadspace] Sanshas Nation", 0.31, 0.39, 0.47, 0.56],
|
||||
["[NPC][Deadspace] Serpentis", 0.49, 0.38, 0.29, 0.56],
|
||||
["[NPC][Mission] Amarr Empire", 0.34, 0.38, 0.42, 0.46],
|
||||
["[NPC][Mission] Caldari State", 0.51, 0.38, 0.3, 0.51],
|
||||
["[NPC][Mission] CONCORD", 0.47, 0.46, 0.47, 0.47],
|
||||
["[NPC][Mission] Gallente Federation", 0.51, 0.38, 0.31, 0.52],
|
||||
["[NPC][Mission] Khanid", 0.51, 0.42, 0.36, 0.4],
|
||||
["[NPC][Mission] Minmatar Republic", 0.51, 0.46, 0.41, 0.35],
|
||||
["[NPC][Mission] Mordus Legion", 0.32, 0.48, 0.4, 0.62],
|
||||
["[NPC][Other] Sleeper", 0.61, 0.61, 0.61, 0.61],
|
||||
["[NPC][Other] Sansha Incursion", 0.65, 0.63, 0.64, 0.65],
|
||||
["[NPC][Burner] Cruor (Blood Raiders)", 0.8, 0.73, 0.69, 0.67],
|
||||
["[NPC][Burner] Dramiel (Angel)", 0.35, 0.48, 0.61, 0.68],
|
||||
["[NPC][Burner] Daredevil (Serpentis)", 0.69, 0.59, 0.59, 0.43],
|
||||
["[NPC][Burner] Succubus (Sanshas Nation)", 0.35, 0.48, 0.61, 0.68],
|
||||
["[NPC][Burner] Worm (Guristas)", 0.48, 0.58, 0.69, 0.74],
|
||||
["[NPC][Burner] Enyo", 0.58, 0.72, 0.86, 0.24],
|
||||
["[NPC][Burner] Hawk", 0.3, 0.86, 0.79, 0.65],
|
||||
["[NPC][Burner] Jaguar", 0.78, 0.65, 0.48, 0.56],
|
||||
["[NPC][Burner] Vengeance", 0.66, 0.56, 0.75, 0.86],
|
||||
["[NPC][Burner] Ashimmu (Blood Raiders)", 0.8, 0.76, 0.68, 0.7],
|
||||
["[NPC][Burner] Talos", 0.68, 0.59, 0.59, 0.43],
|
||||
["[NPC][Burner] Sentinel", 0.58, 0.45, 0.52, 0.66]]
|
||||
|
||||
for targetProfileRow in targetProfileList:
|
||||
name = targetProfileRow[0]
|
||||
em = targetProfileRow[1]
|
||||
therm = targetProfileRow[2]
|
||||
kin = targetProfileRow[3]
|
||||
exp = targetProfileRow[4]
|
||||
try:
|
||||
maxVel = targetProfileRow[5]
|
||||
except IndexError:
|
||||
maxVel = None
|
||||
try:
|
||||
sigRad = targetProfileRow[6]
|
||||
except IndexError:
|
||||
sigRad = None
|
||||
try:
|
||||
radius = targetProfileRow[7]
|
||||
except:
|
||||
radius = None
|
||||
targetProfile = eos.db.getTargetProfile(name)
|
||||
if targetProfile is None:
|
||||
targetProfile = es_TargetProfile(em, therm, kin, exp, maxVel, sigRad, radius)
|
||||
targetProfile.name = name
|
||||
eos.db.add(targetProfile)
|
||||
else:
|
||||
targetProfile.emAmount = em
|
||||
targetProfile.thermalAmount = therm
|
||||
targetProfile.kineticAmount = kin
|
||||
targetProfile.explosiveAmount = exp
|
||||
targetProfile.maxVelocity = maxVel
|
||||
targetProfile.signatureRadius = sigRad
|
||||
targetProfile.radius = radius
|
||||
eos.db.commit()
|
||||
|
||||
|
||||
@classmethod
|
||||
def importRequiredDefaults(cls):
|
||||
damageProfileList = [["Uniform", 25, 25, 25, 25]]
|
||||
|
||||
for damageProfileRow in damageProfileList:
|
||||
name, em, therm, kin, exp = damageProfileRow
|
||||
damageProfile = eos.db.getDamagePattern(name)
|
||||
if damageProfile is None:
|
||||
damageProfile = es_DamagePattern(em, therm, kin, exp)
|
||||
damageProfile.name = name
|
||||
eos.db.add(damageProfile)
|
||||
else:
|
||||
damageProfile.emAmount = em
|
||||
damageProfile.thermalAmount = therm
|
||||
damageProfile.kineticAmount = kin
|
||||
damageProfile.explosiveAmount = exp
|
||||
eos.db.commit()
|
||||
@@ -18,14 +18,14 @@
|
||||
# ===============================================================================
|
||||
|
||||
from sqlalchemy import Table, Column, Integer, Float, ForeignKey, CheckConstraint, Boolean, DateTime
|
||||
from sqlalchemy.orm.collections import attribute_mapped_collection
|
||||
from sqlalchemy.orm import relation, mapper
|
||||
from sqlalchemy.orm.collections import attribute_mapped_collection
|
||||
import datetime
|
||||
|
||||
from eos.db import saveddata_meta
|
||||
from eos.saveddata.module import Module
|
||||
from eos.saveddata.mutator import Mutator
|
||||
from eos.saveddata.fit import Fit
|
||||
from eos.saveddata.mutator import MutatorModule
|
||||
|
||||
modules_table = Table("modules", saveddata_meta,
|
||||
Column("ID", Integer, primary_key=True),
|
||||
@@ -45,13 +45,12 @@ modules_table = Table("modules", saveddata_meta,
|
||||
Column("projectionRange", Float, nullable=True),
|
||||
CheckConstraint('("dummySlot" = NULL OR "itemID" = NULL) AND "dummySlot" != "itemID"'))
|
||||
|
||||
|
||||
mapper(Module, modules_table,
|
||||
properties={
|
||||
"owner": relation(Fit),
|
||||
"mutators": relation(
|
||||
Mutator,
|
||||
backref="module",
|
||||
MutatorModule,
|
||||
backref="item",
|
||||
cascade="all,delete-orphan",
|
||||
collection_class=attribute_mapped_collection('attrID')
|
||||
)
|
||||
})
|
||||
collection_class=attribute_mapped_collection('attrID'))})
|
||||
|
||||
@@ -23,13 +23,14 @@ from sqlalchemy import Column, DateTime, Float, ForeignKey, Integer, Table
|
||||
from sqlalchemy.orm import mapper
|
||||
|
||||
from eos.db import saveddata_meta
|
||||
from eos.saveddata.mutator import Mutator
|
||||
from eos.saveddata.mutator import MutatorDrone
|
||||
|
||||
mutator_table = Table("mutators", saveddata_meta,
|
||||
Column("moduleID", Integer, ForeignKey("modules.ID"), primary_key=True, index=True),
|
||||
Column("attrID", Integer, primary_key=True, index=True),
|
||||
Column("value", Float, nullable=False),
|
||||
Column("created", DateTime, nullable=True, default=datetime.datetime.now),
|
||||
Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now))
|
||||
mutatorDrones_table = Table(
|
||||
"mutatorsDrones", saveddata_meta,
|
||||
Column("groupID", Integer, ForeignKey("drones.groupID"), primary_key=True, index=True),
|
||||
Column("attrID", Integer, primary_key=True, index=True),
|
||||
Column("value", Float, nullable=False),
|
||||
Column("created", DateTime, nullable=True, default=datetime.datetime.now),
|
||||
Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now))
|
||||
|
||||
mapper(Mutator, mutator_table)
|
||||
mapper(MutatorDrone, mutatorDrones_table)
|
||||
36
eos/db/saveddata/mutatorMod.py
Normal file
36
eos/db/saveddata/mutatorMod.py
Normal file
@@ -0,0 +1,36 @@
|
||||
# ===============================================================================
|
||||
# Copyright (C) 2010 Diego Duclos
|
||||
#
|
||||
# This file is part of eos.
|
||||
#
|
||||
# eos is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# eos 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 Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
||||
# ===============================================================================
|
||||
|
||||
import datetime
|
||||
|
||||
from sqlalchemy import Column, DateTime, Float, ForeignKey, Integer, Table
|
||||
from sqlalchemy.orm import mapper
|
||||
|
||||
from eos.db import saveddata_meta
|
||||
from eos.saveddata.mutator import MutatorModule
|
||||
|
||||
mutatorMods_table = Table(
|
||||
"mutators", saveddata_meta,
|
||||
Column("moduleID", Integer, ForeignKey("modules.ID"), primary_key=True, index=True),
|
||||
Column("attrID", Integer, primary_key=True, index=True),
|
||||
Column("value", Float, nullable=False),
|
||||
Column("created", DateTime, nullable=True, default=datetime.datetime.now),
|
||||
Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now))
|
||||
|
||||
mapper(MutatorModule, mutatorMods_table)
|
||||
@@ -413,7 +413,7 @@ def getDamagePattern(lookfor, eager=None):
|
||||
eager = processEager(eager)
|
||||
with sd_lock:
|
||||
pattern = saveddata_session.query(DamagePattern).options(*eager).filter(
|
||||
DamagePattern.name == lookfor).first()
|
||||
DamagePattern.rawName == lookfor).first()
|
||||
else:
|
||||
raise TypeError("Need integer or string as argument")
|
||||
return pattern
|
||||
@@ -434,7 +434,7 @@ def getTargetProfile(lookfor, eager=None):
|
||||
eager = processEager(eager)
|
||||
with sd_lock:
|
||||
pattern = saveddata_session.query(TargetProfile).options(*eager).filter(
|
||||
TargetProfile.name == lookfor).first()
|
||||
TargetProfile.rawName == lookfor).first()
|
||||
else:
|
||||
raise TypeError("Need integer or string as argument")
|
||||
return pattern
|
||||
@@ -470,7 +470,7 @@ def searchFits(nameLike, where=None, eager=None):
|
||||
filter = processWhere(Fit.name.like(nameLike, escape="\\"), where)
|
||||
eager = processEager(eager)
|
||||
with sd_lock:
|
||||
fits = removeInvalid(saveddata_session.query(Fit).options(*eager).filter(filter).all())
|
||||
fits = removeInvalid(saveddata_session.query(Fit).options(*eager).filter(filter).limit(100).all())
|
||||
|
||||
return fits
|
||||
|
||||
@@ -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):
|
||||
@@ -560,6 +563,8 @@ def commit():
|
||||
with sd_lock:
|
||||
try:
|
||||
saveddata_session.commit()
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
raise
|
||||
except Exception:
|
||||
saveddata_session.rollback()
|
||||
exc_info = sys.exc_info()
|
||||
@@ -570,6 +575,8 @@ def flush():
|
||||
with sd_lock:
|
||||
try:
|
||||
saveddata_session.flush()
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
raise
|
||||
except Exception:
|
||||
saveddata_session.rollback()
|
||||
exc_info = sys.exc_info()
|
||||
|
||||
@@ -24,23 +24,30 @@ import datetime
|
||||
from eos.db import saveddata_meta
|
||||
from eos.saveddata.targetProfile import TargetProfile
|
||||
|
||||
targetProfiles_table = Table("targetResists", saveddata_meta,
|
||||
Column("ID", Integer, primary_key=True),
|
||||
Column("name", String),
|
||||
Column("emAmount", Float),
|
||||
Column("thermalAmount", Float),
|
||||
Column("kineticAmount", Float),
|
||||
Column("explosiveAmount", Float),
|
||||
Column("maxVelocity", Float, nullable=True),
|
||||
Column("signatureRadius", Float, nullable=True),
|
||||
Column("radius", 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)
|
||||
)
|
||||
|
||||
mapper(TargetProfile, targetProfiles_table,
|
||||
properties={
|
||||
"_maxVelocity": targetProfiles_table.c.maxVelocity,
|
||||
"_signatureRadius": targetProfiles_table.c.signatureRadius,
|
||||
"_radius": targetProfiles_table.c.radius})
|
||||
targetProfiles_table = Table(
|
||||
'targetResists',
|
||||
saveddata_meta,
|
||||
Column('ID', Integer, primary_key=True),
|
||||
Column('name', String),
|
||||
Column('emAmount', Float),
|
||||
Column('thermalAmount', Float),
|
||||
Column('kineticAmount', Float),
|
||||
Column('explosiveAmount', Float),
|
||||
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))
|
||||
|
||||
mapper(
|
||||
TargetProfile,
|
||||
targetProfiles_table,
|
||||
properties={
|
||||
'rawName': targetProfiles_table.c.name,
|
||||
'_maxVelocity': targetProfiles_table.c.maxVelocity,
|
||||
'_signatureRadius': targetProfiles_table.c.signatureRadius,
|
||||
'_radius': targetProfiles_table.c.radius,
|
||||
'_hp': targetProfiles_table.c.hp})
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
|
||||
from logbook import Logger
|
||||
from sqlalchemy.orm.attributes import flag_dirty
|
||||
from sqlalchemy.orm.collections import collection
|
||||
|
||||
|
||||
@@ -108,10 +109,7 @@ class HandledList(list):
|
||||
|
||||
def remove(self, thing):
|
||||
# We must flag it as modified, otherwise it not be removed from the database
|
||||
# @todo: flag_modified isn't in os x skel. need to rebuild to include
|
||||
# flag_modified(thing, "itemID")
|
||||
if thing.isInvalid: # see GH issue #324
|
||||
thing.itemID = 0
|
||||
flag_dirty(thing)
|
||||
list.remove(self, thing)
|
||||
|
||||
def sort(self, *args, **kwargs):
|
||||
|
||||
10624
eos/effects.py
10624
eos/effects.py
File diff suppressed because it is too large
Load Diff
152
eos/gamedata.py
152
eos/gamedata.py
@@ -18,7 +18,8 @@
|
||||
# ===============================================================================
|
||||
|
||||
|
||||
from collections import OrderedDict
|
||||
import json
|
||||
import re
|
||||
|
||||
from logbook import Logger
|
||||
from sqlalchemy.orm import reconstructor
|
||||
@@ -32,6 +33,10 @@ from .eqBase import EqBase
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
|
||||
def _t(x):
|
||||
return x
|
||||
|
||||
|
||||
class Effect(EqBase):
|
||||
"""
|
||||
The effect handling class, it is used to proxy and load effect handler code,
|
||||
@@ -145,6 +150,12 @@ class Effect(EqBase):
|
||||
|
||||
return self.__effectDef is not None
|
||||
|
||||
@property
|
||||
def dealsDamage(self):
|
||||
if not self.__generated:
|
||||
self.__generateHandler()
|
||||
return self.__dealsDamage
|
||||
|
||||
def isType(self, type):
|
||||
"""
|
||||
Check if this effect is of the passed type
|
||||
@@ -166,6 +177,7 @@ class Effect(EqBase):
|
||||
self.__handler = getattr(effectDef, "handler", eos.effects.BaseEffect.handler)
|
||||
self.__runTime = getattr(effectDef, "runTime", "normal")
|
||||
self.__activeByDefault = getattr(effectDef, "activeByDefault", True)
|
||||
self.__dealsDamage = effectDef.dealsDamage
|
||||
effectType = getattr(effectDef, "type", None)
|
||||
effectType = effectType if isinstance(effectType, tuple) or effectType is None else (effectType,)
|
||||
self.__type = effectType
|
||||
@@ -174,6 +186,7 @@ class Effect(EqBase):
|
||||
self.__handler = eos.effects.DummyEffect.handler
|
||||
self.__runTime = "normal"
|
||||
self.__activeByDefault = True
|
||||
self.__dealsDamage = False
|
||||
self.__type = None
|
||||
pyfalog.debug("ImportError generating handler: {0}", e)
|
||||
except AttributeError as e:
|
||||
@@ -181,12 +194,16 @@ class Effect(EqBase):
|
||||
self.__handler = eos.effects.DummyEffect.handler
|
||||
self.__runTime = "normal"
|
||||
self.__activeByDefault = True
|
||||
self.__dealsDamage = False
|
||||
self.__type = None
|
||||
pyfalog.error("AttributeError generating handler: {0}", e)
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
raise
|
||||
except Exception as e:
|
||||
self.__handler = eos.effects.DummyEffect.handler
|
||||
self.__runTime = "normal"
|
||||
self.__activeByDefault = True
|
||||
self.__dealsDamage = False
|
||||
self.__type = None
|
||||
pyfalog.critical("Exception generating handler:")
|
||||
pyfalog.critical(e)
|
||||
@@ -199,45 +216,20 @@ class Effect(EqBase):
|
||||
|
||||
try:
|
||||
return self.__effectDef.get(key, None)
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
raise
|
||||
except:
|
||||
return getattr(self.__effectDef, key, None)
|
||||
|
||||
|
||||
class Item(EqBase):
|
||||
MOVE_ATTRS = (4, # Mass
|
||||
38, # Capacity
|
||||
161) # Volume
|
||||
|
||||
MOVE_ATTR_INFO = None
|
||||
|
||||
ABYSSAL_TYPES = None
|
||||
|
||||
@classmethod
|
||||
def getMoveAttrInfo(cls):
|
||||
info = getattr(cls, "MOVE_ATTR_INFO", None)
|
||||
if info is None:
|
||||
cls.MOVE_ATTR_INFO = info = []
|
||||
for id in cls.MOVE_ATTRS:
|
||||
info.append(eos.db.getAttributeInfo(id))
|
||||
|
||||
return info
|
||||
|
||||
def moveAttrs(self):
|
||||
self.__moved = True
|
||||
for info in self.getMoveAttrInfo():
|
||||
val = getattr(self, info.name, 0)
|
||||
if val != 0:
|
||||
attr = Attribute()
|
||||
attr.info = info
|
||||
attr.value = val
|
||||
self.__attributes[info.name] = attr
|
||||
|
||||
@reconstructor
|
||||
def init(self):
|
||||
self.__race = None
|
||||
self.__requiredSkills = None
|
||||
self.__requiredFor = None
|
||||
self.__moved = False
|
||||
self.__offensive = None
|
||||
self.__assistive = None
|
||||
self.__overrides = None
|
||||
@@ -258,10 +250,11 @@ class Item(EqBase):
|
||||
return shortName
|
||||
|
||||
@property
|
||||
def attributes(self):
|
||||
if not self.__moved:
|
||||
self.moveAttrs()
|
||||
def customName(self):
|
||||
return re.sub(_t('Caustic'), _t('Tachyon'), self.name)
|
||||
|
||||
@property
|
||||
def attributes(self):
|
||||
return self.__attributes
|
||||
|
||||
@property
|
||||
@@ -314,50 +307,26 @@ class Item(EqBase):
|
||||
eos.db.saveddata_session.delete(override)
|
||||
eos.db.commit()
|
||||
|
||||
srqIDMap = {182: 277, 183: 278, 184: 279, 1285: 1286, 1289: 1287, 1290: 1288}
|
||||
|
||||
@property
|
||||
def requiredSkills(self):
|
||||
if self.__requiredSkills is None:
|
||||
requiredSkills = OrderedDict()
|
||||
self.__requiredSkills = requiredSkills
|
||||
# Map containing attribute IDs we may need for required skills
|
||||
# { requiredSkillX : requiredSkillXLevel }
|
||||
combinedAttrIDs = set(self.srqIDMap.keys()).union(set(self.srqIDMap.values()))
|
||||
# Map containing result of the request
|
||||
# { attributeID : attributeValue }
|
||||
skillAttrs = {}
|
||||
# Get relevant attribute values from db (required skill IDs and levels) for our item
|
||||
for attrInfo in eos.db.directAttributeRequest((self.ID,), tuple(combinedAttrIDs)):
|
||||
attrID = attrInfo[1]
|
||||
attrVal = attrInfo[2]
|
||||
skillAttrs[attrID] = attrVal
|
||||
# Go through all attributeID pairs
|
||||
for srqIDAtrr, srqLvlAttr in self.srqIDMap.items():
|
||||
# Check if we have both in returned result
|
||||
if srqIDAtrr in skillAttrs and srqLvlAttr in skillAttrs:
|
||||
skillID = int(skillAttrs[srqIDAtrr])
|
||||
skillLvl = skillAttrs[srqLvlAttr]
|
||||
# Fetch item from database and fill map
|
||||
item = eos.db.getItem(skillID)
|
||||
requiredSkills[item] = skillLvl
|
||||
self.__requiredSkills = {}
|
||||
if self.reqskills:
|
||||
for skillTypeID, skillLevel in json.loads(self.reqskills).items():
|
||||
skillItem = eos.db.getItem(int(skillTypeID))
|
||||
if skillItem:
|
||||
self.__requiredSkills[skillItem] = skillLevel
|
||||
return self.__requiredSkills
|
||||
|
||||
@property
|
||||
def requiredFor(self):
|
||||
if self.__requiredFor is None:
|
||||
self.__requiredFor = dict()
|
||||
|
||||
# Map containing attribute IDs we may need for required skills
|
||||
|
||||
# Get relevant attribute values from db (required skill IDs and levels) for our item
|
||||
q = eos.db.getRequiredFor(self.ID, self.srqIDMap)
|
||||
|
||||
for itemID, lvl in q:
|
||||
# Fetch item from database and fill map
|
||||
item = eos.db.getItem(itemID)
|
||||
self.__requiredFor[item] = lvl
|
||||
|
||||
self.__requiredFor = {}
|
||||
if self.requiredfor:
|
||||
for typeID, skillLevel in json.loads(self.requiredfor).items():
|
||||
requiredForItem = eos.db.getItem(int(typeID))
|
||||
if requiredForItem:
|
||||
self.__requiredFor[requiredForItem] = skillLevel
|
||||
return self.__requiredFor
|
||||
|
||||
factionMap = {
|
||||
@@ -373,7 +342,10 @@ class Item(EqBase):
|
||||
500016: "sisters",
|
||||
500018: "mordu",
|
||||
500019: "sansha",
|
||||
500020: "serpentis"
|
||||
500020: "serpentis",
|
||||
500026: "triglavian",
|
||||
500027: "upwell",
|
||||
500029: "deathless",
|
||||
}
|
||||
|
||||
@property
|
||||
@@ -381,7 +353,7 @@ class Item(EqBase):
|
||||
if self.__race is None:
|
||||
|
||||
try:
|
||||
if self.category.categoryName == 'Structure':
|
||||
if self.category.name == 'Structure':
|
||||
self.__race = "upwell"
|
||||
else:
|
||||
self.__race = self.factionMap[self.factionID]
|
||||
@@ -399,10 +371,12 @@ class Item(EqBase):
|
||||
9 : "guristas", # Caldari + Gallente
|
||||
10 : "angelserp", # Minmatar + Gallente, final race depends on the order of skills
|
||||
12 : "sisters", # Amarr + Gallente
|
||||
15 : "concord",
|
||||
16 : "jove",
|
||||
32 : "sansha", # Incrusion Sansha
|
||||
128: "ore",
|
||||
135: "triglavian"
|
||||
135: "triglavian",
|
||||
168: "upwell",
|
||||
}
|
||||
# Race is None by default
|
||||
race = None
|
||||
@@ -456,7 +430,7 @@ class Item(EqBase):
|
||||
def requiresSkill(self, skill, level=None):
|
||||
for s, l in self.requiredSkills.items():
|
||||
if isinstance(skill, str):
|
||||
if s.name == skill and (level is None or l == level):
|
||||
if s.typeName == skill and (level is None or l == level):
|
||||
return True
|
||||
|
||||
elif isinstance(skill, int) and (level is None or l == level):
|
||||
@@ -514,6 +488,10 @@ class Item(EqBase):
|
||||
def isCharge(self):
|
||||
return self.category.name == 'Charge'
|
||||
|
||||
@property
|
||||
def isCommodity(self):
|
||||
return self.category.name == 'Commodity'
|
||||
|
||||
@property
|
||||
def isDrone(self):
|
||||
return self.category.name == 'Drone'
|
||||
@@ -539,8 +517,8 @@ class Item(EqBase):
|
||||
return False
|
||||
|
||||
def __repr__(self):
|
||||
return "Item(ID={}, name={}) at {}".format(
|
||||
self.ID, self.name, hex(id(self))
|
||||
return "Item(ID={}, name={}, display={}) at {}".format(
|
||||
self.ID, self.typeName, self.name, hex(id(self))
|
||||
)
|
||||
|
||||
|
||||
@@ -588,7 +566,23 @@ class Group(EqBase):
|
||||
|
||||
|
||||
class DynamicItem(EqBase):
|
||||
pass
|
||||
|
||||
@property
|
||||
def shortName(self):
|
||||
name = self.item.customName
|
||||
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(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
|
||||
|
||||
|
||||
class DynamicItemAttribute(EqBase):
|
||||
@@ -740,5 +734,15 @@ class Unit(EqBase):
|
||||
|
||||
return value
|
||||
|
||||
|
||||
class Traits(EqBase):
|
||||
pass
|
||||
|
||||
|
||||
class ImplantSet(EqBase):
|
||||
|
||||
@property
|
||||
def fullName(self):
|
||||
if not self.gradeName:
|
||||
return self.setName
|
||||
return '{} {}'.format(self.gradeName, self.setName)
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
||||
# ===============================================================================
|
||||
|
||||
import collections
|
||||
|
||||
from collections.abc import MutableMapping
|
||||
from copy import copy
|
||||
from math import exp
|
||||
|
||||
@@ -70,33 +71,33 @@ class ItemAttrShortcut:
|
||||
|
||||
def getModifiedItemAttr(self, key, default=0):
|
||||
return_value = self.itemModifiedAttributes.get(key)
|
||||
return return_value or default
|
||||
return return_value if return_value is not None else default
|
||||
|
||||
def getModifiedItemAttrExtended(self, key, extraMultipliers=None, ignoreAfflictors=(), default=0):
|
||||
return_value = self.itemModifiedAttributes.getExtended(key, extraMultipliers=extraMultipliers, ignoreAfflictors=ignoreAfflictors)
|
||||
return return_value or default
|
||||
return return_value if return_value is not None else default
|
||||
|
||||
def getItemBaseAttrValue(self, key, default=0):
|
||||
return_value = self.itemModifiedAttributes.getOriginal(key)
|
||||
return return_value or default
|
||||
return return_value if return_value is not None else default
|
||||
|
||||
|
||||
class ChargeAttrShortcut:
|
||||
|
||||
def getModifiedChargeAttr(self, key, default=0):
|
||||
return_value = self.chargeModifiedAttributes.get(key)
|
||||
return return_value or default
|
||||
return return_value if return_value is not None else default
|
||||
|
||||
def getModifiedChargeAttrExtended(self, key, extraMultipliers=None, ignoreAfflictors=(), default=0):
|
||||
return_value = self.chargeModifiedAttributes.getExtended(key, extraMultipliers=extraMultipliers, ignoreAfflictors=ignoreAfflictors)
|
||||
return return_value or default
|
||||
return return_value if return_value is not None else default
|
||||
|
||||
def getChargeBaseAttrValue(self, key, default=0):
|
||||
return_value = self.chargeModifiedAttributes.getOriginal(key)
|
||||
return return_value or default
|
||||
return return_value if return_value is not None else default
|
||||
|
||||
|
||||
class ModifiedAttributeDict(collections.MutableMapping):
|
||||
class ModifiedAttributeDict(MutableMapping):
|
||||
overrides_enabled = False
|
||||
|
||||
class CalculationPlaceholder:
|
||||
@@ -323,7 +324,7 @@ class ModifiedAttributeDict(collections.MutableMapping):
|
||||
cappingAttrKeyCache[key] = cappingKey
|
||||
|
||||
if cappingKey:
|
||||
cappingValue = self.original.get(cappingKey, self.__calculateValue(cappingKey))
|
||||
cappingValue = self[cappingKey]
|
||||
cappingValue = cappingValue.value if hasattr(cappingValue, "value") else cappingValue
|
||||
else:
|
||||
cappingValue = None
|
||||
|
||||
@@ -159,6 +159,6 @@ class Booster(HandledItem, ItemAttrShortcut):
|
||||
sideEffect.active = sideEffectStates[sideEffect.effectID]
|
||||
|
||||
def __repr__(self):
|
||||
return "Booster(ID={}, name={}) at {}".format(
|
||||
self.item.ID, self.item.name, hex(id(self))
|
||||
)
|
||||
if self.item is not None:
|
||||
return f"Booster(ID={self.item.ID}, name={self.item.name}) at {hex(id(self))}"
|
||||
return f"Booster(ID={self.itemID}) at {hex(id(self))}"
|
||||
|
||||
@@ -21,6 +21,9 @@ from logbook import Logger
|
||||
|
||||
from sqlalchemy.orm import reconstructor
|
||||
|
||||
from eos.utils.round import roundToPrec
|
||||
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
|
||||
@@ -56,9 +59,8 @@ class BoosterSideEffect:
|
||||
@property
|
||||
def name(self):
|
||||
return "{0}% {1}".format(
|
||||
self.booster.getModifiedItemAttr(self.attr),
|
||||
self.__effect.getattr('displayName') or self.__effect.name,
|
||||
)
|
||||
roundToPrec(self.booster.getModifiedItemAttr(self.attr), 5),
|
||||
self.__effect.getattr('displayName') or self.__effect.name)
|
||||
|
||||
@property
|
||||
def attr(self):
|
||||
|
||||
@@ -96,7 +96,7 @@ class Character:
|
||||
if cls.__itemNameMap is None:
|
||||
map = {}
|
||||
for skill in cls.getSkillList():
|
||||
map[skill.name] = skill
|
||||
map[skill.typeName] = skill
|
||||
|
||||
cls.__itemNameMap = map
|
||||
|
||||
|
||||
@@ -18,26 +18,209 @@
|
||||
# ===============================================================================
|
||||
|
||||
import re
|
||||
from collections import OrderedDict
|
||||
|
||||
from sqlalchemy.orm import reconstructor
|
||||
|
||||
import eos.db
|
||||
|
||||
|
||||
def _t(x):
|
||||
return x
|
||||
|
||||
|
||||
def _c(x):
|
||||
return '[' + x + ']'
|
||||
|
||||
|
||||
# Order is significant here - UI uses order as-is for built-in patterns
|
||||
BUILTINS = OrderedDict([
|
||||
(-1, (_t('Uniform'), 25, 25, 25, 25)),
|
||||
(-2, (_c(_t('Generic')) + _t('EM'), 1, 0, 0, 0)),
|
||||
(-3, (_c(_t('Generic')) + _t('Thermal'), 0, 1, 0, 0)),
|
||||
(-4, (_c(_t('Generic')) + _t('Kinetic'), 0, 0, 1, 0)),
|
||||
(-5, (_c(_t('Generic')) + _t('Explosive'), 0, 0, 0, 1)),
|
||||
(-6, (_c(_t('Frequency Crystals')) + '|' + _t('[T2] Aurora'), 5, 3, 0, 0)),
|
||||
(-7, (_c(_t('Frequency Crystals')) + '|' + _t('[T2] Scorch'), 9, 2, 0, 0)),
|
||||
(-8, (_c(_t('Frequency Crystals')) + _t('Radio'), 5, 0, 0, 0)),
|
||||
(-9, (_c(_t('Frequency Crystals')) + _t('Microwave'), 4, 2, 0, 0)),
|
||||
(-10, (_c(_t('Frequency Crystals')) + _t('Infrared'), 5, 2, 0, 0)),
|
||||
(-11, (_c(_t('Frequency Crystals')) + _t('Standard'), 5, 3, 0, 0)),
|
||||
(-12, (_c(_t('Frequency Crystals')) + _t('Ultraviolet'), 6, 3, 0, 0)),
|
||||
(-13, (_c(_t('Frequency Crystals')) + _t('Xray'), 6, 4, 0, 0)),
|
||||
(-14, (_c(_t('Frequency Crystals')) + _t('Gamma'), 7, 4, 0, 0)),
|
||||
(-15, (_c(_t('Frequency Crystals')) + _t('Multifrequency'), 7, 5, 0, 0)),
|
||||
(-16, (_c(_t('Frequency Crystals')) + '|' + _t('[T2] Gleam'), 7, 7, 0, 0)),
|
||||
(-17, (_c(_t('Frequency Crystals')) + '|' + _t('[T2] Conflagration'), 7.7, 7.7, 0, 0)),
|
||||
# Different sizes of plasma do different damage ratios, the values here
|
||||
# are average of ratios across sizes
|
||||
(-18, (_c(_t('Exotic Plasma')) + '|' + _t('[T2] Mystic'), 0, 66319, 0, 33681)),
|
||||
(-19, (_c(_t('Exotic Plasma')) + _t('Meson'), 0, 60519, 0, 39481)),
|
||||
(-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)),
|
||||
# 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
|
||||
(-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
|
||||
(-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
|
||||
(-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)),
|
||||
(-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)),
|
||||
(-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)),
|
||||
(-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:
|
||||
DAMAGE_TYPES = ("em", "thermal", "kinetic", "explosive")
|
||||
DAMAGE_TYPES = ('em', 'thermal', 'kinetic', 'explosive')
|
||||
_builtins = None
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.builtin = False
|
||||
self.update(*args, **kwargs)
|
||||
|
||||
@reconstructor
|
||||
def init(self):
|
||||
self.builtin = False
|
||||
|
||||
def update(self, emAmount=25, thermalAmount=25, kineticAmount=25, explosiveAmount=25):
|
||||
self.emAmount = emAmount
|
||||
self.thermalAmount = thermalAmount
|
||||
self.kineticAmount = kineticAmount
|
||||
self.explosiveAmount = explosiveAmount
|
||||
|
||||
def calculateEhp(self, fit):
|
||||
@classmethod
|
||||
def getBuiltinList(cls):
|
||||
if cls._builtins is None:
|
||||
cls.__generateBuiltins()
|
||||
return list(cls._builtins.values())
|
||||
|
||||
@classmethod
|
||||
def getBuiltinById(cls, id):
|
||||
if cls._builtins is None:
|
||||
cls.__generateBuiltins()
|
||||
return cls._builtins.get(id)
|
||||
|
||||
@classmethod
|
||||
def getDefaultBuiltin(cls):
|
||||
if cls._builtins is None:
|
||||
cls.__generateBuiltins()
|
||||
return cls._builtins.get(-1)
|
||||
|
||||
@classmethod
|
||||
def __generateBuiltins(cls):
|
||||
cls._builtins = OrderedDict()
|
||||
for id, (rawName, em, therm, kin, explo) in BUILTINS.items():
|
||||
pattern = DamagePattern(emAmount=em, thermalAmount=therm, kineticAmount=kin, explosiveAmount=explo)
|
||||
pattern.ID = id
|
||||
pattern.rawName = rawName
|
||||
pattern.builtin = True
|
||||
cls._builtins[id] = pattern
|
||||
|
||||
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
|
||||
|
||||
@@ -48,14 +231,15 @@ class DamagePattern:
|
||||
"armorRepair": "armor",
|
||||
"armorRepairPreSpool": "armor",
|
||||
"armorRepairFullSpool": "armor",
|
||||
"hullRepair": "hull"}
|
||||
"hullRepair": "hull"
|
||||
}
|
||||
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
|
||||
@@ -64,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
|
||||
@@ -72,12 +256,21 @@ class DamagePattern:
|
||||
return amount / (specificDivider or 1)
|
||||
|
||||
importMap = {
|
||||
"em" : "em",
|
||||
"em": "em",
|
||||
"therm": "thermal",
|
||||
"kin" : "kinetic",
|
||||
"exp" : "explosive"
|
||||
"kin": "kinetic",
|
||||
"exp": "explosive"
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def oneType(cls, damageType, amount=100):
|
||||
pattern = DamagePattern()
|
||||
pattern.update(amount if damageType == "em" else 0,
|
||||
amount if damageType == "thermal" else 0,
|
||||
amount if damageType == "kinetic" else 0,
|
||||
amount if damageType == "explosive" else 0)
|
||||
return pattern
|
||||
|
||||
@classmethod
|
||||
def importPatterns(cls, text):
|
||||
lines = re.split('[\n\r]+', text)
|
||||
@@ -89,7 +282,7 @@ class DamagePattern:
|
||||
lookup = {}
|
||||
current = eos.db.getDamagePatternList()
|
||||
for pattern in current:
|
||||
lookup[pattern.name] = pattern
|
||||
lookup[pattern.rawName] = pattern
|
||||
|
||||
for line in lines:
|
||||
try:
|
||||
@@ -98,6 +291,8 @@ class DamagePattern:
|
||||
line = line.split('#', 1)[0] # allows for comments
|
||||
type, data = line.rsplit('=', 1)
|
||||
type, data = type.strip(), data.split(',')
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
raise
|
||||
except:
|
||||
# Data isn't in correct format, continue to next line
|
||||
continue
|
||||
@@ -112,6 +307,8 @@ class DamagePattern:
|
||||
for index, val in enumerate(data):
|
||||
try:
|
||||
fields["%sAmount" % cls.DAMAGE_TYPES[index]] = int(val)
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
raise
|
||||
except:
|
||||
continue
|
||||
|
||||
@@ -122,7 +319,7 @@ class DamagePattern:
|
||||
eos.db.save(pattern)
|
||||
else:
|
||||
pattern = DamagePattern(**fields)
|
||||
pattern.name = name.strip()
|
||||
pattern.rawName = name.strip()
|
||||
eos.db.save(pattern)
|
||||
patterns.append(pattern)
|
||||
|
||||
@@ -138,11 +335,41 @@ class DamagePattern:
|
||||
out += "# Values are in following format:\n"
|
||||
out += "# DamageProfile = [name],[EM amount],[Thermal amount],[Kinetic amount],[Explosive amount]\n\n"
|
||||
for dp in patterns:
|
||||
out += cls.EXPORT_FORMAT % (dp.name, dp.emAmount, dp.thermalAmount, dp.kineticAmount, dp.explosiveAmount)
|
||||
out += cls.EXPORT_FORMAT % (dp.rawName, dp.emAmount, dp.thermalAmount, dp.kineticAmount, dp.explosiveAmount)
|
||||
|
||||
return out.strip()
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self.rawName
|
||||
|
||||
@property
|
||||
def fullName(self):
|
||||
categories, tail = self.__parseRawName()
|
||||
return '{}{}'.format(''.join('[{}]'.format(c) for c in categories), tail)
|
||||
|
||||
@property
|
||||
def shortName(self):
|
||||
return self.__parseRawName()[1]
|
||||
|
||||
@property
|
||||
def hierarchy(self):
|
||||
return self.__parseRawName()[0]
|
||||
|
||||
def __parseRawName(self):
|
||||
categories = []
|
||||
remainingName = self.rawName.strip() if self.rawName else ''
|
||||
while True:
|
||||
start, end = remainingName.find('['), remainingName.find(']')
|
||||
if start == -1 or end == -1:
|
||||
return categories, remainingName
|
||||
splitter = remainingName.find('|')
|
||||
if splitter != -1 and splitter == start - 1:
|
||||
return categories, remainingName[1:]
|
||||
categories.append(remainingName[start + 1:end])
|
||||
remainingName = remainingName[end + 1:].strip()
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
p = DamagePattern(self.emAmount, self.thermalAmount, self.kineticAmount, self.explosiveAmount)
|
||||
p.name = "%s copy" % self.name
|
||||
p.rawName = "%s copy" % self.rawName
|
||||
return p
|
||||
|
||||
@@ -19,12 +19,15 @@
|
||||
|
||||
import math
|
||||
|
||||
from copy import deepcopy
|
||||
from logbook import Logger
|
||||
from sqlalchemy.orm import reconstructor, validates
|
||||
|
||||
import eos.db
|
||||
from eos.effectHandlerHelpers import HandledCharge, HandledItem
|
||||
from eos.modifiedAttributeDict import ChargeAttrShortcut, ItemAttrShortcut, ModifiedAttributeDict
|
||||
from eos.saveddata.mutatedMixin import MutatedMixin, MutaError
|
||||
from eos.saveddata.mutator import MutatorDrone
|
||||
from eos.utils.cycles import CycleInfo
|
||||
from eos.utils.default import DEFAULT
|
||||
from eos.utils.stats import DmgTypes, RRTypes
|
||||
@@ -33,12 +36,13 @@ from eos.utils.stats import DmgTypes, RRTypes
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
|
||||
class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut, MutatedMixin):
|
||||
MINING_ATTRIBUTES = ("miningAmount",)
|
||||
|
||||
def __init__(self, item):
|
||||
def __init__(self, item, baseItem=None, mutaplasmid=None):
|
||||
"""Initialize a drone from the program"""
|
||||
self.__item = item
|
||||
self._item = item
|
||||
self._mutaInit(baseItem=baseItem, mutaplasmid=mutaplasmid)
|
||||
|
||||
if self.isInvalid:
|
||||
raise ValueError("Passed item is not a Drone")
|
||||
@@ -53,14 +57,19 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
@reconstructor
|
||||
def init(self):
|
||||
"""Initialize a drone from the database and validate"""
|
||||
self.__item = None
|
||||
self._item = None
|
||||
|
||||
if self.itemID:
|
||||
self.__item = eos.db.getItem(self.itemID)
|
||||
if self.__item is None:
|
||||
self._item = eos.db.getItem(self.itemID)
|
||||
if self._item is None:
|
||||
pyfalog.error("Item (id: {0}) does not exist", self.itemID)
|
||||
return
|
||||
|
||||
try:
|
||||
self._mutaReconstruct()
|
||||
except MutaError:
|
||||
return
|
||||
|
||||
if self.isInvalid:
|
||||
pyfalog.error("Item (id: {0}) is not a Drone", self.itemID)
|
||||
return
|
||||
@@ -72,15 +81,19 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
self.__charge = None
|
||||
self.__baseVolley = None
|
||||
self.__baseRRAmount = None
|
||||
self.__miningyield = None
|
||||
self.__miningYield = None
|
||||
self.__miningDrain = None
|
||||
self.__ehp = None
|
||||
self.__itemModifiedAttributes = ModifiedAttributeDict()
|
||||
self.__itemModifiedAttributes.original = self.__item.attributes
|
||||
self.__itemModifiedAttributes.overrides = self.__item.overrides
|
||||
|
||||
self.__itemModifiedAttributes.original = self._item.attributes
|
||||
self.__itemModifiedAttributes.overrides = self._item.overrides
|
||||
self.__chargeModifiedAttributes = ModifiedAttributeDict()
|
||||
# pheonix todo: check the attribute itself, not the modified. this will always return 0 now.
|
||||
|
||||
self._mutaLoadMutators(mutatorClass=MutatorDrone)
|
||||
self.__itemModifiedAttributes.mutators = self.mutators
|
||||
|
||||
chargeID = self.getModifiedItemAttr("entityMissileTypeID", None)
|
||||
if chargeID is not None:
|
||||
if chargeID:
|
||||
charge = eos.db.getItem(int(chargeID))
|
||||
self.__charge = charge
|
||||
self.__chargeModifiedAttributes.original = charge.attributes
|
||||
@@ -96,11 +109,17 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
|
||||
@property
|
||||
def isInvalid(self):
|
||||
return self.__item is None or self.__item.category.name != "Drone"
|
||||
if self._item is None:
|
||||
return True
|
||||
if self._item.category.name != "Drone":
|
||||
return True
|
||||
if self._mutaIsInvalid:
|
||||
return True
|
||||
return False
|
||||
|
||||
@property
|
||||
def item(self):
|
||||
return self.__item
|
||||
return self._item
|
||||
|
||||
@property
|
||||
def charge(self):
|
||||
@@ -111,9 +130,9 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
if self.hasAmmo:
|
||||
cycleTime = self.getModifiedItemAttr("missileLaunchDuration", 0)
|
||||
else:
|
||||
for attr in ("speed", "duration"):
|
||||
cycleTime = self.getModifiedItemAttr(attr, None)
|
||||
if cycleTime is not None:
|
||||
for attr in ("speed", "duration", "durationHighisGood"):
|
||||
cycleTime = self.getModifiedItemAttr(attr)
|
||||
if cycleTime:
|
||||
break
|
||||
if cycleTime is None:
|
||||
return 0
|
||||
@@ -143,7 +162,7 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
|
||||
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))
|
||||
@@ -152,11 +171,8 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
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):
|
||||
@@ -165,16 +181,12 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
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):
|
||||
@@ -220,35 +232,49 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
|
||||
def getCycleParameters(self, reloadOverride=None):
|
||||
cycleTime = self.cycleTime
|
||||
if cycleTime == 0:
|
||||
if not cycleTime:
|
||||
return None
|
||||
return CycleInfo(self.cycleTime, 0, math.inf, False)
|
||||
|
||||
@property
|
||||
def miningStats(self):
|
||||
if self.__miningyield is None:
|
||||
if self.mines is True and self.amountActive > 0:
|
||||
getter = self.getModifiedItemAttr
|
||||
cycleParams = self.getCycleParameters()
|
||||
if cycleParams is None:
|
||||
self.__miningyield = 0
|
||||
else:
|
||||
cycleTime = cycleParams.averageTime
|
||||
volley = sum([getter(d) for d in self.MINING_ATTRIBUTES]) * self.amountActive
|
||||
self.__miningyield = volley / (cycleTime / 1000.0)
|
||||
else:
|
||||
self.__miningyield = 0
|
||||
def getMiningYPS(self, ignoreState=False):
|
||||
if not ignoreState and self.amountActive <= 0:
|
||||
return 0
|
||||
if self.__miningYield is None:
|
||||
self.__miningYield, self.__miningDrain = self.__calculateMining()
|
||||
return self.__miningYield
|
||||
|
||||
return self.__miningyield
|
||||
def getMiningDPS(self, ignoreState=False):
|
||||
if not ignoreState and self.amountActive <= 0:
|
||||
return 0
|
||||
if self.__miningDrain is None:
|
||||
self.__miningYield, self.__miningDrain = self.__calculateMining()
|
||||
return self.__miningDrain
|
||||
|
||||
def __calculateMining(self):
|
||||
if self.mines is True:
|
||||
getter = self.getModifiedItemAttr
|
||||
cycleParams = self.getCycleParameters()
|
||||
if cycleParams is None:
|
||||
yps = 0
|
||||
else:
|
||||
cycleTime = cycleParams.averageTime
|
||||
yield_ = sum([getter(d) for d in self.MINING_ATTRIBUTES]) * self.amount
|
||||
yps = yield_ / (cycleTime / 1000.0)
|
||||
wasteChance = self.getModifiedItemAttr("miningWasteProbability")
|
||||
wasteMult = self.getModifiedItemAttr("miningWastedVolumeMultiplier")
|
||||
dps = yps * (1 + max(0, min(1, wasteChance / 100)) * wasteMult)
|
||||
return yps, dps
|
||||
else:
|
||||
return 0, 0
|
||||
|
||||
@property
|
||||
def maxRange(self):
|
||||
attrs = ("shieldTransferRange", "powerTransferRange",
|
||||
"energyDestabilizationRange", "empFieldRange",
|
||||
"ecmBurstRange", "maxRange")
|
||||
"ecmBurstRange", "maxRange", "ECMRangeOptimal")
|
||||
for attr in attrs:
|
||||
maxRange = self.getModifiedItemAttr(attr, None)
|
||||
if maxRange is not None:
|
||||
maxRange = self.getModifiedItemAttr(attr)
|
||||
if maxRange:
|
||||
return maxRange
|
||||
if self.charge is not None:
|
||||
delay = self.getModifiedChargeAttr("explosionDelay")
|
||||
@@ -256,6 +282,29 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
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.
|
||||
@@ -263,8 +312,8 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
def falloff(self):
|
||||
attrs = ("falloff", "falloffEffectiveness")
|
||||
for attr in attrs:
|
||||
falloff = self.getModifiedItemAttr(attr, None)
|
||||
if falloff is not None:
|
||||
falloff = self.getModifiedItemAttr(attr)
|
||||
if falloff:
|
||||
return falloff
|
||||
|
||||
@validates("ID", "itemID", "chargeID", "amount", "amountActive")
|
||||
@@ -285,7 +334,9 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
def clear(self):
|
||||
self.__baseVolley = None
|
||||
self.__baseRRAmount = None
|
||||
self.__miningyield = None
|
||||
self.__miningYield = None
|
||||
self.__miningDrain = None
|
||||
self.__ehp = None
|
||||
self.itemModifiedAttributes.clear()
|
||||
self.chargeModifiedAttributes.clear()
|
||||
|
||||
@@ -337,10 +388,11 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
effect.handler(fit, self, ("droneCharge",), projectionRange, effect=effect)
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
copy = Drone(self.item)
|
||||
copy = Drone(self.item, self.baseItem, self.mutaplasmid)
|
||||
copy.amount = self.amount
|
||||
copy.amountActive = self.amountActive
|
||||
copy.projectionRange = self.projectionRange
|
||||
self._mutaApplyMutators(mutatorClass=MutatorDrone, targetInstance=copy)
|
||||
return copy
|
||||
|
||||
def rebase(self, item):
|
||||
@@ -348,19 +400,28 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
amountActive = self.amountActive
|
||||
projectionRange = self.projectionRange
|
||||
|
||||
Drone.__init__(self, item)
|
||||
Drone.__init__(self, item, self.baseItem, self.mutaplasmid)
|
||||
self.amount = amount
|
||||
self.amountActive = amountActive
|
||||
self.projectionRange = projectionRange
|
||||
self._mutaApplyMutators(mutatorClass=MutatorDrone)
|
||||
|
||||
def fits(self, fit):
|
||||
fitDroneGroupLimits = set()
|
||||
for i in range(1, 3):
|
||||
groneGrp = fit.ship.getModifiedItemAttr("allowedDroneGroup%d" % i, None)
|
||||
if groneGrp is not None:
|
||||
groneGrp = fit.ship.getModifiedItemAttr("allowedDroneGroup%d" % i)
|
||||
if groneGrp:
|
||||
fitDroneGroupLimits.add(int(groneGrp))
|
||||
if len(fitDroneGroupLimits) == 0:
|
||||
return True
|
||||
if self.item.groupID in fitDroneGroupLimits:
|
||||
return True
|
||||
return False
|
||||
|
||||
def canDealDamage(self, ignoreState=False):
|
||||
if self.item is None:
|
||||
return False
|
||||
for effect in self.item.effects.values():
|
||||
if effect.dealsDamage and (ignoreState or self.amountActive > 0):
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
import math
|
||||
|
||||
from copy import deepcopy
|
||||
from logbook import Logger
|
||||
from sqlalchemy.orm import reconstructor, validates
|
||||
|
||||
@@ -96,10 +97,11 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
self.__charge = None
|
||||
self.__baseVolley = None
|
||||
self.__miningyield = None
|
||||
self.__ehp = None
|
||||
self.__itemModifiedAttributes = ModifiedAttributeDict()
|
||||
self.__chargeModifiedAttributes = ModifiedAttributeDict()
|
||||
|
||||
if len(self.abilities) != len(self.item.effects):
|
||||
if {a.effectID for a in self.abilities} != {e.ID for e in self.item.effects.values()}:
|
||||
self.__abilities = []
|
||||
for ability in self.__getAbilities():
|
||||
self.__abilities.append(ability)
|
||||
@@ -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]
|
||||
@@ -441,3 +453,15 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def canDealDamage(self, ignoreState=False, ignoreAbilityState=False):
|
||||
if self.item is None:
|
||||
return False
|
||||
if not self.active and not ignoreState:
|
||||
return False
|
||||
for ability in self.abilities:
|
||||
if not ability.active and not ignoreAbilityState:
|
||||
continue
|
||||
if ability.effect.dealsDamage:
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -21,27 +21,34 @@ import datetime
|
||||
import time
|
||||
from copy import deepcopy
|
||||
from itertools import chain
|
||||
from math import asinh, log, sqrt
|
||||
from math import ceil, log, sqrt
|
||||
|
||||
from logbook import Logger
|
||||
from sqlalchemy.orm import reconstructor, validates
|
||||
|
||||
import eos.db
|
||||
from eos import capSim
|
||||
from eos.calc import calculateLockTime, calculateMultiplier
|
||||
from eos.const import CalcType, FitSystemSecurity, FittingHardpoint, FittingModuleState, FittingSlot, ImplantLocation
|
||||
from eos.effectHandlerHelpers import (
|
||||
HandledBoosterList, HandledDroneCargoList, HandledImplantList,
|
||||
HandledModuleList, HandledProjectedDroneList, HandledProjectedModList)
|
||||
from eos.saveddata.character import Character
|
||||
from eos.saveddata.citadel import Citadel
|
||||
from eos.saveddata.damagePattern import DamagePattern
|
||||
from eos.saveddata.module import Module
|
||||
from eos.saveddata.ship import Ship
|
||||
from eos.saveddata.targetProfile import TargetProfile
|
||||
from eos.utils.float import floatUnerr
|
||||
from eos.utils.stats import DmgTypes, RRTypes
|
||||
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
|
||||
def _t(x):
|
||||
return x
|
||||
|
||||
|
||||
class FitLite:
|
||||
|
||||
def __init__(self, id=None, name=None, shipID=None, shipName=None, shipNameShort=None):
|
||||
@@ -132,9 +139,11 @@ class Fit:
|
||||
self.__weaponVolleyMap = {}
|
||||
self.__remoteRepMap = {}
|
||||
self.__minerYield = None
|
||||
self.__droneYield = None
|
||||
self.__minerDrain = None
|
||||
self.__droneDrain = None
|
||||
self.__droneDps = None
|
||||
self.__droneVolley = None
|
||||
self.__droneYield = None
|
||||
self.__sustainableTank = None
|
||||
self.__effectiveSustainableTank = None
|
||||
self.__effectiveTank = None
|
||||
@@ -148,8 +157,14 @@ class Fit:
|
||||
self.factorReload = False
|
||||
self.boostsFits = set()
|
||||
self.gangBoosts = None
|
||||
self.ecmProjectedStr = 1
|
||||
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
|
||||
@@ -162,14 +177,29 @@ class Fit:
|
||||
self.__capUsed = None
|
||||
self.__capRecharge = None
|
||||
self.__savedCapSimData.clear()
|
||||
# Ancillary tank modules affect this
|
||||
self.__sustainableTank = None
|
||||
self.__effectiveSustainableTank = None
|
||||
|
||||
@property
|
||||
def targetProfile(self):
|
||||
return self.__targetProfile
|
||||
if self.__userTargetProfile is not None:
|
||||
return self.__userTargetProfile
|
||||
if self.__builtinTargetProfileID is not None:
|
||||
return TargetProfile.getBuiltinById(self.__builtinTargetProfileID)
|
||||
return None
|
||||
|
||||
@targetProfile.setter
|
||||
def targetProfile(self, targetProfile):
|
||||
self.__targetProfile = targetProfile
|
||||
if targetProfile is None:
|
||||
self.__userTargetProfile = None
|
||||
self.__builtinTargetProfileID = None
|
||||
elif targetProfile.builtin:
|
||||
self.__userTargetProfile = None
|
||||
self.__builtinTargetProfileID = targetProfile.ID
|
||||
else:
|
||||
self.__userTargetProfile = targetProfile
|
||||
self.__builtinTargetProfileID = None
|
||||
self.__weaponDpsMap = {}
|
||||
self.__weaponVolleyMap = {}
|
||||
self.__droneDps = None
|
||||
@@ -177,11 +207,25 @@ class Fit:
|
||||
|
||||
@property
|
||||
def damagePattern(self):
|
||||
return self.__damagePattern
|
||||
if self.__userDamagePattern is not None:
|
||||
return self.__userDamagePattern
|
||||
if self.__builtinDamagePatternID is not None:
|
||||
pattern = DamagePattern.getBuiltinById(self.__builtinDamagePatternID)
|
||||
if pattern is not None:
|
||||
return pattern
|
||||
return DamagePattern.getDefaultBuiltin()
|
||||
|
||||
@damagePattern.setter
|
||||
def damagePattern(self, damagePattern):
|
||||
self.__damagePattern = damagePattern
|
||||
if damagePattern is None:
|
||||
self.__userDamagePattern = None
|
||||
self.__builtinDamagePatternID = None
|
||||
elif damagePattern.builtin:
|
||||
self.__userDamagePattern = None
|
||||
self.__builtinDamagePatternID = damagePattern.ID
|
||||
else:
|
||||
self.__userDamagePattern = damagePattern
|
||||
self.__builtinDamagePatternID = None
|
||||
self.__ehp = None
|
||||
self.__effectiveTank = None
|
||||
|
||||
@@ -329,25 +373,44 @@ class Fit:
|
||||
@property
|
||||
def minerYield(self):
|
||||
if self.__minerYield is None:
|
||||
self.calculateMiningStats()
|
||||
self.calculatemining()
|
||||
|
||||
return self.__minerYield
|
||||
|
||||
@property
|
||||
def minerDrain(self):
|
||||
if self.__minerDrain is None:
|
||||
self.calculatemining()
|
||||
|
||||
return self.__minerDrain
|
||||
|
||||
@property
|
||||
def droneYield(self):
|
||||
if self.__droneYield is None:
|
||||
self.calculateMiningStats()
|
||||
self.calculatemining()
|
||||
|
||||
return self.__droneYield
|
||||
|
||||
@property
|
||||
def droneDrain(self):
|
||||
if self.__droneDrain is None:
|
||||
self.calculatemining()
|
||||
|
||||
return self.__droneDrain
|
||||
|
||||
@property
|
||||
def totalYield(self):
|
||||
return self.droneYield + self.minerYield
|
||||
|
||||
@property
|
||||
def totalDrain(self):
|
||||
return self.droneDrain + self.minerDrain
|
||||
|
||||
@property
|
||||
def maxTargets(self):
|
||||
return min(self.extraAttributes["maxTargetsLockedFromSkills"],
|
||||
self.ship.getModifiedItemAttr("maxLockedTargets"))
|
||||
maxTargets = min(self.extraAttributes["maxTargetsLockedFromSkills"],
|
||||
self.ship.getModifiedItemAttr("maxLockedTargets"))
|
||||
return ceil(floatUnerr(maxTargets))
|
||||
|
||||
@property
|
||||
def maxTargetRange(self):
|
||||
@@ -361,20 +424,24 @@ class Fit:
|
||||
@property
|
||||
def scanType(self):
|
||||
maxStr = -1
|
||||
type = None
|
||||
for scanType in ("Magnetometric", "Ladar", "Radar", "Gravimetric"):
|
||||
type_ = None
|
||||
for scanType in (_t("Magnetometric"), _t("Ladar"), _t("Radar"), _t("Gravimetric")):
|
||||
currStr = self.ship.getModifiedItemAttr("scan%sStrength" % scanType)
|
||||
if currStr > maxStr:
|
||||
maxStr = currStr
|
||||
type = scanType
|
||||
type_ = scanType
|
||||
elif currStr == maxStr:
|
||||
type = "Multispectral"
|
||||
type_ = _t("Multispectral")
|
||||
|
||||
return type
|
||||
return type_
|
||||
|
||||
@property
|
||||
def jamChance(self):
|
||||
return (1 - self.ecmProjectedStr) * 100
|
||||
sensors = self.scanStrength
|
||||
retainLockChance = 1
|
||||
for jamStr in self.__ecmProjectedList:
|
||||
retainLockChance *= 1 - min(1, jamStr / sensors)
|
||||
return (1 - retainLockChance) * 100
|
||||
|
||||
@property
|
||||
def maxSpeed(self):
|
||||
@@ -409,9 +476,9 @@ class Fit:
|
||||
@validates("ID", "ownerID", "shipID")
|
||||
def validator(self, key, val):
|
||||
map = {
|
||||
"ID" : lambda _val: isinstance(_val, int),
|
||||
"ID": lambda _val: isinstance(_val, int),
|
||||
"ownerID": lambda _val: isinstance(_val, int) or _val is None,
|
||||
"shipID" : lambda _val: isinstance(_val, int) or _val is None
|
||||
"shipID": lambda _val: isinstance(_val, int) or _val is None
|
||||
}
|
||||
|
||||
if not map[key](val):
|
||||
@@ -450,11 +517,13 @@ class Fit:
|
||||
self.__weaponVolleyMap = {}
|
||||
self.__remoteRepMap = {}
|
||||
self.__minerYield = None
|
||||
self.__droneYield = None
|
||||
self.__minerDrain = None
|
||||
self.__droneDrain = None
|
||||
self.__effectiveSustainableTank = None
|
||||
self.__sustainableTank = None
|
||||
self.__droneDps = None
|
||||
self.__droneVolley = None
|
||||
self.__droneYield = None
|
||||
self.__ehp = None
|
||||
self.__calculated = False
|
||||
self.__capStable = None
|
||||
@@ -462,7 +531,7 @@ class Fit:
|
||||
self.__capUsed = None
|
||||
self.__capRecharge = None
|
||||
self.__savedCapSimData.clear()
|
||||
self.ecmProjectedStr = 1
|
||||
self.__ecmProjectedList = []
|
||||
# self.commandBonuses = {}
|
||||
|
||||
del self.__calculatedTargets[:]
|
||||
@@ -487,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.
|
||||
@@ -525,6 +600,9 @@ class Fit:
|
||||
if warfareBuffID not in self.commandBonuses or abs(self.commandBonuses[warfareBuffID][1]) < abs(value):
|
||||
self.commandBonuses[warfareBuffID] = (runTime, value, module, effect)
|
||||
|
||||
def addProjectedEcm(self, strength):
|
||||
self.__ecmProjectedList.append(strength)
|
||||
|
||||
def __runCommandBoosts(self, runTime="normal"):
|
||||
pyfalog.debug("Applying gang boosts for {0}", repr(self))
|
||||
for warfareBuffID in list(self.commandBonuses.keys()):
|
||||
@@ -544,29 +622,37 @@ class Fit:
|
||||
|
||||
if warfareBuffID == 11: # Shield Burst: Active Shielding: Repair Duration/Capacitor
|
||||
self.modules.filteredItemBoost(
|
||||
lambda mod: mod.item.requiresSkill("Shield Operation") or mod.item.requiresSkill(
|
||||
"Shield Emission Systems"), "capacitorNeed", value)
|
||||
lambda mod: mod.item.requiresSkill("Shield Operation") or
|
||||
mod.item.requiresSkill("Shield Emission Systems") or
|
||||
mod.item.requiresSkill("Capital Shield Emission Systems"),
|
||||
"capacitorNeed", value)
|
||||
self.modules.filteredItemBoost(
|
||||
lambda mod: mod.item.requiresSkill("Shield Operation") or mod.item.requiresSkill(
|
||||
"Shield Emission Systems"), "duration", value)
|
||||
lambda mod: mod.item.requiresSkill("Shield Operation") or
|
||||
mod.item.requiresSkill("Shield Emission Systems") or
|
||||
mod.item.requiresSkill("Capital Shield Emission Systems"),
|
||||
"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"):
|
||||
self.ship.boostItemAttr("armor%sDamageResonance" % damageType, value, stackingPenalties=True)
|
||||
|
||||
if warfareBuffID == 14: # Armor Burst: Rapid Repair: Repair Duration/Capacitor
|
||||
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems") or
|
||||
mod.item.requiresSkill("Repair Systems"),
|
||||
"capacitorNeed", value)
|
||||
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems") or
|
||||
mod.item.requiresSkill("Repair Systems"),
|
||||
"duration", value)
|
||||
self.modules.filteredItemBoost(
|
||||
lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems") or
|
||||
mod.item.requiresSkill("Repair Systems") or
|
||||
mod.item.requiresSkill("Capital Remote Armor Repair Systems"),
|
||||
"capacitorNeed", value)
|
||||
self.modules.filteredItemBoost(
|
||||
lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems") or
|
||||
mod.item.requiresSkill("Repair Systems") or
|
||||
mod.item.requiresSkill("Capital Remote Armor Repair Systems"),
|
||||
"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)
|
||||
@@ -616,15 +702,12 @@ class Fit:
|
||||
mod.item.requiresSkill("High Speed Maneuvering"),
|
||||
"speedFactor", value, stackingPenalties=True)
|
||||
|
||||
if warfareBuffID == 23: # Mining Burst: Mining Laser Field Enhancement: Mining/Survey Range
|
||||
if warfareBuffID == 23: # Mining Burst: Mining Laser Field Enhancement: Mining Range
|
||||
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining") or
|
||||
mod.item.requiresSkill("Ice Harvesting") or
|
||||
mod.item.requiresSkill("Gas Cloud Harvesting"),
|
||||
"maxRange", value, stackingPenalties=True)
|
||||
|
||||
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("CPU Management"),
|
||||
"surveyScanRange", value, stackingPenalties=True)
|
||||
|
||||
if warfareBuffID == 24: # Mining Burst: Mining Laser Optimization: Mining Capacitor/Duration
|
||||
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining") or
|
||||
mod.item.requiresSkill("Ice Harvesting") or
|
||||
@@ -637,8 +720,8 @@ class Fit:
|
||||
"duration", value, stackingPenalties=True)
|
||||
|
||||
if warfareBuffID == 25: # Mining Burst: Mining Equipment Preservation: Crystal Volatility
|
||||
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining"),
|
||||
"crystalVolatilityChance", value, stackingPenalties=True)
|
||||
self.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Mining"),
|
||||
"crystalVolatilityChance", value, stackingPenalties=True)
|
||||
|
||||
if warfareBuffID == 26: # Information Burst: Sensor Optimization: Targeting Range
|
||||
self.ship.boostItemAttr("maxTargetRange", value, stackingPenalties=True)
|
||||
@@ -660,7 +743,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"):
|
||||
@@ -682,7 +765,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"):
|
||||
@@ -710,14 +793,14 @@ class Fit:
|
||||
|
||||
if warfareBuffID == 79: # AOE_Beacon_bioluminescence_cloud
|
||||
self.ship.boostItemAttr("signatureRadius", value, stackingPenalties=True)
|
||||
self.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"),
|
||||
"signatureRadius", value, stackingPenalties=True)
|
||||
|
||||
if warfareBuffID == 80: # AOE_Beacon_caustic_cloud_local_repair
|
||||
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Repair Systems"),
|
||||
"armorDamageAmount", value, stackingPenalties=True)
|
||||
if warfareBuffID == 80: # AOE_Beacon_caustic_cloud_inertia
|
||||
self.ship.boostItemAttr("agility", value, stackingPenalties=True)
|
||||
|
||||
if warfareBuffID == 81: # AOE_Beacon_caustic_cloud_remote_repair
|
||||
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"),
|
||||
"armorDamageAmount", value, stackingPenalties=True)
|
||||
if warfareBuffID == 81: # AOE_Beacon_caustic_cloud_velocity
|
||||
self.ship.boostItemAttr("maxVelocity", value, stackingPenalties=True)
|
||||
|
||||
if warfareBuffID == 88: # AOE_Beacon_filament_cloud_shield_booster_shield_bonus
|
||||
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Operation"),
|
||||
@@ -732,7 +815,11 @@ class Fit:
|
||||
if warfareBuffID == 90: # Weather_electric_storm_EM_resistance_penalty
|
||||
for tankType in ("shield", "armor"):
|
||||
self.ship.boostItemAttr("{}EmDamageResonance".format(tankType), value)
|
||||
self.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"),
|
||||
"{}EmDamageResonance".format(tankType), value)
|
||||
self.ship.boostItemAttr("emDamageResonance", value) # for hull
|
||||
self.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"),
|
||||
"emDamageResonance", value) #for hull
|
||||
|
||||
if warfareBuffID == 92: # Weather_electric_storm_capacitor_recharge_bonus
|
||||
self.ship.boostItemAttr("rechargeRate", value, stackingPenalties=True)
|
||||
@@ -740,36 +827,132 @@ class Fit:
|
||||
if warfareBuffID == 93: # Weather_xenon_gas_explosive_resistance_penalty
|
||||
for tankType in ("shield", "armor"):
|
||||
self.ship.boostItemAttr("{}ExplosiveDamageResonance".format(tankType), value)
|
||||
self.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"),
|
||||
"{}ExplosiveDamageResonance".format(tankType), value)
|
||||
self.ship.boostItemAttr("explosiveDamageResonance", value) # for hull
|
||||
self.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"),
|
||||
"explosiveDamageResonance", value) # for hull
|
||||
|
||||
if warfareBuffID == 94: # Weather_xenon_gas_shield_hp_bonus
|
||||
self.ship.boostItemAttr("shieldCapacity", value) # for hull
|
||||
self.ship.boostItemAttr("shieldCapacity", value)
|
||||
self.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"),
|
||||
"shieldCapacity", value)
|
||||
|
||||
if warfareBuffID == 95: # Weather_infernal_thermal_resistance_penalty
|
||||
for tankType in ("shield", "armor"):
|
||||
self.ship.boostItemAttr("{}ThermalDamageResonance".format(tankType), value)
|
||||
self.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"),
|
||||
"{}ThermalDamageResonance".format(tankType), value)
|
||||
self.ship.boostItemAttr("thermalDamageResonance", value) # for hull
|
||||
self.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"),
|
||||
"thermalDamageResonance", value) # for hull
|
||||
|
||||
if warfareBuffID == 96: # Weather_infernal_armor_hp_bonus
|
||||
self.ship.boostItemAttr("armorHP", value) # for hull
|
||||
self.ship.boostItemAttr("armorHP", value)
|
||||
self.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"),
|
||||
"armorHP", value)
|
||||
|
||||
if warfareBuffID == 97: # Weather_darkness_turret_range_penalty
|
||||
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Gunnery"),
|
||||
"maxRange", value, stackingPenalties=True)
|
||||
self.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"),
|
||||
"maxRange", value, stackingPenalties=True)
|
||||
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Gunnery"),
|
||||
"falloff", value, stackingPenalties=True)
|
||||
self.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"),
|
||||
"falloff", value, stackingPenalties=True)
|
||||
|
||||
if warfareBuffID == 98: # Weather_darkness_velocity_bonus
|
||||
self.ship.boostItemAttr("maxVelocity", value)
|
||||
self.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"),
|
||||
"maxVelocity", value)
|
||||
|
||||
if warfareBuffID == 99: # Weather_caustic_toxin_kinetic_resistance_penalty
|
||||
for tankType in ("shield", "armor"):
|
||||
self.ship.boostItemAttr("{}KineticDamageResonance".format(tankType), value)
|
||||
self.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"),
|
||||
"{}KineticDamageResonance".format(tankType), value)
|
||||
self.ship.boostItemAttr("kineticDamageResonance", value) # for hull
|
||||
self.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"),
|
||||
"kineticDamageResonance", value) # for hull
|
||||
|
||||
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)
|
||||
|
||||
# Sov upgrades buffs
|
||||
if warfareBuffID == 2433: # Sov System Modifier Shield HP Bonus
|
||||
self.ship.boostItemAttr("shieldCapacity", value)
|
||||
if warfareBuffID == 2434: # Sov System Modifier Capacitor Capacity Bonus
|
||||
self.ship.boostItemAttr("capacitorCapacity", value)
|
||||
if warfareBuffID == 2435: # Sov System Modifier Armor HP Bonus
|
||||
self.ship.boostItemAttr("armorHP", value)
|
||||
if warfareBuffID == 2436: # Sov System Modifier Overheating Bonus - Includes Ewar
|
||||
for attr in (
|
||||
'overloadDurationBonus', 'overloadRofBonus', 'overloadSelfDurationBonus',
|
||||
'overloadHardeningBonus', 'overloadDamageModifier', 'overloadRangeBonus',
|
||||
'overloadSpeedFactorBonus', 'overloadECMStrengthBonus', 'overloadECCMStrenghtBonus',
|
||||
'overloadArmorDamageAmount', 'overloadShieldBonus', 'overloadTrackingModuleStrengthBonus',
|
||||
'overloadSensorModuleStrengthBonus', 'overloadPainterStrengthBonus',
|
||||
):
|
||||
self.modules.filteredItemBoost(lambda mod: attr in mod.itemModifiedAttributes, attr, value)
|
||||
if warfareBuffID == 2437: # Sov System Modifier Capacitor Recharge Bonus
|
||||
self.ship.boostItemAttr("rechargeRate", value)
|
||||
if warfareBuffID == 2438: # Sov System Modifier Targeting and DScan Range Bonus
|
||||
self.ship.boostItemAttr("maxTargetRange", value)
|
||||
self.ship.boostItemAttr("maxDirectionalScanRange", value)
|
||||
if warfareBuffID == 2439: # Sov System Modifier Scan Resolution Bonus
|
||||
self.ship.boostItemAttr("scanResolution", value)
|
||||
if warfareBuffID == 2440: # Sov System Modifier Warp Speed Addition
|
||||
self.ship.increaseItemAttr('warpSpeedMultiplier', value)
|
||||
if warfareBuffID == 2441: # Sov System Modifier Shield Booster Bonus
|
||||
self.modules.filteredItemBoost(
|
||||
lambda mod: (mod.item.requiresSkill("Shield Operation")
|
||||
or mod.item.requiresSkill("Capital Shield Operation")),
|
||||
"shieldBonus", value, stackingPenalties=True)
|
||||
if warfareBuffID == 2442: # Sov System Modifier Armor Repairer Bonus
|
||||
self.modules.filteredItemBoost(
|
||||
lambda mod: (mod.item.requiresSkill("Repair Systems")
|
||||
or mod.item.requiresSkill("Capital Repair Systems")),
|
||||
"armorDamageAmount", value, stackingPenalties=True)
|
||||
if warfareBuffID == 2464: # Expedition Burst: Probe Strength
|
||||
self.modules.filteredChargeBoost(
|
||||
lambda mod: mod.charge.requiresSkill('Astrometrics'),
|
||||
'baseSensorStrength', value, stackingPenalties=True)
|
||||
if warfareBuffID == 2465: # Expedition Burst: Directional Scanner, Hacking and Salvager Range
|
||||
self.ship.boostItemAttr("maxDirectionalScanRange", value)
|
||||
self.modules.filteredItemBoost(
|
||||
lambda mod: mod.item.group.name in ("Data Miners", "Salvager"), "maxRange", value, stackingPenalties=True)
|
||||
if warfareBuffID == 2466: # Expedition Burst: Maximum Scan Deviation Modifier
|
||||
self.modules.filteredChargeBoost(
|
||||
lambda mod: mod.charge.requiresSkill('Astrometrics'),
|
||||
'baseMaxScanDeviation', value, stackingPenalties=True)
|
||||
if warfareBuffID == 2468: # Expedition Burst: Virus Coherence
|
||||
self.modules.filteredItemIncrease(
|
||||
lambda mod: mod.item.group.name == "Data Miners", "virusCoherence", value)
|
||||
if warfareBuffID == 2474: # Mining burst charges
|
||||
self.ship.forceItemAttr("miningScannerUpgrade", value)
|
||||
if warfareBuffID == 2481: # Expedition Burst: Salvager duration bonus
|
||||
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Salvaging"), "duration", value)
|
||||
if warfareBuffID == 2516: # Mining Burst: Mining Crit Chance
|
||||
self.modules.filteredItemBoost(
|
||||
lambda mod: mod.item.requiresSkill("Mining") or mod.item.requiresSkill("Ice Harvesting"),
|
||||
"miningCritChance", value)
|
||||
if warfareBuffID == 2517: # Mining Burst: Mining Residue Chance Reduction
|
||||
self.modules.filteredItemBoost(
|
||||
lambda mod: (
|
||||
mod.item.requiresSkill("Mining")
|
||||
or mod.item.requiresSkill("Ice Harvesting")
|
||||
or mod.item.requiresSkill("Gas Cloud Harvesting")),
|
||||
"miningWasteProbability", value, stackingPenalties=True)
|
||||
|
||||
del self.commandBonuses[warfareBuffID]
|
||||
|
||||
def __resetDependentCalcs(self):
|
||||
@@ -938,14 +1121,14 @@ class Fit:
|
||||
for _ in range(projectionInfo.amount):
|
||||
targetFit.register(item, origin=self)
|
||||
item.calculateModifiedAttributes(
|
||||
targetFit, runTime, forceProjected=True,
|
||||
forcedProjRange=0)
|
||||
targetFit, runTime, forceProjected=True,
|
||||
forcedProjRange=0)
|
||||
for mod in self.modules:
|
||||
for _ in range(projectionInfo.amount):
|
||||
targetFit.register(mod, origin=self)
|
||||
mod.calculateModifiedAttributes(
|
||||
targetFit, runTime, forceProjected=True,
|
||||
forcedProjRange=projectionInfo.projectionRange)
|
||||
targetFit, runTime, forceProjected=True,
|
||||
forcedProjRange=projectionInfo.projectionRange)
|
||||
|
||||
def fill(self):
|
||||
"""
|
||||
@@ -961,7 +1144,8 @@ class Fit:
|
||||
|
||||
# Look for any dummies of that type to remove
|
||||
posToRemove = {}
|
||||
for slotType in (FittingSlot.LOW.value, FittingSlot.MED.value, FittingSlot.HIGH.value, FittingSlot.RIG.value, FittingSlot.SUBSYSTEM.value, FittingSlot.SERVICE.value):
|
||||
for slotType in (
|
||||
FittingSlot.LOW.value, FittingSlot.MED.value, FittingSlot.HIGH.value, FittingSlot.RIG.value, FittingSlot.SUBSYSTEM.value, FittingSlot.SERVICE.value):
|
||||
amount = self.getSlotsFree(slotType, True)
|
||||
if amount > 0:
|
||||
for _ in range(int(amount)):
|
||||
@@ -986,6 +1170,16 @@ class Fit:
|
||||
if mod.isEmpty:
|
||||
del self.modules[i]
|
||||
|
||||
def clearTail(self):
|
||||
tailPositions = {}
|
||||
for mod in reversed(self.modules):
|
||||
if not mod.isEmpty:
|
||||
break
|
||||
tailPositions[self.modules.index(mod)] = mod.slot
|
||||
for pos in sorted(tailPositions, reverse=True):
|
||||
self.modules.remove(self.modules[pos])
|
||||
return tailPositions
|
||||
|
||||
@property
|
||||
def modCount(self):
|
||||
x = 0
|
||||
@@ -1028,22 +1222,23 @@ class Fit:
|
||||
|
||||
for mod in chain(self.modules, self.fighters):
|
||||
if mod.slot is type and (not getattr(mod, "isEmpty", False) or countDummies):
|
||||
if type in (FittingSlot.F_HEAVY, FittingSlot.F_SUPPORT, FittingSlot.F_LIGHT, FittingSlot.FS_HEAVY, FittingSlot.FS_LIGHT, FittingSlot.FS_SUPPORT) and not mod.active:
|
||||
if type in (FittingSlot.F_HEAVY, FittingSlot.F_SUPPORT, FittingSlot.F_LIGHT, FittingSlot.FS_HEAVY, FittingSlot.FS_LIGHT,
|
||||
FittingSlot.FS_SUPPORT) and not mod.active:
|
||||
continue
|
||||
amount += 1
|
||||
|
||||
return amount
|
||||
|
||||
slots = {
|
||||
FittingSlot.LOW : "lowSlots",
|
||||
FittingSlot.MED : "medSlots",
|
||||
FittingSlot.HIGH : "hiSlots",
|
||||
FittingSlot.RIG : "rigSlots",
|
||||
FittingSlot.LOW: "lowSlots",
|
||||
FittingSlot.MED: "medSlots",
|
||||
FittingSlot.HIGH: "hiSlots",
|
||||
FittingSlot.RIG: "rigSlots",
|
||||
FittingSlot.SUBSYSTEM: "maxSubSystems",
|
||||
FittingSlot.SERVICE : "serviceSlots",
|
||||
FittingSlot.F_LIGHT : "fighterLightSlots",
|
||||
FittingSlot.SERVICE: "serviceSlots",
|
||||
FittingSlot.F_LIGHT: "fighterLightSlots",
|
||||
FittingSlot.F_SUPPORT: "fighterSupportSlots",
|
||||
FittingSlot.F_HEAVY : "fighterHeavySlots",
|
||||
FittingSlot.F_HEAVY: "fighterHeavySlots",
|
||||
FittingSlot.FS_LIGHT: "fighterStandupLightSlots",
|
||||
FittingSlot.FS_SUPPORT: "fighterStandupSupportSlots",
|
||||
FittingSlot.FS_HEAVY: "fighterStandupHeavySlots",
|
||||
@@ -1095,7 +1290,7 @@ class Fit:
|
||||
def droneBayUsed(self):
|
||||
amount = 0
|
||||
for d in self.drones:
|
||||
amount += d.item.volume * d.amount
|
||||
amount += d.item.attributes['volume'].value * d.amount
|
||||
|
||||
return amount
|
||||
|
||||
@@ -1103,7 +1298,7 @@ class Fit:
|
||||
def fighterBayUsed(self):
|
||||
amount = 0
|
||||
for f in self.fighters:
|
||||
amount += f.item.volume * f.amount
|
||||
amount += f.item.attributes['volume'].value * f.amount
|
||||
|
||||
return amount
|
||||
|
||||
@@ -1325,8 +1520,8 @@ class Fit:
|
||||
"""Return how much cap regen do we gain from having this module"""
|
||||
currentRegen = self.calculateCapRecharge()
|
||||
nomodRegen = self.calculateCapRecharge(
|
||||
capacity=self.ship.getModifiedItemAttrExtended("capacitorCapacity", ignoreAfflictors=[mod]),
|
||||
rechargeRate=self.ship.getModifiedItemAttrExtended("rechargeRate", ignoreAfflictors=[mod]) / 1000.0)
|
||||
capacity=self.ship.getModifiedItemAttrExtended("capacitorCapacity", ignoreAfflictors=[mod]),
|
||||
rechargeRate=self.ship.getModifiedItemAttrExtended("rechargeRate", ignoreAfflictors=[mod]) / 1000.0)
|
||||
return currentRegen - nomodRegen
|
||||
|
||||
def getRemoteReps(self, spoolOptions=None):
|
||||
@@ -1357,7 +1552,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
|
||||
@@ -1366,11 +1561,12 @@ 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
|
||||
|
||||
@property
|
||||
@@ -1406,11 +1602,12 @@ 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
|
||||
groupAttrMap = {
|
||||
@@ -1418,14 +1615,16 @@ class Fit:
|
||||
"Ancillary Shield Booster": "shieldBonus",
|
||||
"Armor Repair Unit": "armorDamageAmount",
|
||||
"Ancillary Armor Repairer": "armorDamageAmount",
|
||||
"Hull Repair Unit": "structureDamageAmount"}
|
||||
"Hull Repair Unit": "structureDamageAmount"
|
||||
}
|
||||
# Map local repairer type to tank type
|
||||
groupStoreMap = {
|
||||
"Shield Booster": "shieldRepair",
|
||||
"Ancillary Shield Booster": "shieldRepair",
|
||||
"Armor Repair Unit": "armorRepair",
|
||||
"Ancillary Armor Repairer": "armorRepair",
|
||||
"Hull Repair Unit": "hullRepair"}
|
||||
"Hull Repair Unit": "hullRepair"
|
||||
}
|
||||
repairers = []
|
||||
localAdjustment = {"shieldRepair": 0, "armorRepair": 0, "hullRepair": 0}
|
||||
capUsed = self.capUsed
|
||||
@@ -1477,7 +1676,7 @@ class Fit:
|
||||
|
||||
# Sort repairers by efficiency. We want to use the most efficient repairers first
|
||||
repairers.sort(key=lambda _mod: _mod.getModifiedItemAttr(
|
||||
groupAttrMap[_mod.item.group.name]) * (_mod.getModifiedItemAttr(
|
||||
groupAttrMap[_mod.item.group.name]) * (_mod.getModifiedItemAttr(
|
||||
"chargedArmorDamageMultiplier") or 1) / _mod.getModifiedItemAttr("capacitorNeed"), reverse=True)
|
||||
|
||||
# Loop through every module until we're above peak recharge
|
||||
@@ -1529,47 +1728,56 @@ class Fit:
|
||||
def calculateLockTime(self, radius):
|
||||
scanRes = self.ship.getModifiedItemAttr("scanResolution")
|
||||
if scanRes is not None and scanRes > 0:
|
||||
# Yes, this function returns time in seconds, not miliseconds.
|
||||
# 40,000 is indeed the correct constant here.
|
||||
return min(40000 / scanRes / asinh(radius) ** 2, 30 * 60)
|
||||
return calculateLockTime(srcScanRes=scanRes, tgtSigRadius=radius)
|
||||
else:
|
||||
return self.ship.getModifiedItemAttr("scanSpeed") / 1000.0
|
||||
|
||||
def calculateMiningStats(self):
|
||||
def calculatemining(self):
|
||||
minerYield = 0
|
||||
minerDrain = 0
|
||||
droneYield = 0
|
||||
droneDrain = 0
|
||||
|
||||
for mod in self.modules:
|
||||
minerYield += mod.miningStats
|
||||
|
||||
minerYield += mod.getMiningYPS()
|
||||
minerDrain += mod.getMiningDPS()
|
||||
for drone in self.drones:
|
||||
droneYield += drone.miningStats
|
||||
droneYield += drone.getMiningYPS()
|
||||
droneDrain += drone.getMiningDPS()
|
||||
|
||||
self.__minerYield = minerYield
|
||||
self.__minerDrain = minerDrain
|
||||
self.__droneYield = droneYield
|
||||
self.__droneDrain = droneDrain
|
||||
|
||||
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
|
||||
@@ -1604,6 +1812,17 @@ 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:
|
||||
@@ -1626,6 +1845,54 @@ class Fit:
|
||||
if ability.active:
|
||||
yield fighter, ability
|
||||
|
||||
def getDampMultScanRes(self):
|
||||
damps = []
|
||||
for mod in self.activeModulesIter():
|
||||
for effectName in ('remoteSensorDampFalloff', 'structureModuleEffectRemoteSensorDampener'):
|
||||
if effectName in mod.item.effects:
|
||||
damps.append((mod.getModifiedItemAttr('scanResolutionBonus'), 'default'))
|
||||
if 'doomsdayAOEDamp' in mod.item.effects:
|
||||
damps.append((mod.getModifiedItemAttr('scanResolutionBonus'), 'default'))
|
||||
for drone in self.activeDronesIter():
|
||||
if 'remoteSensorDampEntity' in drone.item.effects:
|
||||
damps.extend(drone.amountActive * ((drone.getModifiedItemAttr('scanResolutionBonus'), 'default'),))
|
||||
mults = {}
|
||||
for strength, stackingGroup in damps:
|
||||
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
|
||||
@@ -1638,6 +1905,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:
|
||||
|
||||
@@ -23,7 +23,6 @@ from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut
|
||||
|
||||
class Mode(ItemAttrShortcut, HandledItem):
|
||||
|
||||
|
||||
def __init__(self, item, owner=None):
|
||||
if item.group.name != "Ship Modifiers":
|
||||
raise ValueError(
|
||||
@@ -34,7 +33,6 @@ class Mode(ItemAttrShortcut, HandledItem):
|
||||
self.__itemModifiedAttributes.original = self.item.attributes
|
||||
self.__itemModifiedAttributes.overrides = self.item.overrides
|
||||
|
||||
|
||||
@property
|
||||
def item(self):
|
||||
return self.__item
|
||||
|
||||
@@ -27,12 +27,13 @@ from eos.const import FittingHardpoint, FittingModuleState, FittingSlot
|
||||
from eos.effectHandlerHelpers import HandledCharge, HandledItem
|
||||
from eos.modifiedAttributeDict import ChargeAttrShortcut, ItemAttrShortcut, ModifiedAttributeDict
|
||||
from eos.saveddata.citadel import Citadel
|
||||
from eos.saveddata.mutator import Mutator
|
||||
from eos.saveddata.mutatedMixin import MutatedMixin, MutaError
|
||||
from eos.saveddata.mutator import MutatorModule
|
||||
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__)
|
||||
@@ -61,30 +62,20 @@ ProjectedSystem = {
|
||||
}
|
||||
|
||||
|
||||
class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut, MutatedMixin):
|
||||
"""An instance of this class represents a module together with its charge and modified attributes"""
|
||||
MINING_ATTRIBUTES = ("miningAmount",)
|
||||
SYSTEM_GROUPS = ("Effect Beacon", "MassiveEnvironments", "Abyssal Hazards", "Non-Interactable Object")
|
||||
SYSTEM_GROUPS = (
|
||||
"Effect Beacon", "MassiveEnvironments", "Abyssal Hazards", "Non-Interactable Object",
|
||||
"Destructible Effect Beacon", "Sovereignty Hub System Effect Generator Upgrades")
|
||||
|
||||
def __init__(self, item, baseItem=None, mutaplasmid=None):
|
||||
"""Initialize a module from the program"""
|
||||
|
||||
self.itemID = item.ID if item is not None else None
|
||||
self.baseItemID = baseItem.ID if baseItem is not None else None
|
||||
self.mutaplasmidID = mutaplasmid.ID if mutaplasmid is not None else None
|
||||
|
||||
if baseItem is not None:
|
||||
# we're working with a mutated module, need to get abyssal module loaded with the base attributes
|
||||
# Note: there may be a better way of doing this, such as a metho on this classe to convert(mutaplamid). This
|
||||
# will require a bit more research though, considering there has never been a need to "swap" out the item of a Module
|
||||
# before, and there may be assumptions taken with regards to the item never changing (pre-calculated / cached results, for example)
|
||||
self.__item = eos.db.getItemWithBaseItemAttribute(self.itemID, self.baseItemID)
|
||||
self.__baseItem = baseItem
|
||||
self.__mutaplasmid = mutaplasmid
|
||||
else:
|
||||
self.__item = item
|
||||
self.__baseItem = baseItem
|
||||
self.__mutaplasmid = mutaplasmid
|
||||
self._item = item
|
||||
self._mutaInit(baseItem=baseItem, mutaplasmid=mutaplasmid)
|
||||
|
||||
if item is not None and self.isInvalid:
|
||||
raise ValueError("Passed item is not a Module")
|
||||
@@ -99,27 +90,22 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
@reconstructor
|
||||
def init(self):
|
||||
"""Initialize a module from the database and validate"""
|
||||
self.__item = None
|
||||
self.__baseItem = None
|
||||
self._item = None
|
||||
self.__charge = None
|
||||
self.__mutaplasmid = None
|
||||
|
||||
# we need this early if module is invalid and returns early
|
||||
self.__slot = self.dummySlot
|
||||
|
||||
if self.itemID:
|
||||
self.__item = eos.db.getItem(self.itemID)
|
||||
if self.__item is None:
|
||||
self._item = eos.db.getItem(self.itemID)
|
||||
if self._item is None:
|
||||
pyfalog.error("Item (id: {0}) does not exist", self.itemID)
|
||||
return
|
||||
|
||||
if self.baseItemID:
|
||||
self.__item = eos.db.getItemWithBaseItemAttribute(self.itemID, self.baseItemID)
|
||||
self.__baseItem = eos.db.getItem(self.baseItemID)
|
||||
self.__mutaplasmid = eos.db.getMutaplasmid(self.mutaplasmidID)
|
||||
if self.__baseItem is None:
|
||||
pyfalog.error("Base Item (id: {0}) does not exist", self.itemID)
|
||||
return
|
||||
try:
|
||||
self._mutaReconstruct()
|
||||
except MutaError:
|
||||
return
|
||||
|
||||
if self.isInvalid:
|
||||
pyfalog.error("Item (id: {0}) is not a Module", self.itemID)
|
||||
@@ -136,9 +122,12 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
if self.__charge and self.__charge.category.name != "Charge":
|
||||
self.__charge = None
|
||||
|
||||
self.rahPatternOverride = None
|
||||
|
||||
self.__baseVolley = None
|
||||
self.__baseRRAmount = None
|
||||
self.__miningyield = None
|
||||
self.__miningYield = None
|
||||
self.__miningDrain = None
|
||||
self.__reloadTime = None
|
||||
self.__reloadForce = None
|
||||
self.__chargeCycles = None
|
||||
@@ -147,21 +136,13 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
self.__chargeModifiedAttributes = ModifiedAttributeDict(parent=self)
|
||||
self.__slot = self.dummySlot # defaults to None
|
||||
|
||||
if self.__item:
|
||||
self.__itemModifiedAttributes.original = self.__item.attributes
|
||||
self.__itemModifiedAttributes.overrides = self.__item.overrides
|
||||
self.__hardpoint = self.__calculateHardpoint(self.__item)
|
||||
self.__slot = self.calculateSlot(self.__item)
|
||||
|
||||
# Instantiate / remove mutators if this is a mutated module
|
||||
if self.__baseItem:
|
||||
for x in self.mutaplasmid.attributes:
|
||||
attr = self.item.attributes[x.name]
|
||||
id = attr.ID
|
||||
if id not in self.mutators: # create the mutator
|
||||
Mutator(self, attr, attr.value)
|
||||
# @todo: remove attributes that are no longer part of the mutaplasmid.
|
||||
if self._item:
|
||||
self.__itemModifiedAttributes.original = self._item.attributes
|
||||
self.__itemModifiedAttributes.overrides = self._item.overrides
|
||||
self.__hardpoint = self.__calculateHardpoint(self._item)
|
||||
self.__slot = self.calculateSlot(self._item)
|
||||
|
||||
self._mutaLoadMutators(mutatorClass=MutatorModule)
|
||||
self.__itemModifiedAttributes.mutators = self.mutators
|
||||
|
||||
if self.__charge:
|
||||
@@ -196,15 +177,21 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
# todo: validate baseItem as well if it's set.
|
||||
if self.isEmpty:
|
||||
return False
|
||||
return (
|
||||
self.__item is None or (
|
||||
self.__item.category.name not in ("Module", "Subsystem", "Structure Module") and
|
||||
self.__item.group.name not in self.SYSTEM_GROUPS) or
|
||||
(self.item.isAbyssal and not self.isMutated))
|
||||
|
||||
@property
|
||||
def isMutated(self):
|
||||
return self.baseItemID and self.mutaplasmidID
|
||||
if self._item is None:
|
||||
return True
|
||||
if (
|
||||
self._item.category.name not in ("Module", "Subsystem", "Structure Module")
|
||||
and self._item.group.name not in self.SYSTEM_GROUPS
|
||||
):
|
||||
return True
|
||||
if (
|
||||
self._item.category.name == "Structure Module"
|
||||
and self._item.group.name == "Quantum Cores"
|
||||
):
|
||||
return True
|
||||
if self._mutaIsInvalid:
|
||||
return True
|
||||
return False
|
||||
|
||||
@property
|
||||
def numCharges(self):
|
||||
@@ -214,8 +201,8 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
if charge is None:
|
||||
charges = 0
|
||||
else:
|
||||
chargeVolume = charge.volume
|
||||
containerCapacity = self.item.capacity
|
||||
chargeVolume = charge.attributes['volume'].value
|
||||
containerCapacity = self.item.attributes['capacity'].value
|
||||
if chargeVolume is None or containerCapacity is None:
|
||||
charges = 0
|
||||
else:
|
||||
@@ -265,7 +252,9 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
|
||||
@property
|
||||
def isExclusiveSystemEffect(self):
|
||||
return self.item.group.name in ("Effect Beacon", "Non-Interactable Object", "MassiveEnvironments")
|
||||
# See issue #2258
|
||||
# return self.item.group.name in ("Effect Beacon", "Non-Interactable Object", "MassiveEnvironments")
|
||||
return False
|
||||
|
||||
@property
|
||||
def isCapitalSize(self):
|
||||
@@ -318,43 +307,75 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
"energyDestabilizationRange", "empFieldRange",
|
||||
"ecmBurstRange", "warpScrambleRange", "cargoScanRange",
|
||||
"shipScanRange", "surveyScanRange")
|
||||
maxRange = None
|
||||
for attr in attrs:
|
||||
maxRange = self.getModifiedItemAttr(attr, None)
|
||||
if maxRange is not None:
|
||||
return maxRange
|
||||
if self.charge is not None:
|
||||
try:
|
||||
chargeName = self.charge.group.name
|
||||
except AttributeError:
|
||||
pass
|
||||
else:
|
||||
if chargeName in ("Scanner Probe", "Survey Probe"):
|
||||
return None
|
||||
maxRange = self.getModifiedItemAttr(attr)
|
||||
if maxRange:
|
||||
break
|
||||
if maxRange:
|
||||
if 'burst projector' in self.item.name.lower():
|
||||
maxRange -= self.owner.ship.getModifiedItemAttr("radius")
|
||||
return maxRange
|
||||
missileMaxRangeData = self.missileMaxRangeData
|
||||
if missileMaxRangeData is None:
|
||||
return None
|
||||
lowerRange, higherRange, higherChance = missileMaxRangeData
|
||||
maxRange = lowerRange * (1 - higherChance) + higherRange * higherChance
|
||||
return maxRange
|
||||
|
||||
@property
|
||||
def missileMaxRangeData(self):
|
||||
if self.charge is None:
|
||||
return None
|
||||
try:
|
||||
chargeName = self.charge.group.name
|
||||
except AttributeError:
|
||||
pass
|
||||
else:
|
||||
if chargeName in ("Scanner Probe", "Survey Probe"):
|
||||
return None
|
||||
|
||||
def calculateRange(maxVelocity, mass, agility, flightTime):
|
||||
# Source: http://www.eveonline.com/ingameboard.asp?a=topic&threadID=1307419&page=1#15
|
||||
# D_m = V_m * (T_m + T_0*[exp(- T_m/T_0)-1])
|
||||
maxVelocity = self.getModifiedChargeAttr("maxVelocity")
|
||||
flightTime = self.getModifiedChargeAttr("explosionDelay") / 1000.0
|
||||
mass = self.getModifiedChargeAttr("mass")
|
||||
agility = self.getModifiedChargeAttr("agility")
|
||||
if maxVelocity and (flightTime or mass or agility):
|
||||
accelTime = min(flightTime, mass * agility / 1000000)
|
||||
# Average distance done during acceleration
|
||||
duringAcceleration = maxVelocity / 2 * accelTime
|
||||
# Distance done after being at full speed
|
||||
fullSpeed = maxVelocity * (flightTime - accelTime)
|
||||
maxRange = duringAcceleration + fullSpeed
|
||||
if 'fofMissileLaunching' in self.charge.effects:
|
||||
rangeLimit = self.getModifiedChargeAttr("maxFOFTargetRange")
|
||||
if rangeLimit:
|
||||
maxRange = min(maxRange, rangeLimit)
|
||||
return maxRange
|
||||
accelTime = min(flightTime, mass * agility / 1000000)
|
||||
# Average distance done during acceleration
|
||||
duringAcceleration = maxVelocity / 2 * accelTime
|
||||
# Distance done after being at full speed
|
||||
fullSpeed = maxVelocity * (flightTime - accelTime)
|
||||
maxRange = duringAcceleration + fullSpeed
|
||||
return maxRange
|
||||
|
||||
maxVelocity = self.getModifiedChargeAttr("maxVelocity")
|
||||
if not maxVelocity:
|
||||
return None
|
||||
shipRadius = self.owner.ship.getModifiedItemAttr("radius")
|
||||
# Flight time has bonus based on ship radius, see https://github.com/pyfa-org/Pyfa/issues/2083
|
||||
flightTime = floatUnerr(self.getModifiedChargeAttr("explosionDelay") / 1000 + shipRadius / maxVelocity)
|
||||
mass = self.getModifiedChargeAttr("mass")
|
||||
agility = self.getModifiedChargeAttr("agility")
|
||||
lowerTime = math.floor(flightTime)
|
||||
higherTime = math.ceil(flightTime)
|
||||
lowerRange = calculateRange(maxVelocity, mass, agility, lowerTime)
|
||||
higherRange = calculateRange(maxVelocity, mass, agility, higherTime)
|
||||
# Fof range limit is supposedly calculated based on overview (surface-to-surface) range
|
||||
if 'fofMissileLaunching' in self.charge.effects:
|
||||
rangeLimit = self.getModifiedChargeAttr("maxFOFTargetRange")
|
||||
if rangeLimit:
|
||||
lowerRange = min(lowerRange, rangeLimit)
|
||||
higherRange = min(higherRange, rangeLimit)
|
||||
# Make range center-to-surface, as missiles spawn in the center of the ship
|
||||
lowerRange = max(0, lowerRange - shipRadius)
|
||||
higherRange = max(0, higherRange - shipRadius)
|
||||
higherChance = flightTime - lowerTime
|
||||
return lowerRange, higherRange, higherChance
|
||||
|
||||
@property
|
||||
def falloff(self):
|
||||
attrs = ("falloffEffectiveness", "falloff", "shipScanFalloff")
|
||||
for attr in attrs:
|
||||
falloff = self.getModifiedItemAttr(attr, None)
|
||||
if falloff is not None:
|
||||
falloff = self.getModifiedItemAttr(attr)
|
||||
if falloff:
|
||||
return falloff
|
||||
|
||||
@property
|
||||
@@ -371,15 +392,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
|
||||
@property
|
||||
def item(self):
|
||||
return self.__item if self.__item != 0 else None
|
||||
|
||||
@property
|
||||
def baseItem(self):
|
||||
return self.__baseItem
|
||||
|
||||
@property
|
||||
def mutaplasmid(self):
|
||||
return self.__mutaplasmid
|
||||
return self._item if self._item != 0 else None
|
||||
|
||||
@property
|
||||
def charge(self):
|
||||
@@ -399,28 +412,42 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
|
||||
self.__itemModifiedAttributes.clear()
|
||||
|
||||
@property
|
||||
def miningStats(self):
|
||||
if self.__miningyield is None:
|
||||
if self.isEmpty:
|
||||
self.__miningyield = 0
|
||||
else:
|
||||
if self.state >= FittingModuleState.ACTIVE:
|
||||
volley = self.getModifiedItemAttr("specialtyMiningAmount") or self.getModifiedItemAttr(
|
||||
"miningAmount") or 0
|
||||
if volley:
|
||||
cycleParams = self.getCycleParameters()
|
||||
if cycleParams is None:
|
||||
self.__miningyield = 0
|
||||
else:
|
||||
cycleTime = cycleParams.averageTime
|
||||
self.__miningyield = volley / (cycleTime / 1000.0)
|
||||
else:
|
||||
self.__miningyield = 0
|
||||
else:
|
||||
self.__miningyield = 0
|
||||
def getMiningYPS(self, ignoreState=False):
|
||||
if self.isEmpty:
|
||||
return 0
|
||||
if not ignoreState and self.state < FittingModuleState.ACTIVE:
|
||||
return 0
|
||||
if self.__miningYield is None:
|
||||
self.__miningYield, self.__miningDrain = self.__calculateMining()
|
||||
return self.__miningYield
|
||||
|
||||
return self.__miningyield
|
||||
def getMiningDPS(self, ignoreState=False):
|
||||
if self.isEmpty:
|
||||
return 0
|
||||
if not ignoreState and self.state < FittingModuleState.ACTIVE:
|
||||
return 0
|
||||
if self.__miningDrain is None:
|
||||
self.__miningYield, self.__miningDrain = self.__calculateMining()
|
||||
return self.__miningDrain
|
||||
|
||||
def __calculateMining(self):
|
||||
yield_ = self.getModifiedItemAttr("miningAmount")
|
||||
if yield_:
|
||||
cycleParams = self.getCycleParameters()
|
||||
if cycleParams is None:
|
||||
yps = 0
|
||||
else:
|
||||
cycleTime = cycleParams.averageTime
|
||||
yps = yield_ / (cycleTime / 1000.0)
|
||||
else:
|
||||
yps = 0
|
||||
wasteChance = self.getModifiedItemAttr("miningWasteProbability")
|
||||
wasteMult = self.getModifiedItemAttr("miningWastedVolumeMultiplier")
|
||||
dps = yps * (1 + max(0, min(1, wasteChance / 100)) * wasteMult)
|
||||
critChance = self.getModifiedItemAttr("miningCritChance")
|
||||
critBonusMult = self.getModifiedItemAttr("miningCritBonusYield")
|
||||
yps += yps * critChance * critBonusMult
|
||||
return yps, dps
|
||||
|
||||
def isDealingDamage(self, ignoreState=False):
|
||||
volleyParams = self.getVolleyParameters(ignoreState=ignoreState)
|
||||
@@ -429,71 +456,96 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
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
|
||||
for effect in self.item.effects.values():
|
||||
if effect.dealsDamage and (
|
||||
ignoreState or
|
||||
effect.isType('offline') or
|
||||
(effect.isType('passive') and self.state >= FittingModuleState.ONLINE) or
|
||||
(effect.isType('active') and self.state >= FittingModuleState.ACTIVE) or
|
||||
(effect.isType('overheat') and self.state >= FittingModuleState.OVERHEATED)
|
||||
):
|
||||
return True
|
||||
return False
|
||||
|
||||
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):
|
||||
dmgDuringCycle = DmgTypes(0, 0, 0, 0)
|
||||
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
|
||||
return dps
|
||||
if self.isBreacher:
|
||||
return volleyParams[min(volleyParams)]
|
||||
for volleyValue in volleyParams.values():
|
||||
dmgDuringCycle += volleyValue
|
||||
dps += volleyValue
|
||||
dpsFactor = 1 / (avgCycleTime / 1000)
|
||||
dps = DmgTypes(
|
||||
em=dmgDuringCycle.em * dpsFactor,
|
||||
thermal=dmgDuringCycle.thermal * dpsFactor,
|
||||
kinetic=dmgDuringCycle.kinetic * dpsFactor,
|
||||
explosive=dmgDuringCycle.explosive * dpsFactor)
|
||||
dps *= dpsFactor
|
||||
return dps
|
||||
|
||||
def isRemoteRepping(self, ignoreState=False):
|
||||
@@ -613,6 +665,8 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
"""
|
||||
|
||||
slot = self.slot
|
||||
if slot is None:
|
||||
return False
|
||||
if fit.getSlotsFree(slot) <= (0 if self.owner != fit else -1):
|
||||
return False
|
||||
|
||||
@@ -651,20 +705,26 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
return False
|
||||
|
||||
# Check max group fitted
|
||||
max = self.getModifiedItemAttr("maxGroupFitted", None)
|
||||
if max is not None:
|
||||
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:
|
||||
if fit.getHardpointsFree(self.hardpoint) < 1:
|
||||
if fit.getHardpointsFree(self.hardpoint) < (1 if self.owner != fit else 0):
|
||||
return False
|
||||
|
||||
return True
|
||||
@@ -680,6 +740,9 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
return False
|
||||
elif state == FittingModuleState.OVERHEATED and not self.item.isType("overheat"):
|
||||
return False
|
||||
# Some destructible effect beacons contain active effects, hardcap those at online state
|
||||
elif state > FittingModuleState.ONLINE and self.slot == FittingSlot.SYSTEM:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@@ -702,7 +765,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
# Check if the local module is over it's max limit; if it's not, we're fine
|
||||
maxGroupOnline = self.getModifiedItemAttr("maxGroupOnline", None)
|
||||
maxGroupActive = self.getModifiedItemAttr("maxGroupActive", None)
|
||||
if maxGroupOnline is None and maxGroupActive is None and projectedOnto is None:
|
||||
if not maxGroupOnline and not maxGroupActive and projectedOnto is None:
|
||||
return True
|
||||
|
||||
# Following is applicable only to local modules, we do not want to limit projected
|
||||
@@ -718,11 +781,11 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
currOnline += 1
|
||||
if mod.state >= FittingModuleState.ACTIVE:
|
||||
currActive += 1
|
||||
if maxGroupOnline is not None and currOnline > maxGroupOnline:
|
||||
if maxGroupOnline and currOnline > maxGroupOnline:
|
||||
if maxState is None or maxState > FittingModuleState.OFFLINE:
|
||||
maxState = FittingModuleState.OFFLINE
|
||||
break
|
||||
if maxGroupActive is not None and currActive > maxGroupActive:
|
||||
if maxGroupActive and currActive > maxGroupActive:
|
||||
if maxState is None or maxState > FittingModuleState.ONLINE:
|
||||
maxState = FittingModuleState.ONLINE
|
||||
return True if maxState is None else maxState
|
||||
@@ -746,8 +809,8 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
# Check sizes, if 'charge size > module volume' it won't fit
|
||||
if charge is None:
|
||||
return True
|
||||
chargeVolume = charge.volume
|
||||
moduleCapacity = self.item.capacity
|
||||
chargeVolume = charge.attributes['volume'].value
|
||||
moduleCapacity = self.item.attributes['capacity'].value
|
||||
if chargeVolume is not None and moduleCapacity is not None and chargeVolume > moduleCapacity:
|
||||
return False
|
||||
|
||||
@@ -760,7 +823,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
chargeGroup = charge.groupID
|
||||
for i in range(5):
|
||||
itemChargeGroup = self.getModifiedItemAttr('chargeGroup' + str(i), None)
|
||||
if itemChargeGroup is None:
|
||||
if not itemChargeGroup:
|
||||
continue
|
||||
if itemChargeGroup == chargeGroup:
|
||||
return True
|
||||
@@ -771,7 +834,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
validCharges = set()
|
||||
for i in range(5):
|
||||
itemChargeGroup = self.getModifiedItemAttr('chargeGroup' + str(i), None)
|
||||
if itemChargeGroup is not None:
|
||||
if itemChargeGroup:
|
||||
g = eos.db.getGroup(int(itemChargeGroup), eager="items.attributes")
|
||||
if g is None:
|
||||
continue
|
||||
@@ -833,7 +896,8 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
def clear(self):
|
||||
self.__baseVolley = None
|
||||
self.__baseRRAmount = None
|
||||
self.__miningyield = None
|
||||
self.__miningYield = None
|
||||
self.__miningDrain = None
|
||||
self.__reloadTime = None
|
||||
self.__reloadForce = None
|
||||
self.__chargeCycles = None
|
||||
@@ -894,6 +958,13 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
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
|
||||
@@ -956,6 +1027,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
speed = max(
|
||||
self.getModifiedItemAttr("speed", 0), # Most weapons
|
||||
self.getModifiedItemAttr("duration", 0), # Most average modules
|
||||
self.getModifiedItemAttr("durationHighisGood", 0), # Most average modules
|
||||
self.getModifiedItemAttr("durationSensorDampeningBurstProjector", 0),
|
||||
self.getModifiedItemAttr("durationTargetIlluminationBurstProjector", 0),
|
||||
self.getModifiedItemAttr("durationECMJammerBurstProjector", 0),
|
||||
@@ -1005,7 +1077,10 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
elif click == "ctrl":
|
||||
state = FittingModuleState.OFFLINE
|
||||
else:
|
||||
state = transitionMap[currState]
|
||||
try:
|
||||
state = transitionMap[currState]
|
||||
except KeyError:
|
||||
state = min(transitionMap)
|
||||
# If passive module tries to transition into online and fails,
|
||||
# put it to passive instead
|
||||
if not mod.isValidState(state) and currState == FittingModuleState.ONLINE:
|
||||
@@ -1024,9 +1099,8 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
copy.spoolType = self.spoolType
|
||||
copy.spoolAmount = self.spoolAmount
|
||||
copy.projectionRange = self.projectionRange
|
||||
|
||||
for x in self.mutators.values():
|
||||
Mutator(copy, x.attribute, x.value)
|
||||
copy.rahPatternOverride = self.rahPatternOverride
|
||||
self._mutaApplyMutators(mutatorClass=MutatorModule, targetInstance=copy)
|
||||
|
||||
return copy
|
||||
|
||||
@@ -1036,6 +1110,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
spoolType = self.spoolType
|
||||
spoolAmount = self.spoolAmount
|
||||
projectionRange = self.projectionRange
|
||||
rahPatternOverride = self.rahPatternOverride
|
||||
|
||||
Module.__init__(self, item, self.baseItem, self.mutaplasmid)
|
||||
self.state = state
|
||||
@@ -1044,14 +1119,12 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
self.spoolType = spoolType
|
||||
self.spoolAmount = spoolAmount
|
||||
self.projectionRange = projectionRange
|
||||
for x in self.mutators.values():
|
||||
Mutator(self, x.attribute, x.value)
|
||||
self.rahPatternOverride = rahPatternOverride
|
||||
self._mutaApplyMutators(mutatorClass=MutatorModule)
|
||||
|
||||
def __repr__(self):
|
||||
if self.item:
|
||||
return "Module(ID={}, name={}) at {}".format(
|
||||
self.item.ID, self.item.name, hex(id(self))
|
||||
)
|
||||
return "Module(ID={}, name={}) at {}".format(self.item.ID, self.item.name, hex(id(self)))
|
||||
else:
|
||||
return "EmptyModule() at {}".format(hex(id(self)))
|
||||
|
||||
|
||||
105
eos/saveddata/mutatedMixin.py
Normal file
105
eos/saveddata/mutatedMixin.py
Normal file
@@ -0,0 +1,105 @@
|
||||
# ===============================================================================
|
||||
# Copyright (C) 2010 Diego Duclos
|
||||
#
|
||||
# This file is part of eos.
|
||||
#
|
||||
# eos is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# eos 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 Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
||||
# ===============================================================================
|
||||
|
||||
|
||||
import eos.db
|
||||
|
||||
from logbook import Logger
|
||||
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
|
||||
class MutaError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class MutatedMixin:
|
||||
|
||||
@property
|
||||
def isMutated(self):
|
||||
return bool(self.baseItemID and self.mutaplasmidID)
|
||||
|
||||
@property
|
||||
def baseItem(self):
|
||||
return self.__baseItem
|
||||
|
||||
@property
|
||||
def mutaplasmid(self):
|
||||
return self.__mutaplasmid
|
||||
|
||||
@property
|
||||
def fullName(self):
|
||||
if self.isMutated:
|
||||
mutaShortName = self.mutaplasmid.shortName
|
||||
mutaFullName = self.mutaplasmid.item.customName
|
||||
# Short name can be unavailable for non-english language
|
||||
if mutaShortName != mutaFullName:
|
||||
return f'{self.mutaplasmid.shortName} {self.baseItem.customName}'
|
||||
return self.item.customName
|
||||
|
||||
def _mutaInit(self, baseItem, mutaplasmid):
|
||||
self.baseItemID = baseItem.ID if baseItem is not None else None
|
||||
self.mutaplasmidID = mutaplasmid.ID if mutaplasmid is not None else None
|
||||
if baseItem is not None:
|
||||
# we're working with a mutated module, need to get abyssal module loaded with the base attributes
|
||||
# Note: there may be a better way of doing this, such as a metho on this classe to convert(mutaplamid). This
|
||||
# will require a bit more research though, considering there has never been a need to "swap" out the item of a Module
|
||||
# before, and there may be assumptions taken with regards to the item never changing (pre-calculated / cached results, for example)
|
||||
self._item = eos.db.getItemWithBaseItemAttribute(self._item.ID, self.baseItemID)
|
||||
self.__baseItem = baseItem
|
||||
self.__mutaplasmid = mutaplasmid
|
||||
else:
|
||||
self.__baseItem = None
|
||||
self.__mutaplasmid = None
|
||||
|
||||
def _mutaReconstruct(self):
|
||||
self.__baseItem = None
|
||||
self.__mutaplasmid = None
|
||||
if self.baseItemID:
|
||||
self._item = eos.db.getItemWithBaseItemAttribute(self.itemID, self.baseItemID)
|
||||
self.__baseItem = eos.db.getItem(self.baseItemID)
|
||||
self.__mutaplasmid = eos.db.getMutaplasmid(self.mutaplasmidID)
|
||||
if self.__baseItem is None:
|
||||
pyfalog.error("Base Item (id: {0}) does not exist", self.itemID)
|
||||
raise MutaError
|
||||
|
||||
def _mutaLoadMutators(self, mutatorClass):
|
||||
# Instantiate / remove mutators if this is a mutated module
|
||||
if self.__baseItem:
|
||||
for x in self.mutaplasmid.attributes:
|
||||
attr = self.item.attributes[x.name]
|
||||
id = attr.ID
|
||||
if id not in self.mutators: # create the mutator
|
||||
mutatorClass(self, attr, attr.value)
|
||||
# @todo: remove attributes that are no longer part of the mutaplasmid.
|
||||
|
||||
@property
|
||||
def _mutaIsInvalid(self):
|
||||
if self.item.isAbyssal and not self.isMutated:
|
||||
return True
|
||||
if self.isMutated and not self.__mutaplasmid:
|
||||
return True
|
||||
return False
|
||||
|
||||
def _mutaApplyMutators(self, mutatorClass, targetInstance=None):
|
||||
if targetInstance is None:
|
||||
targetInstance = self
|
||||
for x in self.mutators.values():
|
||||
mutatorClass(targetInstance, x.attribute, x.value)
|
||||
@@ -27,10 +27,10 @@ from eos.eqBase import EqBase
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
|
||||
class Mutator(EqBase):
|
||||
""" Mutators are the object that represent an attribute override on the module level, in conjunction with
|
||||
mutaplasmids. Each mutated module, when created, is instantiated with a list of these objects, dictated by the
|
||||
mutaplasmid that is used on the base module.
|
||||
class MutatorBase(EqBase):
|
||||
""" Mutators are the object that represent an attribute override on the eos item level, in conjunction with
|
||||
mutaplasmids. Each mutated item, when created, is instantiated with a list of these objects, dictated by the
|
||||
mutaplasmid that is used on the base item.
|
||||
|
||||
A note on the different attributes on this object:
|
||||
* attribute: points to the definition of the attribute from dgmattribs.
|
||||
@@ -40,13 +40,13 @@ class Mutator(EqBase):
|
||||
This could probably be cleaned up with smarter relationships, but whatever
|
||||
"""
|
||||
|
||||
def __init__(self, module, attr, value):
|
||||
# this needs to be above module assignment, as assigning the module will add it to the list and it via
|
||||
def __init__(self, item, attr, value):
|
||||
# this needs to be above item assignment, as assigning the item will add it to the list and it via
|
||||
# relationship and needs this set 4correctly
|
||||
self.attrID = attr.ID
|
||||
|
||||
self.module = module
|
||||
self.moduleID = module.ID
|
||||
self.item = item
|
||||
self.itemID = item.ID
|
||||
|
||||
self.__attr = attr
|
||||
self.build()
|
||||
@@ -67,30 +67,31 @@ class Mutator(EqBase):
|
||||
|
||||
def build(self):
|
||||
# try...except here to catch orphaned mutators. Pretty rare, only happens so far if hacking the database
|
||||
# But put it here to remove the module link if it happens, until a better solution can be developed
|
||||
# But put it here to remove the eos item link if it happens, until a better solution can be developed
|
||||
try:
|
||||
# dynamic attribute links to the Mutaplasmids attribute definition for this mutated definition
|
||||
self.dynamicAttribute = next(a for a in self.module.mutaplasmid.attributes if a.attributeID == self.attrID)
|
||||
self.dynamicAttribute = next(a for a in self.item.mutaplasmid.attributes if a.attributeID == self.attrID)
|
||||
# base attribute links to the base ite's attribute for this mutated definition (contains original, base value)
|
||||
self.baseAttribute = self.module.item.attributes[self.dynamicAttribute.name]
|
||||
self.baseAttribute = self.item.baseItem.attributes[self.dynamicAttribute.name]
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
raise
|
||||
except:
|
||||
self.module = None
|
||||
self.item = None
|
||||
|
||||
@validates("value")
|
||||
def validator(self, key, val):
|
||||
""" Validates values as properly falling within the range of the modules' Mutaplasmid """
|
||||
""" Validates values as properly falling within the range of the items' Mutaplasmid """
|
||||
if self.baseValue == 0:
|
||||
return 0
|
||||
mod = val / self.baseValue
|
||||
|
||||
if self.minMod <= mod <= self.maxMod:
|
||||
# sweet, all good
|
||||
returnVal = val
|
||||
else:
|
||||
# need to fudge the numbers a bit. Go with the value closest to base
|
||||
if val >= 0:
|
||||
returnVal = min(self.maxValue, max(self.minValue, val))
|
||||
else:
|
||||
returnVal = max(self.maxValue, min(self.minValue, val))
|
||||
|
||||
actualMin = min(self.minValue, self.maxValue)
|
||||
actualMax = max(self.minValue, self.maxValue)
|
||||
returnVal = min(actualMax, max(actualMin, val))
|
||||
return returnVal
|
||||
|
||||
@property
|
||||
@@ -98,7 +99,7 @@ class Mutator(EqBase):
|
||||
# @todo: need to test what happens:
|
||||
# 1) if an attribute is removed from the EVE database
|
||||
# 2) if a mutaplasmid does not have the attribute anymore
|
||||
# 3) if a mutaplasmid does not exist (in eve or on the module's item)
|
||||
# 3) if a mutaplasmid does not exist (in eve or on the pyfa item's item)
|
||||
# Can remove invalid ones in a SQLAlchemy collection class... eventually
|
||||
return self.__attr is None
|
||||
|
||||
@@ -116,16 +117,33 @@ class Mutator(EqBase):
|
||||
|
||||
@property
|
||||
def baseValue(self):
|
||||
return self.baseAttribute.value
|
||||
try:
|
||||
return self.baseAttribute.value
|
||||
except AttributeError:
|
||||
return 0
|
||||
|
||||
@property
|
||||
def minValue(self):
|
||||
return self.minMod * self.baseAttribute.value
|
||||
try:
|
||||
return self.minMod * self.baseAttribute.value
|
||||
except AttributeError:
|
||||
return 0
|
||||
|
||||
@property
|
||||
def maxValue(self):
|
||||
return self.maxMod * self.baseAttribute.value
|
||||
try:
|
||||
return self.maxMod * self.baseAttribute.value
|
||||
except AttributeError:
|
||||
return 0
|
||||
|
||||
@property
|
||||
def attribute(self):
|
||||
return self.__attr
|
||||
|
||||
|
||||
class MutatorModule(MutatorBase):
|
||||
pass
|
||||
|
||||
|
||||
class MutatorDrone(MutatorBase):
|
||||
pass
|
||||
|
||||
@@ -126,7 +126,7 @@ class Ship(ItemAttrShortcut, HandledItem):
|
||||
valid Item objects, not the Mode objects. Returns None if not a
|
||||
t3 dessy
|
||||
"""
|
||||
if self.item.group.name != "Tactical Destroyer":
|
||||
if self.item.group.name != "Tactical Destroyer" and self.item.name != "Anhinga":
|
||||
return None
|
||||
|
||||
items = []
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -19,23 +19,242 @@
|
||||
|
||||
import math
|
||||
import re
|
||||
from collections import OrderedDict
|
||||
|
||||
from logbook import Logger
|
||||
from sqlalchemy.orm import reconstructor
|
||||
|
||||
import eos.db
|
||||
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
|
||||
def _t(x):
|
||||
return x
|
||||
|
||||
|
||||
def _c(x):
|
||||
return '[' + x + ']'
|
||||
|
||||
|
||||
BUILTINS = OrderedDict([
|
||||
# 0 is taken by ideal target profile, composed manually in one of TargetProfile methods
|
||||
(-1, (_t('Uniform (25%)'), 0.25, 0.25, 0.25, 0.25)),
|
||||
(-2, (_t('Uniform (50%)'), 0.50, 0.50, 0.50, 0.50)),
|
||||
(-3, (_t('Uniform (75%)'), 0.75, 0.75, 0.75, 0.75)),
|
||||
(-4, (_t('Uniform (90%)'), 0.90, 0.90, 0.90, 0.90)),
|
||||
(-5, (_c(_t('T1 Resist')) + _t('Shield'), 0.0, 0.20, 0.40, 0.50)),
|
||||
(-6, (_c(_t('T1 Resist')) + _t('Armor'), 0.50, 0.45, 0.25, 0.10)),
|
||||
(-7, (_c(_t('T1 Resist')) + _t('Hull'), 0.33, 0.33, 0.33, 0.33)),
|
||||
(-8, (_c(_t('T1 Resist')) + _t('Shield (+T2 DCU)'), 0.125, 0.30, 0.475, 0.562)),
|
||||
(-9, (_c(_t('T1 Resist')) + _t('Armor (+T2 DCU)'), 0.575, 0.532, 0.363, 0.235)),
|
||||
(-10, (_c(_t('T1 Resist')) + _t('Hull (+T2 DCU)'), 0.598, 0.598, 0.598, 0.598)),
|
||||
(-11, (_c(_t('T2 Resist')) + _t('Amarr (Shield)'), 0.0, 0.20, 0.70, 0.875)),
|
||||
(-12, (_c(_t('T2 Resist')) + _t('Amarr (Armor)'), 0.50, 0.35, 0.625, 0.80)),
|
||||
(-13, (_c(_t('T2 Resist')) + _t('Caldari (Shield)'), 0.20, 0.84, 0.76, 0.60)),
|
||||
(-14, (_c(_t('T2 Resist')) + _t('Caldari (Armor)'), 0.50, 0.8625, 0.625, 0.10)),
|
||||
(-15, (_c(_t('T2 Resist')) + _t('Gallente (Shield)'), 0.0, 0.60, 0.85, 0.50)),
|
||||
(-16, (_c(_t('T2 Resist')) + _t('Gallente (Armor)'), 0.50, 0.675, 0.8375, 0.10)),
|
||||
(-17, (_c(_t('T2 Resist')) + _t('Minmatar (Shield)'), 0.75, 0.60, 0.40, 0.50)),
|
||||
(-18, (_c(_t('T2 Resist')) + _t('Minmatar (Armor)'), 0.90, 0.675, 0.25, 0.10)),
|
||||
(-19, (_c(_t('NPC')) + _c(_t('Asteroid')) + _t('Angel Cartel'), 0.54, 0.42, 0.37, 0.32)),
|
||||
(-20, (_c(_t('NPC')) + _c(_t('Asteroid')) + _t('Blood Raiders'), 0.34, 0.39, 0.45, 0.52)),
|
||||
(-21, (_c(_t('NPC')) + _c(_t('Asteroid')) + _t('Guristas'), 0.55, 0.35, 0.3, 0.48)),
|
||||
(-22, (_c(_t('NPC')) + _c(_t('Asteroid')) + _t('Rogue Drones'), 0.35, 0.38, 0.44, 0.49)),
|
||||
(-23, (_c(_t('NPC')) + _c(_t('Asteroid')) + _t('Sanshas Nation'), 0.35, 0.4, 0.47, 0.53)),
|
||||
(-24, (_c(_t('NPC')) + _c(_t('Asteroid')) + _t('Serpentis'), 0.49, 0.38, 0.29, 0.51)),
|
||||
(-25, (_c(_t('NPC')) + _c(_t('Deadspace')) + _t('Angel Cartel'), 0.59, 0.48, 0.4, 0.32)),
|
||||
(-26, (_c(_t('NPC')) + _c(_t('Deadspace')) + _t('Blood Raiders'), 0.31, 0.39, 0.47, 0.56)),
|
||||
(-27, (_c(_t('NPC')) + _c(_t('Deadspace')) + _t('Guristas'), 0.57, 0.39, 0.31, 0.5)),
|
||||
(-28, (_c(_t('NPC')) + _c(_t('Deadspace')) + _t('Rogue Drones'), 0.42, 0.42, 0.47, 0.49)),
|
||||
(-29, (_c(_t('NPC')) + _c(_t('Deadspace')) + _t('Sanshas Nation'), 0.31, 0.39, 0.47, 0.56)),
|
||||
(-30, (_c(_t('NPC')) + _c(_t('Deadspace')) + _t('Serpentis'), 0.49, 0.38, 0.29, 0.56)),
|
||||
(-31, (_c(_t('NPC')) + _c(_t('Mission')) + _t('Amarr Empire'), 0.34, 0.38, 0.42, 0.46)),
|
||||
(-32, (_c(_t('NPC')) + _c(_t('Mission')) + _t('Caldari State'), 0.51, 0.38, 0.3, 0.51)),
|
||||
(-33, (_c(_t('NPC')) + _c(_t('Mission')) + _t('CONCORD'), 0.47, 0.46, 0.47, 0.47)),
|
||||
(-34, (_c(_t('NPC')) + _c(_t('Mission')) + _t('Gallente Federation'), 0.51, 0.38, 0.31, 0.52)),
|
||||
(-35, (_c(_t('NPC')) + _c(_t('Mission')) + _t('Khanid'), 0.51, 0.42, 0.36, 0.4)),
|
||||
(-36, (_c(_t('NPC')) + _c(_t('Mission')) + _t('Minmatar Republic'), 0.51, 0.46, 0.41, 0.35)),
|
||||
(-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)),
|
||||
# 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
|
||||
(-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
|
||||
(-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:
|
||||
# also determined import/export order - VERY IMPORTANT
|
||||
DAMAGE_TYPES = ("em", "thermal", "kinetic", "explosive")
|
||||
DAMAGE_TYPES = ('em', 'thermal', 'kinetic', 'explosive')
|
||||
_idealTarget = None
|
||||
_builtins = None
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.builtin = False
|
||||
self.update(*args, **kwargs)
|
||||
|
||||
def update(self, emAmount=0, thermalAmount=0, kineticAmount=0, explosiveAmount=0, maxVelocity=None, signatureRadius=None, radius=None):
|
||||
@reconstructor
|
||||
def init(self):
|
||||
self.builtin = False
|
||||
|
||||
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
|
||||
@@ -43,22 +262,46 @@ class TargetProfile:
|
||||
self._maxVelocity = maxVelocity
|
||||
self._signatureRadius = signatureRadius
|
||||
self._radius = radius
|
||||
self._hp = hp
|
||||
|
||||
_idealTarget = None
|
||||
@classmethod
|
||||
def getBuiltinList(cls):
|
||||
if cls._builtins is None:
|
||||
cls.__generateBuiltins()
|
||||
return list(cls._builtins.values())
|
||||
|
||||
@classmethod
|
||||
def getBuiltinById(cls, id):
|
||||
if cls._builtins is None:
|
||||
cls.__generateBuiltins()
|
||||
return cls._builtins.get(id)
|
||||
|
||||
@classmethod
|
||||
def __generateBuiltins(cls):
|
||||
cls._builtins = OrderedDict()
|
||||
for id, data in BUILTINS.items():
|
||||
rawName = data[0]
|
||||
data = data[1:]
|
||||
profile = TargetProfile(*data)
|
||||
profile.ID = id
|
||||
profile.rawName = rawName
|
||||
profile.builtin = True
|
||||
cls._builtins[id] = profile
|
||||
|
||||
@classmethod
|
||||
def getIdeal(cls):
|
||||
if cls._idealTarget is None:
|
||||
cls._idealTarget = cls(
|
||||
emAmount=0,
|
||||
thermalAmount=0,
|
||||
kineticAmount=0,
|
||||
explosiveAmount=0,
|
||||
maxVelocity=0,
|
||||
signatureRadius=None,
|
||||
radius=0)
|
||||
cls._idealTarget.name = 'Ideal Target'
|
||||
cls._idealTarget.ID = -1
|
||||
emAmount=0,
|
||||
thermalAmount=0,
|
||||
kineticAmount=0,
|
||||
explosiveAmount=0,
|
||||
maxVelocity=0,
|
||||
signatureRadius=None,
|
||||
radius=0)
|
||||
cls._idealTarget.rawName = _t('Ideal Target')
|
||||
cls._idealTarget.ID = 0
|
||||
cls._idealTarget.builtin = True
|
||||
return cls._idealTarget
|
||||
|
||||
@property
|
||||
@@ -89,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)
|
||||
@@ -100,7 +355,7 @@ class TargetProfile:
|
||||
lookup = {}
|
||||
current = eos.db.getTargetProfileList()
|
||||
for pattern in current:
|
||||
lookup[pattern.name] = pattern
|
||||
lookup[pattern.rawName] = pattern
|
||||
|
||||
for line in lines:
|
||||
try:
|
||||
@@ -109,6 +364,8 @@ class TargetProfile:
|
||||
line = line.split('#', 1)[0] # allows for comments
|
||||
type, data = line.rsplit('=', 1)
|
||||
type, data = type.strip(), [d.strip() for d in data.split(',')]
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
raise
|
||||
except:
|
||||
pyfalog.warning("Data isn't in correct format, continue to next line.")
|
||||
continue
|
||||
@@ -127,6 +384,8 @@ class TargetProfile:
|
||||
try:
|
||||
assert 0 <= val <= 100
|
||||
fields["%sAmount" % cls.DAMAGE_TYPES[index]] = val / 100
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
raise
|
||||
except:
|
||||
pyfalog.warning("Caught unhandled exception in import patterns.")
|
||||
continue
|
||||
@@ -149,7 +408,7 @@ class TargetProfile:
|
||||
eos.db.save(pattern)
|
||||
else:
|
||||
pattern = TargetProfile(**fields)
|
||||
pattern.name = name.strip()
|
||||
pattern.rawName = name.strip()
|
||||
eos.db.save(pattern)
|
||||
patterns.append(pattern)
|
||||
|
||||
@@ -166,7 +425,7 @@ class TargetProfile:
|
||||
out += "# TargetProfile = [name],[EM %],[Thermal %],[Kinetic %],[Explosive %],[Max velocity m/s],[Signature radius m],[Radius m]\n\n"
|
||||
for dp in patterns:
|
||||
out += cls.EXPORT_FORMAT % (
|
||||
dp.name,
|
||||
dp.rawName,
|
||||
dp.emAmount * 100,
|
||||
dp.thermalAmount * 100,
|
||||
dp.kineticAmount * 100,
|
||||
@@ -178,9 +437,39 @@ class TargetProfile:
|
||||
|
||||
return out.strip()
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self.rawName
|
||||
|
||||
@property
|
||||
def fullName(self):
|
||||
categories, tail = self.__parseRawName()
|
||||
return '{}{}'.format(''.join('[{}]'.format(c) for c in categories), tail)
|
||||
|
||||
@property
|
||||
def shortName(self):
|
||||
return self.__parseRawName()[1]
|
||||
|
||||
@property
|
||||
def hierarchy(self):
|
||||
return self.__parseRawName()[0]
|
||||
|
||||
def __parseRawName(self):
|
||||
hierarchy = []
|
||||
remainingName = self.rawName.strip() if self.rawName else ''
|
||||
while True:
|
||||
start, end = remainingName.find('['), remainingName.find(']')
|
||||
if start == -1 or end == -1:
|
||||
return hierarchy, remainingName
|
||||
splitter = remainingName.find('|')
|
||||
if splitter != -1 and splitter == start - 1:
|
||||
return hierarchy, remainingName[1:]
|
||||
hierarchy.append(remainingName[start + 1:end])
|
||||
remainingName = remainingName[end + 1:].strip()
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
p = TargetProfile(
|
||||
self.emAmount, self.thermalAmount, self.kineticAmount, self.explosiveAmount,
|
||||
self._maxVelocity, self._signatureRadius, self._radius)
|
||||
p.name = "%s copy" % self.name
|
||||
self.emAmount, self.thermalAmount, self.kineticAmount, self.explosiveAmount,
|
||||
self._maxVelocity, self._signatureRadius, self._radius)
|
||||
p.rawName = "%s copy" % self.rawName
|
||||
return p
|
||||
|
||||
39
eos/utils/pyinst_support.py
Normal file
39
eos/utils/pyinst_support.py
Normal file
@@ -0,0 +1,39 @@
|
||||
"""
|
||||
Slightly modified version of function taken from here:
|
||||
https://github.com/pyinstaller/pyinstaller/issues/1905#issuecomment-525221546
|
||||
"""
|
||||
|
||||
|
||||
import pkgutil
|
||||
|
||||
|
||||
def iterNamespace(name, path):
|
||||
"""Pyinstaller-compatible namespace iteration.
|
||||
|
||||
Yields the name of all modules found at a given Fully-qualified path.
|
||||
|
||||
To have it running with pyinstaller, it requires to ensure a hook inject the
|
||||
"hidden" modules from your plugins folder inside the executable:
|
||||
|
||||
- if your plugins are under the ``myappname/pluginfolder`` module
|
||||
- create a file ``specs/hook-<myappname.pluginfolder>.py``
|
||||
- content of this file should be:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from PyInstaller.utils.hooks import collect_submodules
|
||||
hiddenimports = collect_submodules('<myappname.pluginfolder>')
|
||||
"""
|
||||
prefix = name + "."
|
||||
for p in pkgutil.iter_modules(path, prefix):
|
||||
yield p[1]
|
||||
|
||||
# special handling when the package is bundled with PyInstaller 3.5
|
||||
# See https://github.com/pyinstaller/pyinstaller/issues/1905#issuecomment-445787510
|
||||
toc = set()
|
||||
for importer in pkgutil.iter_importers(name.partition(".")[0]):
|
||||
if hasattr(importer, 'toc'):
|
||||
toc |= importer.toc
|
||||
for name in toc:
|
||||
if name.startswith(prefix):
|
||||
yield name
|
||||
27
eos/utils/round.py
Normal file
27
eos/utils/round.py
Normal file
@@ -0,0 +1,27 @@
|
||||
import math
|
||||
|
||||
|
||||
def roundToPrec(val, prec, nsValue=None):
|
||||
"""
|
||||
nsValue: custom value which should be used to determine normalization shift
|
||||
"""
|
||||
# We're not rounding integers anyway
|
||||
# Also make sure that we do not ask to calculate logarithm of zero
|
||||
if int(val) == val:
|
||||
return int(val)
|
||||
roundFactor = int(prec - math.floor(math.log10(abs(val if nsValue is None else nsValue))) - 1)
|
||||
# But we don't want to round integers
|
||||
if roundFactor < 0:
|
||||
roundFactor = 0
|
||||
# Do actual rounding
|
||||
val = round(val, roundFactor)
|
||||
# Make sure numbers with .0 part designating float don't get through
|
||||
if int(val) == val:
|
||||
val = int(val)
|
||||
return val
|
||||
|
||||
|
||||
def roundDec(val, prec):
|
||||
if int(val) == val:
|
||||
return int(val)
|
||||
return round(val, prec)
|
||||
@@ -18,19 +18,144 @@
|
||||
# ===============================================================================
|
||||
|
||||
|
||||
import math
|
||||
from collections import defaultdict
|
||||
|
||||
from eos.utils.float import floatUnerr
|
||||
from utils.repr import makeReprStr
|
||||
|
||||
|
||||
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):
|
||||
@@ -38,6 +163,7 @@ class DmgTypes:
|
||||
yield self.thermal
|
||||
yield self.kinetic
|
||||
yield self.explosive
|
||||
yield self.pure
|
||||
yield self.total
|
||||
|
||||
def __eq__(self, other):
|
||||
@@ -46,72 +172,92 @@ 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 = ['em', 'thermal', 'kinetic', 'explosive', '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, 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]
|
||||
|
||||
return value
|
||||
|
||||
|
||||
class RRTypes:
|
||||
@@ -136,10 +282,10 @@ class RRTypes:
|
||||
# Round for comparison's sake because often tanking numbers are
|
||||
# generated from data which includes float errors
|
||||
return (
|
||||
floatUnerr(self.shield) == floatUnerr(other.shield) and
|
||||
floatUnerr(self.armor) == floatUnerr(other.armor) and
|
||||
floatUnerr(self.hull) == floatUnerr(other.hull) and
|
||||
floatUnerr(self.capacitor) == floatUnerr(other.capacitor))
|
||||
floatUnerr(self.shield) == floatUnerr(other.shield) and
|
||||
floatUnerr(self.armor) == floatUnerr(other.armor) and
|
||||
floatUnerr(self.hull) == floatUnerr(other.hull) and
|
||||
floatUnerr(self.capacitor) == floatUnerr(other.capacitor))
|
||||
|
||||
def __bool__(self):
|
||||
return any((self.shield, self.armor, self.hull, self.capacitor))
|
||||
@@ -191,5 +337,17 @@ class RRTypes:
|
||||
return self
|
||||
|
||||
def __repr__(self):
|
||||
spec = ['shield', 'armor', 'hull', 'capacitor']
|
||||
spec = RRTypes.names(False)
|
||||
return makeReprStr(self, spec)
|
||||
|
||||
@staticmethod
|
||||
def names(ehpOnly=True, postProcessor=None):
|
||||
value = ['shield', 'armor', 'hull']
|
||||
|
||||
if not ehpOnly:
|
||||
value.append('capacitor')
|
||||
|
||||
if postProcessor:
|
||||
value = [postProcessor(x) for x in value]
|
||||
|
||||
return value
|
||||
|
||||
@@ -18,40 +18,9 @@
|
||||
# =============================================================================
|
||||
|
||||
|
||||
import math
|
||||
|
||||
from service.settings import GraphSettings
|
||||
|
||||
|
||||
# Just copy-paste penalization chain calculation code (with some modifications,
|
||||
# as multipliers arrive in different form) in here to not make actual attribute
|
||||
# calculations slower than they already are due to extra function calls
|
||||
def calculateMultiplier(multipliers):
|
||||
"""
|
||||
multipliers: dictionary in format:
|
||||
{stacking group name: [(mult, resist attr ID), (mult, resist attr ID)]}
|
||||
"""
|
||||
val = 1
|
||||
for penalizedMultipliers in multipliers.values():
|
||||
# A quick explanation of how this works:
|
||||
# 1: Bonuses and penalties are calculated seperately, so we'll have to filter each of them
|
||||
l1 = [v[0] for v in penalizedMultipliers if v[0] > 1]
|
||||
l2 = [v[0] for v in penalizedMultipliers if v[0] < 1]
|
||||
# 2: The most significant bonuses take the smallest penalty,
|
||||
# This means we'll have to sort
|
||||
abssort = lambda _val: -abs(_val - 1)
|
||||
l1.sort(key=abssort)
|
||||
l2.sort(key=abssort)
|
||||
# 3: The first module doesn't get penalized at all
|
||||
# Any module after the first takes penalties according to:
|
||||
# 1 + (multiplier - 1) * math.exp(- math.pow(i, 2) / 7.1289)
|
||||
for l in (l1, l2):
|
||||
for i in range(len(l)):
|
||||
bonus = l[i]
|
||||
val *= 1 + (bonus - 1) * math.exp(- i ** 2 / 7.1289)
|
||||
return val
|
||||
|
||||
|
||||
def checkLockRange(src, distance):
|
||||
if distance is None:
|
||||
return True
|
||||
|
||||
@@ -26,3 +26,5 @@ from . import fitCapacitor
|
||||
from . import fitMobility
|
||||
from . import fitWarpTime
|
||||
from . import fitLockTime
|
||||
# Hidden graphs, available via ctrl-alt-g
|
||||
from . import fitEcmBurstScanresDamps
|
||||
|
||||
@@ -26,11 +26,12 @@ VectorDef = namedtuple('VectorDef', ('lengthHandle', 'lengthUnit', 'angleHandle'
|
||||
|
||||
class YDef:
|
||||
|
||||
def __init__(self, handle, unit, label, selectorLabel=None):
|
||||
def __init__(self, handle, unit, label, selectorLabel=None, hidden=False):
|
||||
self.handle = handle
|
||||
self.unit = unit
|
||||
self.label = label
|
||||
self._selectorLabel = selectorLabel
|
||||
self.hidden = hidden
|
||||
|
||||
@property
|
||||
def selectorLabel(self):
|
||||
@@ -53,12 +54,13 @@ class YDef:
|
||||
|
||||
class XDef:
|
||||
|
||||
def __init__(self, handle, unit, label, mainInput, selectorLabel=None):
|
||||
def __init__(self, handle, unit, label, mainInput, selectorLabel=None, hidden=False):
|
||||
self.handle = handle
|
||||
self.unit = unit
|
||||
self.label = label
|
||||
self.mainInput = mainInput
|
||||
self._selectorLabel = selectorLabel
|
||||
self.hidden = hidden
|
||||
|
||||
@property
|
||||
def selectorLabel(self):
|
||||
|
||||
@@ -29,6 +29,7 @@ from service.const import GraphCacheCleanupReason
|
||||
class FitGraph(metaclass=ABCMeta):
|
||||
|
||||
# UI stuff
|
||||
hidden = False
|
||||
views = []
|
||||
viewMap = {}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user