Compare commits
1985 Commits
v2.5.0b1+c
...
v2.20.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
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 | ||
|
|
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 | ||
|
|
2962ce1945 | ||
|
|
0063d2955e | ||
|
|
9787a18666 | ||
|
|
d8cd3197b5 | ||
|
|
e07c4f65ab | ||
|
|
c3108f773a | ||
|
|
a400821268 | ||
|
|
ca4bac07da | ||
|
|
0038eacc3f | ||
|
|
4bd633a0d4 | ||
|
|
ee837f0b04 | ||
|
|
016e18cc89 | ||
|
|
1d6ac53183 | ||
|
|
1e32dfa463 | ||
|
|
4431753570 | ||
|
|
6fdb57318c | ||
|
|
649db019de | ||
|
|
1d528be0ef | ||
|
|
2d6f6df83d | ||
|
|
7fa998f276 | ||
|
|
6a3157a4c8 | ||
|
|
74efa50576 | ||
|
|
e99be78c54 | ||
|
|
e386232de1 | ||
|
|
a15896044d | ||
|
|
72c74513ce | ||
|
|
34d6d13cb2 | ||
|
|
323bb8e064 | ||
|
|
b628f8ef9b | ||
|
|
1900216d36 | ||
|
|
c045ed81c1 | ||
|
|
39edec60e3 | ||
|
|
259214e907 | ||
|
|
2e1c53392d | ||
|
|
7a77d12686 | ||
|
|
763a7714ed | ||
|
|
e89f35d654 | ||
|
|
3196751b7a | ||
|
|
f221024974 | ||
|
|
66cab8a0d4 | ||
|
|
daa49a8cd4 | ||
|
|
b8601ff240 | ||
|
|
5df3eeca64 | ||
|
|
274e4ac2ca | ||
|
|
f6485251ca | ||
|
|
c03fb70def | ||
|
|
8652a2891b | ||
|
|
a09af0a9eb | ||
|
|
c7d4b93fba | ||
|
|
745c0db530 | ||
|
|
aa56ab8d6c | ||
|
|
043c430221 | ||
|
|
dc5cc5855e | ||
|
|
59d6266e2b | ||
|
|
7495ba67f8 | ||
|
|
e649683a4d | ||
|
|
bec58a5772 | ||
|
|
e8f9ae8a9c | ||
|
|
679382e220 | ||
|
|
7f86782f54 | ||
|
|
f80b7d972f | ||
|
|
c0bd489c1b | ||
|
|
41e4c2107d | ||
|
|
cfc95c272a | ||
|
|
f778f9ceae | ||
|
|
7fb6170bcb | ||
|
|
fa37428cd3 | ||
|
|
2a2d9d3456 | ||
|
|
a91efb681f | ||
|
|
386e05be8f | ||
|
|
65e7bf609d | ||
|
|
7a58d97652 | ||
|
|
c5118da417 | ||
|
|
13b505525d | ||
|
|
b682dec363 | ||
|
|
6aa98e2214 | ||
|
|
8fba988222 | ||
|
|
24a82efe50 | ||
|
|
8054fa9267 | ||
|
|
a0e39a3725 | ||
|
|
185d6d0c51 | ||
|
|
1975e96848 | ||
|
|
ab37d228ea | ||
|
|
f48483d754 | ||
|
|
e6cfd33435 | ||
|
|
c29126ce1d | ||
|
|
c52170b731 | ||
|
|
6607dd31bf | ||
|
|
c6c74be38d | ||
|
|
41c6062ff9 | ||
|
|
bbb96a73b7 | ||
|
|
9eb3b9e017 | ||
|
|
6b3e94729c | ||
|
|
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 | ||
|
|
536eb1efa5 | ||
|
|
c4c763089e | ||
|
|
cdfd4c0d8e | ||
|
|
f9bb8836e5 | ||
|
|
58b2634c8c | ||
|
|
093ae008ce | ||
|
|
5f62fc0cdc | ||
|
|
e7a4b4ac26 | ||
|
|
66e9944cb5 | ||
|
|
ad8528c248 | ||
|
|
07d22cd8e4 | ||
|
|
a6d5922d77 | ||
|
|
958d7bff99 | ||
|
|
7819b80be4 | ||
|
|
2ca50a4658 | ||
|
|
09ff4fd128 | ||
|
|
3e53863f9e | ||
|
|
63d2289f97 | ||
|
|
2663ef2e66 | ||
|
|
d4bdf47d62 | ||
|
|
660ee7c4bf | ||
|
|
1db1f3070b | ||
|
|
3dba82c497 | ||
|
|
25e7b7a9f7 | ||
|
|
9582212ae0 | ||
|
|
7d2b60c327 | ||
|
|
0121a0064e | ||
|
|
2aa96fc819 | ||
|
|
8d81db0a3a | ||
|
|
e5ba35fde9 | ||
|
|
885cd32cb0 | ||
|
|
18d8ed6558 | ||
|
|
9618ece4b4 | ||
|
|
a80a77a422 | ||
|
|
3806be3ddd | ||
|
|
3e803fef30 | ||
|
|
12956d435a | ||
|
|
a3381007f3 | ||
|
|
1efe4ee5e5 | ||
|
|
ec21f93d3c | ||
|
|
f384b32ed6 | ||
|
|
22d8f34c75 | ||
|
|
6128cd8322 | ||
|
|
386f403430 | ||
|
|
5f7d9aea89 | ||
|
|
b367c449a9 | ||
|
|
26b3dff9d4 | ||
|
|
873a62e3f0 | ||
|
|
d967ab375e | ||
|
|
fcf2d6a72c | ||
|
|
843ced15bf | ||
|
|
813db9340f | ||
|
|
acbd8a3298 | ||
|
|
561e22e894 | ||
|
|
05ac0a528a | ||
|
|
c040353f6e | ||
|
|
f23a8fa0c8 | ||
|
|
ba93467646 | ||
|
|
00d480860f | ||
|
|
c94384acb8 | ||
|
|
0c2c0ac6ef | ||
|
|
61a33a331e | ||
|
|
e374a6f2c6 | ||
|
|
dbd84dce28 | ||
|
|
9d554f9c68 | ||
|
|
576cf56735 | ||
|
|
e2aaabbc16 | ||
|
|
ef226898c0 | ||
|
|
a0db235e5a | ||
|
|
5bf05ba775 | ||
|
|
c073b1fa2a | ||
|
|
5f58307bf3 | ||
|
|
8741b17a5e | ||
|
|
4c1fa09795 | ||
|
|
ce7df2d01f | ||
|
|
b433b0ea7c | ||
|
|
20868d6b44 | ||
|
|
33103dbee9 | ||
|
|
2a05ac5a85 | ||
|
|
a013828128 | ||
|
|
e19510b3d4 | ||
|
|
390f2048f2 | ||
|
|
0bb732300e | ||
|
|
fd017df561 | ||
|
|
0ed16b9a6f | ||
|
|
865978fcc1 | ||
|
|
a43f9930de | ||
|
|
c13cd23d54 | ||
|
|
ed1f52a114 | ||
|
|
7dd063f04e | ||
|
|
6e9fc1d1d9 | ||
|
|
cae0172e48 | ||
|
|
e2b492ee8d | ||
|
|
545ddc7492 | ||
|
|
d0b7c58a1d | ||
|
|
a9ad094422 | ||
|
|
68154333c2 | ||
|
|
5df2db5879 | ||
|
|
5a34db0d2f | ||
|
|
6f50be1e7e | ||
|
|
d15fefcf1b | ||
|
|
07bf1b400c | ||
|
|
9f975a958e | ||
|
|
c2a240bab0 | ||
|
|
40c3bf723f | ||
|
|
7a92ace2db | ||
|
|
500f5b8310 | ||
|
|
44830a4de6 | ||
|
|
f3f13e7ba8 | ||
|
|
0269a64ae1 | ||
|
|
5d6cdcbd23 | ||
|
|
81906a7bd2 | ||
|
|
b25b038934 | ||
|
|
b469fa520e | ||
|
|
4f865896c7 | ||
|
|
3b50dddef2 | ||
|
|
380e9c2e87 | ||
|
|
1c1443c862 | ||
|
|
0529baac4a | ||
|
|
7dab220009 | ||
|
|
0ea0f8cdf2 | ||
|
|
eebd59413b | ||
|
|
f4a635eb43 | ||
|
|
0e57258cc5 | ||
|
|
67462c3278 | ||
|
|
fce8129fa2 | ||
|
|
1d76f3ec31 | ||
|
|
707dbeecf8 | ||
|
|
56672f5830 | ||
|
|
13a0bf9d42 | ||
|
|
1bff10c973 | ||
|
|
1d4aece7cc | ||
|
|
cdb79f499a | ||
|
|
837dbb3677 | ||
|
|
3d3bf4ce2c | ||
|
|
46ae2a006e | ||
|
|
0062206f87 | ||
|
|
b6642aa76c | ||
|
|
522f1c8314 | ||
|
|
6fceda5f27 | ||
|
|
668a947543 | ||
|
|
f3eadc9ef1 | ||
|
|
7693483720 | ||
|
|
219e1c11dc | ||
|
|
99c6614d86 | ||
|
|
93af60c3d0 | ||
|
|
b4789bbebf | ||
|
|
160c2f0942 | ||
|
|
9605efe643 | ||
|
|
56e09b8528 | ||
|
|
7dc17543df | ||
|
|
3793721dc3 | ||
|
|
54aa284fd9 | ||
|
|
207818537b | ||
|
|
d32ff668e1 | ||
|
|
1b2cd62629 | ||
|
|
2d96af9fc8 | ||
|
|
20422d3046 | ||
|
|
0e2ae0e0f0 | ||
|
|
9572a51f28 | ||
|
|
cf6f884b3b | ||
|
|
4ca737281d | ||
|
|
847f4e343e | ||
|
|
e0db6eb2ad | ||
|
|
c7f625456e | ||
|
|
332eb048a1 | ||
|
|
e2c7f169fd | ||
|
|
4eb68c7c13 | ||
|
|
c1228b95fe | ||
|
|
2ab61f2b9e | ||
|
|
d266aa796e | ||
|
|
e488497a42 | ||
|
|
e8766817f8 | ||
|
|
d577b1d1c6 | ||
|
|
7984b57494 | ||
|
|
567e8df174 | ||
|
|
65e7607221 | ||
|
|
6e3b536d83 | ||
|
|
348c4d71df | ||
|
|
9494885f45 | ||
|
|
01bda70fef | ||
|
|
8b2f9ce59d | ||
|
|
60a8e905b8 | ||
|
|
d9a4b0a359 | ||
|
|
2d43a6ade5 | ||
|
|
d0a56e3ee8 | ||
|
|
9b15f1942d | ||
|
|
edd261c677 | ||
|
|
3c967ba9eb | ||
|
|
8332ccaa7a | ||
|
|
26b1610ca5 | ||
|
|
ae1a5f4e44 | ||
|
|
4594f57961 | ||
|
|
c1d0849f87 | ||
|
|
313264a49f | ||
|
|
a724347236 | ||
|
|
df7ad187f5 | ||
|
|
c142a011a0 | ||
|
|
477c43884a | ||
|
|
f3551ce570 | ||
|
|
6baa9dd322 | ||
|
|
302975c243 | ||
|
|
73f75fb44e | ||
|
|
7f651b144f | ||
|
|
ff98658491 | ||
|
|
53db5943b1 | ||
|
|
33b7ab0d98 | ||
|
|
f0af93f8b9 | ||
|
|
379fd2353a | ||
|
|
98f0766425 | ||
|
|
be07a4735c | ||
|
|
d736a10dc9 | ||
|
|
c5c3b9cba1 | ||
|
|
bfa9ad4d96 | ||
|
|
2f8ece9080 | ||
|
|
c7e769e42e | ||
|
|
3ab06d0832 | ||
|
|
251bc71f86 | ||
|
|
3f3870bb30 | ||
|
|
eb9612b9a3 | ||
|
|
1d7efce197 | ||
|
|
ff60cf313e | ||
|
|
a275878ba0 | ||
|
|
09fb4c1d35 | ||
|
|
483c1c35fd | ||
|
|
810b8be92a | ||
|
|
679ed7b806 | ||
|
|
e1896c0216 | ||
|
|
01310c166e | ||
|
|
3bc93899fe | ||
|
|
69bd988174 | ||
|
|
20bee1196a | ||
|
|
9803da1825 | ||
|
|
bc61e32ee7 | ||
|
|
4f784e2eea | ||
|
|
931d8d355f | ||
|
|
6912b6eb39 | ||
|
|
163d2c9b10 | ||
|
|
cc8def1cf5 | ||
|
|
bcdefdc4ac | ||
|
|
4f2e1be9ac | ||
|
|
663623dec6 | ||
|
|
cecf5d7e31 | ||
|
|
8a4caeaa2d | ||
|
|
f8062ba39f | ||
|
|
3e6e3b0743 | ||
|
|
1e35eaf62a | ||
|
|
8a3dc2f3dc | ||
|
|
34a6fdc07e | ||
|
|
fc65cb6000 | ||
|
|
247add778d | ||
|
|
ab1071f1d7 | ||
|
|
f5cb5c3993 | ||
|
|
ea7f122030 | ||
|
|
2160cc4aaa | ||
|
|
817e99a05d | ||
|
|
53f5656478 | ||
|
|
42d11bd3f1 | ||
|
|
a028ebe198 | ||
|
|
c315adf987 | ||
|
|
01371f227c | ||
|
|
3174deed99 | ||
|
|
6db178e4d2 | ||
|
|
eb07e03e93 | ||
|
|
a881cd2bcc | ||
|
|
676b09720a | ||
|
|
ac7b6d9ecd | ||
|
|
49cb81b516 | ||
|
|
27e361dc5b | ||
|
|
4c330bfb16 | ||
|
|
fcb85c85a3 | ||
|
|
9be1b96226 | ||
|
|
11598f9a09 | ||
|
|
39e23237a5 | ||
|
|
22507673aa | ||
|
|
7fdcd4aa15 | ||
|
|
c559508175 | ||
|
|
95621b6aab | ||
|
|
fb93aa1ad5 | ||
|
|
035c69c60a | ||
|
|
788bbb5d25 | ||
|
|
04178ca824 | ||
|
|
647bdb78df | ||
|
|
ce9099a25b | ||
|
|
4e715750a5 | ||
|
|
b330f72326 | ||
|
|
dd5c95d2f2 | ||
|
|
ae5e0cc71a | ||
|
|
cfe0e36e48 | ||
|
|
63a362286e | ||
|
|
2155aa0d21 | ||
|
|
f52fda3f03 | ||
|
|
f315f8b85a | ||
|
|
72e56246f4 | ||
|
|
5102cb35c8 | ||
|
|
170853f0f4 | ||
|
|
7bc0d88898 | ||
|
|
4834cfe8ca | ||
|
|
2c2065119b | ||
|
|
636672fdce | ||
|
|
bc8c70fa9c | ||
|
|
e3ac9a7722 | ||
|
|
e14d3d7214 | ||
|
|
5b898a678b | ||
|
|
e2d6baaeb1 | ||
|
|
eb87ba1d89 | ||
|
|
0257e70c29 | ||
|
|
70d1a3534b | ||
|
|
16f4903eba | ||
|
|
6b77d72f06 | ||
|
|
ac5768e666 | ||
|
|
f3bd47f347 | ||
|
|
1fbb47d64b | ||
|
|
3797887abc | ||
|
|
8870eef79b | ||
|
|
eefcd9e738 | ||
|
|
5f296bbe30 | ||
|
|
d88fa1131d | ||
|
|
3c6071ad88 | ||
|
|
deb772f0a7 | ||
|
|
858719aad8 | ||
|
|
14debfd25c | ||
|
|
510c9cafec | ||
|
|
6434902f86 | ||
|
|
16be84420b | ||
|
|
8f2283f9aa | ||
|
|
920b84886c | ||
|
|
cb2f0e40ba | ||
|
|
14a9c9910c | ||
|
|
c8d0ae8659 | ||
|
|
fd541ead6c | ||
|
|
71e7ea0230 | ||
|
|
cab2d41269 | ||
|
|
f5b1c79029 | ||
|
|
885a3f1ac9 | ||
|
|
e821b2d09c | ||
|
|
1b2bff8a77 | ||
|
|
d213e94860 | ||
|
|
d2b71d97d2 | ||
|
|
1ff7bdf1a7 | ||
|
|
f221f2df4f | ||
|
|
46f365c42d | ||
|
|
a53c00aeda | ||
|
|
044818aa65 | ||
|
|
a55084dbae | ||
|
|
c2c9528e80 | ||
|
|
9c7ad95f6e | ||
|
|
fe9dc0a3e5 | ||
|
|
25712ef778 | ||
|
|
a63b543e0c | ||
|
|
8ebec1f957 | ||
|
|
0733fee878 | ||
|
|
d52dd535a3 | ||
|
|
cbc6475875 | ||
|
|
c6de92592c | ||
|
|
5f97734881 | ||
|
|
62fbb7c9c8 | ||
|
|
4ddbdebae4 | ||
|
|
542b79fa00 | ||
|
|
9f6f5c8a76 | ||
|
|
8591f649d1 | ||
|
|
3bbd51614d | ||
|
|
cb20c8588f | ||
|
|
10b1c6ebfb | ||
|
|
3c6739b83a | ||
|
|
57426f783e | ||
|
|
0788ff050d | ||
|
|
18d59c119c | ||
|
|
ae34cd5422 | ||
|
|
50807e9381 | ||
|
|
fdb4d4d443 | ||
|
|
ff22f12a56 | ||
|
|
15dc2a325a | ||
|
|
ee9c1db000 | ||
|
|
b3b134ea45 | ||
|
|
2e9b024390 | ||
|
|
ee2193e1bb | ||
|
|
a582cf93bd | ||
|
|
fc2d7cf7b8 | ||
|
|
61836dbb83 | ||
|
|
154122388e | ||
|
|
0114417018 | ||
|
|
e662edc2cc | ||
|
|
9e6cdb2f4f | ||
|
|
98b1fdb476 | ||
|
|
0f0e544f54 | ||
|
|
6d50f03396 | ||
|
|
7ec9d3f122 | ||
|
|
592adb36f1 | ||
|
|
d571191ec2 | ||
|
|
1f5fe47580 | ||
|
|
1e3783c21d | ||
|
|
7190d91d31 | ||
|
|
5f697c166a | ||
|
|
68b5fd9893 | ||
|
|
3e1a91d073 | ||
|
|
c68451228a | ||
|
|
77ae235385 | ||
|
|
c4009bdbd7 | ||
|
|
1eb48b00e1 | ||
|
|
67cef93dd8 | ||
|
|
2aa274f56f | ||
|
|
cd20164d7a | ||
|
|
5a0ca503c1 | ||
|
|
4c1c15e69e | ||
|
|
0320a16ba4 | ||
|
|
fcc8f3c5a7 | ||
|
|
fae3e8568a | ||
|
|
4d2bb5ba87 | ||
|
|
d71cf64564 | ||
|
|
1bb30499c2 | ||
|
|
d8deb98d7b | ||
|
|
e1078ef6da | ||
|
|
a1d807bd45 | ||
|
|
4f3228388c | ||
|
|
81e3edc041 | ||
|
|
077db2ecd6 | ||
|
|
6d2746ad75 | ||
|
|
0ffdae97fd | ||
|
|
70fd1ac6de | ||
|
|
78d056c6ff | ||
|
|
43ba63233d | ||
|
|
adf750fe44 | ||
|
|
250996e8ac | ||
|
|
56639a0812 | ||
|
|
c12e450648 | ||
|
|
4fce6f7b99 | ||
|
|
27b8c12639 | ||
|
|
5d5d9ff153 | ||
|
|
d803c8374f | ||
|
|
a5b22aa112 | ||
|
|
ae8fb25d3f | ||
|
|
530dd1c03b | ||
|
|
473b65850d | ||
|
|
2c49bde5bf | ||
|
|
97b32b33d3 | ||
|
|
1382e87133 | ||
|
|
19d03591b1 | ||
|
|
6f1321aa13 | ||
|
|
365a3798c2 | ||
|
|
8d3981e1a4 | ||
|
|
e34fcb2f9c | ||
|
|
71f1c69f23 | ||
|
|
072ad028a3 | ||
|
|
a652e12fa4 | ||
|
|
58f3618350 | ||
|
|
1b26cee9c1 | ||
|
|
4752e5a20f | ||
|
|
d8e277593d | ||
|
|
edbc341909 | ||
|
|
5f20f249f7 | ||
|
|
a1de3b9225 | ||
|
|
5110e63809 | ||
|
|
26d4cfa2de | ||
|
|
ae1a9950bc | ||
|
|
1c120f2fd6 | ||
|
|
d7e45b0f76 | ||
|
|
e796b748b6 | ||
|
|
a74984d37b | ||
|
|
e342f96fbe | ||
|
|
e262aa7daa | ||
|
|
c64d09ca54 | ||
|
|
8def076175 | ||
|
|
a64fbd8976 | ||
|
|
eda869fe0d | ||
|
|
04a74e278b | ||
|
|
6786cc7eff | ||
|
|
6984bd435f | ||
|
|
ec8b771a24 | ||
|
|
6ce72e4fb3 | ||
|
|
cda9ba5978 | ||
|
|
e2ae89f6b9 | ||
|
|
3bc3705c42 | ||
|
|
522de5ca5a | ||
|
|
efd8a6964e | ||
|
|
1cd10d2109 | ||
|
|
3a09f4b45c | ||
|
|
64bc2c34c2 | ||
|
|
845630437e | ||
|
|
c4484d735a | ||
|
|
5b74c6c5e1 | ||
|
|
3e410540c9 | ||
|
|
5bba1dc88b | ||
|
|
8c0cae8bc3 | ||
|
|
71e55a000b | ||
|
|
7bcdf95f5c | ||
|
|
4402addcb0 | ||
|
|
3d57861481 | ||
|
|
5d1d2b87df | ||
|
|
389b5d57aa | ||
|
|
53de46bab7 | ||
|
|
e6dce726b7 | ||
|
|
63ca8dc559 | ||
|
|
6e083a5af8 | ||
|
|
ac93c5487c | ||
|
|
1f94b28b87 | ||
|
|
78b6eb4283 | ||
|
|
417e478d27 | ||
|
|
78d2dff0d8 | ||
|
|
63c45c5060 | ||
|
|
c4f225003a | ||
|
|
185cf4f625 | ||
|
|
d2b838e9d5 | ||
|
|
15b6a848e8 | ||
|
|
193fcc60d8 | ||
|
|
ae110371fe | ||
|
|
5857413285 | ||
|
|
4448d7e62f | ||
|
|
d3ca0a961e | ||
|
|
405492d9d7 | ||
|
|
d27d7656d5 | ||
|
|
120bd9aa0c | ||
|
|
6ab79ab5c0 | ||
|
|
b8d189c0ad | ||
|
|
86e04321c8 | ||
|
|
6bcc906c4a | ||
|
|
c3becec822 | ||
|
|
aae2e7c531 | ||
|
|
52490144d3 | ||
|
|
c04c672f11 | ||
|
|
f51979b69a | ||
|
|
ab6b9759b0 | ||
|
|
b3027532ff | ||
|
|
494c9b08cb | ||
|
|
c595195519 | ||
|
|
c3efa819f4 | ||
|
|
4e7580b277 | ||
|
|
af642a4259 | ||
|
|
c365efb67e | ||
|
|
fc7613451e | ||
|
|
62b7b44120 | ||
|
|
744fce2e82 | ||
|
|
dd55493b4e | ||
|
|
7e7b49d2e4 | ||
|
|
24494e9b29 | ||
|
|
eff0510092 | ||
|
|
988688939b | ||
|
|
d448116e91 | ||
|
|
75ce6ffbcf | ||
|
|
60933a309f | ||
|
|
428cb5c888 | ||
|
|
d195ec7e68 | ||
|
|
c2017f3cb9 | ||
|
|
66ff4d827c | ||
|
|
745914bf9e | ||
|
|
421146eb54 | ||
|
|
ef81f9c830 | ||
|
|
1e760b2111 | ||
|
|
fe50372b12 | ||
|
|
7ef79eaa79 | ||
|
|
9b282587b2 | ||
|
|
4af36514bc | ||
|
|
5320e99276 | ||
|
|
b733205541 | ||
|
|
b125c62930 | ||
|
|
7895e4076d | ||
|
|
9ec192de7d | ||
|
|
8e41a31d1d | ||
|
|
fa4a2436aa | ||
|
|
22ca78cb68 | ||
|
|
ee4a1f936b | ||
|
|
15a8c5750a | ||
|
|
9f261f5b80 | ||
|
|
b6a58b4ba6 | ||
|
|
09ca85ca81 | ||
|
|
3aa69a6eaf | ||
|
|
2a645b1b04 | ||
|
|
0420f399ad | ||
|
|
509a45dcee | ||
|
|
52724d790b | ||
|
|
4b960af9ab | ||
|
|
022f0c06ee | ||
|
|
5ffd644ad9 | ||
|
|
03183827a6 | ||
|
|
5e7fcc32b6 | ||
|
|
3c0d87940b | ||
|
|
4cf07c4b76 | ||
|
|
28db388fa0 | ||
|
|
2c1905f041 | ||
|
|
30d03f0ab5 | ||
|
|
4ca3f10bc9 | ||
|
|
9cc228cfff | ||
|
|
3359d8cb88 | ||
|
|
738d7f687d | ||
|
|
b224196b05 | ||
|
|
bbcc32c8cf | ||
|
|
3c0b8643f6 | ||
|
|
c85b6e4a36 | ||
|
|
6003302e10 | ||
|
|
e7dd045979 | ||
|
|
6ca7a22c3e | ||
|
|
e8f09514ab | ||
|
|
1a3a656879 | ||
|
|
e77ada4e8c | ||
|
|
41b72c2789 | ||
|
|
a4be7c5e9a | ||
|
|
fb3c183b3e | ||
|
|
3e7dbef659 | ||
|
|
89260d1d36 | ||
|
|
d451bda7ed | ||
|
|
933c84466f | ||
|
|
d4c9100f77 | ||
|
|
c6aa72a3e3 | ||
|
|
7cf6ff04b6 | ||
|
|
5b575fdfe3 | ||
|
|
a8a5fabce7 | ||
|
|
f41d6dd2c1 | ||
|
|
09727c102a | ||
|
|
6580734dc7 | ||
|
|
ff34865067 | ||
|
|
bdd400fd51 | ||
|
|
1e8184a80b | ||
|
|
6a20f04c7f | ||
|
|
d81acc1f9c | ||
|
|
bc84c20cb2 | ||
|
|
d5c5e2698e | ||
|
|
16a78e689e | ||
|
|
066f29660d | ||
|
|
bba0df5f50 | ||
|
|
45452ca680 | ||
|
|
4fbbc18f9f | ||
|
|
22ec280ec2 | ||
|
|
89c06b5201 | ||
|
|
86d5f72988 | ||
|
|
c3e055a4c9 | ||
|
|
e48631956d | ||
|
|
2964f3b009 | ||
|
|
527c66dca4 | ||
|
|
24909f0523 | ||
|
|
ed7494b3a4 | ||
|
|
8fae275e5a | ||
|
|
a09a2a5f4b | ||
|
|
2adc150811 | ||
|
|
f5cad33b6c | ||
|
|
338cf45f65 | ||
|
|
359c60bafb | ||
|
|
f808b73a5d | ||
|
|
8dd87cde58 | ||
|
|
1ec78d9beb | ||
|
|
9c710285f2 | ||
|
|
90f745a18f | ||
|
|
af446579ab | ||
|
|
bcc11bd172 | ||
|
|
0c31f756a8 | ||
|
|
16fdd5a5e6 | ||
|
|
ec1a2c66ee | ||
|
|
c3f41d68e6 | ||
|
|
2f8701b4b2 | ||
|
|
9b2d5410d6 | ||
|
|
1d8c9d2c40 | ||
|
|
8c0817245f | ||
|
|
2a04e60ae0 | ||
|
|
fb5eb220fd | ||
|
|
512f48ebdd | ||
|
|
09db7d26a7 | ||
|
|
690cf5eca1 | ||
|
|
1aee4c59c4 | ||
|
|
f1384074b5 | ||
|
|
203bed06d6 | ||
|
|
0b00e28863 | ||
|
|
d74d331642 | ||
|
|
f075fbdc63 | ||
|
|
d59b6696ca | ||
|
|
eedf6f9a39 | ||
|
|
1cca5729fc | ||
|
|
a7b01ece22 | ||
|
|
d17e6d08d8 | ||
|
|
b29aaa9e20 | ||
|
|
71ae59b2b5 | ||
|
|
51294f6cbc | ||
|
|
0439ace886 | ||
|
|
c85c735f9a | ||
|
|
c65b582497 | ||
|
|
7f2ac83e17 | ||
|
|
5ef2a40d1e | ||
|
|
5b52da737a | ||
|
|
51e8713cd6 | ||
|
|
c9b60f2c65 | ||
|
|
d777999af4 | ||
|
|
74444d56c4 | ||
|
|
a433c9638a | ||
|
|
672141cffc | ||
|
|
ac132cbb92 | ||
|
|
d9535b08b1 | ||
|
|
d93544b3bc | ||
|
|
2320c3cb57 | ||
|
|
bd5710c676 | ||
|
|
49f1412d91 | ||
|
|
54eea7d702 | ||
|
|
e26bcb2e5e | ||
|
|
7305c0a017 | ||
|
|
7d37b9e0e0 | ||
|
|
87f28db730 | ||
|
|
56d9a8b626 | ||
|
|
cb8f76c582 | ||
|
|
af0b7b92c7 | ||
|
|
9418b7a709 | ||
|
|
47c34f2186 | ||
|
|
2ca418c287 | ||
|
|
775e69305c | ||
|
|
0f1cbb4234 | ||
|
|
306710a314 | ||
|
|
776a4ee977 | ||
|
|
9dccfd756a | ||
|
|
15281ee6ce | ||
|
|
9a0dd6c521 | ||
|
|
a570f291ae | ||
|
|
cde7fdcaba | ||
|
|
e4780bc8ba | ||
|
|
4d35e5aee1 | ||
|
|
f7b705b9e2 | ||
|
|
48f44cdb0c | ||
|
|
013a2264c0 | ||
|
|
8222686dda | ||
|
|
7f2121e98d | ||
|
|
4b6c881dca | ||
|
|
5f9bf4a861 | ||
|
|
154db5df0b | ||
|
|
321b939d3a | ||
|
|
95a1d669f5 | ||
|
|
9e3c9bd056 | ||
|
|
bb9b3780ae | ||
|
|
4c976d9f35 | ||
|
|
52a1314803 | ||
|
|
a5475eb244 | ||
|
|
ba0a5db72f | ||
|
|
2bac4a954f | ||
|
|
e9f3453b04 | ||
|
|
c950592b5b | ||
|
|
1cd42669a0 | ||
|
|
2b24f14122 | ||
|
|
4932b685e1 | ||
|
|
cfffa1d99d | ||
|
|
44a7e53b9e | ||
|
|
b35bdd4e33 | ||
|
|
7f52f6fe44 | ||
|
|
34e49da0c1 | ||
|
|
5132698974 | ||
|
|
832cebcaaf | ||
|
|
4eaccd1eed | ||
|
|
5245f289a5 | ||
|
|
672aed44f2 | ||
|
|
8c890cf9a5 | ||
|
|
8f9a95db93 | ||
|
|
5a056e6d47 | ||
|
|
b121085271 | ||
|
|
b3ef55cb7f | ||
|
|
bbc8fd0f97 | ||
|
|
b11a576922 | ||
|
|
7375258b9f | ||
|
|
c447cf06d7 | ||
|
|
a7dcf1ace6 | ||
|
|
bd3d81e2f8 | ||
|
|
732b7a5ab9 | ||
|
|
3c16600c53 | ||
|
|
d0921ba9ec | ||
|
|
8ec840740b | ||
|
|
580ff1c435 | ||
|
|
d68296bfd7 | ||
|
|
c520b5e4f5 | ||
|
|
2da85888be | ||
|
|
bdd4a8cfb7 | ||
|
|
dbef3b8c84 | ||
|
|
63a6d746e8 | ||
|
|
7b8d9f8dbe | ||
|
|
7aa73e4b2f | ||
|
|
33883c562a | ||
|
|
36c17dde8d | ||
|
|
5a9fd188f0 | ||
|
|
4b65662c9f | ||
|
|
cd3d1a9557 | ||
|
|
9f63b0b412 | ||
|
|
9249ef24b3 | ||
|
|
63a599ca85 | ||
|
|
fa2bceaff9 | ||
|
|
e9dffeadf6 | ||
|
|
161c4629cf | ||
|
|
951ffcd35a | ||
|
|
ba21ebe058 | ||
|
|
f8c2604fb2 | ||
|
|
966763aaa4 | ||
|
|
4eb8973c31 | ||
|
|
72fe52e560 | ||
|
|
e346239174 | ||
|
|
dd27a26fa9 | ||
|
|
162b115c91 | ||
|
|
99f4ed6b33 | ||
|
|
53252241e1 | ||
|
|
698328e335 | ||
|
|
dece788f66 | ||
|
|
958fbac582 | ||
|
|
99d72956e8 | ||
|
|
eb601e991a | ||
|
|
cb7f0052c4 | ||
|
|
8b75b5f184 | ||
|
|
bf5935e033 | ||
|
|
67e80deed9 | ||
|
|
e39f9ffecf | ||
|
|
e865c9a399 | ||
|
|
a919510d07 | ||
|
|
bd86d3289f | ||
|
|
c8ff644b63 | ||
|
|
6703a08976 | ||
|
|
3d70d9e37c | ||
|
|
ef62d5cf97 | ||
|
|
caf5f33c80 | ||
|
|
29c29469c6 | ||
|
|
7b564f1f53 | ||
|
|
f280955ac3 | ||
|
|
e09fce8411 | ||
|
|
5777103d21 | ||
|
|
8671b20790 | ||
|
|
dc30b3ed1d | ||
|
|
07a9f77287 | ||
|
|
9d58ceb14d | ||
|
|
71c421702c | ||
|
|
989f12453d | ||
|
|
b7d6892d9f | ||
|
|
cfb351a751 | ||
|
|
dde1e7990d | ||
|
|
6e4ec54ac6 | ||
|
|
81da217a09 | ||
|
|
2d1613d8bc | ||
|
|
ccc2e6ece3 | ||
|
|
1206e95cfb | ||
|
|
921ccd3be9 | ||
|
|
178e3a3d56 | ||
|
|
5d255547e4 | ||
|
|
bd148b8792 | ||
|
|
b88ebdcfc0 | ||
|
|
3a26815d18 | ||
|
|
b70fcd9659 | ||
|
|
71aa557770 | ||
|
|
d35bf6514f | ||
|
|
abe015bee3 | ||
|
|
929520091f | ||
|
|
d4847112a9 | ||
|
|
4e2c3a3fcc | ||
|
|
91e6d89022 | ||
|
|
b9a71c08b7 | ||
|
|
070dd62e6d | ||
|
|
b404abca41 | ||
|
|
99f00b25a1 | ||
|
|
45936b5b98 | ||
|
|
b1aac9f56d | ||
|
|
13f370ceb9 | ||
|
|
b5a4f97cb5 | ||
|
|
0679a0af0f | ||
|
|
53fe3242b9 | ||
|
|
6615bed1cd | ||
|
|
ad0c7a7a9d | ||
|
|
87ba6a9af0 | ||
|
|
5c44df7f21 | ||
|
|
24bc675319 | ||
|
|
be2e0b5de4 | ||
|
|
e4481e8fb4 | ||
|
|
19b1eb161b | ||
|
|
30ed1ac81d | ||
|
|
b4288e17e5 | ||
|
|
c03d000c45 | ||
|
|
881ec8b5b4 | ||
|
|
71d5b28b75 | ||
|
|
a15fdc3b23 | ||
|
|
55cd33e653 | ||
|
|
af0b2b9f1b | ||
|
|
983641d1d5 | ||
|
|
4ab21e92bf | ||
|
|
413f00a475 | ||
|
|
bde2043294 | ||
|
|
d45857f1fc | ||
|
|
8a19bf78ce | ||
|
|
d523722988 | ||
|
|
031cb6fcfb | ||
|
|
72fc560241 | ||
|
|
1d7be66eb1 | ||
|
|
5d32a31dc3 | ||
|
|
4821bd1c72 | ||
|
|
6694caafa0 | ||
|
|
7b71c16cec | ||
|
|
df6e7b5772 | ||
|
|
7abc14eb7f | ||
|
|
4d21fa517a | ||
|
|
f2a82c31c4 | ||
|
|
337973965a | ||
|
|
f0b3aafd54 | ||
|
|
f6b97859aa | ||
|
|
1064a90a1c | ||
|
|
4c736de598 | ||
|
|
8d0ad26159 | ||
|
|
3efa07d821 | ||
|
|
44240c1d37 | ||
|
|
9eaeb60af7 | ||
|
|
cecb8f69a3 | ||
|
|
d402735c8b | ||
|
|
327ad78eb8 | ||
|
|
edc1ef0e38 | ||
|
|
c6bfd0bc05 | ||
|
|
3badab0353 | ||
|
|
8ca5b34c14 | ||
|
|
417ffd396c | ||
|
|
ccb0732f7d | ||
|
|
a994f55011 | ||
|
|
c2d309430e | ||
|
|
c3e1ec2760 | ||
|
|
8c40489049 | ||
|
|
051800bc16 | ||
|
|
d1a3e5c0e8 | ||
|
|
48d795676f | ||
|
|
3fec9ba173 | ||
|
|
264208b42e | ||
|
|
62e8da6ff2 | ||
|
|
e3f21cf700 | ||
|
|
fbc34224bc | ||
|
|
93cd3b97fa | ||
|
|
fda83bcb49 | ||
|
|
85b046a640 | ||
|
|
1177575f77 | ||
|
|
fc4a10efe3 | ||
|
|
f541b4329e | ||
|
|
7ba1a4c78f | ||
|
|
0675ed9a73 | ||
|
|
68a13a6bb8 | ||
|
|
61ef7c3487 | ||
|
|
a5bb16c460 | ||
|
|
e694ced86c | ||
|
|
babc2d1e42 | ||
|
|
8e717b19d9 | ||
|
|
dbca0f9dea | ||
|
|
7380244cd9 | ||
|
|
5b7c777d6b | ||
|
|
2fb9d3479f | ||
|
|
21f095250d | ||
|
|
270376e09c | ||
|
|
1ed71c6580 | ||
|
|
febc98045c | ||
|
|
72ecc62732 | ||
|
|
9e1681d3f9 | ||
|
|
0471ffa924 | ||
|
|
4269a00428 | ||
|
|
440b2caa8d | ||
|
|
7131cdbac6 | ||
|
|
b70c1a7c6c | ||
|
|
f9fd265280 | ||
|
|
9261d29ac1 | ||
|
|
df658d4950 | ||
|
|
69f68bf4ea | ||
|
|
614f3acc7e | ||
|
|
ec77acda1d | ||
|
|
603553517e | ||
|
|
4c04fd93ae | ||
|
|
f4f92b0821 | ||
|
|
3aede13136 | ||
|
|
d171548936 | ||
|
|
3d0db365af | ||
|
|
2716214816 | ||
|
|
91a46a2dd4 | ||
|
|
9fd70a4ba9 | ||
|
|
400bb10ed3 | ||
|
|
53915eb956 | ||
|
|
5457c31148 | ||
|
|
be9ac7cc9c | ||
|
|
3e2d21ea44 | ||
|
|
be374f4eb4 | ||
|
|
ab200ef74b | ||
|
|
3e1ef6e76d | ||
|
|
9fd0f016aa | ||
|
|
ae188826ae | ||
|
|
0736085676 | ||
|
|
fa4c48c6c2 | ||
|
|
82adccadda | ||
|
|
f100971fbe | ||
|
|
af2da86f6f | ||
|
|
5af211778b | ||
|
|
4c739308f9 | ||
|
|
98e834fd30 | ||
|
|
7ae8d1dc1a | ||
|
|
e57fce39fe | ||
|
|
fd54d2c28c | ||
|
|
b5c411349c | ||
|
|
4d31c8bb38 | ||
|
|
fbf3cace10 | ||
|
|
30b12b04e8 | ||
|
|
9301bb56fa | ||
|
|
bc4c35665e | ||
|
|
64bba0cfdb | ||
|
|
a270dc44d2 | ||
|
|
46fa1eb0c6 | ||
|
|
c7ed6367f9 | ||
|
|
9523c6f349 | ||
|
|
8b0f5f871c | ||
|
|
3fa5ac7858 | ||
|
|
4c0f88cdfa | ||
|
|
0fedb17586 | ||
|
|
960bef2b96 | ||
|
|
82777d0b02 | ||
|
|
a560597a85 | ||
|
|
59fb7bcd1b | ||
|
|
801f51b89c | ||
|
|
0b49bf201f | ||
|
|
cc8575b275 | ||
|
|
51a11edc36 | ||
|
|
4a6c1c3920 | ||
|
|
1cb0081420 | ||
|
|
d61d69188f | ||
|
|
a829efa7ff | ||
|
|
e6599d1a40 | ||
|
|
c4c68a4e93 | ||
|
|
f80244d560 | ||
|
|
a8684ef1b9 | ||
|
|
5ac9604fab | ||
|
|
700e249bf3 | ||
|
|
c2b742304e | ||
|
|
c2b0257449 | ||
|
|
0d90c187f2 | ||
|
|
88acec4241 | ||
|
|
9a6255cb29 | ||
|
|
c721869dfa | ||
|
|
1154435a89 | ||
|
|
6d9e60648e | ||
|
|
4522edb814 | ||
|
|
8139d88a52 | ||
|
|
353b845102 | ||
|
|
c3bffcad34 | ||
|
|
9e6031edf2 | ||
|
|
bb47df2119 | ||
|
|
aab9b39fea | ||
|
|
1ca4c73a3e | ||
|
|
7f870cf675 | ||
|
|
5f8ccb9831 | ||
|
|
c94acd9718 | ||
|
|
1733d6abb8 | ||
|
|
aa9f16d3ad | ||
|
|
c997661f3a | ||
|
|
6174bf8a0b | ||
|
|
36c19fe6e8 | ||
|
|
e6c9db3eef | ||
|
|
336af0f669 | ||
|
|
2800637a90 | ||
|
|
1fb9936893 | ||
|
|
d738ba615e | ||
|
|
e5a694384d | ||
|
|
1d98f889fd | ||
|
|
ad03f907fa | ||
|
|
5579929f83 | ||
|
|
e90e9a5ca1 | ||
|
|
2341650437 | ||
|
|
f2c26af791 | ||
|
|
fc82e45d6c | ||
|
|
1b54f07ce0 | ||
|
|
938fa11d13 | ||
|
|
a08aa77afc | ||
|
|
4cbeb34a3e | ||
|
|
d2fab0a10e | ||
|
|
a8c3612248 | ||
|
|
50e4e6fdcf | ||
|
|
69ada4f3ad | ||
|
|
3fc77d03b4 | ||
|
|
8072bb600c | ||
|
|
fda2c43a1d | ||
|
|
7fc98037a6 | ||
|
|
b51c8d8ecf | ||
|
|
dca2db5a6d | ||
|
|
fbb192404f | ||
|
|
7e41d8e20c | ||
|
|
9460998015 | ||
|
|
86056bf282 | ||
|
|
bacb374ea4 | ||
|
|
8d07f11aa4 | ||
|
|
b6a13b32de | ||
|
|
1f1a16f896 | ||
|
|
444d7af7ed | ||
|
|
e45e0b9444 | ||
|
|
33886aea1b | ||
|
|
1ddc2edd88 | ||
|
|
01b16ec5f0 | ||
|
|
ba337599c2 | ||
|
|
211a83defd | ||
|
|
091a7d7ab8 | ||
|
|
b3030bacf9 | ||
|
|
feb164a48e | ||
|
|
b3a556e70a | ||
|
|
9d322ca862 | ||
|
|
9bde08b1ce | ||
|
|
953a67e2c4 | ||
|
|
2435e08dc0 | ||
|
|
31645d20a5 | ||
|
|
5fa96c5e52 | ||
|
|
278d946f77 | ||
|
|
9b8aa67271 | ||
|
|
af197e56cf | ||
|
|
1d5ba89f2e | ||
|
|
f45ad5ceab | ||
|
|
5dae614fa1 | ||
|
|
c144c16a7f | ||
|
|
10425cd5d9 | ||
|
|
1d208a0cf7 | ||
|
|
db07f9d534 | ||
|
|
ca4eeed8d1 | ||
|
|
292d95f6f8 | ||
|
|
bd5ee32227 | ||
|
|
0a705d1d7f | ||
|
|
be1bd24a05 | ||
|
|
a9fb3501ac | ||
|
|
b8a8f9c422 | ||
|
|
6c29b3f38b | ||
|
|
f784f45b4e | ||
|
|
d59111eef5 | ||
|
|
99a2a38f9c | ||
|
|
768c1d37b1 | ||
|
|
b21cf5673f | ||
|
|
a6b8381e25 | ||
|
|
7d8768ca3e | ||
|
|
96e0a02d5c | ||
|
|
6a2bdade80 | ||
|
|
ea9eb3a8b7 | ||
|
|
9c5fe56981 | ||
|
|
8fed78d596 | ||
|
|
3c5846a983 | ||
|
|
fa0892032f | ||
|
|
ed7dd12258 | ||
|
|
1a5fc31e25 | ||
|
|
facb0b1023 | ||
|
|
6dd737cdd1 | ||
|
|
5d012d6959 | ||
|
|
b00dbbbf42 | ||
|
|
b269381818 | ||
|
|
fa72345bcf | ||
|
|
3229652efa | ||
|
|
c02cccf415 | ||
|
|
54277ebbda | ||
|
|
78c6d4f005 | ||
|
|
37ad2faa8e | ||
|
|
26cba1c47a | ||
|
|
f95420d7bf | ||
|
|
9c79fb666b | ||
|
|
a9a9fd1b1a | ||
|
|
253d3107a0 | ||
|
|
656a7fc784 | ||
|
|
be579cfaeb | ||
|
|
d40a7c2efa | ||
|
|
8ba05409e0 | ||
|
|
2b817c5d22 | ||
|
|
ba445e7d4f | ||
|
|
b836ceb216 | ||
|
|
dfa0373497 | ||
|
|
caefd4fbbb | ||
|
|
b7528d11b2 | ||
|
|
07d333d3d5 | ||
|
|
48ac6cb2af | ||
|
|
87a246204b | ||
|
|
3ef056225b | ||
|
|
4fa63aa2bd | ||
|
|
1edaf021da | ||
|
|
37b44befac | ||
|
|
efd58a80a4 | ||
|
|
e43cce20a6 | ||
|
|
0c7601b6d3 | ||
|
|
4cfcfedc14 | ||
|
|
0024bc5514 | ||
|
|
aacb95df7c | ||
|
|
854d60792e | ||
|
|
d922bf8913 | ||
|
|
703dc93dee | ||
|
|
7fd545aad4 | ||
|
|
a69730085f | ||
|
|
1603201166 | ||
|
|
ead8238d7e | ||
|
|
8380f516e8 | ||
|
|
1b7c23b1d3 | ||
|
|
295368635b | ||
|
|
6291fe1626 | ||
|
|
cdaf5cc168 | ||
|
|
5940625e24 | ||
|
|
549353dba1 | ||
|
|
4dc281b37e | ||
|
|
0242cc845c | ||
|
|
e9c6d98db8 | ||
|
|
86459faf76 | ||
|
|
e8af636831 | ||
|
|
eea7220e3d | ||
|
|
cc56611783 | ||
|
|
0b511af48a | ||
|
|
25bbf3dc03 | ||
|
|
72a602b5b0 | ||
|
|
d107156341 | ||
|
|
8063647bba | ||
|
|
573d8e068c | ||
|
|
f6c6f9a0c9 | ||
|
|
d9c5c9cc0e | ||
|
|
bb7fb022ef | ||
|
|
52c9f4076b | ||
|
|
894231b009 | ||
|
|
34fe530ac7 | ||
|
|
33b91a79ab | ||
|
|
51180a9375 | ||
|
|
6e44963d57 | ||
|
|
8114765236 | ||
|
|
7ddd3a35d1 | ||
|
|
52a68adb11 | ||
|
|
566e7fd046 | ||
|
|
a74cdc59bd | ||
|
|
3c98aad4ba | ||
|
|
0063840cfa | ||
|
|
96077ed115 | ||
|
|
19f02ef914 | ||
|
|
de9ce47bed | ||
|
|
ccd3665115 | ||
|
|
1a992a90ad | ||
|
|
fff07078dc | ||
|
|
81d040fba6 | ||
|
|
a35f04cd9a | ||
|
|
c6c364c451 | ||
|
|
8dcc3f2b51 | ||
|
|
42383cef03 | ||
|
|
579081393b | ||
|
|
2290281024 | ||
|
|
712c49d129 | ||
|
|
ac16b19d93 | ||
|
|
51b3cff4a9 | ||
|
|
768b0b3a3a | ||
|
|
538271d737 | ||
|
|
61da8be732 | ||
|
|
90e275e386 | ||
|
|
8e98af8517 | ||
|
|
9a16c579eb | ||
|
|
8b864692fb | ||
|
|
eaddc942a0 | ||
|
|
82c5ae1fe9 | ||
|
|
5c2df66335 | ||
|
|
afa8c3c53a | ||
|
|
bd33efa875 | ||
|
|
f53c8e9efc | ||
|
|
d2b306d030 | ||
|
|
f58c8ec4f9 | ||
|
|
dd9390384e | ||
|
|
22fc6cb13c | ||
|
|
aaec82e493 | ||
|
|
6333a0c74b | ||
|
|
41711706c0 | ||
|
|
e6309bb8bb | ||
|
|
909c9f7b71 | ||
|
|
becf7c3ff2 | ||
|
|
257df58c3a | ||
|
|
23d945c7f2 | ||
|
|
e3971c995e | ||
|
|
8641c41da6 | ||
|
|
b7f53e8402 | ||
|
|
1ef517d87e | ||
|
|
c3e03f2136 | ||
|
|
4d31c2a2a3 | ||
|
|
b03860e765 | ||
|
|
2e49aa1419 | ||
|
|
647c093451 | ||
|
|
d96146034a | ||
|
|
b944f27fc8 | ||
|
|
df80449b0b | ||
|
|
986ae16eaf | ||
|
|
f81cc4ad93 | ||
|
|
c9b0322c9f | ||
|
|
114d72e23f | ||
|
|
f8e6474b39 | ||
|
|
8d0c2c18d2 | ||
|
|
dddec72f13 | ||
|
|
9cfbaf3450 | ||
|
|
9fc16cb287 | ||
|
|
78836add28 | ||
|
|
f919e23360 | ||
|
|
ec3822a6da | ||
|
|
9bdb4ea967 | ||
|
|
af17a4f1c9 | ||
|
|
2ea3394845 | ||
|
|
65dff8dcf7 | ||
|
|
bfaad631bc | ||
|
|
96a5bf7ea9 | ||
|
|
e665161dc8 | ||
|
|
18e03e46c3 | ||
|
|
d9941945e0 | ||
|
|
75d5b48df8 | ||
|
|
8c8e7825e9 | ||
|
|
af3a7553ed | ||
|
|
f68ee81634 | ||
|
|
4385b3651f | ||
|
|
ed24e7a2a3 | ||
|
|
b9e68504ed | ||
|
|
1fcd95d36f | ||
|
|
4dc70dad3b | ||
|
|
e0a54c14e2 | ||
|
|
839d025885 | ||
|
|
8d183a859d | ||
|
|
09bc1eb9d2 | ||
|
|
c6f2072bb9 | ||
|
|
7d42f8dba6 | ||
|
|
8b86f14ee2 | ||
|
|
ef7195a7cc | ||
|
|
43e88fa78d | ||
|
|
5d0900337f | ||
|
|
a5a9dc0877 | ||
|
|
e36b9661f0 | ||
|
|
c66f80d6e5 | ||
|
|
0dfe6707a7 | ||
|
|
f9eef5ee07 | ||
|
|
dc3b5c916b | ||
|
|
0471cda64e | ||
|
|
b2bcdf0a46 | ||
|
|
cf9d2082ef | ||
|
|
032b1589ad | ||
|
|
410c87ed89 | ||
|
|
b377c1c25f | ||
|
|
fbce3536da | ||
|
|
7ac24ff7ee | ||
|
|
7aba11142f | ||
|
|
704042b4b7 | ||
|
|
21dd9bb6e4 | ||
|
|
509849fbf5 | ||
|
|
55f2ebf4a4 | ||
|
|
e96a836ad0 | ||
|
|
2a224e8882 | ||
|
|
0a00d86c6d | ||
|
|
c3d1eaef2a | ||
|
|
93f23b549b | ||
|
|
b3d0554df6 | ||
|
|
dba54e3ec8 | ||
|
|
2b16a8ed76 | ||
|
|
4cbafb5559 | ||
|
|
d02623e365 | ||
|
|
c8eef489da | ||
|
|
d507927ba3 | ||
|
|
09421db28e | ||
|
|
941edd6715 | ||
|
|
fdac5309ac | ||
|
|
449c7d6aa2 | ||
|
|
b60b190b81 | ||
|
|
3cb8313669 | ||
|
|
ba29376464 | ||
|
|
436113dedc | ||
|
|
9dc0ec67d4 | ||
|
|
194ebb96a7 | ||
|
|
fc04a32913 | ||
|
|
deee6fd6ab | ||
|
|
a4fae73a1e | ||
|
|
7bad3bc376 | ||
|
|
7dd1c63898 | ||
|
|
c8eea45454 | ||
|
|
3ed949395e | ||
|
|
6ebc90b8aa | ||
|
|
7769179104 | ||
|
|
cbd1a34c68 | ||
|
|
e0c389a643 | ||
|
|
6a27ae7525 | ||
|
|
591dcffa43 | ||
|
|
b0317ea560 | ||
|
|
9eea99f600 | ||
|
|
d55d6c3e5e | ||
|
|
eeef2104c2 | ||
|
|
2098ff6da1 | ||
|
|
fb5d62304c | ||
|
|
8baac1e207 | ||
|
|
4324b846e0 | ||
|
|
143606d8f8 | ||
|
|
dfc0a896c4 | ||
|
|
980c84b911 | ||
|
|
41b6000f6e | ||
|
|
28fc845250 | ||
|
|
193faaafe7 | ||
|
|
ce9db79552 | ||
|
|
672fd77bec | ||
|
|
89656b04ad | ||
|
|
0997a54324 | ||
|
|
59569d46ae | ||
|
|
3bc1ce195c | ||
|
|
98e6781077 | ||
|
|
f2deb0e6c7 | ||
|
|
a5efea56ab | ||
|
|
1fe35f5aab | ||
|
|
07b8f5b914 | ||
|
|
1f50e7cb51 | ||
|
|
572d337935 | ||
|
|
01f8c65304 | ||
|
|
a1ee8b49ef | ||
|
|
0745ed50e0 | ||
|
|
76e66a99ff | ||
|
|
3259cc3e50 | ||
|
|
5f237e578b | ||
|
|
c645c089dd | ||
|
|
436bee1e85 | ||
|
|
dc5bd7f627 | ||
|
|
d6825fb8b7 | ||
|
|
b9ec89e9c8 | ||
|
|
cc63e62154 | ||
|
|
cc134c36e2 | ||
|
|
17313bd67b | ||
|
|
b831dfbc7f | ||
|
|
b739b791d4 | ||
|
|
0480673460 | ||
|
|
7ffced2151 | ||
|
|
3297ef8975 | ||
|
|
acba971e70 | ||
|
|
dd8169adb4 | ||
|
|
857a625ae2 | ||
|
|
ac3a7386aa | ||
|
|
3eed5bfc64 | ||
|
|
d0992ed56f | ||
|
|
af37ec21e9 | ||
|
|
51ef3ec758 | ||
|
|
f66da9870e | ||
|
|
ca46984a6b | ||
|
|
fe063cf19a | ||
|
|
3bdb12cc5d | ||
|
|
6819dc87a6 | ||
|
|
2ba6c4bc08 | ||
|
|
bb548b796a | ||
|
|
6f8fca7525 | ||
|
|
ff8ea205db | ||
|
|
453536cf14 | ||
|
|
080d94e299 | ||
|
|
ae23fba6b7 | ||
|
|
b8f53f9bb3 | ||
|
|
e143a79b06 | ||
|
|
fa7c93d99a | ||
|
|
f48ce43c0c | ||
|
|
ba12d97c1d | ||
|
|
03e8b8522b | ||
|
|
7a8baac5bd | ||
|
|
58ceff6144 | ||
|
|
7bc82e229f | ||
|
|
74ebd3415c | ||
|
|
de752cbe0a | ||
|
|
25c5abffee | ||
|
|
ec1a2035cd | ||
|
|
b3fbc080db | ||
|
|
f289a72121 | ||
|
|
e6b2d15468 | ||
|
|
e76dc2e92b | ||
|
|
10572c7dbd | ||
|
|
e83e081548 | ||
|
|
85a834d1a3 | ||
|
|
46c95d722c | ||
|
|
70831bb5e4 | ||
|
|
12227cd016 | ||
|
|
2749426a7c | ||
|
|
0a23c3cabb | ||
|
|
5a0670838d | ||
|
|
2dfdead656 | ||
|
|
5fdae11bb1 | ||
|
|
c991562662 | ||
|
|
b4eb6e7fc3 | ||
|
|
d4e8ac761e | ||
|
|
30ff2e0dae | ||
|
|
4f0f8fcdfc | ||
|
|
a1c3d26b73 | ||
|
|
aa3043b0ca | ||
|
|
2d9dcc624c | ||
|
|
aa80a4812d | ||
|
|
5b03d01d32 | ||
|
|
3228c7f74d | ||
|
|
5b60abf4b6 | ||
|
|
47a97772cf | ||
|
|
f4123310ae | ||
|
|
f05439989d | ||
|
|
1df7c0aff8 | ||
|
|
29fde786fb | ||
|
|
4833fc68a0 | ||
|
|
3f0fc3c44b | ||
|
|
375e8de33f | ||
|
|
9d04f06f69 | ||
|
|
0297ec47f6 | ||
|
|
75e1e48ba8 | ||
|
|
89ff9a3d77 | ||
|
|
6fa1593ddc | ||
|
|
d404d10bfe | ||
|
|
194c159379 | ||
|
|
791b6e15d4 | ||
|
|
882e39417d | ||
|
|
8b6d32fe3b | ||
|
|
715f9427d5 | ||
|
|
78b281cf3a | ||
|
|
bbede00cc4 | ||
|
|
e6da57ab57 | ||
|
|
9e08783802 | ||
|
|
1dc98ce33d | ||
|
|
c97750a593 | ||
|
|
aa7e3bd69d | ||
|
|
5a6b9114e2 | ||
|
|
61abcf3018 | ||
|
|
4845b3b0ce | ||
|
|
8f0e8f72cd | ||
|
|
96269a7777 | ||
|
|
bdcc05b7b6 | ||
|
|
060b512bcf | ||
|
|
3907c8c29a | ||
|
|
c786012b28 | ||
|
|
37a084e1d1 | ||
|
|
b9c3675894 | ||
|
|
60ecc95049 | ||
|
|
59d9d47a56 | ||
|
|
c619efa68e | ||
|
|
05e895e7b7 | ||
|
|
58daf2a543 | ||
|
|
0294684bb8 | ||
|
|
fa9b3be41c | ||
|
|
74bf3fbc8b | ||
|
|
dc2a4d4446 | ||
|
|
b0895611d6 | ||
|
|
f664a1cad3 | ||
|
|
19de7e40dc | ||
|
|
de2ee1dce6 | ||
|
|
90698dde90 | ||
|
|
0ab6d6e845 | ||
|
|
74d87845cf | ||
|
|
9e2b15776f | ||
|
|
c8ade0741c | ||
|
|
af8b444ab5 | ||
|
|
2ba26f01b2 | ||
|
|
e77bb11d0a | ||
|
|
c14b56f37d | ||
|
|
e6fe8de5ed | ||
|
|
cf27c2be10 | ||
|
|
f39ba27a13 | ||
|
|
78ff74f0f7 | ||
|
|
29a2a08986 | ||
|
|
881b92a6ea | ||
|
|
50c22c836b | ||
|
|
63115a7c0f | ||
|
|
fd43f33871 | ||
|
|
36bf4ef9ca | ||
|
|
63f9847406 | ||
|
|
46f5a592ce | ||
|
|
f854627888 | ||
|
|
5ec8468d3f | ||
|
|
0da4e4359b | ||
|
|
c5e8742838 | ||
|
|
f9f90852c5 | ||
|
|
6b5f2450ad | ||
|
|
4f2e962a46 | ||
|
|
05ce8fbf1c | ||
|
|
929b2dfb38 | ||
|
|
659e0e5ead | ||
|
|
7bb44f6473 | ||
|
|
60f024fdb7 | ||
|
|
38f1314c05 | ||
|
|
46ece5da0c | ||
|
|
d21ccef269 | ||
|
|
da601c9a63 | ||
|
|
7464de06b1 | ||
|
|
d788abcab2 | ||
|
|
d2b601dd8f | ||
|
|
20fbda9e7a | ||
|
|
15bbf69fcf | ||
|
|
677a9b33c5 | ||
|
|
e9efdcd4e8 | ||
|
|
c3b0b7d1e1 | ||
|
|
d46a2d61bb | ||
|
|
673a2e25e3 | ||
|
|
749adb9ec8 | ||
|
|
5cab8b1070 | ||
|
|
00637f84cb | ||
|
|
bc5a43bfb3 | ||
|
|
723ef5c48d | ||
|
|
53c1a77aeb | ||
|
|
e5389d44d9 | ||
|
|
c27032ef64 | ||
|
|
54b2e5b5fb | ||
|
|
1276016a98 | ||
|
|
7374b00883 | ||
|
|
c54d0dc5ff | ||
|
|
fc0bfd83b2 | ||
|
|
42620c842e | ||
|
|
fd3b3f98bf | ||
|
|
fd7c5c1580 | ||
|
|
c0922adfb2 | ||
|
|
1454cd8a0d | ||
|
|
3e249ec155 | ||
|
|
edfbf71958 | ||
|
|
4876ff7035 | ||
|
|
d5034c9e0d | ||
|
|
9ec54c9004 | ||
|
|
0749e03211 | ||
|
|
e03adad23a | ||
|
|
9b3ba8c9aa | ||
|
|
85746cd681 | ||
|
|
2e64280403 | ||
|
|
d7b81ea87d | ||
|
|
f4404e2649 | ||
|
|
f598e8e3c2 | ||
|
|
27488a0260 | ||
|
|
c53e7964bf | ||
|
|
3516d6fdc5 | ||
|
|
69af1440ae | ||
|
|
31c3101d6d | ||
|
|
5850026aa9 | ||
|
|
b4b8a158f5 | ||
|
|
5c6303995d | ||
|
|
f0353a7dd7 | ||
|
|
1fb19e3b94 | ||
|
|
1788b32b83 | ||
|
|
621672bf34 | ||
|
|
b08406b7d6 | ||
|
|
6211d1de70 | ||
|
|
916b11deb8 | ||
|
|
8e41a90f57 | ||
|
|
9aa2b17ae6 | ||
|
|
7f02a078fd | ||
|
|
07b6c9c437 | ||
|
|
7c7650ac07 | ||
|
|
4e399f9bbb | ||
|
|
b2c718d614 | ||
|
|
12b11d57ba | ||
|
|
bea7102a3f | ||
|
|
b77a577c06 | ||
|
|
c5aab07d65 | ||
|
|
362ae72183 | ||
|
|
6f5d0453a6 | ||
|
|
c552f6a1d4 | ||
|
|
99a8b03697 | ||
|
|
9d63946aa7 | ||
|
|
6eba2b1abd | ||
|
|
b05f1573c6 | ||
|
|
f4311d1cef | ||
|
|
8abad416bd | ||
|
|
52dbd8d9ef | ||
|
|
4e5a70993e | ||
|
|
dd0fbfddb9 | ||
|
|
02e35181d5 | ||
|
|
302cab54fd | ||
|
|
ba157af496 | ||
|
|
d1e6647d1f | ||
|
|
0a47fba107 | ||
|
|
3faa57f39a | ||
|
|
75333264a7 | ||
|
|
e7d846063d | ||
|
|
c2b7cc00dd | ||
|
|
a107171543 | ||
|
|
79be16114d | ||
|
|
c530660132 | ||
|
|
ebe0efac81 | ||
|
|
18ee37d8fd | ||
|
|
f2c2e2e65a | ||
|
|
93f1a18b37 | ||
|
|
44aed364b7 | ||
|
|
eb8fa2f259 | ||
|
|
a8c69abc72 | ||
|
|
2dd1ddddd5 | ||
|
|
4a27c60486 | ||
|
|
66d559306f | ||
|
|
83222489c4 | ||
|
|
0a2fa62e21 | ||
|
|
6240f23c02 | ||
|
|
97ef231711 | ||
|
|
82d8c95d2f | ||
|
|
dde5ee3701 | ||
|
|
44a3cb3b2e | ||
|
|
44c58abe81 | ||
|
|
180b766b59 |
214
.appveyor.yml
214
.appveyor.yml
@@ -1,186 +1,88 @@
|
|||||||
|
image: Visual Studio 2019
|
||||||
|
clone_depth: 400
|
||||||
|
|
||||||
environment:
|
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"
|
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
|
- PYTHON: "C:\\Python37-x64"
|
||||||
# Python 2.7.10 is the latest version and is not pre-installed.
|
# Should be enabled only for build process debugging
|
||||||
|
# init:
|
||||||
- PYTHON: "C:\\Python27.10"
|
# - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
|
||||||
PYTHON_VERSION: "2.7.10"
|
|
||||||
PYTHON_ARCH: "32"
|
|
||||||
|
|
||||||
#- PYTHON: "C:\\Python27.10-x64"
|
|
||||||
# PYTHON_VERSION: "2.7.10"
|
|
||||||
# PYTHON_ARCH: "64"
|
|
||||||
|
|
||||||
# Pre-installed Python versions, which Appveyor may upgrade to
|
|
||||||
# a later point release.
|
|
||||||
# See: http://www.appveyor.com/docs/installed-software#python
|
|
||||||
|
|
||||||
#- PYTHON: "C:\\Python27"
|
|
||||||
# PYTHON_VERSION: "2.7.x" # currently 2.7.9
|
|
||||||
# PYTHON_ARCH: "32"
|
|
||||||
|
|
||||||
#- PYTHON: "C:\\Python27-x64"
|
|
||||||
# PYTHON_VERSION: "2.7.x" # currently 2.7.9
|
|
||||||
# PYTHON_ARCH: "64"
|
|
||||||
|
|
||||||
#- PYTHON: "C:\\Python33"
|
|
||||||
# PYTHON_VERSION: "3.3.x" # currently 3.3.5
|
|
||||||
# PYTHON_ARCH: "32"
|
|
||||||
|
|
||||||
#- PYTHON: "C:\\Python33-x64"
|
|
||||||
# PYTHON_VERSION: "3.3.x" # currently 3.3.5
|
|
||||||
# PYTHON_ARCH: "64"
|
|
||||||
|
|
||||||
#- PYTHON: "C:\\Python34"
|
|
||||||
# PYTHON_VERSION: "3.4.x" # currently 3.4.3
|
|
||||||
# PYTHON_ARCH: "32"
|
|
||||||
|
|
||||||
#- PYTHON: "C:\\Python34-x64"
|
|
||||||
# PYTHON_VERSION: "3.4.x" # currently 3.4.3
|
|
||||||
# PYTHON_ARCH: "64"
|
|
||||||
|
|
||||||
# Python versions not pre-installed
|
|
||||||
|
|
||||||
# Python 2.6.6 is the latest Python 2.6 with a Windows installer
|
|
||||||
# See: https://github.com/ogrisel/python-appveyor-demo/issues/10
|
|
||||||
|
|
||||||
#- PYTHON: "C:\\Python266"
|
|
||||||
# PYTHON_VERSION: "2.6.6"
|
|
||||||
# PYTHON_ARCH: "32"
|
|
||||||
|
|
||||||
#- PYTHON: "C:\\Python266-x64"
|
|
||||||
# PYTHON_VERSION: "2.6.6"
|
|
||||||
# PYTHON_ARCH: "64"
|
|
||||||
|
|
||||||
#- PYTHON: "C:\\Python35"
|
|
||||||
# PYTHON_VERSION: "3.5.0"
|
|
||||||
# PYTHON_ARCH: "32"
|
|
||||||
|
|
||||||
#- PYTHON: "C:\\Python35-x64"
|
|
||||||
# PYTHON_VERSION: "3.5.0"
|
|
||||||
# PYTHON_ARCH: "64"
|
|
||||||
|
|
||||||
# Major and minor releases (i.e x.0.0 and x.y.0) prior to 3.3.0 use
|
|
||||||
# a different naming scheme.
|
|
||||||
|
|
||||||
#- PYTHON: "C:\\Python270"
|
|
||||||
# PYTHON_VERSION: "2.7.0"
|
|
||||||
# PYTHON_ARCH: "32"
|
|
||||||
|
|
||||||
#- PYTHON: "C:\\Python270-x64"
|
|
||||||
# PYTHON_VERSION: "2.7.0"
|
|
||||||
# PYTHON_ARCH: "64"
|
|
||||||
|
|
||||||
install:
|
install:
|
||||||
# If there is a newer build queued for the same PR, cancel this one.
|
- ps: echo("OS version:")
|
||||||
# The AppVeyor 'rollout builds' option is supposed to serve the same
|
- ps: "[System.Environment]::OSVersion.Version"
|
||||||
# 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." }
|
|
||||||
|
|
||||||
# Install wxPython
|
- ps: echo("Filesystem - root:")
|
||||||
- 'ECHO Downloading wxPython.'
|
- ps: "ls \"C:\\\""
|
||||||
- "appveyor DownloadFile https://goo.gl/yvO8PB -FileName C:\\wxpython.exe"
|
|
||||||
#- "appveyor DownloadFile https://goo.gl/Uj0jV3 -FileName C:\\wxpython64.exe"
|
|
||||||
|
|
||||||
- 'ECHO Install wxPython'
|
- ps: echo("Filesystem - projects root:")
|
||||||
- "C:\\wxpython.exe /SP- /VERYSILENT /NORESTART"
|
- ps: "ls \"C:\\projects\\\""
|
||||||
#- "C:\\wxpython64.exe /SP- /VERYSILENT /NORESTART"
|
|
||||||
|
|
||||||
- ECHO "Filesystem root:"
|
- ps: echo("Filesystem - pyfa root:")
|
||||||
- ps: "ls \"C:/\""
|
- ps: "ls \"C:\\projects\\$env:APPVEYOR_PROJECT_SLUG\\\""
|
||||||
|
|
||||||
- ECHO "Filesystem pyfa root:"
|
- ps: echo("Filesystem - installed SDKs:")
|
||||||
- ps: "ls \"C:\\projects\\pyfa\\\""
|
- ps: "ls \"C:\\Program Files (x86)\\Windows Kits\\\""
|
||||||
|
|
||||||
- ECHO "Installed SDKs:"
|
|
||||||
- ps: "ls \"C:/Program Files/Microsoft SDKs/Windows\""
|
|
||||||
|
|
||||||
# Install Python (from the official .msi of http://python.org) and pip when
|
|
||||||
# not already installed.
|
|
||||||
# - ps: if (-not(Test-Path($env:PYTHON))) { & appveyor\install.ps1 }
|
|
||||||
|
|
||||||
# Prepend newly installed Python to the PATH of this build (this cannot be
|
# 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
|
# done from inside the powershell script as it would require to restart
|
||||||
# the parent CMD process).
|
# the parent CMD process).
|
||||||
- "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
|
- cmd: "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
|
||||||
|
|
||||||
# Check that we have the expected version and architecture for Python
|
- cmd: "python --version"
|
||||||
- "python --version"
|
- cmd: "python -c \"import struct; print(struct.calcsize('P') * 8)\""
|
||||||
- "python -c \"import struct; print(struct.calcsize('P') * 8)\""
|
|
||||||
|
|
||||||
# Upgrade to the latest version of pip to avoid it displaying warnings
|
# Upgrade to the latest version of pip to avoid it displaying warnings
|
||||||
# about it being out of date.
|
# about it being out of date.
|
||||||
- "pip install --disable-pip-version-check --user --upgrade pip"
|
- cmd: "python -m pip install --upgrade pip"
|
||||||
|
|
||||||
# Install the build dependencies of the project. If some dependencies contain
|
# Install the build dependencies of the project. If some dependencies contain
|
||||||
# compiled extensions and are not provided as pre-built wheel packages,
|
# compiled extensions and are not provided as pre-built wheel packages,
|
||||||
# pip will build them from source using the MSVC compiler matching the
|
# pip will build them from source using the MSVC compiler matching the
|
||||||
# target Python version and architecture
|
# target Python version and architecture
|
||||||
# C:\\projects\\eve-gnosis\\
|
- ps: echo("Install pip requirements:")
|
||||||
- ECHO "Install pip requirements:"
|
# This one is needed to build wxpython 4.0.6 on windows
|
||||||
- "pip install -r requirements.txt"
|
- cmd: "python -m pip install pathlib2"
|
||||||
- "pip install -r requirements_test.txt"
|
- cmd: "python -m pip install -r requirements.txt"
|
||||||
- "pip install -r requirements_build_windows.txt"
|
- cmd: "python -m pip install PyInstaller==3.6"
|
||||||
|
|
||||||
|
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:
|
build_script:
|
||||||
# Build the compiled extension
|
- ps: echo("Build pyfa:")
|
||||||
# - "python setup.py build"
|
# Build gamedata DB
|
||||||
- ECHO "Build pyfa:"
|
- cmd: "python db_update.py"
|
||||||
#- copy C:\projects\pyfa\dist_assets\win\pyfa.spec C:\projects\pyfa\pyfa.spec
|
# Build command for PyInstaller
|
||||||
- "python C:\\projects\\pyfa\\setup.py build"
|
- cmd: "python -m PyInstaller --noupx --clean --windowed --noconsole -y pyfa.spec"
|
||||||
|
# Copy over manifest (See pyfa-org/pyfa#1622)
|
||||||
#- ECHO "Build pyfa (Debug):"
|
- ps: xcopy /y dist_assets\win\pyfa.exe.manifest $env:PYFA_DIST_DIR\pyfa\
|
||||||
#- copy C:\projects\pyfa\dist_assets\win\pyfa_debug.spec C:\projects\pyfa\pyfa_debug.spec
|
# InnoScript EXE building. This is in a separate script because I don't feel like copying over the logic to AppVeyor script right now...
|
||||||
#- "pyinstaller.exe --clean --noconfirm --windowed --upx-dir=C:\\projects\\pyfa\\scripts\\upx.exe C:\\projects\\pyfa\\pyfa_debug.spec"
|
- cmd: "python dist_assets/win/dist.py"
|
||||||
|
- ps: dir $env:PYFA_DIST_DIR/
|
||||||
build: on
|
|
||||||
|
|
||||||
after_build:
|
after_build:
|
||||||
- ps: "ls \"./\""
|
- ps: "ls \"./\""
|
||||||
#- ps: "ls \"C:\\projects\\pyfa\\build\\pyfa\\\""
|
- ps: 7z a "pyfa-$env:PYFA_VERSION-win.zip" -r "$env:PYFA_DIST_DIR\pyfa\*"
|
||||||
- ps: "ls \"C:\\projects\\pyfa\\build\\\""
|
|
||||||
- ps: "ls \"C:\\projects\\pyfa\\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\*.*
|
|
||||||
- 7z a pyfa.zip -r C:\projects\pyfa\build\exe.win32-2.7\*.*
|
|
||||||
#- 7z a pyfa_debug.zip -r C:\projects\pyfa\dist\pyfa_debug\*.*
|
|
||||||
|
|
||||||
on_success:
|
|
||||||
# Do nothing right now
|
|
||||||
|
|
||||||
test_script:
|
test_script:
|
||||||
#- tox
|
# Ha... we're just building
|
||||||
#- "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:
|
artifacts:
|
||||||
# Archive the generated packages in the ci.appveyor.com build report.
|
- path: pyfa*-win.zip
|
||||||
- path: pyfa.zip
|
- path: pyfa*-win.exe
|
||||||
name: 'pyfa.zip'
|
|
||||||
#- path: pyfa_debug.zip
|
|
||||||
# name: Pyfa_debug
|
|
||||||
|
|
||||||
#on_success:
|
deploy:
|
||||||
# - TODO: upload the content of dist/*.whl to a public wheelhouse
|
tag: $(pyfa_version)
|
||||||
#
|
release: pyfa $(pyfa_version)
|
||||||
|
description: 'Release description'
|
||||||
|
provider: GitHub
|
||||||
|
auth_token:
|
||||||
|
secure: X+U3hOAMTt7HGXCR/LXaGNF6qyhUXetrjz5+xlWiNJQ3XEdzhZZmHK75m0Hm6qre
|
||||||
|
draft: true
|
||||||
|
force_update: false
|
||||||
|
# deploy on tag push only
|
||||||
|
on:
|
||||||
|
APPVEYOR_REPO_TAG: true
|
||||||
|
|||||||
61
.travis.yml
61
.travis.yml
@@ -1,36 +1,31 @@
|
|||||||
dist: trusty
|
os: linux
|
||||||
sudo: required
|
|
||||||
language: python
|
language: python
|
||||||
cache: pip
|
git:
|
||||||
|
depth: 400
|
||||||
python:
|
python:
|
||||||
- '3.6'
|
- 3.8
|
||||||
env:
|
matrix:
|
||||||
- TOXENV=pep8
|
include:
|
||||||
addons:
|
- os: osx
|
||||||
apt:
|
osx_image: xcode11.3
|
||||||
packages:
|
language: generic
|
||||||
before_install:
|
before_install:
|
||||||
- sudo apt-get update && sudo apt-get --reinstall install -qq language-pack-en language-pack-ru language-pack-he language-pack-zh-hans
|
- bash scripts/setup-osx.sh
|
||||||
- pip install tox
|
install:
|
||||||
# We're not actually installing Tox, but have to run it before we install wxPython via Conda. This is fugly but vOv
|
- python3 db_update.py
|
||||||
- tox
|
- export PYFA_VERSION="$(python3 scripts/dump_version.py)"
|
||||||
- pip install -U -f https://extras.wxpython.org/wxPython4/extras/linux/gtk2/ubuntu-14.04 wxPython==4.0.0b2
|
- bash scripts/package-osx.sh
|
||||||
# # get Conda
|
before_deploy:
|
||||||
# - if [[ "$TRAVIS_PYTHON_VERSION" == "2.7" ]]; then
|
- export RELEASE_PKG_FILE=$(ls *.deb)
|
||||||
# wget https://repo.continuum.io/miniconda/Miniconda-latest-Linux-x86_64.sh -O miniconda.sh;
|
- echo "deploying $RELEASE_PKG_FILE to GitHub releases"
|
||||||
# else
|
deploy:
|
||||||
# wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh;
|
provider: releases
|
||||||
# fi
|
api_key:
|
||||||
# - bash miniconda.sh -b -p $HOME/miniconda
|
secure: D8tBW0kyHlKf/sXS69aIuexsYTx9auY2DzudKFlfcvdzqat4N2XZqZbZCTVd7YVvptQ8Dj0oZ/p3KUxEGpnJZmlTeJL142rpM/qaNd6wOIMy2yUde/aZl+W9JLFNQp7KHutM+MxObYLzJGihx/8YsupmFx6lxgdngGDXtXYZe/ruDIWDs92ShoKJ4vlce9Csm7eGKv7wv6Z6V9sD5FS3E9J8xdWStHxsbrkPBOflmG+uHU09dpEqzUm+ZYROIoTwig1Xbw3fw+gfjmNrfdSU4fAJcVZI1hrgoenZyJbMfhI2Ej/nZdbZgaXcZNF/eUpqOGgbPe1JljqFnHTbexcE+LPBVyAToScsGMpByHhig67DrZ0nk9gSZoC6CPNl5YS6xub+5dncMJ3P5L03DOGYRu4SL9NczbeuQyKuea7+JPP/8VLwfFDSEqbNEAmgzABAzrdfano+VXtuBuE/Tiy5eE7le9hJu6aSQoKW1SA3cUhMsmr2amzdO96sh+PN8FA1oNr45Yuy0pqOj4SUIkb8JUy4th7vgdhljEkSxrHDK1UcHpxUTp+IIUZkZVVk50aH68dQZxGwSTVOeRxpjrTcEf7VCGaM98qxi/ZK4RW6Ewiq0eo0AxwEeB2Zm841lycGPR/406vM9/ZBzv5IhELIdDdVWTk+dGjJBXB8z5hPJOg=
|
||||||
# - export PATH="$HOME/miniconda/bin:$PATH"
|
file_glob: true
|
||||||
# - hash -r
|
file: "dist/pyfa-*.zip"
|
||||||
# - conda config --set always_yes yes --set changeps1 no
|
skip_cleanup: true
|
||||||
# - conda update -q conda
|
draft: true
|
||||||
# # Useful for debugging any issues with conda
|
on:
|
||||||
# - conda info -a
|
tags: true
|
||||||
#install:
|
repo: pyfa-org/Pyfa
|
||||||
# install wxPython 3.0.0.0
|
|
||||||
# - conda install -c https://conda.anaconda.org/travis wxpython=4.0.0b2
|
|
||||||
script:
|
|
||||||
- tox
|
|
||||||
|
|
||||||
|
|||||||
102
CONTRIBUTING.md
Normal file
102
CONTRIBUTING.md
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
# Contribution
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- Python 3.6
|
||||||
|
- 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 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`
|
||||||
|
|
||||||
|
Check that the libs from *requirements.txt* are installed
|
||||||
|
```
|
||||||
|
pip list
|
||||||
|
```
|
||||||
|
|
||||||
|
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 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).
|
||||||
34
README.md
34
README.md
@@ -2,20 +2,19 @@
|
|||||||
|
|
||||||
[](https://pyfainvite.azurewebsites.net/) [](https://travis-ci.org/pyfa-org/Pyfa)
|
[](https://pyfainvite.azurewebsites.net/) [](https://travis-ci.org/pyfa-org/Pyfa)
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## What is it?
|
## 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 2.x 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, Mac OS X, and Linux.
|
||||||
|
|
||||||
## Latest Version and Changelogs
|
## Latest Version and Changelogs
|
||||||
The latest version along with release notes can always be found on the project's [Releases](https://github.com/DarkFenX/Pyfa/releases) page. pyfa will notify you if you are running an outdated version.
|
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
|
## 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 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.
|
||||||
|
|
||||||
#### OS X
|
### OS X
|
||||||
|
|
||||||
Apart from the official release, there is also a [Homebrew](http://brew.sh) option for installing pyfa on OS X. Please note this is maintained by a third-party and is not tested by pyfa developers. Simply fire up in terminal:
|
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:
|
||||||
```
|
```
|
||||||
$ brew install Caskroom/cask/pyfa
|
$ brew install Caskroom/cask/pyfa
|
||||||
@@ -24,34 +23,33 @@ $ brew install Caskroom/cask/pyfa
|
|||||||
### Linux Distro-specific Packages
|
### 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.
|
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.
|
||||||
|
|
||||||
* Debian/Ubuntu/derivitives: https://github.com/AdamMajer/Pyfa/releases
|
|
||||||
* Arch: https://aur.archlinux.org/packages/pyfa/
|
* Arch: https://aur.archlinux.org/packages/pyfa/
|
||||||
* openSUSE: https://build.opensuse.org/package/show/home:rmk2/pyfa
|
* Gentoo: https://github.com/ZeroPointEnergy/gentoo-pyfa-overlay
|
||||||
* FreeBSD: http://www.freshports.org/games/pyfa/ (see [#484](https://github.com/pyfa-org/Pyfa/issues/484) for instructions)
|
|
||||||
|
|
||||||
### Dependencies
|
## Contribution
|
||||||
If you wish to help with development or simply need to run pyfa through a Python interpreter, the following software is required:
|
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).
|
||||||
|
|
||||||
* Python 3.6
|
|
||||||
* Requirements as listed in `requirements.txt`
|
|
||||||
|
|
||||||
## Bug Reporting
|
## 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](http://forums.eveonline.com/default.aspx?g=posts&t=247609) on the official EVE Online forums is acceptable. Guidelines for bug reporting can be found on [this wiki page](https://github.com/DarkFenX/Pyfa/wiki/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).
|
||||||
|
|
||||||
## License
|
## License
|
||||||
pyfa is licensed under the GNU GPL v3.0, see LICENSE
|
Pyfa is licensed under the GNU GPL v3.0, see LICENSE
|
||||||
|
|
||||||
## Resources
|
## Resources
|
||||||
* Development repository: [https://github.com/pyfa-org/Pyfa](https://github.com/pyfa-org/Pyfa)
|
* [Development repository](https://github.com/pyfa-org/Pyfa)
|
||||||
* [EVE forum thread](https://forums.eveonline.com/t/27156)
|
* [EVE forum thread](https://forums.eveonline.com/t/27156)
|
||||||
* [EVE University guide using pyfa](http://wiki.eveuniversity.org/Guide_to_using_PYFA)
|
* [EVE University guide using pyfa](https://wiki.eveuniversity.org/PYFA)
|
||||||
* [EVE Online website](http://www.eveonline.com/)
|
* [EVE Online website](http://www.eveonline.com/)
|
||||||
|
|
||||||
## Contacts:
|
## Contacts:
|
||||||
|
* Kadesh / DarkPhoenix
|
||||||
|
* GitHub: @DarkFenX
|
||||||
|
* EVE: Kadesh Priestess
|
||||||
|
* Email: phoenix@mail.ru
|
||||||
* Sable Blitzmann
|
* Sable Blitzmann
|
||||||
* GitHub: @blitzmann
|
* GitHub: @blitzmann
|
||||||
* [TweetFleet Slack](https://www.fuzzwork.co.uk/tweetfleet-slack-invites/): @blitzmann
|
* [TweetFleet Slack](https://www.fuzzwork.co.uk/tweetfleet-slack-invites/): @blitzmann
|
||||||
* [Gitter chat](https://gitter.im/pyfa-org/Pyfa): @ blitzmann
|
* [Gitter chat](https://gitter.im/pyfa-org/Pyfa): @blitzmann
|
||||||
* Email: sable.blitzmann@gmail.com
|
* Email: sable.blitzmann@gmail.com
|
||||||
|
|
||||||
## CCP Copyright Notice
|
## CCP Copyright Notice
|
||||||
|
|||||||
@@ -49,6 +49,8 @@ def DBInMemory_test():
|
|||||||
gamedata_version = gamedata_session.execute(
|
gamedata_version = gamedata_session.execute(
|
||||||
"SELECT `field_value` FROM `metadata` WHERE `field_name` LIKE 'client_build'"
|
"SELECT `field_value` FROM `metadata` WHERE `field_name` LIKE 'client_build'"
|
||||||
).fetchone()[0]
|
).fetchone()[0]
|
||||||
|
except (KeyboardInterrupt, SystemExit):
|
||||||
|
raise
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("Missing gamedata version.")
|
print("Missing gamedata version.")
|
||||||
gamedata_version = None
|
gamedata_version = None
|
||||||
@@ -72,7 +74,7 @@ def DBInMemory_test():
|
|||||||
# noinspection PyPep8
|
# noinspection PyPep8
|
||||||
#from eos.db.gamedata import alphaClones, attribute, category, effect, group, icon, item, marketGroup, metaData, metaGroup, queries, traits, unit
|
#from eos.db.gamedata import alphaClones, attribute, category, effect, group, icon, item, marketGroup, metaData, metaGroup, queries, traits, unit
|
||||||
# noinspection PyPep8
|
# 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, targetResists, 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 using in memory saveddata, you'll want to reflect it so the data structure is good.
|
||||||
if saveddata_connectionstring == "sqlite:///:memory:":
|
if saveddata_connectionstring == "sqlite:///:memory:":
|
||||||
@@ -100,8 +102,8 @@ def DBInMemory():
|
|||||||
import eos.db
|
import eos.db
|
||||||
|
|
||||||
# Output debug info to help us troubleshoot Travis
|
# Output debug info to help us troubleshoot Travis
|
||||||
print((eos.db.saveddata_engine))
|
print(eos.db.saveddata_engine)
|
||||||
print((eos.db.gamedata_engine))
|
print(eos.db.gamedata_engine)
|
||||||
|
|
||||||
helper = {
|
helper = {
|
||||||
'config': eos.config,
|
'config': eos.config,
|
||||||
@@ -129,7 +131,8 @@ def Saveddata():
|
|||||||
from eos.saveddata.ship import Ship
|
from eos.saveddata.ship import Ship
|
||||||
from eos.saveddata.fit import Fit
|
from eos.saveddata.fit import Fit
|
||||||
from eos.saveddata.character import Character
|
from eos.saveddata.character import Character
|
||||||
from eos.saveddata.module import Module, State
|
from eos.saveddata.module import Module
|
||||||
|
from eos.const import FittingModuleState
|
||||||
from eos.saveddata.citadel import Citadel
|
from eos.saveddata.citadel import Citadel
|
||||||
from eos.saveddata.booster import Booster
|
from eos.saveddata.booster import Booster
|
||||||
|
|
||||||
@@ -139,7 +142,7 @@ def Saveddata():
|
|||||||
'Fit' : Fit,
|
'Fit' : Fit,
|
||||||
'Character': Character,
|
'Character': Character,
|
||||||
'Module' : Module,
|
'Module' : Module,
|
||||||
'State' : State,
|
'State' : FittingModuleState,
|
||||||
'Booster' : Booster,
|
'Booster' : Booster,
|
||||||
}
|
}
|
||||||
return helper
|
return helper
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
# noinspection PyPackageRequirements
|
# noinspection PyPackageRequirements
|
||||||
from _development.helpers import DBInMemory as DB, Gamedata, Saveddata
|
|
||||||
|
|
||||||
|
|
||||||
# noinspection PyShadowingNames
|
# noinspection PyShadowingNames
|
||||||
@@ -63,4 +62,4 @@ def HeronFit(DB, Gamedata, Saveddata):
|
|||||||
for _ in range(4):
|
for _ in range(4):
|
||||||
fit.modules.append(mod)
|
fit.modules.append(mod)
|
||||||
|
|
||||||
return fit
|
return fit
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
# noinspection PyPackageRequirements
|
# noinspection PyPackageRequirements
|
||||||
from _development.helpers import DBInMemory as DB, Gamedata, Saveddata
|
|
||||||
|
|
||||||
|
|
||||||
# noinspection PyShadowingNames
|
# noinspection PyShadowingNames
|
||||||
|
|||||||
51
config.py
51
config.py
@@ -1,9 +1,12 @@
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
import yaml
|
||||||
|
import wx
|
||||||
|
|
||||||
from logbook import CRITICAL, DEBUG, ERROR, FingersCrossedHandler, INFO, Logger, NestedSetup, NullHandler, \
|
from logbook import CRITICAL, DEBUG, ERROR, FingersCrossedHandler, INFO, Logger, NestedSetup, NullHandler, \
|
||||||
StreamHandler, TimedRotatingFileHandler, WARNING
|
StreamHandler, TimedRotatingFileHandler, WARNING
|
||||||
import hashlib
|
import hashlib
|
||||||
|
from eos.const import FittingSlot
|
||||||
|
|
||||||
from cryptography.fernet import Fernet
|
from cryptography.fernet import Fernet
|
||||||
|
|
||||||
@@ -22,12 +25,6 @@ debug = False
|
|||||||
# Defines if our saveddata will be in pyfa root or not
|
# Defines if our saveddata will be in pyfa root or not
|
||||||
saveInRoot = False
|
saveInRoot = False
|
||||||
|
|
||||||
# Version data
|
|
||||||
|
|
||||||
version = "2.5.0b1"
|
|
||||||
tag = "Stable"
|
|
||||||
expansionName = "YC120.8"
|
|
||||||
expansionVersion = "1.0"
|
|
||||||
evemonMinVersion = "4081"
|
evemonMinVersion = "4081"
|
||||||
|
|
||||||
minItemSearchLength = 3
|
minItemSearchLength = 3
|
||||||
@@ -36,11 +33,14 @@ pyfaPath = None
|
|||||||
savePath = None
|
savePath = None
|
||||||
saveDB = None
|
saveDB = None
|
||||||
gameDB = None
|
gameDB = None
|
||||||
|
imgsZIP = None
|
||||||
logPath = None
|
logPath = None
|
||||||
loggingLevel = None
|
loggingLevel = None
|
||||||
logging_setup = None
|
logging_setup = None
|
||||||
cipher = None
|
cipher = None
|
||||||
clientHash = None
|
clientHash = None
|
||||||
|
experimentalFeatures = None
|
||||||
|
version = None
|
||||||
|
|
||||||
ESI_CACHE = 'esi_cache'
|
ESI_CACHE = 'esi_cache'
|
||||||
|
|
||||||
@@ -52,6 +52,13 @@ LOGLEVEL_MAP = {
|
|||||||
"debug": DEBUG,
|
"debug": DEBUG,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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: ''
|
||||||
|
}
|
||||||
|
|
||||||
def getClientSecret():
|
def getClientSecret():
|
||||||
return clientHash
|
return clientHash
|
||||||
@@ -79,12 +86,7 @@ def getPyfaRoot():
|
|||||||
|
|
||||||
|
|
||||||
def getVersion():
|
def getVersion():
|
||||||
if os.path.isfile(os.path.join(pyfaPath, '.version')):
|
return version
|
||||||
with open(os.path.join(pyfaPath, '.version')) as f:
|
|
||||||
gitVersion = f.readline()
|
|
||||||
return gitVersion
|
|
||||||
# if no version file exists, then user is running from source or not an official build
|
|
||||||
return version + " (git)"
|
|
||||||
|
|
||||||
|
|
||||||
def getDefaultSave():
|
def getDefaultSave():
|
||||||
@@ -97,10 +99,13 @@ def defPaths(customSavePath=None):
|
|||||||
global savePath
|
global savePath
|
||||||
global saveDB
|
global saveDB
|
||||||
global gameDB
|
global gameDB
|
||||||
|
global imgsZIP
|
||||||
global saveInRoot
|
global saveInRoot
|
||||||
global logPath
|
global logPath
|
||||||
global cipher
|
global cipher
|
||||||
global clientHash
|
global clientHash
|
||||||
|
global version
|
||||||
|
global experimentalFeatures
|
||||||
|
|
||||||
pyfalog.debug("Configuring Pyfa")
|
pyfalog.debug("Configuring Pyfa")
|
||||||
|
|
||||||
@@ -110,6 +115,12 @@ def defPaths(customSavePath=None):
|
|||||||
if pyfaPath is None:
|
if pyfaPath is None:
|
||||||
pyfaPath = getPyfaRoot()
|
pyfaPath = getPyfaRoot()
|
||||||
|
|
||||||
|
# Version data
|
||||||
|
|
||||||
|
with open(os.path.join(pyfaPath, "version.yml"), 'r') as file:
|
||||||
|
data = yaml.load(file, Loader=yaml.SafeLoader)
|
||||||
|
version = data['version']
|
||||||
|
|
||||||
# Where we store the saved fits etc, default is the current users home directory
|
# Where we store the saved fits etc, default is the current users home directory
|
||||||
if saveInRoot is True:
|
if saveInRoot is True:
|
||||||
savePath = getattr(configforced, "savePath", None)
|
savePath = getattr(configforced, "savePath", None)
|
||||||
@@ -149,6 +160,10 @@ def defPaths(customSavePath=None):
|
|||||||
if not gameDB:
|
if not gameDB:
|
||||||
gameDB = os.path.join(pyfaPath, "eve.db")
|
gameDB = os.path.join(pyfaPath, "eve.db")
|
||||||
|
|
||||||
|
imgsZIP = getattr(configforced, "imgsZIP", imgsZIP)
|
||||||
|
if not imgsZIP:
|
||||||
|
imgsZIP = os.path.join(pyfaPath, "imgs.zip")
|
||||||
|
|
||||||
if debug:
|
if debug:
|
||||||
logFile = "pyfa_debug.log"
|
logFile = "pyfa_debug.log"
|
||||||
else:
|
else:
|
||||||
@@ -156,6 +171,10 @@ def defPaths(customSavePath=None):
|
|||||||
|
|
||||||
logPath = os.path.join(savePath, logFile)
|
logPath = os.path.join(savePath, logFile)
|
||||||
|
|
||||||
|
experimentalFeatures = getattr(configforced, "experimentalFeatures", experimentalFeatures)
|
||||||
|
if experimentalFeatures is None:
|
||||||
|
experimentalFeatures = False
|
||||||
|
|
||||||
# DON'T MODIFY ANYTHING BELOW
|
# DON'T MODIFY ANYTHING BELOW
|
||||||
import eos.config
|
import eos.config
|
||||||
|
|
||||||
@@ -165,9 +184,6 @@ def defPaths(customSavePath=None):
|
|||||||
eos.config.saveddata_connectionstring = "sqlite:///" + saveDB + "?check_same_thread=False"
|
eos.config.saveddata_connectionstring = "sqlite:///" + saveDB + "?check_same_thread=False"
|
||||||
eos.config.gamedata_connectionstring = "sqlite:///" + gameDB + "?check_same_thread=False"
|
eos.config.gamedata_connectionstring = "sqlite:///" + gameDB + "?check_same_thread=False"
|
||||||
|
|
||||||
print(eos.config.saveddata_connectionstring)
|
|
||||||
print(eos.config.gamedata_connectionstring)
|
|
||||||
|
|
||||||
# initialize the settings
|
# initialize the settings
|
||||||
from service.settings import EOSSettings
|
from service.settings import EOSSettings
|
||||||
eos.config.settings = EOSSettings.getInstance().EOSSettings # this is kind of confusing, but whatever
|
eos.config.settings = EOSSettings.getInstance().EOSSettings # this is kind of confusing, but whatever
|
||||||
@@ -217,6 +233,8 @@ def defLogging():
|
|||||||
# reset=False,
|
# reset=False,
|
||||||
)
|
)
|
||||||
])
|
])
|
||||||
|
except (KeyboardInterrupt, SystemExit):
|
||||||
|
raise
|
||||||
except:
|
except:
|
||||||
print("Critical error attempting to setup logging. Falling back to console only.")
|
print("Critical error attempting to setup logging. Falling back to console only.")
|
||||||
logging_setup = NestedSetup([
|
logging_setup = NestedSetup([
|
||||||
@@ -230,8 +248,7 @@ def defLogging():
|
|||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
|
class LoggerWriter:
|
||||||
class LoggerWriter(object):
|
|
||||||
def __init__(self, level):
|
def __init__(self, level):
|
||||||
# self.level is really like using log.debug(message)
|
# self.level is really like using log.debug(message)
|
||||||
# at least in my case
|
# at least in my case
|
||||||
|
|||||||
544
db_update.py
Normal file
544
db_update.py
Normal file
@@ -0,0 +1,544 @@
|
|||||||
|
#!/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
|
||||||
|
|
||||||
|
|
||||||
|
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 = 3
|
||||||
|
|
||||||
|
|
||||||
|
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.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
|
||||||
|
|
||||||
|
# Create the database tables
|
||||||
|
eos.db.gamedata_meta.create_all()
|
||||||
|
|
||||||
|
def _readData(minerName, jsonName, keyIdName=None):
|
||||||
|
with open(os.path.join(JSON_DIR, minerName, '{}.json'.format(jsonName)), encoding='utf-8') as f:
|
||||||
|
rawData = json.load(f)
|
||||||
|
if not keyIdName:
|
||||||
|
return rawData
|
||||||
|
# IDs in keys, rows in values
|
||||||
|
data = []
|
||||||
|
for k, v in rawData.items():
|
||||||
|
row = {}
|
||||||
|
row.update(v)
|
||||||
|
if keyIdName not in row:
|
||||||
|
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_lite', 'evetypes', keyIdName='typeID')
|
||||||
|
for row in data:
|
||||||
|
if (
|
||||||
|
# Apparently people really want Civilian modules available
|
||||||
|
(row['typeName'].startswith('Civilian') and "Shuttle" not in row['typeName']) or
|
||||||
|
row['typeName'] in ('Capsule', 'Dark Blood Tracking Disruptor')
|
||||||
|
):
|
||||||
|
row['published'] = True
|
||||||
|
|
||||||
|
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,
|
||||||
|
# the "container" for the abyssal environments
|
||||||
|
1983)
|
||||||
|
):
|
||||||
|
newData.append(row)
|
||||||
|
|
||||||
|
_addRows(newData, eos.gamedata.Item)
|
||||||
|
return newData
|
||||||
|
|
||||||
|
def processEveGroups():
|
||||||
|
print('processing evegroups')
|
||||||
|
data = _readData('fsd_lite', 'evegroups', keyIdName='groupID')
|
||||||
|
_addRows(data, eos.gamedata.Group)
|
||||||
|
return data
|
||||||
|
|
||||||
|
def processEveCategories():
|
||||||
|
print('processing evecategories')
|
||||||
|
data = _readData('fsd_lite', 'evecategories', keyIdName='categoryID')
|
||||||
|
_addRows(data, eos.gamedata.Category)
|
||||||
|
|
||||||
|
def processDogmaAttributes():
|
||||||
|
print('processing dogmaattributes')
|
||||||
|
data = _readData('fsd_binary', 'dogmaattributes', keyIdName='attributeID')
|
||||||
|
_addRows(data, eos.gamedata.AttributeInfo)
|
||||||
|
|
||||||
|
def processDogmaTypeAttributes(eveTypesData):
|
||||||
|
print('processing dogmatypeattributes')
|
||||||
|
data = _readData('fsd_binary', 'typedogma', keyIdName='typeID')
|
||||||
|
eveTypeIds = set(r['typeID'] for r in eveTypesData)
|
||||||
|
newData = []
|
||||||
|
for row in eveTypesData:
|
||||||
|
for attrId, attrName in {4: 'mass', 38: 'capacity', 161: 'volume', 162: 'radius'}.items():
|
||||||
|
if attrName in row:
|
||||||
|
newData.append({'typeID': row['typeID'], 'attributeID': attrId, 'value': row[attrName]})
|
||||||
|
for typeData in data:
|
||||||
|
if typeData['typeID'] not in eveTypeIds:
|
||||||
|
continue
|
||||||
|
for row in typeData.get('dogmaAttributes', ()):
|
||||||
|
row['typeID'] = typeData['typeID']
|
||||||
|
newData.append(row)
|
||||||
|
_addRows(newData, eos.gamedata.Attribute)
|
||||||
|
return newData
|
||||||
|
|
||||||
|
def processDynamicItemAttributes():
|
||||||
|
print('processing dynamicitemattributes')
|
||||||
|
data = _readData('fsd_binary', '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_binary', 'dogmaeffects', keyIdName='effectID')
|
||||||
|
_addRows(data, eos.gamedata.Effect, fieldMap={'resistanceAttributeID': 'resistanceID'})
|
||||||
|
|
||||||
|
def processDogmaTypeEffects(eveTypesData):
|
||||||
|
print('processing dogmatypeeffects')
|
||||||
|
data = _readData('fsd_binary', '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_binary', 'dogmaunits', keyIdName='unitID')
|
||||||
|
_addRows(data, eos.gamedata.Unit, fieldMap={'name': 'unitName'})
|
||||||
|
|
||||||
|
def processMarketGroups():
|
||||||
|
print('processing marketgroups')
|
||||||
|
data = _readData('fsd_binary', 'marketgroups', keyIdName='marketGroupID')
|
||||||
|
_addRows(data, eos.gamedata.MarketGroup, fieldMap={'name': 'marketGroupName'})
|
||||||
|
|
||||||
|
def processMetaGroups():
|
||||||
|
print('processing metagroups')
|
||||||
|
data = _readData('fsd_binary', 'metagroups', keyIdName='metaGroupID')
|
||||||
|
_addRows(data, eos.gamedata.MetaGroup)
|
||||||
|
|
||||||
|
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:
|
||||||
|
typeLines = []
|
||||||
|
typeId = row['typeID']
|
||||||
|
traitData = row['traits']
|
||||||
|
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 = {'typeID': typeId, 'traitText': traitLine}
|
||||||
|
newData.append(newRow)
|
||||||
|
|
||||||
|
_addRows(newData, eos.gamedata.Traits)
|
||||||
|
|
||||||
|
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, skillLevels in raw.items():
|
||||||
|
reqSkills[int(skillTypeID)] = skillLevels[0]
|
||||||
|
return reqSkills
|
||||||
|
|
||||||
|
eveTypeIds = set(r['typeID'] for r in eveTypesData)
|
||||||
|
data = _readData('fsd_binary', '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', '')
|
||||||
|
# Regular sets matching
|
||||||
|
m = re.match('(?P<grade>(High|Mid|Low)-grade) (?P<set>\w+) (?P<implant>(Alpha|Beta|Gamma|Delta|Epsilon|Omega))', typeName, re.IGNORECASE)
|
||||||
|
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(eos.gamedata.Item.name.like('%abyssal%')).all():
|
||||||
|
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)
|
||||||
|
|
||||||
|
eos.db.gamedata_session.commit()
|
||||||
|
eos.db.gamedata_engine.execute('VACUUM')
|
||||||
|
|
||||||
|
print('done')
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
update_db()
|
||||||
@@ -24,16 +24,20 @@ added_files = [
|
|||||||
('../../eve.db', '.'),
|
('../../eve.db', '.'),
|
||||||
('../../README.md', '.'),
|
('../../README.md', '.'),
|
||||||
('../../LICENSE', '.'),
|
('../../LICENSE', '.'),
|
||||||
('../../.version', '.'),
|
('../../version.yml', '.'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
import_these = []
|
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
|
||||||
|
'pkg_resources.py2_warn' # issue 2156
|
||||||
|
]
|
||||||
|
|
||||||
icon = os.path.join(os.getcwd(), "dist_assets", "mac", "pyfa.icns")
|
icon = os.path.join(os.getcwd(), "dist_assets", "mac", "pyfa.icns")
|
||||||
|
|
||||||
# Walk directories that do dynamic importing
|
# Walk directories that do dynamic importing
|
||||||
paths = ('eos/effects', 'eos/db/migrations', 'service/conversions')
|
paths = ('eos/db/migrations', 'service/conversions')
|
||||||
for root, folders, files in chain.from_iterable(os.walk(path) for path in paths):
|
for root, folders, files in chain.from_iterable(os.walk(path) for path in paths):
|
||||||
for file_ in files:
|
for file_ in files:
|
||||||
if file_.endswith(".py") and not file_.startswith("_"):
|
if file_.endswith(".py") and not file_.startswith("_"):
|
||||||
@@ -54,8 +58,10 @@ a = Analysis([r'../../pyfa.py'],
|
|||||||
win_no_prefer_redirects=False,
|
win_no_prefer_redirects=False,
|
||||||
win_private_assemblies=False,
|
win_private_assemblies=False,
|
||||||
cipher=block_cipher)
|
cipher=block_cipher)
|
||||||
|
|
||||||
pyz = PYZ(a.pure, a.zipped_data,
|
pyz = PYZ(a.pure, a.zipped_data,
|
||||||
cipher=block_cipher)
|
cipher=block_cipher)
|
||||||
|
|
||||||
exe = EXE(pyz,
|
exe = EXE(pyz,
|
||||||
a.scripts,
|
a.scripts,
|
||||||
a.binaries,
|
a.binaries,
|
||||||
@@ -70,10 +76,16 @@ exe = EXE(pyz,
|
|||||||
icon=icon,
|
icon=icon,
|
||||||
)
|
)
|
||||||
|
|
||||||
app = BUNDLE(exe,
|
app = BUNDLE(
|
||||||
name='pyfa.app',
|
exe,
|
||||||
icon=icon,
|
name='pyfa.app',
|
||||||
bundle_identifier=None,
|
icon=icon,
|
||||||
info_plist={
|
bundle_identifier=None,
|
||||||
'NSHighResolutionCapable': 'True'
|
info_plist={
|
||||||
})
|
'NSHighResolutionCapable': 'True',
|
||||||
|
'NSPrincipalClass': 'NSApplication',
|
||||||
|
'CFBundleName': 'pyfa',
|
||||||
|
'CFBundleDisplayName': 'pyfa',
|
||||||
|
'CFBundleIdentifier': 'org.pyfaorg.pyfa',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|||||||
@@ -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>
|
|
||||||
@@ -3,44 +3,35 @@
|
|||||||
import os.path
|
import os.path
|
||||||
from subprocess import call
|
from subprocess import call
|
||||||
import zipfile
|
import zipfile
|
||||||
|
from packaging.version import Version
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
|
||||||
def zipdir(path, zip):
|
with open("version.yml", 'r') as file:
|
||||||
for root, dirs, files in os.walk(path):
|
data = yaml.load(file, Loader=yaml.SafeLoader)
|
||||||
for file in files:
|
version = data['version']
|
||||||
zip.write(os.path.join(root, file))
|
|
||||||
|
|
||||||
config = {}
|
os.environ["PYFA_DIST_DIR"] = os.path.join(os.getcwd(), 'dist')
|
||||||
|
|
||||||
exec(compile(open("config.py").read(), "config.py", 'exec'), config)
|
os.environ["PYFA_VERSION"] = version
|
||||||
|
iscc = "C:\Program Files (x86)\Inno Setup 6\ISCC.exe"
|
||||||
|
|
||||||
iscc = "C:\Program Files (x86)\Inno Setup 5\ISCC.exe" # inno script location via wine
|
source = os.path.join(os.environ["PYFA_DIST_DIR"], "pyfa")
|
||||||
|
|
||||||
print("Creating archive")
|
fileName = "pyfa-{}-win".format(os.environ["PYFA_VERSION"])
|
||||||
|
|
||||||
source = os.path.join(os.getcwd(), "dist", "pyfa")
|
|
||||||
|
|
||||||
fileName = "pyfa-{}-{}-{}-win".format(
|
|
||||||
config['version'],
|
|
||||||
config['expansionName'].lower(),
|
|
||||||
config['expansionVersion']
|
|
||||||
)
|
|
||||||
|
|
||||||
archive = zipfile.ZipFile(os.path.join(os.getcwd(), "dist", fileName + ".zip"), 'w', compression=zipfile.ZIP_DEFLATED)
|
|
||||||
zipdir(source, archive)
|
|
||||||
archive.close()
|
|
||||||
|
|
||||||
print("Compiling EXE")
|
print("Compiling EXE")
|
||||||
|
|
||||||
expansion = "%s %s" % (config['expansionName'], config['expansionVersion']),
|
v = Version(version)
|
||||||
|
|
||||||
|
print(v)
|
||||||
|
|
||||||
call([
|
call([
|
||||||
iscc,
|
iscc,
|
||||||
os.path.join(os.getcwd(), "dist_assets", "win", "pyfa-setup.iss"),
|
os.path.join(os.getcwd(), "dist_assets", "win", "pyfa-setup.iss"),
|
||||||
"/dMyAppVersion=%s" % (config['version']),
|
"/dMyAppVersion=%s" % v,
|
||||||
"/dMyAppExpansion=%s" % (expansion),
|
|
||||||
"/dMyAppDir=%s" % source,
|
"/dMyAppDir=%s" % source,
|
||||||
"/dMyOutputDir=%s" % os.path.join(os.getcwd(), "dist"),
|
"/dMyOutputDir=%s" % os.path.join(os.getcwd()),
|
||||||
"/dMyOutputFile=%s" % fileName]) # stdout=devnull, stderr=devnull
|
"/dMyOutputFile=%s" % fileName]) # stdout=devnull, stderr=devnull
|
||||||
|
|
||||||
print("Done")
|
print("Done")
|
||||||
|
|||||||
@@ -7,23 +7,16 @@
|
|||||||
#ifndef MyAppVersion
|
#ifndef MyAppVersion
|
||||||
#define MyAppVersion "2.1.0"
|
#define MyAppVersion "2.1.0"
|
||||||
#endif
|
#endif
|
||||||
#ifndef MyAppExpansion
|
|
||||||
#define MyAppExpansion "Vanguard 1.0"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
; Other config
|
; Other config
|
||||||
|
|
||||||
#define MyAppName "pyfa"
|
#define MyAppName "pyfa"
|
||||||
#define MyAppPublisher "pyfa"
|
#define MyAppPublisher "pyfa"
|
||||||
#define MyAppURL "https://forums.eveonline.com/t/27156"
|
#define MyAppURL "https://github.com/pyfa-org/Pyfa/"
|
||||||
#define MyAppExeName "pyfa.exe"
|
#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
|
#ifndef MyOutputFile
|
||||||
#define MyOutputFile LowerCase(StringChange(MyAppName+'-'+MyAppVersion+'-'+MyAppExpansion+'-win-wx3', " ", "-"))
|
#define MyOutputFile LowerCase(StringChange(MyAppName+'-'+MyAppVersion+'-win', " ", "-"))
|
||||||
#endif
|
#endif
|
||||||
#ifndef MyAppDir
|
#ifndef MyAppDir
|
||||||
#define MyAppDir "pyfa"
|
#define MyAppDir "pyfa"
|
||||||
@@ -33,17 +26,19 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
[Setup]
|
[Setup]
|
||||||
|
|
||||||
; NOTE: The value of AppId uniquely identifies this application.
|
; NOTE: The value of AppId uniquely identifies this application.
|
||||||
; Do not use the same AppId value in installers for other applications.
|
; Do not use the same AppId value in installers for other applications.
|
||||||
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
|
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
|
||||||
AppId={{3DA39096-C08D-49CD-90E0-1D177F32C8AA}
|
AppId={{3DA39096-C08D-49CD-90E0-1D177F32C8AA}
|
||||||
AppName={#MyAppName}
|
AppName={#MyAppName}
|
||||||
AppVersion={#MyAppVersion} ({#MyAppExpansion})
|
AppVersion={#MyAppVersion}
|
||||||
AppPublisher={#MyAppPublisher}
|
AppPublisher={#MyAppPublisher}
|
||||||
AppPublisherURL={#MyAppURL}
|
AppPublisherURL={#MyAppURL}
|
||||||
AppSupportURL={#MyAppURL}
|
AppSupportURL={#MyAppURL}
|
||||||
AppUpdatesURL={#MyAppURL}
|
AppUpdatesURL={#MyAppURL}
|
||||||
|
ArchitecturesAllowed=x64
|
||||||
|
ArchitecturesInstallIn64BitMode=x64
|
||||||
|
CloseApplications=yes
|
||||||
DefaultDirName={pf}\{#MyAppName}
|
DefaultDirName={pf}\{#MyAppName}
|
||||||
DefaultGroupName={#MyAppName}
|
DefaultGroupName={#MyAppName}
|
||||||
AllowNoIcons=yes
|
AllowNoIcons=yes
|
||||||
@@ -51,10 +46,7 @@ LicenseFile={#MyAppDir}\LICENSE
|
|||||||
OutputDir={#MyOutputDir}
|
OutputDir={#MyOutputDir}
|
||||||
OutputBaseFilename={#MyOutputFile}
|
OutputBaseFilename={#MyOutputFile}
|
||||||
SetupIconFile={#MyAppDir}\pyfa.ico
|
SetupIconFile={#MyAppDir}\pyfa.ico
|
||||||
Compression=lzma
|
|
||||||
SolidCompression=yes
|
SolidCompression=yes
|
||||||
CloseApplications=yes
|
|
||||||
AppReadmeFile=https://github.com/pyfa-org/Pyfa/blob/v{#MyAppVersion}/readme.txt
|
|
||||||
|
|
||||||
[Languages]
|
[Languages]
|
||||||
Name: "english"; MessagesFile: "compiler:Default.isl"
|
Name: "english"; MessagesFile: "compiler:Default.isl"
|
||||||
@@ -88,6 +80,7 @@ Type: files; Name: "{app}\*.pyc"
|
|||||||
|
|
||||||
[Code]
|
[Code]
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
function IsAppRunning(const FileName : string): Boolean;
|
function IsAppRunning(const FileName : string): Boolean;
|
||||||
var
|
var
|
||||||
FSWbemLocator: Variant;
|
FSWbemLocator: Variant;
|
||||||
@@ -104,6 +97,7 @@ begin
|
|||||||
FSWbemLocator := Unassigned;
|
FSWbemLocator := Unassigned;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
procedure RemoveFromVirtualStore;
|
procedure RemoveFromVirtualStore;
|
||||||
var
|
var
|
||||||
VirtualStore,FileName,FilePath:String;
|
VirtualStore,FileName,FilePath:String;
|
||||||
@@ -120,6 +114,7 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
function PrepareToInstall(var NeedsRestart: Boolean): String;
|
function PrepareToInstall(var NeedsRestart: Boolean): String;
|
||||||
begin
|
begin
|
||||||
if(IsAppRunning( 'pyfa.exe' )) then
|
if(IsAppRunning( 'pyfa.exe' )) then
|
||||||
@@ -132,54 +127,61 @@ begin
|
|||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function GetUninstallString: string;
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
function GetUninstallString(): String;
|
||||||
var
|
var
|
||||||
sUnInstPath: string;
|
sUnInstPath: String;
|
||||||
sUnInstallString: String;
|
sUnInstallString: String;
|
||||||
begin
|
begin
|
||||||
Result := '';
|
|
||||||
sUnInstPath := ExpandConstant('Software\Microsoft\Windows\CurrentVersion\Uninstall\{{3DA39096-C08D-49CD-90E0-1D177F32C8AA}_is1'); //Your App GUID/ID
|
sUnInstPath := ExpandConstant('Software\Microsoft\Windows\CurrentVersion\Uninstall\{{3DA39096-C08D-49CD-90E0-1D177F32C8AA}_is1'); //Your App GUID/ID
|
||||||
sUnInstallString := '';
|
sUnInstallString := '';
|
||||||
if not RegQueryStringValue(HKLM, sUnInstPath, 'UninstallString', sUnInstallString) then
|
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;
|
Result := sUnInstallString;
|
||||||
end;
|
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
|
begin
|
||||||
Result := (GetUninstallString() <> '');
|
Result := (GetUninstallString() <> '');
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function InitializeSetup: Boolean;
|
/////////////////////////////////////////////////////////////////////
|
||||||
var
|
procedure CurStepChanged(CurStep: TSetupStep);
|
||||||
V: Integer;
|
|
||||||
iResultCode: Integer;
|
|
||||||
sUnInstallString: string;
|
|
||||||
iOldVersionMajor: Cardinal;
|
|
||||||
iOldVersionMinor: Cardinal;
|
|
||||||
begin
|
begin
|
||||||
Result := True; // in case when no previous version is found
|
if (CurStep=ssInstall) then
|
||||||
if RegValueExists(HKEY_LOCAL_MACHINE,'Software\Microsoft\Windows\CurrentVersion\Uninstall\{3DA39096-C08D-49CD-90E0-1D177F32C8AA}_is1', 'UninstallString') then //Your App GUID/ID
|
|
||||||
begin
|
begin
|
||||||
RegQueryDWordValue(HKEY_LOCAL_MACHINE,
|
if (IsUpgrade()) then
|
||||||
'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.
|
|
||||||
begin
|
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
|
UnInstallOldVersion();
|
||||||
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
|
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|||||||
@@ -8,11 +8,6 @@
|
|||||||
</requestedPrivileges>
|
</requestedPrivileges>
|
||||||
</security>
|
</security>
|
||||||
</trustInfo>
|
</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">
|
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||||
<application>
|
<application>
|
||||||
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
|
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
|
||||||
|
|||||||
@@ -5,8 +5,7 @@ from itertools import chain
|
|||||||
import subprocess
|
import subprocess
|
||||||
import requests.certs
|
import requests.certs
|
||||||
|
|
||||||
label = subprocess.check_output([
|
label = subprocess.check_output(["git", "describe", "--tags"]).strip()
|
||||||
"git", "describe", "--tags"]).strip()
|
|
||||||
|
|
||||||
with open('.version', 'w+') as f:
|
with open('.version', 'w+') as f:
|
||||||
f.write(label.decode())
|
f.write(label.decode())
|
||||||
@@ -21,18 +20,21 @@ added_files = [
|
|||||||
('../../service/jargon/*.yaml', 'service/jargon'),
|
('../../service/jargon/*.yaml', 'service/jargon'),
|
||||||
('../../dist_assets/win/pyfa.ico', '.'),
|
('../../dist_assets/win/pyfa.ico', '.'),
|
||||||
('../../dist_assets/win/pyfa.exe.manifest', '.'),
|
('../../dist_assets/win/pyfa.exe.manifest', '.'),
|
||||||
('../../dist_assets/win/Microsoft.VC90.CRT.manifest', '.'),
|
|
||||||
(requests.certs.where(), '.'), # is this needed anymore?
|
(requests.certs.where(), '.'), # is this needed anymore?
|
||||||
('../../eve.db', '.'),
|
('../../eve.db', '.'),
|
||||||
('../../README.md', '.'),
|
('../../README.md', '.'),
|
||||||
('../../LICENSE', '.'),
|
('../../LICENSE', '.'),
|
||||||
('../../.version', '.'),
|
('../../version.yml', '.'),
|
||||||
]
|
]
|
||||||
|
|
||||||
import_these = []
|
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
|
||||||
|
'pkg_resources.py2_warn' # issue 2156
|
||||||
|
]
|
||||||
|
|
||||||
# Walk directories that do dynamic importing
|
# Walk directories that do dynamic importing
|
||||||
paths = ('eos/effects', 'eos/db/migrations', 'service/conversions')
|
paths = ('eos/db/migrations', 'service/conversions')
|
||||||
for root, folders, files in chain.from_iterable(os.walk(path) for path in paths):
|
for root, folders, files in chain.from_iterable(os.walk(path) for path in paths):
|
||||||
for file_ in files:
|
for file_ in files:
|
||||||
if file_.endswith(".py") and not file_.startswith("_"):
|
if file_.endswith(".py") and not file_.startswith("_"):
|
||||||
|
|||||||
@@ -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',
|
|
||||||
)
|
|
||||||
71
eos/calc.py
Normal file
71
eos/calc.py
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
# =============================================================================
|
||||||
|
# Copyright (C) 2019 Ryan Holmes
|
||||||
|
#
|
||||||
|
# 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 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:
|
||||||
|
return 1
|
||||||
|
if srcFalloffRange > 0:
|
||||||
|
# Most modules cannot be activated when at 3x falloff range, with few exceptions like guns
|
||||||
|
if restrictedRange and distance > srcOptimalRange + 3 * srcFalloffRange:
|
||||||
|
return 0
|
||||||
|
return 0.5 ** ((max(0, distance - srcOptimalRange) / srcFalloffRange) ** 2)
|
||||||
|
elif distance <= srcOptimalRange:
|
||||||
|
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)
|
||||||
198
eos/capSim.py
198
eos/capSim.py
@@ -1,7 +1,7 @@
|
|||||||
import heapq
|
import heapq
|
||||||
import time
|
import time
|
||||||
from math import sqrt, exp
|
from math import sqrt, exp
|
||||||
from functools import reduce
|
from collections import Counter
|
||||||
|
|
||||||
DAY = 24 * 60 * 60 * 1000
|
DAY = 24 * 60 * 60 * 1000
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@ def lcm(a, b):
|
|||||||
return n / a
|
return n / a
|
||||||
|
|
||||||
|
|
||||||
class CapSimulator(object):
|
class CapSimulator:
|
||||||
"""Entity's EVE Capacitor Simulator"""
|
"""Entity's EVE Capacitor Simulator"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@@ -21,6 +21,7 @@ class CapSimulator(object):
|
|||||||
|
|
||||||
self.capacitorCapacity = 100
|
self.capacitorCapacity = 100
|
||||||
self.capacitorRecharge = 1000
|
self.capacitorRecharge = 1000
|
||||||
|
self.startingCapacity = 1000
|
||||||
|
|
||||||
# max simulated time.
|
# max simulated time.
|
||||||
self.t_max = DAY
|
self.t_max = DAY
|
||||||
@@ -41,6 +42,14 @@ class CapSimulator(object):
|
|||||||
# relevant decimal digits of capacitor for LCM period optimization
|
# relevant decimal digits of capacitor for LCM period optimization
|
||||||
self.stability_precision = 1
|
self.stability_precision = 1
|
||||||
|
|
||||||
|
# Stores how cap sim changed cap values outside of cap regen time
|
||||||
|
self.saved_changes = ()
|
||||||
|
self.saved_changes_internal = None
|
||||||
|
|
||||||
|
# Reports if sim was stopped due to detecting stability early
|
||||||
|
self.optimize_repeats = True
|
||||||
|
self.result_optimized_repeats = None
|
||||||
|
|
||||||
def scale_activation(self, duration, capNeed):
|
def scale_activation(self, duration, capNeed):
|
||||||
for res in self.scale_resolutions:
|
for res in self.scale_resolutions:
|
||||||
mod = duration % res
|
mod = duration % res
|
||||||
@@ -59,7 +68,7 @@ class CapSimulator(object):
|
|||||||
return duration, capNeed
|
return duration, capNeed
|
||||||
|
|
||||||
def init(self, modules):
|
def init(self, modules):
|
||||||
"""prepare modules. a list of (duration, capNeed, clipSize, disableStagger) tuples is
|
"""prepare modules. a list of (duration, capNeed, clipSize, disableStagger, reloadTime, isInjector) tuples is
|
||||||
expected, with clipSize 0 if the module has infinite ammo.
|
expected, with clipSize 0 if the module has infinite ammo.
|
||||||
"""
|
"""
|
||||||
self.modules = modules
|
self.modules = modules
|
||||||
@@ -67,48 +76,57 @@ class CapSimulator(object):
|
|||||||
def reset(self):
|
def reset(self):
|
||||||
"""Reset the simulator state"""
|
"""Reset the simulator state"""
|
||||||
self.state = []
|
self.state = []
|
||||||
|
self.saved_changes_internal = {}
|
||||||
|
self.result_optimized_repeats = False
|
||||||
mods = {}
|
mods = {}
|
||||||
period = 1
|
period = 1
|
||||||
disable_period = False
|
disable_period = False
|
||||||
|
|
||||||
# Loop over modules, clearing clipSize if applicable, and group modules based on attributes
|
# Loop over modules, clearing clipSize if applicable, and group modules based on attributes
|
||||||
for (duration, capNeed, clipSize, disableStagger, reloadTime) in self.modules:
|
for (duration, capNeed, clipSize, disableStagger, reloadTime, isInjector) in self.modules:
|
||||||
if self.scale:
|
if self.scale:
|
||||||
duration, capNeed = self.scale_activation(duration, capNeed)
|
duration, capNeed = self.scale_activation(duration, capNeed)
|
||||||
|
|
||||||
# set clipSize to infinite if reloads are disabled unless it's
|
# set clipSize to infinite if reloads are disabled unless it's
|
||||||
# a cap booster module.
|
# a cap booster module
|
||||||
if not self.reload and capNeed > 0:
|
if not self.reload and not isInjector:
|
||||||
clipSize = 0
|
clipSize = 0
|
||||||
reloadTime = 0
|
reloadTime = 0
|
||||||
|
|
||||||
# Group modules based on their properties
|
# Group modules based on their properties
|
||||||
if (duration, capNeed, clipSize, disableStagger, reloadTime) in mods:
|
key = (duration, capNeed, clipSize, disableStagger, reloadTime, isInjector)
|
||||||
mods[(duration, capNeed, clipSize, disableStagger, reloadTime)] += 1
|
if key in mods:
|
||||||
|
mods[key] += 1
|
||||||
else:
|
else:
|
||||||
mods[(duration, capNeed, clipSize, disableStagger, reloadTime)] = 1
|
mods[key] = 1
|
||||||
|
|
||||||
# Loop over grouped modules, configure staggering and push to the simulation state
|
# Loop over grouped modules, configure staggering and push to the simulation state
|
||||||
for (duration, capNeed, clipSize, disableStagger, reloadTime), amount in mods.items():
|
for (duration, capNeed, clipSize, disableStagger, reloadTime, isInjector), amount in mods.items():
|
||||||
|
# period optimization doesn't work when reloads are active.
|
||||||
|
if clipSize:
|
||||||
|
disable_period = True
|
||||||
|
# Just push multiple instances if item is injector. We do not want to stagger them as we will
|
||||||
|
# use them as needed and want them to be available right away
|
||||||
|
if isInjector:
|
||||||
|
for i in range(amount):
|
||||||
|
heapq.heappush(self.state, [0, duration, capNeed, 0, clipSize, reloadTime, isInjector])
|
||||||
|
continue
|
||||||
if self.stagger and not disableStagger:
|
if self.stagger and not disableStagger:
|
||||||
|
# Stagger all mods if they do not need to be reloaded
|
||||||
if clipSize == 0:
|
if clipSize == 0:
|
||||||
duration = int(duration / amount)
|
duration = int(duration / amount)
|
||||||
|
# Stagger mods after first
|
||||||
else:
|
else:
|
||||||
stagger_amount = (duration * clipSize + reloadTime) / (amount * clipSize)
|
stagger_amount = (duration * clipSize + reloadTime) / (amount * clipSize)
|
||||||
for i in range(1, amount):
|
for i in range(1, amount):
|
||||||
heapq.heappush(self.state,
|
heapq.heappush(self.state, [i * stagger_amount, duration, capNeed, 0, clipSize, reloadTime, isInjector])
|
||||||
[i * stagger_amount, duration,
|
# If mods are not staggered - just multiply cap use
|
||||||
capNeed, 0, clipSize, reloadTime])
|
|
||||||
else:
|
else:
|
||||||
capNeed *= amount
|
capNeed *= amount
|
||||||
|
|
||||||
period = lcm(period, duration)
|
period = lcm(period, duration)
|
||||||
|
|
||||||
# period optimization doesn't work when reloads are active.
|
heapq.heappush(self.state, [0, duration, capNeed, 0, clipSize, reloadTime, isInjector])
|
||||||
if clipSize:
|
|
||||||
disable_period = True
|
|
||||||
|
|
||||||
heapq.heappush(self.state, [0, duration, capNeed, 0, clipSize, reloadTime])
|
|
||||||
|
|
||||||
if disable_period:
|
if disable_period:
|
||||||
self.period = self.t_max
|
self.period = self.t_max
|
||||||
@@ -119,7 +137,8 @@ class CapSimulator(object):
|
|||||||
"""Run the simulation"""
|
"""Run the simulation"""
|
||||||
|
|
||||||
start = time.time()
|
start = time.time()
|
||||||
|
awaitingInjectors = []
|
||||||
|
awaitingInjectorsCounterWrap = Counter()
|
||||||
self.reset()
|
self.reset()
|
||||||
|
|
||||||
push = heapq.heappush
|
push = heapq.heappush
|
||||||
@@ -129,27 +148,36 @@ class CapSimulator(object):
|
|||||||
stability_precision = self.stability_precision
|
stability_precision = self.stability_precision
|
||||||
period = self.period
|
period = self.period
|
||||||
|
|
||||||
|
activation = None
|
||||||
iterations = 0
|
iterations = 0
|
||||||
|
|
||||||
capCapacity = self.capacitorCapacity
|
capCapacity = self.capacitorCapacity
|
||||||
tau = self.capacitorRecharge / 5.0
|
tau = self.capacitorRecharge / 5.0
|
||||||
|
|
||||||
cap_wrap = capCapacity # cap value at last period
|
cap_wrap = self.startingCapacity # cap value at last period
|
||||||
cap_lowest = capCapacity # lowest cap value encountered
|
cap_lowest = self.startingCapacity # lowest cap value encountered
|
||||||
cap_lowest_pre = capCapacity # lowest cap value before activations
|
cap_lowest_pre = self.startingCapacity # lowest cap value before activations
|
||||||
cap = capCapacity # current cap value
|
cap = self.startingCapacity # current cap value
|
||||||
t_wrap = self.period # point in time of next period
|
t_wrap = self.period # point in time of next period
|
||||||
|
|
||||||
t_last = 0
|
t_last = 0
|
||||||
t_max = self.t_max
|
t_max = self.t_max
|
||||||
|
|
||||||
while 1:
|
while 1:
|
||||||
activation = pop(state)
|
# Nothing to pop - might happen when no mods are activated, or when
|
||||||
t_now, duration, capNeed, shot, clipSize, reloadTime = activation
|
# only cap injectors are active (and are postponed by code below)
|
||||||
|
try:
|
||||||
|
activation = pop(state)
|
||||||
|
except IndexError:
|
||||||
|
break
|
||||||
|
t_now, duration, capNeed, shot, clipSize, reloadTime, isInjector = activation
|
||||||
|
|
||||||
|
# Max time reached, stop simulation - we're stable
|
||||||
if t_now >= t_max:
|
if t_now >= t_max:
|
||||||
break
|
break
|
||||||
|
|
||||||
cap = ((1.0 + (sqrt(cap / capCapacity) - 1.0) * exp((t_last - t_now) / tau)) ** 2) * capCapacity
|
# Regenerate cap from last time point
|
||||||
|
if t_now > t_last:
|
||||||
|
cap = ((1.0 + (sqrt(cap / capCapacity) - 1.0) * exp((t_last - t_now) / tau)) ** 2) * capCapacity
|
||||||
|
|
||||||
if t_now != t_last:
|
if t_now != t_last:
|
||||||
if cap < cap_lowest_pre:
|
if cap < cap_lowest_pre:
|
||||||
@@ -157,36 +185,104 @@ class CapSimulator(object):
|
|||||||
if t_now == t_wrap:
|
if t_now == t_wrap:
|
||||||
# history is repeating itself, so if we have more cap now than last
|
# history is repeating itself, so if we have more cap now than last
|
||||||
# time this happened, it is a stable setup.
|
# time this happened, it is a stable setup.
|
||||||
if cap >= cap_wrap:
|
awaitingInjectorsCounterNow = Counter(awaitingInjectors)
|
||||||
|
if self.optimize_repeats and cap >= cap_wrap and awaitingInjectorsCounterNow == awaitingInjectorsCounterWrap:
|
||||||
|
self.result_optimized_repeats = True
|
||||||
break
|
break
|
||||||
cap_wrap = round(cap, stability_precision)
|
cap_wrap = round(cap, stability_precision)
|
||||||
|
awaitingInjectorsCounterWrap = awaitingInjectorsCounterNow
|
||||||
t_wrap += period
|
t_wrap += period
|
||||||
|
|
||||||
cap -= capNeed
|
t_last = t_now
|
||||||
if cap > capCapacity:
|
|
||||||
cap = capCapacity
|
|
||||||
|
|
||||||
iterations += 1
|
iterations += 1
|
||||||
|
|
||||||
t_last = t_now
|
# If injecting cap will "overshoot" max cap, postpone it
|
||||||
|
if isInjector and cap - capNeed > capCapacity:
|
||||||
|
awaitingInjectors.append((duration, capNeed, shot, clipSize, reloadTime, isInjector))
|
||||||
|
|
||||||
if cap < cap_lowest:
|
else:
|
||||||
if cap < 0.0:
|
# If we will need more cap than we have, but we are not at 100% -
|
||||||
break
|
# use awaiting cap injectors to top us up until we have enough or
|
||||||
cap_lowest = cap
|
# until we're full
|
||||||
|
if capNeed > cap and cap < capCapacity:
|
||||||
|
while awaitingInjectors and capNeed > cap and capCapacity > cap:
|
||||||
|
neededInjection = min(capNeed - cap, capCapacity - cap)
|
||||||
|
# Find injectors which have just enough cap or more
|
||||||
|
goodInjectors = [i for i in awaitingInjectors if -i[1] >= neededInjection]
|
||||||
|
if goodInjectors:
|
||||||
|
# Pick injector which overshoots the least
|
||||||
|
bestInjector = min(goodInjectors, key=lambda i: -i[1])
|
||||||
|
else:
|
||||||
|
# Take the one which provides the most cap
|
||||||
|
bestInjector = max(goodInjectors, key=lambda i: -i[1])
|
||||||
|
# Use injector
|
||||||
|
awaitingInjectors.remove(bestInjector)
|
||||||
|
inj_duration, inj_capNeed, inj_shot, inj_clipSize, inj_reloadTime, inj_isInjector = bestInjector
|
||||||
|
cap -= inj_capNeed
|
||||||
|
if cap > capCapacity:
|
||||||
|
cap = capCapacity
|
||||||
|
self.saved_changes_internal[t_now] = cap
|
||||||
|
# Add injector to regular state tracker
|
||||||
|
inj_t_now = t_now
|
||||||
|
inj_t_now += inj_duration
|
||||||
|
inj_shot += 1
|
||||||
|
if inj_clipSize:
|
||||||
|
if inj_shot % inj_clipSize == 0:
|
||||||
|
inj_shot = 0
|
||||||
|
inj_t_now += inj_reloadTime
|
||||||
|
push(state, [inj_t_now, inj_duration, inj_capNeed, inj_shot, inj_clipSize, inj_reloadTime, inj_isInjector])
|
||||||
|
|
||||||
# queue the next activation of this module
|
# Apply cap modification
|
||||||
t_now += duration
|
cap -= capNeed
|
||||||
shot += 1
|
if cap > capCapacity:
|
||||||
if clipSize:
|
cap = capCapacity
|
||||||
if shot % clipSize == 0:
|
self.saved_changes_internal[t_now] = cap
|
||||||
shot = 0
|
|
||||||
t_now += reloadTime # include reload time
|
|
||||||
activation[0] = t_now
|
|
||||||
activation[3] = shot
|
|
||||||
|
|
||||||
|
if cap < cap_lowest:
|
||||||
|
# Negative cap - we're unstable, simulation is over
|
||||||
|
if cap < 0.0:
|
||||||
|
break
|
||||||
|
cap_lowest = cap
|
||||||
|
|
||||||
|
# Try using awaiting injectors to top up the cap after spending some
|
||||||
|
while awaitingInjectors and cap < capCapacity:
|
||||||
|
neededInjection = capCapacity - cap
|
||||||
|
# Find injectors which do not overshoot max cap
|
||||||
|
goodInjectors = [i for i in awaitingInjectors if -i[1] <= neededInjection]
|
||||||
|
if not goodInjectors:
|
||||||
|
break
|
||||||
|
# Take the one which provides the most cap
|
||||||
|
bestInjector = max(goodInjectors, key=lambda i: -i[1])
|
||||||
|
# Use injector
|
||||||
|
awaitingInjectors.remove(bestInjector)
|
||||||
|
inj_duration, inj_capNeed, inj_shot, inj_clipSize, inj_reloadTime, inj_isInjector = bestInjector
|
||||||
|
cap -= inj_capNeed
|
||||||
|
if cap > capCapacity:
|
||||||
|
cap = capCapacity
|
||||||
|
self.saved_changes_internal[t_now] = cap
|
||||||
|
# Add injector to regular state tracker
|
||||||
|
inj_t_now = t_now
|
||||||
|
inj_t_now += inj_duration
|
||||||
|
inj_shot += 1
|
||||||
|
if inj_clipSize:
|
||||||
|
if inj_shot % inj_clipSize == 0:
|
||||||
|
inj_shot = 0
|
||||||
|
inj_t_now += inj_reloadTime
|
||||||
|
push(state, [inj_t_now, inj_duration, inj_capNeed, inj_shot, inj_clipSize, inj_reloadTime, inj_isInjector])
|
||||||
|
|
||||||
|
# queue the next activation of this module
|
||||||
|
t_now += duration
|
||||||
|
shot += 1
|
||||||
|
if clipSize:
|
||||||
|
if shot % clipSize == 0:
|
||||||
|
shot = 0
|
||||||
|
t_now += reloadTime # include reload time
|
||||||
|
activation[0] = t_now
|
||||||
|
activation[3] = shot
|
||||||
|
|
||||||
|
push(state, activation)
|
||||||
|
if activation is not None:
|
||||||
push(state, activation)
|
push(state, activation)
|
||||||
push(state, activation)
|
|
||||||
|
|
||||||
# update instance with relevant results.
|
# update instance with relevant results.
|
||||||
self.t = t_last
|
self.t = t_last
|
||||||
@@ -194,7 +290,7 @@ class CapSimulator(object):
|
|||||||
|
|
||||||
# calculate EVE's stability value
|
# calculate EVE's stability value
|
||||||
try:
|
try:
|
||||||
avgDrain = reduce(float.__add__, [x[2] / x[1] for x in self.state], 0.0)
|
avgDrain = sum(x[2] / x[1] for x in self.state)
|
||||||
self.cap_stable_eve = 0.25 * (1.0 + sqrt(-(2.0 * avgDrain * tau - capCapacity) / capCapacity)) ** 2
|
self.cap_stable_eve = 0.25 * (1.0 + sqrt(-(2.0 * avgDrain * tau - capCapacity) / capCapacity)) ** 2
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self.cap_stable_eve = 0.0
|
self.cap_stable_eve = 0.0
|
||||||
@@ -204,7 +300,9 @@ class CapSimulator(object):
|
|||||||
self.cap_stable_low = cap_lowest
|
self.cap_stable_low = cap_lowest
|
||||||
self.cap_stable_high = cap_lowest_pre
|
self.cap_stable_high = cap_lowest_pre
|
||||||
else:
|
else:
|
||||||
self.cap_stable_low = \
|
self.cap_stable_low = self.cap_stable_high = 0.0
|
||||||
self.cap_stable_high = 0.0
|
|
||||||
|
self.saved_changes = tuple((k / 1000, max(0, self.saved_changes_internal[k])) for k in sorted(self.saved_changes_internal))
|
||||||
|
self.saved_changes_internal = None
|
||||||
|
|
||||||
self.runtime = time.time() - start
|
self.runtime = time.time() - start
|
||||||
|
|||||||
@@ -19,13 +19,14 @@ if istravis is True or hasattr(sys, '_called_from_test'):
|
|||||||
# Running in Travis. Run saveddata database in memory.
|
# Running in Travis. Run saveddata database in memory.
|
||||||
saveddata_connectionstring = 'sqlite:///:memory:'
|
saveddata_connectionstring = 'sqlite:///:memory:'
|
||||||
else:
|
else:
|
||||||
saveddata_connectionstring = 'sqlite:///' + realpath(join(dirname(abspath(__file__)), "..", "saveddata", "saveddata-py3-db.db"))
|
saveddata_connectionstring = 'sqlite:///' + realpath(join(dirname(abspath(__file__)), "..", "saveddata", "saveddata.db"))
|
||||||
|
|
||||||
pyfalog.debug("Saveddata connection string: {0}", saveddata_connectionstring)
|
pyfalog.debug("Saveddata connection string: {0}", saveddata_connectionstring)
|
||||||
|
|
||||||
settings = {
|
settings = {
|
||||||
"useStaticAdaptiveArmorHardener": False,
|
"useStaticAdaptiveArmorHardener": False,
|
||||||
"strictSkillLevels": True,
|
"strictSkillLevels": True,
|
||||||
|
"globalDefaultSpoolupPercentage": 1.0
|
||||||
}
|
}
|
||||||
|
|
||||||
# Autodetect path, only change if the autodetection bugs out.
|
# Autodetect path, only change if the autodetection bugs out.
|
||||||
|
|||||||
115
eos/const.py
Normal file
115
eos/const.py
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
# =============================================================================
|
||||||
|
# Copyright (C) 2019 Ryan Holmes
|
||||||
|
#
|
||||||
|
# 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/>.
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
from enum import IntEnum,unique
|
||||||
|
|
||||||
|
|
||||||
|
@unique
|
||||||
|
class FittingSlot(IntEnum):
|
||||||
|
"""
|
||||||
|
Contains slots for ship fittings
|
||||||
|
"""
|
||||||
|
# These are self-explanatory
|
||||||
|
LOW = 1
|
||||||
|
MED = 2
|
||||||
|
HIGH = 3
|
||||||
|
RIG = 4
|
||||||
|
SUBSYSTEM = 5
|
||||||
|
# not a real slot, need for pyfa display rack separation
|
||||||
|
MODE = 6
|
||||||
|
# system effects. They are projected "modules" and pyfa assumes all modules
|
||||||
|
# have a slot. In this case, make one up.
|
||||||
|
SYSTEM = 7
|
||||||
|
# used for citadel services
|
||||||
|
SERVICE = 8
|
||||||
|
# fighter 'slots'. Just easier to put them here...
|
||||||
|
F_LIGHT = 10
|
||||||
|
F_SUPPORT = 11
|
||||||
|
F_HEAVY = 12
|
||||||
|
# fighter 'slots' (for structures)
|
||||||
|
FS_LIGHT = 13
|
||||||
|
FS_SUPPORT = 14
|
||||||
|
FS_HEAVY = 15
|
||||||
|
|
||||||
|
|
||||||
|
@unique
|
||||||
|
class ImplantLocation(IntEnum):
|
||||||
|
"""
|
||||||
|
Contains location of the implant
|
||||||
|
"""
|
||||||
|
FIT = 0
|
||||||
|
CHARACTER = 1
|
||||||
|
|
||||||
|
|
||||||
|
@unique
|
||||||
|
class CalcType(IntEnum):
|
||||||
|
"""
|
||||||
|
Contains location of the calculation
|
||||||
|
"""
|
||||||
|
LOCAL = 0
|
||||||
|
PROJECTED = 1
|
||||||
|
COMMAND = 2
|
||||||
|
|
||||||
|
|
||||||
|
@unique
|
||||||
|
class FittingModuleState(IntEnum):
|
||||||
|
"""
|
||||||
|
Contains the state of a fitting module
|
||||||
|
"""
|
||||||
|
OFFLINE = -1
|
||||||
|
ONLINE = 0
|
||||||
|
ACTIVE = 1
|
||||||
|
OVERHEATED = 2
|
||||||
|
|
||||||
|
|
||||||
|
@unique
|
||||||
|
class FittingHardpoint(IntEnum):
|
||||||
|
"""
|
||||||
|
Contains the types of a fitting hardpoint
|
||||||
|
"""
|
||||||
|
NONE = 0
|
||||||
|
MISSILE = 1
|
||||||
|
TURRET = 2
|
||||||
|
|
||||||
|
|
||||||
|
@unique
|
||||||
|
class SpoolType(IntEnum):
|
||||||
|
# Spool and cycle scale are different in case if max spool amount cannot
|
||||||
|
# be divided by spool step without remainder
|
||||||
|
SPOOL_SCALE = 0 # [0..1]
|
||||||
|
CYCLE_SCALE = 1 # [0..1]
|
||||||
|
TIME = 2 # Expressed via time in seconds since spool up started
|
||||||
|
CYCLES = 3 # Expressed in amount of cycles since spool up started
|
||||||
|
|
||||||
|
|
||||||
|
@unique
|
||||||
|
class FitSystemSecurity(IntEnum):
|
||||||
|
HISEC = 0
|
||||||
|
LOWSEC = 1
|
||||||
|
NULLSEC = 2
|
||||||
|
WSPACE = 3
|
||||||
|
|
||||||
|
|
||||||
|
@unique
|
||||||
|
class Operator(IntEnum):
|
||||||
|
PREASSIGN = 0
|
||||||
|
PREINCREASE = 1
|
||||||
|
MULTIPLY = 2
|
||||||
|
POSTINCREASE = 3
|
||||||
|
FORCE = 4
|
||||||
@@ -17,34 +17,63 @@
|
|||||||
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
||||||
# ===============================================================================
|
# ===============================================================================
|
||||||
|
|
||||||
|
import re
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
from sqlalchemy import MetaData, create_engine
|
from sqlalchemy import MetaData, create_engine, event
|
||||||
from sqlalchemy.orm import sessionmaker
|
from sqlalchemy.orm import sessionmaker, scoped_session
|
||||||
|
|
||||||
from . import migration
|
from . import migration
|
||||||
from eos import config
|
from eos import config
|
||||||
from logbook import Logger
|
from logbook import Logger
|
||||||
|
|
||||||
|
|
||||||
pyfalog = Logger(__name__)
|
pyfalog = Logger(__name__)
|
||||||
pyfalog.info("Initializing database")
|
pyfalog.info("Initializing database")
|
||||||
pyfalog.info("Gamedata connection: {0}", config.gamedata_connectionstring)
|
pyfalog.info("Gamedata connection: {0}", config.gamedata_connectionstring)
|
||||||
pyfalog.info("Saveddata connection: {0}", config.saveddata_connectionstring)
|
pyfalog.info("Saveddata connection: {0}", config.saveddata_connectionstring)
|
||||||
|
|
||||||
|
|
||||||
class ReadOnlyException(Exception):
|
class ReadOnlyException(Exception):
|
||||||
pass
|
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
|
gamedata_connectionstring = config.gamedata_connectionstring
|
||||||
if callable(gamedata_connectionstring):
|
if callable(gamedata_connectionstring):
|
||||||
gamedata_engine = create_engine("sqlite://", creator=gamedata_connectionstring, echo=config.debug)
|
gamedata_engine = create_engine("sqlite://", creator=gamedata_connectionstring, echo=config.debug)
|
||||||
else:
|
else:
|
||||||
gamedata_engine = create_engine(gamedata_connectionstring, echo=config.debug)
|
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 = MetaData()
|
||||||
gamedata_meta.bind = gamedata_engine
|
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 = {}
|
||||||
|
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
|
# This should be moved elsewhere, maybe as an actual query. Current, without try-except, it breaks when making a new
|
||||||
# game db because we haven't reached gamedata_meta.create_all()
|
# game db because we haven't reached gamedata_meta.create_all()
|
||||||
try:
|
try:
|
||||||
@@ -54,12 +83,15 @@ try:
|
|||||||
config.gamedata_date = gamedata_session.execute(
|
config.gamedata_date = gamedata_session.execute(
|
||||||
"SELECT `field_value` FROM `metadata` WHERE `field_name` LIKE 'dump_time'"
|
"SELECT `field_value` FROM `metadata` WHERE `field_name` LIKE 'dump_time'"
|
||||||
).fetchone()[0]
|
).fetchone()[0]
|
||||||
|
except (KeyboardInterrupt, SystemExit):
|
||||||
|
raise
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
pyfalog.warning("Missing gamedata version.")
|
pyfalog.warning("Missing gamedata version.")
|
||||||
pyfalog.critical(e)
|
pyfalog.critical(e)
|
||||||
config.gamedata_version = None
|
config.gamedata_version = None
|
||||||
config.gamedata_date = None
|
config.gamedata_date = None
|
||||||
|
|
||||||
|
pyfalog.debug('Initializing saveddata')
|
||||||
saveddata_connectionstring = config.saveddata_connectionstring
|
saveddata_connectionstring = config.saveddata_connectionstring
|
||||||
if saveddata_connectionstring is not None:
|
if saveddata_connectionstring is not None:
|
||||||
if callable(saveddata_connectionstring):
|
if callable(saveddata_connectionstring):
|
||||||
@@ -76,16 +108,19 @@ else:
|
|||||||
# Lock controlling any changes introduced to session
|
# Lock controlling any changes introduced to session
|
||||||
sd_lock = threading.RLock()
|
sd_lock = threading.RLock()
|
||||||
|
|
||||||
|
pyfalog.debug('Importing gamedata DB scheme')
|
||||||
# Import all the definitions for all our database stuff
|
# Import all the definitions for all our database stuff
|
||||||
# noinspection PyPep8
|
# 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
|
# noinspection PyPep8
|
||||||
from eos.db.saveddata import booster, cargo, character, damagePattern, databaseRepair, drone, fighter, fit, implant, implantSet, loadDefaultDatabaseValues, \
|
from eos.db.saveddata import booster, cargo, character, damagePattern, databaseRepair, drone, fighter, fit, implant, implantSet, \
|
||||||
miscData, mutator, module, override, price, queries, skill, targetResists, user
|
miscData, mutator, module, override, price, queries, skill, targetProfile, user
|
||||||
|
|
||||||
# Import queries
|
pyfalog.debug('Importing gamedata queries')
|
||||||
# noinspection PyPep8
|
# noinspection PyPep8
|
||||||
from eos.db.gamedata.queries import *
|
from eos.db.gamedata.queries import *
|
||||||
|
pyfalog.debug('Importing saveddata queries')
|
||||||
# noinspection PyPep8
|
# noinspection PyPep8
|
||||||
from eos.db.saveddata.queries import *
|
from eos.db.saveddata.queries import *
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
__all__ = ["attribute", "category", "effect", "group", "metaData", "dynamicAttributes",
|
__all__ = ["attribute", "category", "effect", "group", "metaData", "dynamicAttributes",
|
||||||
"item", "marketGroup", "metaGroup", "unit", "alphaClones"]
|
"item", "marketGroup", "metaGroup", "unit", "alphaClones", "implantSet"]
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ attributes_table = Table("dgmattribs", gamedata_meta,
|
|||||||
Column("displayName", String),
|
Column("displayName", String),
|
||||||
Column("highIsGood", Boolean),
|
Column("highIsGood", Boolean),
|
||||||
Column("iconID", Integer),
|
Column("iconID", Integer),
|
||||||
|
Column("attributeCategory", Integer),
|
||||||
|
Column("tooltipDescription", Integer),
|
||||||
Column("unitID", Integer, ForeignKey("dgmunits.unitID")))
|
Column("unitID", Integer, ForeignKey("dgmunits.unitID")))
|
||||||
|
|
||||||
mapper(Attribute, typeattributes_table,
|
mapper(Attribute, typeattributes_table,
|
||||||
|
|||||||
@@ -17,8 +17,8 @@
|
|||||||
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
||||||
# ===============================================================================
|
# ===============================================================================
|
||||||
|
|
||||||
from sqlalchemy import Column, String, Integer, ForeignKey, Boolean, Table
|
from sqlalchemy import Boolean, Column, Integer, String, Table
|
||||||
from sqlalchemy.orm import relation, mapper, synonym, deferred
|
from sqlalchemy.orm import deferred, mapper, synonym
|
||||||
|
|
||||||
from eos.db import gamedata_meta
|
from eos.db import gamedata_meta
|
||||||
from eos.gamedata import Category
|
from eos.gamedata import Category
|
||||||
|
|||||||
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,15 +17,15 @@
|
|||||||
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
||||||
# ===============================================================================
|
# ===============================================================================
|
||||||
|
|
||||||
from sqlalchemy import Column, String, Integer, Boolean, ForeignKey, Table, Float
|
from sqlalchemy import Boolean, Column, Float, ForeignKey, Integer, String, Table
|
||||||
from sqlalchemy.ext.associationproxy import association_proxy
|
from sqlalchemy.ext.associationproxy import association_proxy
|
||||||
from sqlalchemy.orm import relation, mapper, synonym, deferred, backref
|
from sqlalchemy.orm import backref, deferred, mapper, relation, synonym
|
||||||
from sqlalchemy.orm.collections import attribute_mapped_collection
|
from sqlalchemy.orm.collections import attribute_mapped_collection
|
||||||
from eos.db.gamedata.effect import typeeffects_table
|
|
||||||
|
|
||||||
from eos.db import gamedata_meta
|
from eos.db import gamedata_meta
|
||||||
from eos.gamedata import Attribute, Effect, Group, Item, MetaType, Traits, DynamicItemItem, DynamicItem
|
from eos.db.gamedata.dynamicAttributes import dynamicApplicable_table
|
||||||
from eos.db.gamedata.dynamicAttributes import dynamicApplicable_table, dynamic_table
|
from eos.db.gamedata.effect import typeeffects_table
|
||||||
|
from eos.gamedata import Attribute, DynamicItem, Effect, Group, Item, Traits, MetaGroup
|
||||||
|
|
||||||
items_table = Table("invtypes", gamedata_meta,
|
items_table = Table("invtypes", gamedata_meta,
|
||||||
Column("typeID", Integer, primary_key=True),
|
Column("typeID", Integer, primary_key=True),
|
||||||
@@ -40,9 +40,14 @@ items_table = Table("invtypes", gamedata_meta,
|
|||||||
Column("marketGroupID", Integer, ForeignKey("invmarketgroups.marketGroupID")),
|
Column("marketGroupID", Integer, ForeignKey("invmarketgroups.marketGroupID")),
|
||||||
Column("iconID", Integer),
|
Column("iconID", Integer),
|
||||||
Column("graphicID", Integer),
|
Column("graphicID", Integer),
|
||||||
Column("groupID", Integer, ForeignKey("invgroups.groupID"), index=True))
|
Column("groupID", Integer, ForeignKey("invgroups.groupID"), index=True),
|
||||||
|
Column("metaLevel", Integer),
|
||||||
|
Column("metaGroupID", Integer, ForeignKey("invmetagroups.metaGroupID"), index=True),
|
||||||
|
Column("variationParentTypeID", Integer, ForeignKey("invtypes.typeID"), index=True),
|
||||||
|
Column("replacements", String),
|
||||||
|
Column("reqskills", String),
|
||||||
|
Column("requiredfor", String))
|
||||||
|
|
||||||
from .metaGroup import metatypes_table # noqa
|
|
||||||
from .traits import traits_table # noqa
|
from .traits import traits_table # noqa
|
||||||
|
|
||||||
mapper(Item, items_table,
|
mapper(Item, items_table,
|
||||||
@@ -50,9 +55,8 @@ mapper(Item, items_table,
|
|||||||
"group" : relation(Group, backref=backref("items", cascade="all,delete")),
|
"group" : relation(Group, backref=backref("items", cascade="all,delete")),
|
||||||
"_Item__attributes": relation(Attribute, cascade='all, delete, delete-orphan', collection_class=attribute_mapped_collection('name')),
|
"_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')),
|
"effects": relation(Effect, secondary=typeeffects_table, collection_class=attribute_mapped_collection('name')),
|
||||||
"metaGroup" : relation(MetaType,
|
"metaGroup" : relation(MetaGroup, backref=backref("items", cascade="all,delete")),
|
||||||
primaryjoin=metatypes_table.c.typeID == items_table.c.typeID,
|
"varParent" : relation(Item, backref=backref("varChildren", cascade="all,delete"), remote_side=items_table.c.typeID),
|
||||||
uselist=False),
|
|
||||||
"ID" : synonym("typeID"),
|
"ID" : synonym("typeID"),
|
||||||
"name" : synonym("typeName"),
|
"name" : synonym("typeName"),
|
||||||
"description" : deferred(items_table.c.description),
|
"description" : deferred(items_table.c.description),
|
||||||
@@ -63,7 +67,6 @@ mapper(Item, items_table,
|
|||||||
primaryjoin=dynamicApplicable_table.c.applicableTypeID == items_table.c.typeID,
|
primaryjoin=dynamicApplicable_table.c.applicableTypeID == items_table.c.typeID,
|
||||||
secondaryjoin=dynamicApplicable_table.c.typeID == DynamicItem.typeID,
|
secondaryjoin=dynamicApplicable_table.c.typeID == DynamicItem.typeID,
|
||||||
secondary=dynamicApplicable_table,
|
secondary=dynamicApplicable_table,
|
||||||
backref="applicableItems")
|
backref="applicableItems")})
|
||||||
})
|
|
||||||
|
|
||||||
Item.category = association_proxy("group", "category")
|
Item.category = association_proxy("group", "category")
|
||||||
|
|||||||
@@ -17,35 +17,17 @@
|
|||||||
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
||||||
# ===============================================================================
|
# ===============================================================================
|
||||||
|
|
||||||
from sqlalchemy import Table, Column, Integer, ForeignKey, String
|
from sqlalchemy import Table, Column, Integer, String
|
||||||
from sqlalchemy.ext.associationproxy import association_proxy
|
from sqlalchemy.orm import mapper, synonym
|
||||||
from sqlalchemy.orm import relation, mapper, synonym
|
|
||||||
|
|
||||||
from eos.db import gamedata_meta
|
from eos.db import gamedata_meta
|
||||||
from eos.db.gamedata.item import items_table
|
from eos.gamedata import MetaGroup
|
||||||
from eos.gamedata import Item, MetaGroup, MetaType
|
|
||||||
|
|
||||||
metagroups_table = Table("invmetagroups", gamedata_meta,
|
metagroups_table = Table("invmetagroups", gamedata_meta,
|
||||||
Column("metaGroupID", Integer, primary_key=True),
|
Column("metaGroupID", Integer, primary_key=True),
|
||||||
Column("metaGroupName", String))
|
Column("metaGroupName", String))
|
||||||
|
|
||||||
metatypes_table = Table("invmetatypes", gamedata_meta,
|
|
||||||
Column("typeID", Integer, ForeignKey("invtypes.typeID"), primary_key=True),
|
|
||||||
Column("parentTypeID", Integer, ForeignKey("invtypes.typeID")),
|
|
||||||
Column("metaGroupID", Integer, ForeignKey("invmetagroups.metaGroupID")))
|
|
||||||
|
|
||||||
mapper(MetaGroup, metagroups_table,
|
mapper(MetaGroup, metagroups_table,
|
||||||
properties={
|
properties={
|
||||||
"ID" : synonym("metaGroupID"),
|
"ID" : synonym("metaGroupID"),
|
||||||
"name": synonym("metaGroupName")
|
"name": synonym("metaGroupName")})
|
||||||
})
|
|
||||||
|
|
||||||
mapper(MetaType, metatypes_table,
|
|
||||||
properties={
|
|
||||||
"ID" : synonym("metaGroupID"),
|
|
||||||
"parent": relation(Item, primaryjoin=metatypes_table.c.parentTypeID == items_table.c.typeID),
|
|
||||||
"items" : relation(Item, primaryjoin=metatypes_table.c.typeID == items_table.c.typeID),
|
|
||||||
"info" : relation(MetaGroup, lazy=False)
|
|
||||||
})
|
|
||||||
|
|
||||||
MetaType.name = association_proxy("info", "name")
|
|
||||||
|
|||||||
@@ -17,16 +17,16 @@
|
|||||||
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
||||||
# ===============================================================================
|
# ===============================================================================
|
||||||
|
|
||||||
from sqlalchemy.orm import join, exc, aliased, joinedload, subqueryload
|
|
||||||
from sqlalchemy.sql import and_, or_, select
|
|
||||||
from sqlalchemy.inspection import inspect
|
from sqlalchemy.inspection import inspect
|
||||||
|
from sqlalchemy.orm import aliased, exc, join
|
||||||
|
from sqlalchemy.sql import and_, or_, select
|
||||||
|
|
||||||
import eos.config
|
import eos.config
|
||||||
from eos.db import gamedata_session
|
from eos.db import get_gamedata_session
|
||||||
from eos.db.gamedata.metaGroup import metatypes_table, items_table
|
from eos.db.gamedata.item import items_table
|
||||||
from eos.db.gamedata.group import groups_table
|
from eos.db.gamedata.group import groups_table
|
||||||
from eos.db.util import processEager, processWhere
|
from eos.db.util import processEager, processWhere
|
||||||
from eos.gamedata import AlphaClone, Attribute, Category, Group, Item, MarketGroup, MetaGroup, AttributeInfo, MetaData, DynamicItem
|
from eos.gamedata import AlphaClone, Attribute, AttributeInfo, Category, DynamicItem, Group, Item, MarketGroup, MetaData, MetaGroup, ImplantSet
|
||||||
|
|
||||||
cache = {}
|
cache = {}
|
||||||
configVal = getattr(eos.config, "gamedataCache", None)
|
configVal = getattr(eos.config, "gamedataCache", None)
|
||||||
@@ -64,7 +64,7 @@ else:
|
|||||||
return deco
|
return deco
|
||||||
|
|
||||||
|
|
||||||
def sqlizeString(line):
|
def sqlizeNormalString(line):
|
||||||
# Escape backslashes first, as they will be as escape symbol in queries
|
# Escape backslashes first, as they will be as escape symbol in queries
|
||||||
# Then escape percent and underscore signs
|
# Then escape percent and underscore signs
|
||||||
# Finally, replace generic wildcards with sql-style wildcards
|
# Finally, replace generic wildcards with sql-style wildcards
|
||||||
@@ -79,28 +79,39 @@ itemNameMap = {}
|
|||||||
def getItem(lookfor, eager=None):
|
def getItem(lookfor, eager=None):
|
||||||
if isinstance(lookfor, int):
|
if isinstance(lookfor, int):
|
||||||
if eager is None:
|
if eager is None:
|
||||||
item = gamedata_session.query(Item).get(lookfor)
|
item = get_gamedata_session().query(Item).get(lookfor)
|
||||||
else:
|
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):
|
elif isinstance(lookfor, str):
|
||||||
if lookfor in itemNameMap:
|
if lookfor in itemNameMap:
|
||||||
id = itemNameMap[lookfor]
|
id = itemNameMap[lookfor]
|
||||||
if eager is None:
|
if eager is None:
|
||||||
item = gamedata_session.query(Item).get(id)
|
item = get_gamedata_session().query(Item).get(id)
|
||||||
else:
|
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:
|
else:
|
||||||
# Item names are unique, so we can use first() instead of one()
|
# 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()
|
item = get_gamedata_session().query(Item).options(*processEager(eager)).filter(Item.name == lookfor).first()
|
||||||
itemNameMap[lookfor] = item.ID
|
if item is not None:
|
||||||
|
itemNameMap[lookfor] = item.ID
|
||||||
else:
|
else:
|
||||||
raise TypeError("Need integer or string as argument")
|
raise TypeError("Need integer or string as argument")
|
||||||
return item
|
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):
|
def getMutaplasmid(lookfor, eager=None):
|
||||||
if isinstance(lookfor, int):
|
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:
|
else:
|
||||||
raise TypeError("Need integer as argument")
|
raise TypeError("Need integer as argument")
|
||||||
return item
|
return item
|
||||||
@@ -108,7 +119,7 @@ def getMutaplasmid(lookfor, eager=None):
|
|||||||
|
|
||||||
def getItemWithBaseItemAttribute(lookfor, baseItemID, eager=None):
|
def getItemWithBaseItemAttribute(lookfor, baseItemID, eager=None):
|
||||||
# A lot of this is described in more detail in #1597
|
# 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)
|
base = getItem(baseItemID)
|
||||||
|
|
||||||
# we have to load all attributes for this object, otherwise we'll lose access to them when we expunge.
|
# 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.
|
# 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,
|
# 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
|
# we want to generate a completely new object to work with
|
||||||
gamedata_session.expunge(item)
|
get_gamedata_session().expunge(item)
|
||||||
return item
|
return item
|
||||||
|
|
||||||
|
|
||||||
@@ -147,7 +158,7 @@ def getItems(lookfor, eager=None):
|
|||||||
|
|
||||||
if len(toGet) > 0:
|
if len(toGet) > 0:
|
||||||
# Get items that aren't currently cached, and store them in the cache
|
# 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:
|
for item in items:
|
||||||
cache[(item.ID, None)] = item
|
cache[(item.ID, None)] = item
|
||||||
results += items
|
results += items
|
||||||
@@ -161,9 +172,9 @@ def getItems(lookfor, eager=None):
|
|||||||
def getAlphaClone(lookfor, eager=None):
|
def getAlphaClone(lookfor, eager=None):
|
||||||
if isinstance(lookfor, int):
|
if isinstance(lookfor, int):
|
||||||
if eager is None:
|
if eager is None:
|
||||||
item = gamedata_session.query(AlphaClone).get(lookfor)
|
item = get_gamedata_session().query(AlphaClone).get(lookfor)
|
||||||
else:
|
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:
|
else:
|
||||||
raise TypeError("Need integer as argument")
|
raise TypeError("Need integer as argument")
|
||||||
return item
|
return item
|
||||||
@@ -171,7 +182,7 @@ def getAlphaClone(lookfor, eager=None):
|
|||||||
|
|
||||||
def getAlphaCloneList(eager=None):
|
def getAlphaCloneList(eager=None):
|
||||||
eager = processEager(eager)
|
eager = processEager(eager)
|
||||||
clones = gamedata_session.query(AlphaClone).options(*eager).all()
|
clones = get_gamedata_session().query(AlphaClone).options(*eager).all()
|
||||||
return clones
|
return clones
|
||||||
|
|
||||||
|
|
||||||
@@ -182,20 +193,21 @@ groupNameMap = {}
|
|||||||
def getGroup(lookfor, eager=None):
|
def getGroup(lookfor, eager=None):
|
||||||
if isinstance(lookfor, int):
|
if isinstance(lookfor, int):
|
||||||
if eager is None:
|
if eager is None:
|
||||||
group = gamedata_session.query(Group).get(lookfor)
|
group = get_gamedata_session().query(Group).get(lookfor)
|
||||||
else:
|
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):
|
elif isinstance(lookfor, str):
|
||||||
if lookfor in groupNameMap:
|
if lookfor in groupNameMap:
|
||||||
id = groupNameMap[lookfor]
|
id = groupNameMap[lookfor]
|
||||||
if eager is None:
|
if eager is None:
|
||||||
group = gamedata_session.query(Group).get(id)
|
group = get_gamedata_session().query(Group).get(id)
|
||||||
else:
|
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:
|
else:
|
||||||
# Group names are unique, so we can use first() instead of one()
|
# 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()
|
group = get_gamedata_session().query(Group).options(*processEager(eager)).filter(Group.name == lookfor).first()
|
||||||
groupNameMap[lookfor] = group.ID
|
if group is not None:
|
||||||
|
groupNameMap[lookfor] = group.ID
|
||||||
else:
|
else:
|
||||||
raise TypeError("Need integer or string as argument")
|
raise TypeError("Need integer or string as argument")
|
||||||
return group
|
return group
|
||||||
@@ -208,23 +220,24 @@ categoryNameMap = {}
|
|||||||
def getCategory(lookfor, eager=None):
|
def getCategory(lookfor, eager=None):
|
||||||
if isinstance(lookfor, int):
|
if isinstance(lookfor, int):
|
||||||
if eager is None:
|
if eager is None:
|
||||||
category = gamedata_session.query(Category).get(lookfor)
|
category = get_gamedata_session().query(Category).get(lookfor)
|
||||||
else:
|
else:
|
||||||
category = gamedata_session.query(Category).options(*processEager(eager)).filter(
|
category = get_gamedata_session().query(Category).options(*processEager(eager)).filter(
|
||||||
Category.ID == lookfor).first()
|
Category.ID == lookfor).first()
|
||||||
elif isinstance(lookfor, str):
|
elif isinstance(lookfor, str):
|
||||||
if lookfor in categoryNameMap:
|
if lookfor in categoryNameMap:
|
||||||
id = categoryNameMap[lookfor]
|
id = categoryNameMap[lookfor]
|
||||||
if eager is None:
|
if eager is None:
|
||||||
category = gamedata_session.query(Category).get(id)
|
category = get_gamedata_session().query(Category).get(id)
|
||||||
else:
|
else:
|
||||||
category = gamedata_session.query(Category).options(*processEager(eager)).filter(
|
category = get_gamedata_session().query(Category).options(*processEager(eager)).filter(
|
||||||
Category.ID == id).first()
|
Category.ID == id).first()
|
||||||
else:
|
else:
|
||||||
# Category names are unique, so we can use first() instead of one()
|
# 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()
|
Category.name == lookfor).first()
|
||||||
categoryNameMap[lookfor] = category.ID
|
if category is not None:
|
||||||
|
categoryNameMap[lookfor] = category.ID
|
||||||
else:
|
else:
|
||||||
raise TypeError("Need integer or string as argument")
|
raise TypeError("Need integer or string as argument")
|
||||||
return category
|
return category
|
||||||
@@ -237,41 +250,55 @@ metaGroupNameMap = {}
|
|||||||
def getMetaGroup(lookfor, eager=None):
|
def getMetaGroup(lookfor, eager=None):
|
||||||
if isinstance(lookfor, int):
|
if isinstance(lookfor, int):
|
||||||
if eager is None:
|
if eager is None:
|
||||||
metaGroup = gamedata_session.query(MetaGroup).get(lookfor)
|
metaGroup = get_gamedata_session().query(MetaGroup).get(lookfor)
|
||||||
else:
|
else:
|
||||||
metaGroup = gamedata_session.query(MetaGroup).options(*processEager(eager)).filter(
|
metaGroup = get_gamedata_session().query(MetaGroup).options(*processEager(eager)).filter(
|
||||||
MetaGroup.ID == lookfor).first()
|
MetaGroup.ID == lookfor).first()
|
||||||
elif isinstance(lookfor, str):
|
elif isinstance(lookfor, str):
|
||||||
if lookfor in metaGroupNameMap:
|
if lookfor in metaGroupNameMap:
|
||||||
id = metaGroupNameMap[lookfor]
|
id = metaGroupNameMap[lookfor]
|
||||||
if eager is None:
|
if eager is None:
|
||||||
metaGroup = gamedata_session.query(MetaGroup).get(id)
|
metaGroup = get_gamedata_session().query(MetaGroup).get(id)
|
||||||
else:
|
else:
|
||||||
metaGroup = gamedata_session.query(MetaGroup).options(*processEager(eager)).filter(
|
metaGroup = get_gamedata_session().query(MetaGroup).options(*processEager(eager)).filter(
|
||||||
MetaGroup.ID == id).first()
|
MetaGroup.ID == id).first()
|
||||||
else:
|
else:
|
||||||
# MetaGroup names are unique, so we can use first() instead of one()
|
# MetaGroup names are unique, so we can use first() instead of one()
|
||||||
metaGroup = gamedata_session.query(MetaGroup).options(*processEager(eager)).filter(
|
metaGroup = get_gamedata_session().query(MetaGroup).options(*processEager(eager)).filter(
|
||||||
MetaGroup.name == lookfor).first()
|
MetaGroup.name == lookfor).first()
|
||||||
metaGroupNameMap[lookfor] = metaGroup.ID
|
if metaGroup is not None:
|
||||||
|
metaGroupNameMap[lookfor] = metaGroup.ID
|
||||||
else:
|
else:
|
||||||
raise TypeError("Need integer or string as argument")
|
raise TypeError("Need integer or string as argument")
|
||||||
return metaGroup
|
return metaGroup
|
||||||
|
|
||||||
|
|
||||||
|
def getMetaGroups():
|
||||||
|
return get_gamedata_session().query(MetaGroup).all()
|
||||||
|
|
||||||
|
|
||||||
@cachedQuery(1, "lookfor")
|
@cachedQuery(1, "lookfor")
|
||||||
def getMarketGroup(lookfor, eager=None):
|
def getMarketGroup(lookfor, eager=None):
|
||||||
if isinstance(lookfor, int):
|
if isinstance(lookfor, int):
|
||||||
if eager is None:
|
if eager is None:
|
||||||
marketGroup = gamedata_session.query(MarketGroup).get(lookfor)
|
marketGroup = get_gamedata_session().query(MarketGroup).get(lookfor)
|
||||||
else:
|
else:
|
||||||
marketGroup = gamedata_session.query(MarketGroup).options(*processEager(eager)).filter(
|
marketGroup = get_gamedata_session().query(MarketGroup).options(*processEager(eager)).filter(
|
||||||
MarketGroup.ID == lookfor).first()
|
MarketGroup.ID == lookfor).first()
|
||||||
else:
|
else:
|
||||||
raise TypeError("Need integer as argument")
|
raise TypeError("Need integer as argument")
|
||||||
return marketGroup
|
return marketGroup
|
||||||
|
|
||||||
|
|
||||||
|
def getMarketTreeNodeIds(rootNodeIds):
|
||||||
|
allIds = set()
|
||||||
|
addedIds = set(rootNodeIds)
|
||||||
|
while addedIds:
|
||||||
|
allIds.update(addedIds)
|
||||||
|
addedIds = {mg.ID for mg in get_gamedata_session().query(MarketGroup).filter(MarketGroup.parentGroupID.in_(addedIds))}
|
||||||
|
return allIds
|
||||||
|
|
||||||
|
|
||||||
@cachedQuery(2, "where", "filter")
|
@cachedQuery(2, "where", "filter")
|
||||||
def getItemsByCategory(filter, where=None, eager=None):
|
def getItemsByCategory(filter, where=None, eager=None):
|
||||||
if isinstance(filter, int):
|
if isinstance(filter, int):
|
||||||
@@ -282,7 +309,7 @@ def getItemsByCategory(filter, where=None, eager=None):
|
|||||||
raise TypeError("Need integer or string as argument")
|
raise TypeError("Need integer or string as argument")
|
||||||
|
|
||||||
filter = processWhere(filter, where)
|
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()
|
filter).all()
|
||||||
|
|
||||||
|
|
||||||
@@ -297,9 +324,9 @@ def searchItems(nameLike, where=None, join=None, eager=None):
|
|||||||
if not hasattr(join, "__iter__"):
|
if not hasattr(join, "__iter__"):
|
||||||
join = (join,)
|
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(' '):
|
for token in nameLike.split(' '):
|
||||||
token_safe = "%{0}%".format(sqlizeString(token))
|
token_safe = "%{0}%".format(sqlizeNormalString(token))
|
||||||
if where is not None:
|
if where is not None:
|
||||||
items = items.filter(and_(Item.name.like(token_safe, escape="\\"), where))
|
items = items.filter(and_(Item.name.like(token_safe, escape="\\"), where))
|
||||||
else:
|
else:
|
||||||
@@ -308,14 +335,35 @@ def searchItems(nameLike, where=None, join=None, eager=None):
|
|||||||
return items
|
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")
|
@cachedQuery(3, "where", "nameLike", "join")
|
||||||
def searchSkills(nameLike, where=None, eager=None):
|
def searchSkills(nameLike, where=None, eager=None):
|
||||||
if not isinstance(nameLike, str):
|
if not isinstance(nameLike, str):
|
||||||
raise TypeError("Need string as argument")
|
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(' '):
|
for token in nameLike.split(' '):
|
||||||
token_safe = "%{0}%".format(sqlizeString(token))
|
token_safe = "%{0}%".format(sqlizeNormalString(token))
|
||||||
if where is not None:
|
if where is not None:
|
||||||
items = items.filter(and_(Item.name.like(token_safe, escape="\\"), Category.ID == 16, where))
|
items = items.filter(and_(Item.name.like(token_safe, escape="\\"), Category.ID == 16, where))
|
||||||
else:
|
else:
|
||||||
@@ -333,11 +381,9 @@ def getVariations(itemids, groupIDs=None, where=None, eager=None):
|
|||||||
if len(itemids) == 0:
|
if len(itemids) == 0:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
itemfilter = or_(*(metatypes_table.c.parentTypeID == itemid for itemid in itemids))
|
itemfilter = or_(*(items_table.c.variationParentTypeID == itemid for itemid in itemids))
|
||||||
filter = processWhere(itemfilter, where)
|
filter = processWhere(itemfilter, where)
|
||||||
joinon = items_table.c.typeID == metatypes_table.c.typeID
|
vars = get_gamedata_session().query(Item).options(*processEager(eager)).filter(filter).all()
|
||||||
vars = gamedata_session.query(Item).options(*processEager(eager)).join((metatypes_table, joinon)).filter(
|
|
||||||
filter).all()
|
|
||||||
|
|
||||||
if vars:
|
if vars:
|
||||||
return vars
|
return vars
|
||||||
@@ -345,7 +391,7 @@ def getVariations(itemids, groupIDs=None, where=None, eager=None):
|
|||||||
itemfilter = or_(*(groups_table.c.groupID == groupID for groupID in groupIDs))
|
itemfilter = or_(*(groups_table.c.groupID == groupID for groupID in groupIDs))
|
||||||
filter = processWhere(itemfilter, where)
|
filter = processWhere(itemfilter, where)
|
||||||
joinon = items_table.c.groupID == groups_table.c.groupID
|
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()
|
filter).all()
|
||||||
|
|
||||||
return vars
|
return vars
|
||||||
@@ -360,7 +406,7 @@ def getAttributeInfo(attr, eager=None):
|
|||||||
else:
|
else:
|
||||||
raise TypeError("Need integer or string as argument")
|
raise TypeError("Need integer or string as argument")
|
||||||
try:
|
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:
|
except exc.NoResultFound:
|
||||||
result = None
|
result = None
|
||||||
return result
|
return result
|
||||||
@@ -369,7 +415,7 @@ def getAttributeInfo(attr, eager=None):
|
|||||||
@cachedQuery(1, "field")
|
@cachedQuery(1, "field")
|
||||||
def getMetaData(field):
|
def getMetaData(field):
|
||||||
if isinstance(field, str):
|
if isinstance(field, str):
|
||||||
data = gamedata_session.query(MetaData).get(field)
|
data = get_gamedata_session().query(MetaData).get(field)
|
||||||
else:
|
else:
|
||||||
raise TypeError("Need string as argument")
|
raise TypeError("Need string as argument")
|
||||||
return data
|
return data
|
||||||
@@ -388,31 +434,30 @@ def directAttributeRequest(itemIDs, attrIDs):
|
|||||||
and_(Attribute.attributeID.in_(attrIDs), Item.typeID.in_(itemIDs)),
|
and_(Attribute.attributeID.in_(attrIDs), Item.typeID.in_(itemIDs)),
|
||||||
from_obj=[join(Attribute, Item)])
|
from_obj=[join(Attribute, Item)])
|
||||||
|
|
||||||
result = gamedata_session.execute(q).fetchall()
|
result = get_gamedata_session().execute(q).fetchall()
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def getAbyssalTypes():
|
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()])
|
||||||
|
|
||||||
|
|
||||||
def getRequiredFor(itemID, attrMapping):
|
@cachedQuery(1, "itemID")
|
||||||
Attribute1 = aliased(Attribute)
|
def getDynamicItem(itemID, eager=None):
|
||||||
Attribute2 = aliased(Attribute)
|
try:
|
||||||
|
if isinstance(itemID, int):
|
||||||
skillToLevelClauses = []
|
if eager is None:
|
||||||
|
result = get_gamedata_session().query(DynamicItem).filter(DynamicItem.ID == itemID).one()
|
||||||
for attrSkill, attrLevel in attrMapping.items():
|
else:
|
||||||
skillToLevelClauses.append(and_(Attribute1.attributeID == attrSkill, Attribute2.attributeID == attrLevel))
|
result = get_gamedata_session().query(DynamicItem).options(*processEager(eager)).filter(DynamicItem.ID == itemID).one()
|
||||||
|
else:
|
||||||
queryOr = or_(*skillToLevelClauses)
|
raise TypeError("Need integer as argument")
|
||||||
|
except exc.NoResultFound:
|
||||||
q = select((Attribute2.typeID, Attribute2.value),
|
result = None
|
||||||
and_(Attribute1.value == itemID, queryOr),
|
|
||||||
from_obj=[
|
|
||||||
join(Attribute1, Attribute2, Attribute1.typeID == Attribute2.typeID)
|
|
||||||
])
|
|
||||||
|
|
||||||
result = gamedata_session.execute(q).fetchall()
|
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
@cachedQuery(1, "lookfor")
|
||||||
|
def getAllImplantSets():
|
||||||
|
implantSets = get_gamedata_session().query(ImplantSet).all()
|
||||||
|
return implantSets
|
||||||
|
|||||||
@@ -8,43 +8,25 @@ many upgrade files as there are database versions (version 5 would include
|
|||||||
upgrade files 1-5)
|
upgrade files 1-5)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import pkgutil
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
from eos.utils.pyinst_support import iterNamespace
|
||||||
|
|
||||||
updates = {}
|
updates = {}
|
||||||
appVersion = 0
|
appVersion = 0
|
||||||
|
|
||||||
prefix = __name__ + "."
|
prefix = __name__ + "."
|
||||||
|
|
||||||
# load modules to work based with and without pyinstaller
|
for modName in iterNamespace(__name__, __path__):
|
||||||
# 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:
|
|
||||||
# loop through python files, extracting update number and function, and
|
# loop through python files, extracting update number and function, and
|
||||||
# adding it to a list
|
# adding it to a list
|
||||||
modname_tail = modname.rsplit('.', 1)[-1]
|
modname_tail = modName.rsplit('.', 1)[-1]
|
||||||
module = __import__(modname, fromlist=True)
|
|
||||||
m = re.match("^upgrade(?P<index>\d+)$", modname_tail)
|
m = re.match("^upgrade(?P<index>\d+)$", modname_tail)
|
||||||
if not m:
|
if not m:
|
||||||
continue
|
continue
|
||||||
index = int(m.group("index"))
|
index = int(m.group("index"))
|
||||||
appVersion = max(appVersion, index)
|
appVersion = max(appVersion, index)
|
||||||
|
module = __import__(modName, fromlist=True)
|
||||||
upgrade = getattr(module, "upgrade", False)
|
upgrade = getattr(module, "upgrade", False)
|
||||||
if upgrade:
|
if upgrade:
|
||||||
updates[index] = upgrade
|
updates[index] = upgrade
|
||||||
|
|||||||
@@ -33,9 +33,13 @@ def upgrade(saveddata_engine):
|
|||||||
try:
|
try:
|
||||||
saveddata_session.execute(commandFits_table.insert(),
|
saveddata_session.execute(commandFits_table.insert(),
|
||||||
{"boosterID": value, "boostedID": boosted, "active": 1})
|
{"boosterID": value, "boostedID": boosted, "active": 1})
|
||||||
|
except (KeyboardInterrupt, SystemExit):
|
||||||
|
raise
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
saveddata_session.commit()
|
saveddata_session.commit()
|
||||||
|
except (KeyboardInterrupt, SystemExit):
|
||||||
|
raise
|
||||||
except:
|
except:
|
||||||
# Shouldn't fail unless you have updated database without the old fleet schema and manually modify the database version
|
# 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
|
# If it does, simply fail. Fleet data migration isn't critically important here
|
||||||
|
|||||||
@@ -4235,6 +4235,8 @@ def upgrade(saveddata_engine):
|
|||||||
|
|
||||||
# And last but not least, delete the last subsystem
|
# And last but not least, delete the last subsystem
|
||||||
saveddata_engine.execute("DELETE FROM modules WHERE ID = ?", (oldModules[4][0],))
|
saveddata_engine.execute("DELETE FROM modules WHERE ID = ?", (oldModules[4][0],))
|
||||||
|
except (KeyboardInterrupt, SystemExit):
|
||||||
|
raise
|
||||||
except:
|
except:
|
||||||
# if something fails, fuck it, we tried. It'll default to the generic conversion below
|
# if something fails, fuck it, we tried. It'll default to the generic conversion below
|
||||||
continue
|
continue
|
||||||
|
|||||||
18
eos/db/migrations/upgrade29.py
Normal file
18
eos/db/migrations/upgrade29.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
"""
|
||||||
|
Migration 29
|
||||||
|
|
||||||
|
- adds spoolType and spoolAmount to modules table
|
||||||
|
"""
|
||||||
|
import sqlalchemy
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade(saveddata_engine):
|
||||||
|
try:
|
||||||
|
saveddata_engine.execute("SELECT spoolType FROM modules LIMIT 1")
|
||||||
|
except sqlalchemy.exc.DatabaseError:
|
||||||
|
saveddata_engine.execute("ALTER TABLE modules ADD COLUMN spoolType INT;")
|
||||||
|
|
||||||
|
try:
|
||||||
|
saveddata_engine.execute("SELECT spoolAmount FROM modules LIMIT 1")
|
||||||
|
except sqlalchemy.exc.DatabaseError:
|
||||||
|
saveddata_engine.execute("ALTER TABLE modules ADD COLUMN spoolAmount FLOAT;")
|
||||||
17
eos/db/migrations/upgrade30.py
Normal file
17
eos/db/migrations/upgrade30.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
"""
|
||||||
|
Migration 30
|
||||||
|
|
||||||
|
- changes to prices table
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
import sqlalchemy
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade(saveddata_engine):
|
||||||
|
try:
|
||||||
|
saveddata_engine.execute("SELECT status FROM prices LIMIT 1")
|
||||||
|
except sqlalchemy.exc.DatabaseError:
|
||||||
|
# Just drop table, table will be re-created by sqlalchemy and
|
||||||
|
# data will be re-fetched
|
||||||
|
saveddata_engine.execute("DROP TABLE prices;")
|
||||||
15
eos/db/migrations/upgrade31.py
Normal file
15
eos/db/migrations/upgrade31.py
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
"""
|
||||||
|
Migration 31
|
||||||
|
|
||||||
|
- added fit system security column
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
import sqlalchemy
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade(saveddata_engine):
|
||||||
|
try:
|
||||||
|
saveddata_engine.execute("SELECT systemSecurity FROM fits LIMIT 1")
|
||||||
|
except sqlalchemy.exc.DatabaseError:
|
||||||
|
saveddata_engine.execute("ALTER TABLE fits ADD COLUMN systemSecurity INT")
|
||||||
16
eos/db/migrations/upgrade32.py
Normal file
16
eos/db/migrations/upgrade32.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
"""
|
||||||
|
Migration 32
|
||||||
|
|
||||||
|
- added speed, sig and radius columns to targetResists table
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
import sqlalchemy
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade(saveddata_engine):
|
||||||
|
for column in ('maxVelocity', 'signatureRadius', 'radius'):
|
||||||
|
try:
|
||||||
|
saveddata_engine.execute("SELECT {} FROM targetResists LIMIT 1;".format(column))
|
||||||
|
except sqlalchemy.exc.DatabaseError:
|
||||||
|
saveddata_engine.execute("ALTER TABLE targetResists ADD COLUMN {} FLOAT;".format(column))
|
||||||
30
eos/db/migrations/upgrade33.py
Normal file
30
eos/db/migrations/upgrade33.py
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
"""
|
||||||
|
Migration 33
|
||||||
|
|
||||||
|
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"),
|
||||||
|
FOREIGN KEY("ownerID") REFERENCES users ("ID")
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade(saveddata_engine):
|
||||||
|
saveddata_engine.execute(tmpTable)
|
||||||
|
saveddata_engine.execute(
|
||||||
|
'INSERT INTO damagePatternsTemp (ID, name, emAmount, thermalAmount, kineticAmount, explosiveAmount, ownerID, created, modified) '
|
||||||
|
'SELECT ID, name, emAmount, thermalAmount, kineticAmount, explosiveAmount, ownerID, created, modified FROM damagePatterns')
|
||||||
|
saveddata_engine.execute('DROP TABLE damagePatterns')
|
||||||
|
saveddata_engine.execute('ALTER TABLE damagePatternsTemp RENAME TO damagePatterns')
|
||||||
25
eos/db/migrations/upgrade34.py
Normal file
25
eos/db/migrations/upgrade34.py
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
"""
|
||||||
|
Migration 34
|
||||||
|
|
||||||
|
- Adds projection range columns to projectable entities
|
||||||
|
"""
|
||||||
|
import sqlalchemy
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade(saveddata_engine):
|
||||||
|
try:
|
||||||
|
saveddata_engine.execute("SELECT projectionRange FROM projectedFits LIMIT 1")
|
||||||
|
except sqlalchemy.exc.DatabaseError:
|
||||||
|
saveddata_engine.execute("ALTER TABLE projectedFits ADD COLUMN projectionRange FLOAT;")
|
||||||
|
try:
|
||||||
|
saveddata_engine.execute("SELECT projectionRange FROM modules LIMIT 1")
|
||||||
|
except sqlalchemy.exc.DatabaseError:
|
||||||
|
saveddata_engine.execute("ALTER TABLE modules ADD COLUMN projectionRange FLOAT;")
|
||||||
|
try:
|
||||||
|
saveddata_engine.execute("SELECT projectionRange FROM drones LIMIT 1")
|
||||||
|
except sqlalchemy.exc.DatabaseError:
|
||||||
|
saveddata_engine.execute("ALTER TABLE drones ADD COLUMN projectionRange FLOAT;")
|
||||||
|
try:
|
||||||
|
saveddata_engine.execute("SELECT projectionRange FROM fighters LIMIT 1")
|
||||||
|
except sqlalchemy.exc.DatabaseError:
|
||||||
|
saveddata_engine.execute("ALTER TABLE fighters ADD COLUMN projectionRange FLOAT;")
|
||||||
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))
|
||||||
@@ -11,8 +11,7 @@ __all__ = [
|
|||||||
"implant",
|
"implant",
|
||||||
"damagePattern",
|
"damagePattern",
|
||||||
"miscData",
|
"miscData",
|
||||||
"targetResists",
|
"targetProfile",
|
||||||
"override",
|
"override",
|
||||||
"implantSet",
|
"implantSet"
|
||||||
"loadDefaultDatabaseValues"
|
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import datetime
|
|||||||
|
|
||||||
from eos.db import saveddata_meta
|
from eos.db import saveddata_meta
|
||||||
from eos.db.saveddata.implant import charImplants_table
|
from eos.db.saveddata.implant import charImplants_table
|
||||||
from eos.effectHandlerHelpers import HandledImplantBoosterList, HandledSsoCharacterList
|
from eos.effectHandlerHelpers import HandledImplantList, HandledSsoCharacterList
|
||||||
from eos.saveddata.implant import Implant
|
from eos.saveddata.implant import Implant
|
||||||
from eos.saveddata.user import User
|
from eos.saveddata.user import User
|
||||||
from eos.saveddata.character import Character, Skill
|
from eos.saveddata.character import Character, Skill
|
||||||
@@ -75,7 +75,7 @@ mapper(Character, characters_table,
|
|||||||
cascade="all,delete-orphan"),
|
cascade="all,delete-orphan"),
|
||||||
"_Character__implants" : relation(
|
"_Character__implants" : relation(
|
||||||
Implant,
|
Implant,
|
||||||
collection_class=HandledImplantBoosterList,
|
collection_class=HandledImplantList,
|
||||||
cascade='all,delete-orphan',
|
cascade='all,delete-orphan',
|
||||||
backref='character',
|
backref='character',
|
||||||
single_parent=True,
|
single_parent=True,
|
||||||
|
|||||||
@@ -17,23 +17,27 @@
|
|||||||
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
||||||
# ===============================================================================
|
# ===============================================================================
|
||||||
|
|
||||||
from sqlalchemy import Table, Column, Integer, ForeignKey, String, DateTime
|
from sqlalchemy import Table, Column, Integer, Float, ForeignKey, String, DateTime
|
||||||
from sqlalchemy.orm import mapper
|
from sqlalchemy.orm import mapper
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from eos.db import saveddata_meta
|
from eos.db import saveddata_meta
|
||||||
from eos.saveddata.damagePattern import DamagePattern
|
from eos.saveddata.damagePattern import DamagePattern
|
||||||
|
|
||||||
damagePatterns_table = Table("damagePatterns", saveddata_meta,
|
damagePatterns_table = Table(
|
||||||
Column("ID", Integer, primary_key=True),
|
'damagePatterns',
|
||||||
Column("name", String),
|
saveddata_meta,
|
||||||
Column("emAmount", Integer),
|
Column('ID', Integer, primary_key=True),
|
||||||
Column("thermalAmount", Integer),
|
Column('name', String),
|
||||||
Column("kineticAmount", Integer),
|
Column('emAmount', Float),
|
||||||
Column("explosiveAmount", Integer),
|
Column('thermalAmount', Float),
|
||||||
Column("ownerID", ForeignKey("users.ID"), nullable=True),
|
Column('kineticAmount', Float),
|
||||||
Column("created", DateTime, nullable=True, default=datetime.datetime.now),
|
Column('explosiveAmount', Float),
|
||||||
Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now)
|
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})
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ from logbook import Logger
|
|||||||
pyfalog = Logger(__name__)
|
pyfalog = Logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class DatabaseCleanup(object):
|
class DatabaseCleanup:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
||||||
# ===============================================================================
|
# ===============================================================================
|
||||||
|
|
||||||
from sqlalchemy import Table, Column, Integer, ForeignKey, Boolean, DateTime
|
from sqlalchemy import Table, Column, Integer, Float, ForeignKey, Boolean, DateTime
|
||||||
from sqlalchemy.orm import mapper, relation
|
from sqlalchemy.orm import mapper, relation
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
@@ -33,7 +33,8 @@ drones_table = Table("drones", saveddata_meta,
|
|||||||
Column("amountActive", Integer, nullable=False),
|
Column("amountActive", Integer, nullable=False),
|
||||||
Column("projected", Boolean, default=False),
|
Column("projected", Boolean, default=False),
|
||||||
Column("created", DateTime, nullable=True, default=datetime.datetime.now),
|
Column("created", DateTime, nullable=True, default=datetime.datetime.now),
|
||||||
Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now)
|
Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now),
|
||||||
|
Column("projectionRange", Float, nullable=True)
|
||||||
)
|
)
|
||||||
|
|
||||||
mapper(Drone, drones_table,
|
mapper(Drone, drones_table,
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
||||||
# ===============================================================================
|
# ===============================================================================
|
||||||
|
|
||||||
from sqlalchemy import Table, Column, Integer, ForeignKey, Boolean, DateTime
|
from sqlalchemy import Table, Column, Integer, Float, ForeignKey, Boolean, DateTime
|
||||||
from sqlalchemy.orm import mapper, relation
|
from sqlalchemy.orm import mapper, relation
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
@@ -34,7 +34,8 @@ fighters_table = Table("fighters", saveddata_meta,
|
|||||||
Column("amount", Integer, nullable=False),
|
Column("amount", Integer, nullable=False),
|
||||||
Column("projected", Boolean, default=False),
|
Column("projected", Boolean, default=False),
|
||||||
Column("created", DateTime, nullable=True, default=datetime.datetime.now),
|
Column("created", DateTime, nullable=True, default=datetime.datetime.now),
|
||||||
Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now)
|
Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now),
|
||||||
|
Column("projectionRange", Float, nullable=True),
|
||||||
)
|
)
|
||||||
|
|
||||||
fighter_abilities_table = Table("fightersAbilities", saveddata_meta,
|
fighter_abilities_table = Table("fightersAbilities", saveddata_meta,
|
||||||
@@ -46,6 +47,7 @@ fighter_abilities_table = Table("fightersAbilities", saveddata_meta,
|
|||||||
mapper(Fighter, fighters_table,
|
mapper(Fighter, fighters_table,
|
||||||
properties={
|
properties={
|
||||||
"owner" : relation(Fit),
|
"owner" : relation(Fit),
|
||||||
|
"_amount" : fighters_table.c.amount,
|
||||||
"_Fighter__abilities": relation(
|
"_Fighter__abilities": relation(
|
||||||
FighterAbility,
|
FighterAbility,
|
||||||
backref="fighter",
|
backref="fighter",
|
||||||
|
|||||||
@@ -17,33 +17,33 @@
|
|||||||
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
||||||
# ===============================================================================
|
# ===============================================================================
|
||||||
|
|
||||||
from sqlalchemy.ext.associationproxy import association_proxy
|
|
||||||
from sqlalchemy.orm.collections import attribute_mapped_collection
|
|
||||||
from sqlalchemy.sql import and_
|
|
||||||
from sqlalchemy.orm import relation, reconstructor, mapper, relationship
|
|
||||||
from sqlalchemy import ForeignKey, Column, Integer, String, Table, Boolean, DateTime
|
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from eos.db import saveddata_meta
|
from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer, Float, String, Table
|
||||||
from eos.db import saveddata_session
|
from sqlalchemy.ext.associationproxy import association_proxy
|
||||||
|
from sqlalchemy.orm import mapper, reconstructor, relation, relationship
|
||||||
|
from sqlalchemy.orm.collections import attribute_mapped_collection
|
||||||
|
from sqlalchemy.sql import and_
|
||||||
|
|
||||||
|
from eos.db import saveddata_meta, saveddata_session
|
||||||
from eos.db.saveddata.cargo import cargo_table
|
from eos.db.saveddata.cargo import cargo_table
|
||||||
from eos.db.saveddata.drone import drones_table
|
from eos.db.saveddata.drone import drones_table
|
||||||
from eos.db.saveddata.fighter import fighters_table
|
from eos.db.saveddata.fighter import fighters_table
|
||||||
from eos.db.saveddata.implant import fitImplants_table
|
from eos.db.saveddata.implant import fitImplants_table
|
||||||
from eos.db.saveddata.module import modules_table
|
from eos.db.saveddata.module import modules_table
|
||||||
from eos.effectHandlerHelpers import HandledModuleList, HandledImplantBoosterList, HandledProjectedModList, \
|
from eos.effectHandlerHelpers import HandledDroneCargoList, HandledImplantList, HandledBoosterList, HandledModuleList, HandledProjectedDroneList, HandledProjectedModList
|
||||||
HandledDroneCargoList, HandledProjectedDroneList
|
|
||||||
from eos.saveddata.implant import Implant
|
|
||||||
from eos.saveddata.character import Character
|
|
||||||
from eos.saveddata.user import User
|
|
||||||
from eos.saveddata.fighter import Fighter
|
|
||||||
from eos.saveddata.fit import Fit as es_Fit, ImplantLocation
|
|
||||||
from eos.saveddata.drone import Drone
|
|
||||||
from eos.saveddata.booster import Booster
|
from eos.saveddata.booster import Booster
|
||||||
from eos.saveddata.module import Module
|
|
||||||
from eos.saveddata.cargo import Cargo
|
from eos.saveddata.cargo import Cargo
|
||||||
|
from eos.saveddata.character import Character
|
||||||
from eos.saveddata.damagePattern import DamagePattern
|
from eos.saveddata.damagePattern import DamagePattern
|
||||||
from eos.saveddata.targetResists import TargetResists
|
from eos.saveddata.drone import Drone
|
||||||
|
from eos.saveddata.fighter import Fighter
|
||||||
|
from eos.saveddata.fit import Fit as es_Fit
|
||||||
|
from eos.saveddata.implant import Implant
|
||||||
|
from eos.saveddata.module import Module
|
||||||
|
from eos.saveddata.targetProfile import TargetProfile
|
||||||
|
from eos.saveddata.user import User
|
||||||
|
|
||||||
|
|
||||||
fits_table = Table("fits", saveddata_meta,
|
fits_table = Table("fits", saveddata_meta,
|
||||||
Column("ID", Integer, primary_key=True),
|
Column("ID", Integer, primary_key=True),
|
||||||
@@ -53,14 +53,17 @@ fits_table = Table("fits", saveddata_meta,
|
|||||||
Column("timestamp", Integer, nullable=False),
|
Column("timestamp", Integer, nullable=False),
|
||||||
Column("characterID", ForeignKey("characters.ID"), nullable=True),
|
Column("characterID", ForeignKey("characters.ID"), nullable=True),
|
||||||
Column("damagePatternID", ForeignKey("damagePatterns.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("booster", Boolean, nullable=False, index=True, default=0),
|
||||||
Column("targetResistsID", ForeignKey("targetResists.ID"), nullable=True),
|
Column("targetResistsID", ForeignKey("targetResists.ID"), nullable=True),
|
||||||
|
Column("builtinTargetResistsID", Integer, nullable=True),
|
||||||
Column("modeID", Integer, nullable=True),
|
Column("modeID", Integer, nullable=True),
|
||||||
Column("implantLocation", Integer, nullable=False),
|
Column("implantLocation", Integer, nullable=False),
|
||||||
Column("notes", String, nullable=True),
|
Column("notes", String, nullable=True),
|
||||||
Column("ignoreRestrictions", Boolean, default=0),
|
Column("ignoreRestrictions", Boolean, default=0),
|
||||||
Column("created", DateTime, nullable=True, default=datetime.datetime.now),
|
Column("created", DateTime, nullable=True, default=datetime.datetime.now),
|
||||||
Column("modified", DateTime, nullable=True, default=datetime.datetime.now, onupdate=datetime.datetime.now)
|
Column("modified", DateTime, nullable=True, default=datetime.datetime.now, onupdate=datetime.datetime.now),
|
||||||
|
Column("systemSecurity", Integer, nullable=True)
|
||||||
)
|
)
|
||||||
|
|
||||||
projectedFits_table = Table("projectedFits", saveddata_meta,
|
projectedFits_table = Table("projectedFits", saveddata_meta,
|
||||||
@@ -69,7 +72,8 @@ projectedFits_table = Table("projectedFits", saveddata_meta,
|
|||||||
Column("amount", Integer, nullable=False, default=1),
|
Column("amount", Integer, nullable=False, default=1),
|
||||||
Column("active", Boolean, nullable=False, default=1),
|
Column("active", Boolean, nullable=False, default=1),
|
||||||
Column("created", DateTime, nullable=True, default=datetime.datetime.now),
|
Column("created", DateTime, nullable=True, default=datetime.datetime.now),
|
||||||
Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now)
|
Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now),
|
||||||
|
Column("projectionRange", Float, nullable=True),
|
||||||
)
|
)
|
||||||
|
|
||||||
commandFits_table = Table("commandFits", saveddata_meta,
|
commandFits_table = Table("commandFits", saveddata_meta,
|
||||||
@@ -81,7 +85,8 @@ commandFits_table = Table("commandFits", saveddata_meta,
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ProjectedFit(object):
|
class ProjectedFit:
|
||||||
|
|
||||||
def __init__(self, sourceID, source_fit, amount=1, active=True):
|
def __init__(self, sourceID, source_fit, amount=1, active=True):
|
||||||
self.sourceID = sourceID
|
self.sourceID = sourceID
|
||||||
self.source_fit = source_fit
|
self.source_fit = source_fit
|
||||||
@@ -112,7 +117,7 @@ class ProjectedFit(object):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class CommandFit(object):
|
class CommandFit:
|
||||||
def __init__(self, boosterID, booster_fit, active=True):
|
def __init__(self, boosterID, booster_fit, active=True):
|
||||||
self.boosterID = boosterID
|
self.boosterID = boosterID
|
||||||
self.booster_fit = booster_fit
|
self.booster_fit = booster_fit
|
||||||
@@ -184,7 +189,7 @@ mapper(es_Fit, fits_table,
|
|||||||
"shipID": fits_table.c.shipID,
|
"shipID": fits_table.c.shipID,
|
||||||
"_Fit__boosters": relation(
|
"_Fit__boosters": relation(
|
||||||
Booster,
|
Booster,
|
||||||
collection_class=HandledImplantBoosterList,
|
collection_class=HandledBoosterList,
|
||||||
cascade='all, delete, delete-orphan',
|
cascade='all, delete, delete-orphan',
|
||||||
backref='owner',
|
backref='owner',
|
||||||
single_parent=True),
|
single_parent=True),
|
||||||
@@ -220,7 +225,7 @@ mapper(es_Fit, fits_table,
|
|||||||
primaryjoin=and_(fighters_table.c.fitID == fits_table.c.ID, fighters_table.c.projected == True)), # noqa
|
primaryjoin=and_(fighters_table.c.fitID == fits_table.c.ID, fighters_table.c.projected == True)), # noqa
|
||||||
"_Fit__implants": relation(
|
"_Fit__implants": relation(
|
||||||
Implant,
|
Implant,
|
||||||
collection_class=HandledImplantBoosterList,
|
collection_class=HandledImplantList,
|
||||||
cascade='all, delete, delete-orphan',
|
cascade='all, delete, delete-orphan',
|
||||||
backref='owner',
|
backref='owner',
|
||||||
single_parent=True,
|
single_parent=True,
|
||||||
@@ -230,8 +235,10 @@ mapper(es_Fit, fits_table,
|
|||||||
"_Fit__character": relation(
|
"_Fit__character": relation(
|
||||||
Character,
|
Character,
|
||||||
backref="fits"),
|
backref="fits"),
|
||||||
"_Fit__damagePattern": relation(DamagePattern),
|
"_Fit__userDamagePattern": relation(DamagePattern),
|
||||||
"_Fit__targetResists": relation(TargetResists),
|
"_Fit__builtinDamagePatternID": fits_table.c.builtinDamagePatternID,
|
||||||
|
"_Fit__userTargetProfile": relation(TargetProfile),
|
||||||
|
"_Fit__builtinTargetProfileID": fits_table.c.builtinTargetResistsID,
|
||||||
"projectedOnto": projectedFitSourceRel,
|
"projectedOnto": projectedFitSourceRel,
|
||||||
"victimOf": relationship(
|
"victimOf": relationship(
|
||||||
ProjectedFit,
|
ProjectedFit,
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import datetime
|
|||||||
|
|
||||||
from eos.db import saveddata_meta
|
from eos.db import saveddata_meta
|
||||||
from eos.db.saveddata.implant import implantsSetMap_table
|
from eos.db.saveddata.implant import implantsSetMap_table
|
||||||
from eos.effectHandlerHelpers import HandledImplantBoosterList
|
from eos.effectHandlerHelpers import HandledImplantList
|
||||||
from eos.saveddata.implant import Implant
|
from eos.saveddata.implant import Implant
|
||||||
from eos.saveddata.implantSet import ImplantSet
|
from eos.saveddata.implantSet import ImplantSet
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ mapper(ImplantSet, implant_set_table,
|
|||||||
properties={
|
properties={
|
||||||
"_ImplantSet__implants": relation(
|
"_ImplantSet__implants": relation(
|
||||||
Implant,
|
Implant,
|
||||||
collection_class=HandledImplantBoosterList,
|
collection_class=HandledImplantList,
|
||||||
cascade='all, delete, delete-orphan',
|
cascade='all, delete, delete-orphan',
|
||||||
backref='set',
|
backref='set',
|
||||||
single_parent=True,
|
single_parent=True,
|
||||||
|
|||||||
@@ -1,198 +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.targetResists import TargetResists as es_TargetResists
|
|
||||||
|
|
||||||
|
|
||||||
class ImportError(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class DefaultDatabaseValues(object):
|
|
||||||
def __init__(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
instance = None
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def importDamageProfileDefaults(cls):
|
|
||||||
damageProfileList = [["Uniform", "25", "25", "25", "25"], ["[Generic]EM", "100", "0", "0", "0"],
|
|
||||||
["[Generic]Thermal", "0", "100", "0", "0"], ["[Generic]Kinetic", "0", "0", "100", "0"],
|
|
||||||
["[Generic]Explosive", "0", "0", "0", "100"],
|
|
||||||
["[NPC][Asteroid] Blood Raiders", "5067", "4214", "0", "0"],
|
|
||||||
["[Bombs]Concussion Bomb", "0", "0", "6400", "0"],
|
|
||||||
["[Bombs]Electron Bomb", "6400", "0", "0", "0"],
|
|
||||||
["[Bombs]Scorch Bomb", "0", "6400", "0", "0"],
|
|
||||||
["[Bombs]Shrapnel Bomb", "0", "0", "0", "6400"],
|
|
||||||
["[Frequency Crystals][T2] Gleam", "56", "56", "0", "0"],
|
|
||||||
["[Frequency Crystals][T2] Aurora", "40", "24", "0", "0"],
|
|
||||||
["[Frequency Crystals][T2] Scorch", "72", "16", "0", "0"],
|
|
||||||
["[Frequency Crystals][T2] Conflagration", "61.6", "61.6", "0", "0"],
|
|
||||||
["[Frequency Crystals]Gamma", "61.6", "35.2", "0", "0"],
|
|
||||||
["[Frequency Crystals]Infrared", "44", "17.6", "0", "0"],
|
|
||||||
["[Frequency Crystals]Microwave", "35.2", "17.6", "0", "0"],
|
|
||||||
["[Frequency Crystals]Multifrequency", "61.6", "44", "0", "0"],
|
|
||||||
["[Frequency Crystals]Radio", "44", "0", "0", "0"],
|
|
||||||
["[Frequency Crystals]Standard", "44", "26.4", "0", "0"],
|
|
||||||
["[Frequency Crystals]Ultraviolet", "52.8", "26.4", "0", "0"],
|
|
||||||
["[Frequency Crystals]Xray", "52.8", "35.2", "0", "0"],
|
|
||||||
["[Hybrid Charges][T2] Void", "0", "61.6", "61.6", "0"],
|
|
||||||
["[Hybrid Charges][T2] Null", "0", "48", "40", "0"],
|
|
||||||
["[Hybrid Charges][T2] Javelin", "0", "64", "48", "0"],
|
|
||||||
["[Hybrid Charges][T2] Spike", "0", "32", "32", "0"],
|
|
||||||
["[Hybrid Charges]Antimatter", "0", "48", "67.2", "0"],
|
|
||||||
["[Hybrid Charges]Iridium", "0", "28.8", "38.4", "0"],
|
|
||||||
["[Hybrid Charges]Iron", "0", "19.2", "28.8", "0"],
|
|
||||||
["[Hybrid Charges]Lead", "0", "28.8", "48", "0"],
|
|
||||||
["[Hybrid Charges]Plutonium", "0", "48", "57.6", "0"],
|
|
||||||
["[Hybrid Charges]Thorium", "0", "38.4", "48", "0"],
|
|
||||||
["[Hybrid Charges]Tungsten", "0", "19.2", "38.4", "0"],
|
|
||||||
["[Hybrid Charges]Uranium", "0", "38.4", "57.6", "0"],
|
|
||||||
["[Missiles]Mjolnir", "100", "0", "0", "0"], ["[Missiles]Inferno", "0", "100", "0", "0"],
|
|
||||||
["[Missiles]Scourge", "0", "0", "100", "0"], ["[Missiles]Nova", "0", "0", "0", "100"],
|
|
||||||
["[Missiles][Structure] Standup Missile", "100", "100", "100", "100"],
|
|
||||||
["[Projectile Ammo][T2] Tremor", "0", "0", "24", "40"],
|
|
||||||
["[Projectile Ammo][T2] Quake", "0", "0", "40", "72"],
|
|
||||||
["[Projectile Ammo][T2] Hail", "0", "0", "26.4", "96.8"],
|
|
||||||
["[Projectile Ammo][T2] Barrage", "0", "0", "40", "48"],
|
|
||||||
["[Projectile Ammo]Carbonized Lead", "0", "0", "35.2", "8.8"],
|
|
||||||
["[Projectile Ammo]Depleted Uranium", "0", "26.4", "17.6", "26.4"],
|
|
||||||
["[Projectile Ammo]EMP", "79.2", "0", "8.8", "17.6"],
|
|
||||||
["[Projectile Ammo]Fusion", "0", "0", "17.6", "88"],
|
|
||||||
["[Projectile Ammo]Nuclear", "0", "0", "8.8", "35.2"],
|
|
||||||
["[Projectile Ammo]Phased Plasma", "0", "88", "17.6", "0"],
|
|
||||||
["[Projectile Ammo]Proton", "26.4", "0", "17.6", "0"],
|
|
||||||
["[Projectile Ammo]Titanium Sabot", "0", "0", "52.8", "176"],
|
|
||||||
["[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.save(damageProfile)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def importResistProfileDefaults(cls):
|
|
||||||
targetResistProfileList = [["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 targetResistProfileRow in targetResistProfileList:
|
|
||||||
name, em, therm, kin, exp = targetResistProfileRow
|
|
||||||
resistsProfile = eos.db.eos.db.getTargetResists(name)
|
|
||||||
if resistsProfile is None:
|
|
||||||
resistsProfile = es_TargetResists(em, therm, kin, exp)
|
|
||||||
resistsProfile.name = name
|
|
||||||
eos.db.save(resistsProfile)
|
|
||||||
|
|
||||||
@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.save(damageProfile)
|
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
||||||
# ===============================================================================
|
# ===============================================================================
|
||||||
|
|
||||||
from sqlalchemy import Table, Column, Integer, ForeignKey, CheckConstraint, Boolean, DateTime
|
from sqlalchemy import Table, Column, Integer, Float, ForeignKey, CheckConstraint, Boolean, DateTime
|
||||||
from sqlalchemy.orm.collections import attribute_mapped_collection
|
from sqlalchemy.orm.collections import attribute_mapped_collection
|
||||||
from sqlalchemy.orm import relation, mapper
|
from sqlalchemy.orm import relation, mapper
|
||||||
import datetime
|
import datetime
|
||||||
@@ -40,6 +40,9 @@ modules_table = Table("modules", saveddata_meta,
|
|||||||
Column("position", Integer),
|
Column("position", Integer),
|
||||||
Column("created", DateTime, nullable=True, default=datetime.datetime.now),
|
Column("created", DateTime, nullable=True, default=datetime.datetime.now),
|
||||||
Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now),
|
Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now),
|
||||||
|
Column("spoolType", Integer, nullable=True),
|
||||||
|
Column("spoolAmount", Float, nullable=True),
|
||||||
|
Column("projectionRange", Float, nullable=True),
|
||||||
CheckConstraint('("dummySlot" = NULL OR "itemID" = NULL) AND "dummySlot" != "itemID"'))
|
CheckConstraint('("dummySlot" = NULL OR "itemID" = NULL) AND "dummySlot" != "itemID"'))
|
||||||
|
|
||||||
mapper(Module, modules_table,
|
mapper(Module, modules_table,
|
||||||
|
|||||||
@@ -17,10 +17,11 @@
|
|||||||
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
||||||
# ===============================================================================
|
# ===============================================================================
|
||||||
|
|
||||||
from sqlalchemy import Table, Column, Integer, ForeignKey, Boolean, DateTime, Float
|
|
||||||
from sqlalchemy.orm import mapper
|
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
|
from sqlalchemy import Column, DateTime, Float, ForeignKey, Integer, Table
|
||||||
|
from sqlalchemy.orm import mapper
|
||||||
|
|
||||||
from eos.db import saveddata_meta
|
from eos.db import saveddata_meta
|
||||||
from eos.saveddata.mutator import Mutator
|
from eos.saveddata.mutator import Mutator
|
||||||
|
|
||||||
|
|||||||
@@ -17,18 +17,19 @@
|
|||||||
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
||||||
# ===============================================================================
|
# ===============================================================================
|
||||||
|
|
||||||
|
|
||||||
from sqlalchemy import Table, Column, Float, Integer
|
from sqlalchemy import Table, Column, Float, Integer
|
||||||
from sqlalchemy.orm import mapper
|
from sqlalchemy.orm import mapper
|
||||||
|
|
||||||
from eos.db import saveddata_meta
|
from eos.db import saveddata_meta
|
||||||
from eos.saveddata.price import Price
|
from eos.saveddata.price import Price
|
||||||
|
|
||||||
|
|
||||||
prices_table = Table("prices", saveddata_meta,
|
prices_table = Table("prices", saveddata_meta,
|
||||||
Column("typeID", Integer, primary_key=True),
|
Column("typeID", Integer, primary_key=True),
|
||||||
Column("price", Float, default=0.0),
|
Column("price", Float, default=0.0),
|
||||||
Column("time", Integer, nullable=False),
|
Column("time", Integer, nullable=False),
|
||||||
Column("failed", Integer))
|
Column("status", Integer, nullable=False))
|
||||||
|
|
||||||
mapper(Price, prices_table, properties={
|
|
||||||
"_Price__price": prices_table.c.price,
|
mapper(Price, prices_table)
|
||||||
})
|
|
||||||
|
|||||||
@@ -21,18 +21,19 @@ import sys
|
|||||||
|
|
||||||
from sqlalchemy.sql import and_
|
from sqlalchemy.sql import and_
|
||||||
from sqlalchemy import desc, select
|
from sqlalchemy import desc, select
|
||||||
|
from sqlalchemy import func
|
||||||
|
|
||||||
from eos.db import saveddata_session, sd_lock
|
from eos.db import saveddata_session, sd_lock
|
||||||
from eos.db.saveddata.fit import projectedFits_table
|
from eos.db.saveddata.fit import fits_table, projectedFits_table
|
||||||
from eos.db.util import processEager, processWhere
|
from eos.db.util import processEager, processWhere
|
||||||
from eos.saveddata.price import Price
|
from eos.saveddata.price import Price
|
||||||
from eos.saveddata.user import User
|
from eos.saveddata.user import User
|
||||||
from eos.saveddata.ssocharacter import SsoCharacter
|
from eos.saveddata.ssocharacter import SsoCharacter
|
||||||
from eos.saveddata.damagePattern import DamagePattern
|
from eos.saveddata.damagePattern import DamagePattern
|
||||||
from eos.saveddata.targetResists import TargetResists
|
from eos.saveddata.targetProfile import TargetProfile
|
||||||
from eos.saveddata.character import Character
|
from eos.saveddata.character import Character
|
||||||
from eos.saveddata.implantSet import ImplantSet
|
from eos.saveddata.implantSet import ImplantSet
|
||||||
from eos.saveddata.fit import Fit
|
from eos.saveddata.fit import Fit, FitLite
|
||||||
from eos.saveddata.module import Module
|
from eos.saveddata.module import Module
|
||||||
from eos.saveddata.miscData import MiscData
|
from eos.saveddata.miscData import MiscData
|
||||||
from eos.saveddata.override import Override
|
from eos.saveddata.override import Override
|
||||||
@@ -283,6 +284,12 @@ def countAllFits():
|
|||||||
return count
|
return count
|
||||||
|
|
||||||
|
|
||||||
|
def countFitGroupedByShip():
|
||||||
|
with sd_lock:
|
||||||
|
count = eos.db.saveddata_session.query(Fit.shipID, func.count(Fit.shipID)).group_by(Fit.shipID).all()
|
||||||
|
return count
|
||||||
|
|
||||||
|
|
||||||
def countFitsWithShip(lookfor, ownerID=None, where=None, eager=None):
|
def countFitsWithShip(lookfor, ownerID=None, where=None, eager=None):
|
||||||
"""
|
"""
|
||||||
Get all the fits using a certain ship.
|
Get all the fits using a certain ship.
|
||||||
@@ -319,6 +326,17 @@ def getFitList(eager=None):
|
|||||||
return fits
|
return fits
|
||||||
|
|
||||||
|
|
||||||
|
def getFitListLite():
|
||||||
|
with sd_lock:
|
||||||
|
stmt = select([fits_table.c.ID, fits_table.c.name, fits_table.c.shipID])
|
||||||
|
data = eos.db.saveddata_session.execute(stmt).fetchall()
|
||||||
|
fits = []
|
||||||
|
for fitID, fitName, shipID in data:
|
||||||
|
fit = FitLite(id=fitID, name=fitName, shipID=shipID)
|
||||||
|
fits.append(fit)
|
||||||
|
return fits
|
||||||
|
|
||||||
|
|
||||||
@cachedQuery(Price, 1, "typeID")
|
@cachedQuery(Price, 1, "typeID")
|
||||||
def getPrice(typeID):
|
def getPrice(typeID):
|
||||||
if isinstance(typeID, int):
|
if isinstance(typeID, int):
|
||||||
@@ -359,16 +377,16 @@ def clearDamagePatterns():
|
|||||||
return deleted_rows
|
return deleted_rows
|
||||||
|
|
||||||
|
|
||||||
def getTargetResistsList(eager=None):
|
def getTargetProfileList(eager=None):
|
||||||
eager = processEager(eager)
|
eager = processEager(eager)
|
||||||
with sd_lock:
|
with sd_lock:
|
||||||
patterns = saveddata_session.query(TargetResists).options(*eager).all()
|
patterns = saveddata_session.query(TargetProfile).options(*eager).all()
|
||||||
return patterns
|
return patterns
|
||||||
|
|
||||||
|
|
||||||
def clearTargetResists():
|
def clearTargetProfiles():
|
||||||
with sd_lock:
|
with sd_lock:
|
||||||
deleted_rows = saveddata_session.query(TargetResists).delete()
|
deleted_rows = saveddata_session.query(TargetProfile).delete()
|
||||||
commit()
|
commit()
|
||||||
return deleted_rows
|
return deleted_rows
|
||||||
|
|
||||||
@@ -395,28 +413,28 @@ def getDamagePattern(lookfor, eager=None):
|
|||||||
eager = processEager(eager)
|
eager = processEager(eager)
|
||||||
with sd_lock:
|
with sd_lock:
|
||||||
pattern = saveddata_session.query(DamagePattern).options(*eager).filter(
|
pattern = saveddata_session.query(DamagePattern).options(*eager).filter(
|
||||||
DamagePattern.name == lookfor).first()
|
DamagePattern.rawName == lookfor).first()
|
||||||
else:
|
else:
|
||||||
raise TypeError("Need integer or string as argument")
|
raise TypeError("Need integer or string as argument")
|
||||||
return pattern
|
return pattern
|
||||||
|
|
||||||
|
|
||||||
@cachedQuery(TargetResists, 1, "lookfor")
|
@cachedQuery(TargetProfile, 1, "lookfor")
|
||||||
def getTargetResists(lookfor, eager=None):
|
def getTargetProfile(lookfor, eager=None):
|
||||||
if isinstance(lookfor, int):
|
if isinstance(lookfor, int):
|
||||||
if eager is None:
|
if eager is None:
|
||||||
with sd_lock:
|
with sd_lock:
|
||||||
pattern = saveddata_session.query(TargetResists).get(lookfor)
|
pattern = saveddata_session.query(TargetProfile).get(lookfor)
|
||||||
else:
|
else:
|
||||||
eager = processEager(eager)
|
eager = processEager(eager)
|
||||||
with sd_lock:
|
with sd_lock:
|
||||||
pattern = saveddata_session.query(TargetResists).options(*eager).filter(
|
pattern = saveddata_session.query(TargetProfile).options(*eager).filter(
|
||||||
TargetResists.ID == lookfor).first()
|
TargetProfile.ID == lookfor).first()
|
||||||
elif isinstance(lookfor, str):
|
elif isinstance(lookfor, str):
|
||||||
eager = processEager(eager)
|
eager = processEager(eager)
|
||||||
with sd_lock:
|
with sd_lock:
|
||||||
pattern = saveddata_session.query(TargetResists).options(*eager).filter(
|
pattern = saveddata_session.query(TargetProfile).options(*eager).filter(
|
||||||
TargetResists.name == lookfor).first()
|
TargetProfile.rawName == lookfor).first()
|
||||||
else:
|
else:
|
||||||
raise TypeError("Need integer or string as argument")
|
raise TypeError("Need integer or string as argument")
|
||||||
return pattern
|
return pattern
|
||||||
@@ -432,11 +450,11 @@ def getImplantSet(lookfor, eager=None):
|
|||||||
eager = processEager(eager)
|
eager = processEager(eager)
|
||||||
with sd_lock:
|
with sd_lock:
|
||||||
pattern = saveddata_session.query(ImplantSet).options(*eager).filter(
|
pattern = saveddata_session.query(ImplantSet).options(*eager).filter(
|
||||||
TargetResists.ID == lookfor).first()
|
TargetProfile.ID == lookfor).first()
|
||||||
elif isinstance(lookfor, str):
|
elif isinstance(lookfor, str):
|
||||||
eager = processEager(eager)
|
eager = processEager(eager)
|
||||||
with sd_lock:
|
with sd_lock:
|
||||||
pattern = saveddata_session.query(ImplantSet).options(*eager).filter(TargetResists.name == lookfor).first()
|
pattern = saveddata_session.query(ImplantSet).options(*eager).filter(TargetProfile.name == lookfor).first()
|
||||||
else:
|
else:
|
||||||
raise TypeError("Improper argument")
|
raise TypeError("Improper argument")
|
||||||
return pattern
|
return pattern
|
||||||
@@ -542,8 +560,21 @@ def commit():
|
|||||||
with sd_lock:
|
with sd_lock:
|
||||||
try:
|
try:
|
||||||
saveddata_session.commit()
|
saveddata_session.commit()
|
||||||
saveddata_session.flush()
|
except (KeyboardInterrupt, SystemExit):
|
||||||
except Exception as ex:
|
raise
|
||||||
|
except Exception:
|
||||||
|
saveddata_session.rollback()
|
||||||
|
exc_info = sys.exc_info()
|
||||||
|
raise exc_info[0](exc_info[1]).with_traceback(exc_info[2])
|
||||||
|
|
||||||
|
|
||||||
|
def flush():
|
||||||
|
with sd_lock:
|
||||||
|
try:
|
||||||
|
saveddata_session.flush()
|
||||||
|
except (KeyboardInterrupt, SystemExit):
|
||||||
|
raise
|
||||||
|
except Exception:
|
||||||
saveddata_session.rollback()
|
saveddata_session.rollback()
|
||||||
exc_info = sys.exc_info()
|
exc_info = sys.exc_info()
|
||||||
raise exc_info[0](exc_info[1]).with_traceback(exc_info[2])
|
raise exc_info[0](exc_info[1]).with_traceback(exc_info[2])
|
||||||
|
|||||||
51
eos/db/saveddata/targetProfile.py
Normal file
51
eos/db/saveddata/targetProfile.py
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
# ===============================================================================
|
||||||
|
# Copyright (C) 2014 Ryan Holmes
|
||||||
|
#
|
||||||
|
# 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 Table, Column, Integer, Float, ForeignKey, String, DateTime
|
||||||
|
from sqlalchemy.orm import mapper
|
||||||
|
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={
|
||||||
|
'rawName': targetProfiles_table.c.name,
|
||||||
|
'_maxVelocity': targetProfiles_table.c.maxVelocity,
|
||||||
|
'_signatureRadius': targetProfiles_table.c.signatureRadius,
|
||||||
|
'_radius': targetProfiles_table.c.radius})
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
# ===============================================================================
|
|
||||||
# Copyright (C) 2014 Ryan Holmes
|
|
||||||
#
|
|
||||||
# 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 Table, Column, Integer, Float, ForeignKey, String, DateTime
|
|
||||||
from sqlalchemy.orm import mapper
|
|
||||||
import datetime
|
|
||||||
|
|
||||||
from eos.db import saveddata_meta
|
|
||||||
from eos.saveddata.targetResists import TargetResists
|
|
||||||
|
|
||||||
targetResists_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("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(TargetResists, targetResists_table)
|
|
||||||
@@ -17,8 +17,10 @@
|
|||||||
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
||||||
# ===============================================================================
|
# ===============================================================================
|
||||||
|
|
||||||
|
|
||||||
from logbook import Logger
|
from logbook import Logger
|
||||||
from utils.deprecated import deprecated
|
from sqlalchemy.orm.collections import collection
|
||||||
|
|
||||||
|
|
||||||
pyfalog = Logger(__name__)
|
pyfalog = Logger(__name__)
|
||||||
|
|
||||||
@@ -112,8 +114,14 @@ class HandledList(list):
|
|||||||
thing.itemID = 0
|
thing.itemID = 0
|
||||||
list.remove(self, thing)
|
list.remove(self, thing)
|
||||||
|
|
||||||
|
def sort(self, *args, **kwargs):
|
||||||
|
# We need it here to prevent external users from accidentally sorting the list as alot of
|
||||||
|
# external logic relies on keeping modules at their places
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
class HandledModuleList(HandledList):
|
class HandledModuleList(HandledList):
|
||||||
|
|
||||||
def append(self, mod):
|
def append(self, mod):
|
||||||
emptyPosition = float("Inf")
|
emptyPosition = float("Inf")
|
||||||
for i in range(len(self)):
|
for i in range(len(self)):
|
||||||
@@ -124,56 +132,90 @@ class HandledModuleList(HandledList):
|
|||||||
emptyPosition = currPos
|
emptyPosition = currPos
|
||||||
|
|
||||||
if emptyPosition < len(self):
|
if emptyPosition < len(self):
|
||||||
del self[emptyPosition]
|
|
||||||
mod.position = emptyPosition
|
mod.position = emptyPosition
|
||||||
HandledList.insert(self, emptyPosition, mod)
|
self.__toModule(emptyPosition, mod)
|
||||||
if mod.isInvalid:
|
if mod.isInvalid:
|
||||||
self.remove(mod)
|
self.__toDummy(mod.position)
|
||||||
return
|
else:
|
||||||
|
self.appendIgnoreEmpty(mod)
|
||||||
|
|
||||||
|
@collection.appender
|
||||||
|
def appendIgnoreEmpty(self, mod):
|
||||||
mod.position = len(self)
|
mod.position = len(self)
|
||||||
HandledList.append(self, mod)
|
super().append(mod)
|
||||||
if mod.isInvalid:
|
if mod.isInvalid:
|
||||||
self.remove(mod)
|
self.remove(mod)
|
||||||
return
|
|
||||||
|
|
||||||
def insert(self, index, mod):
|
def replace(self, idx, mod):
|
||||||
mod.position = index
|
try:
|
||||||
i = index
|
oldMod = self[idx]
|
||||||
|
except IndexError:
|
||||||
|
return
|
||||||
|
self.__toModule(idx, mod)
|
||||||
|
if mod.isInvalid:
|
||||||
|
self.__toModule(idx, oldMod)
|
||||||
|
|
||||||
|
def replaceRackPosition(self, rackPosition, mod):
|
||||||
|
listPositions = []
|
||||||
|
for currPos in range(len(self)):
|
||||||
|
currMod = self[currPos]
|
||||||
|
if currMod.slot == mod.slot:
|
||||||
|
listPositions.append(currPos)
|
||||||
|
listPositions.sort()
|
||||||
|
try:
|
||||||
|
modListPosition = listPositions[rackPosition]
|
||||||
|
except IndexError:
|
||||||
|
self.appendIgnoreEmpty(mod)
|
||||||
|
else:
|
||||||
|
oldMod = self[modListPosition]
|
||||||
|
if mod.isEmpty:
|
||||||
|
self.__toDummy(modListPosition)
|
||||||
|
else:
|
||||||
|
self.__toModule(modListPosition, mod)
|
||||||
|
# If new module cannot be appended, restore old state
|
||||||
|
if mod.isInvalid:
|
||||||
|
if oldMod.isEmpty:
|
||||||
|
self.__toDummy(modListPosition)
|
||||||
|
else:
|
||||||
|
self.__toModule(modListPosition, oldMod)
|
||||||
|
|
||||||
|
def insert(self, idx, mod):
|
||||||
|
mod.position = idx
|
||||||
|
i = idx
|
||||||
while i < len(self):
|
while i < len(self):
|
||||||
self[i].position += 1
|
self[i].position += 1
|
||||||
i += 1
|
i += 1
|
||||||
HandledList.insert(self, index, mod)
|
HandledList.insert(self, idx, mod)
|
||||||
|
if mod.isInvalid:
|
||||||
|
self.remove(mod)
|
||||||
|
|
||||||
def remove(self, mod):
|
def remove(self, mod):
|
||||||
HandledList.remove(self, mod)
|
HandledList.remove(self, mod)
|
||||||
oldPos = mod.position
|
oldPos = mod.position
|
||||||
|
|
||||||
mod.position = None
|
mod.position = None
|
||||||
for i in range(oldPos, len(self)):
|
for i in range(oldPos, len(self)):
|
||||||
self[i].position -= 1
|
self[i].position -= 1
|
||||||
|
|
||||||
def toDummy(self, index):
|
def free(self, idx):
|
||||||
|
self.__toDummy(idx)
|
||||||
|
|
||||||
|
def __toDummy(self, index):
|
||||||
mod = self[index]
|
mod = self[index]
|
||||||
if not mod.isEmpty:
|
if not mod.isEmpty:
|
||||||
dummy = mod.buildEmpty(mod.slot)
|
dummy = mod.buildEmpty(mod.slot)
|
||||||
dummy.position = index
|
dummy.position = index
|
||||||
self[index] = dummy
|
self[index] = dummy
|
||||||
|
mod.position = None
|
||||||
|
|
||||||
def toModule(self, index, mod):
|
def __toModule(self, index, mod):
|
||||||
|
oldMod = self[index]
|
||||||
mod.position = index
|
mod.position = index
|
||||||
self[index] = mod
|
self[index] = mod
|
||||||
|
oldMod.position = None
|
||||||
@deprecated
|
|
||||||
def freeSlot(self, slot):
|
|
||||||
for i in range(len(self)):
|
|
||||||
mod = self[i]
|
|
||||||
if mod.getModifiedItemAttr("subSystemSlot") == slot:
|
|
||||||
self.toDummy(i)
|
|
||||||
break
|
|
||||||
|
|
||||||
|
|
||||||
class HandledDroneCargoList(HandledList):
|
class HandledDroneCargoList(HandledList):
|
||||||
|
|
||||||
def find(self, item):
|
def find(self, item):
|
||||||
for o in self:
|
for o in self:
|
||||||
if o.item == item:
|
if o.item == item:
|
||||||
@@ -185,35 +227,99 @@ class HandledDroneCargoList(HandledList):
|
|||||||
|
|
||||||
def append(self, thing):
|
def append(self, thing):
|
||||||
HandledList.append(self, thing)
|
HandledList.append(self, thing)
|
||||||
|
if thing.isInvalid:
|
||||||
|
self.remove(thing)
|
||||||
|
|
||||||
|
def insert(self, idx, thing):
|
||||||
|
HandledList.insert(self, idx, thing)
|
||||||
if thing.isInvalid:
|
if thing.isInvalid:
|
||||||
self.remove(thing)
|
self.remove(thing)
|
||||||
|
|
||||||
|
|
||||||
class HandledImplantBoosterList(HandledList):
|
class HandledImplantList(HandledList):
|
||||||
def append(self, thing):
|
|
||||||
if thing.isInvalid:
|
def append(self, implant):
|
||||||
HandledList.append(self, thing)
|
if implant.isInvalid:
|
||||||
self.remove(thing)
|
HandledList.append(self, implant)
|
||||||
|
self.remove(implant)
|
||||||
return
|
return
|
||||||
|
if self.__slotCheck(implant):
|
||||||
|
HandledList.append(self, implant)
|
||||||
|
self.remove(implant)
|
||||||
|
return
|
||||||
|
HandledList.append(self, implant)
|
||||||
|
|
||||||
self.makeRoom(thing)
|
def insert(self, idx, implant):
|
||||||
HandledList.append(self, thing)
|
if implant.isInvalid:
|
||||||
|
HandledList.insert(self, idx, implant)
|
||||||
|
self.remove(implant)
|
||||||
|
return
|
||||||
|
if self.__slotCheck(implant):
|
||||||
|
HandledList.insert(self, idx, implant)
|
||||||
|
self.remove(implant)
|
||||||
|
return
|
||||||
|
HandledList.insert(self, idx, implant)
|
||||||
|
|
||||||
def makeRoom(self, thing):
|
def makeRoom(self, implant):
|
||||||
# if needed, remove booster that was occupying slot
|
# if needed, remove booster that was occupying slot
|
||||||
oldObj = next((m for m in self if m.slot == thing.slot), None)
|
oldObj = next((i for i in self if i.slot == implant.slot), None)
|
||||||
if oldObj:
|
if oldObj is not None:
|
||||||
pyfalog.info("Slot {0} occupied with {1}, replacing with {2}", thing.slot, oldObj.item.name,
|
pyfalog.info("Slot {0} occupied with {1}, replacing with {2}", implant.slot, oldObj.item.name, implant.item.name)
|
||||||
thing.item.name)
|
position = self.index(oldObj)
|
||||||
itemID = oldObj.itemID
|
from gui.fitCommands.helpers import ImplantInfo
|
||||||
|
implantInfo = ImplantInfo.fromImplant(oldObj)
|
||||||
oldObj.itemID = 0 # hack to remove from DB. See GH issue #324
|
oldObj.itemID = 0 # hack to remove from DB. See GH issue #324
|
||||||
self.remove(oldObj)
|
self.remove(oldObj)
|
||||||
return itemID
|
return position, implantInfo
|
||||||
return None
|
return None, None
|
||||||
|
|
||||||
|
def __slotCheck(self, implant):
|
||||||
|
return any(i.slot == implant.slot for i in self)
|
||||||
|
|
||||||
|
|
||||||
|
class HandledBoosterList(HandledList):
|
||||||
|
|
||||||
|
def append(self, booster):
|
||||||
|
if booster.isInvalid:
|
||||||
|
HandledList.append(self, booster)
|
||||||
|
self.remove(booster)
|
||||||
|
return
|
||||||
|
if self.__slotCheck(booster):
|
||||||
|
HandledList.append(self, booster)
|
||||||
|
self.remove(booster)
|
||||||
|
return
|
||||||
|
HandledList.append(self, booster)
|
||||||
|
|
||||||
|
def insert(self, idx, booster):
|
||||||
|
if booster.isInvalid:
|
||||||
|
HandledList.insert(self, idx, booster)
|
||||||
|
self.remove(booster)
|
||||||
|
return
|
||||||
|
if self.__slotCheck(booster):
|
||||||
|
HandledList.insert(self, idx, booster)
|
||||||
|
self.remove(booster)
|
||||||
|
return
|
||||||
|
HandledList.insert(self, idx, booster)
|
||||||
|
|
||||||
|
def makeRoom(self, booster):
|
||||||
|
# if needed, remove booster that was occupying slot
|
||||||
|
oldObj = next((b for b in self if b.slot == booster.slot), None)
|
||||||
|
if oldObj is not None:
|
||||||
|
pyfalog.info("Slot {0} occupied with {1}, replacing with {2}", booster.slot, oldObj.item.name, booster.item.name)
|
||||||
|
position = self.index(oldObj)
|
||||||
|
from gui.fitCommands.helpers import BoosterInfo
|
||||||
|
boosterInfo = BoosterInfo.fromBooster(oldObj)
|
||||||
|
oldObj.itemID = 0 # hack to remove from DB. See GH issue #324
|
||||||
|
self.remove(oldObj)
|
||||||
|
return position, boosterInfo
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
def __slotCheck(self, booster):
|
||||||
|
return any(b.slot == booster.slot for b in self)
|
||||||
|
|
||||||
|
|
||||||
class HandledSsoCharacterList(list):
|
class HandledSsoCharacterList(list):
|
||||||
|
|
||||||
def append(self, character):
|
def append(self, character):
|
||||||
old = next((x for x in self if x.client == character.client), None)
|
old = next((x for x in self if x.client == character.client), None)
|
||||||
if old is not None:
|
if old is not None:
|
||||||
@@ -224,6 +330,7 @@ class HandledSsoCharacterList(list):
|
|||||||
|
|
||||||
|
|
||||||
class HandledProjectedModList(HandledList):
|
class HandledProjectedModList(HandledList):
|
||||||
|
|
||||||
def append(self, proj):
|
def append(self, proj):
|
||||||
if proj.isInvalid:
|
if proj.isInvalid:
|
||||||
# we must include it before we remove it. doing it this way ensures
|
# we must include it before we remove it. doing it this way ensures
|
||||||
@@ -231,40 +338,65 @@ class HandledProjectedModList(HandledList):
|
|||||||
HandledList.append(self, proj)
|
HandledList.append(self, proj)
|
||||||
self.remove(proj)
|
self.remove(proj)
|
||||||
return
|
return
|
||||||
|
|
||||||
proj.projected = True
|
proj.projected = True
|
||||||
isSystemEffect = proj.item.group.name == "Effect Beacon"
|
|
||||||
|
|
||||||
if isSystemEffect:
|
|
||||||
self.makeRoom(proj)
|
|
||||||
|
|
||||||
HandledList.append(self, proj)
|
HandledList.append(self, proj)
|
||||||
|
|
||||||
# Remove non-projectable modules
|
# Remove non-projectable modules
|
||||||
if not proj.item.isType("projected") and not isSystemEffect:
|
if not proj.item.isType("projected") and not proj.isExclusiveSystemEffect:
|
||||||
self.remove(proj)
|
self.remove(proj)
|
||||||
|
|
||||||
def makeRoom(self, proj):
|
def insert(self, idx, proj):
|
||||||
# remove other system effects - only 1 per fit plz
|
if proj.isInvalid:
|
||||||
oldEffect = next((m for m in self if m.item.group.name == "Effect Beacon"), None)
|
# we must include it before we remove it. doing it this way ensures
|
||||||
|
# rows and relationships in database are removed as well
|
||||||
|
HandledList.insert(self, idx, proj)
|
||||||
|
self.remove(proj)
|
||||||
|
return
|
||||||
|
proj.projected = True
|
||||||
|
HandledList.insert(self, idx, proj)
|
||||||
|
# Remove non-projectable modules
|
||||||
|
if not proj.item.isType("projected") and not proj.isExclusiveSystemEffect:
|
||||||
|
self.remove(proj)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def currentSystemEffect(self):
|
||||||
|
return next((m for m in self if m.isExclusiveSystemEffect), None)
|
||||||
|
|
||||||
|
def makeRoom(self, proj):
|
||||||
|
if proj.isExclusiveSystemEffect:
|
||||||
|
# remove other system effects - only 1 per fit plz
|
||||||
|
mod = self.currentSystemEffect
|
||||||
|
|
||||||
|
if mod:
|
||||||
|
pyfalog.info("System effect occupied with {0}, removing it to make space for {1}".format(mod.item.name, proj.item.name))
|
||||||
|
position = self.index(mod)
|
||||||
|
# We need to pack up this info, so whatever...
|
||||||
|
from gui.fitCommands.helpers import ModuleInfo
|
||||||
|
modInfo = ModuleInfo.fromModule(mod)
|
||||||
|
self.remove(mod)
|
||||||
|
return position, modInfo
|
||||||
|
return None, None
|
||||||
|
|
||||||
if oldEffect:
|
|
||||||
pyfalog.info("System effect occupied with {0}, replacing with {1}", oldEffect.item.name, proj.item.name)
|
|
||||||
self.remove(oldEffect)
|
|
||||||
return oldEffect.itemID
|
|
||||||
return None
|
|
||||||
|
|
||||||
class HandledProjectedDroneList(HandledDroneCargoList):
|
class HandledProjectedDroneList(HandledDroneCargoList):
|
||||||
|
|
||||||
def append(self, proj):
|
def append(self, proj):
|
||||||
proj.projected = True
|
proj.projected = True
|
||||||
HandledList.append(self, proj)
|
HandledList.append(self, proj)
|
||||||
|
|
||||||
# Remove invalid or non-projectable drones
|
# Remove invalid or non-projectable drones
|
||||||
if proj.isInvalid or not proj.item.isType("projected"):
|
if proj.isInvalid or not proj.item.isType("projected"):
|
||||||
self.remove(proj)
|
self.remove(proj)
|
||||||
|
proj.projected = False
|
||||||
|
|
||||||
|
def insert(self, idx, proj):
|
||||||
|
proj.projected = True
|
||||||
|
HandledList.insert(self, idx, proj)
|
||||||
|
# Remove invalid or non-projectable drones
|
||||||
|
if proj.isInvalid or not proj.item.isType("projected"):
|
||||||
|
self.remove(proj)
|
||||||
|
proj.projected = False
|
||||||
|
|
||||||
|
|
||||||
class HandledItem(object):
|
class HandledItem:
|
||||||
def preAssignItemAttr(self, *args, **kwargs):
|
def preAssignItemAttr(self, *args, **kwargs):
|
||||||
self.itemModifiedAttributes.preAssign(*args, **kwargs)
|
self.itemModifiedAttributes.preAssign(*args, **kwargs)
|
||||||
|
|
||||||
@@ -281,7 +413,7 @@ class HandledItem(object):
|
|||||||
self.itemModifiedAttributes.force(*args, **kwargs)
|
self.itemModifiedAttributes.force(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class HandledCharge(object):
|
class HandledCharge:
|
||||||
def preAssignChargeAttr(self, *args, **kwargs):
|
def preAssignChargeAttr(self, *args, **kwargs):
|
||||||
self.chargeModifiedAttributes.preAssign(*args, **kwargs)
|
self.chargeModifiedAttributes.preAssign(*args, **kwargs)
|
||||||
|
|
||||||
|
|||||||
36415
eos/effects.py
Normal file
36415
eos/effects.py
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,19 +0,0 @@
|
|||||||
# ===============================================================================
|
|
||||||
# Copyright (C) 2010 Diego Duclos
|
|
||||||
# 2010 Anton Vorobyov
|
|
||||||
#
|
|
||||||
# This file, as well as all files in this folder, are 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/>.
|
|
||||||
# ===============================================================================
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
# accerationControlCapNeedBonusPostPercentCapacitorNeedLocationShipGroupAfterburner
|
|
||||||
#
|
|
||||||
# Used by:
|
|
||||||
# Modules named like: Dynamic Fuel Valve (8 of 8)
|
|
||||||
type = "passive"
|
|
||||||
|
|
||||||
|
|
||||||
def handler(fit, container, context):
|
|
||||||
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Propulsion Module",
|
|
||||||
"capacitorNeed", container.getModifiedItemAttr("capNeedBonus"))
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
# accerationControlSkillAb&MwdSpeedBoost
|
|
||||||
#
|
|
||||||
# Used by:
|
|
||||||
# Implant: Zor's Custom Navigation Hyper-Link
|
|
||||||
# Skill: Acceleration Control
|
|
||||||
type = "passive"
|
|
||||||
|
|
||||||
|
|
||||||
def handler(fit, container, context):
|
|
||||||
level = container.level if "skill" in context else 1
|
|
||||||
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Propulsion Module",
|
|
||||||
"speedFactor", container.getModifiedItemAttr("speedFBonus") * level)
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
# accerationControlSpeedFBonusPostPercentSpeedFactorLocationShipGroupAfterburner
|
|
||||||
#
|
|
||||||
# Used by:
|
|
||||||
# Implants named like: Eifyr and Co. 'Rogue' Acceleration Control AC (6 of 6)
|
|
||||||
type = "passive"
|
|
||||||
|
|
||||||
|
|
||||||
def handler(fit, implant, context):
|
|
||||||
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Propulsion Module",
|
|
||||||
"speedFactor", implant.getModifiedItemAttr("speedFBonus"))
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
# accessDifficultyBonusModifierRequiringArchaelogy
|
|
||||||
#
|
|
||||||
# Used by:
|
|
||||||
# Modules named like: Emission Scope Sharpener (8 of 8)
|
|
||||||
# Implant: Poteque 'Prospector' Archaeology AC-905
|
|
||||||
# Implant: Poteque 'Prospector' Environmental Analysis EY-1005
|
|
||||||
type = "passive"
|
|
||||||
|
|
||||||
|
|
||||||
def handler(fit, container, context):
|
|
||||||
fit.modules.filteredItemIncrease(lambda module: module.item.requiresSkill("Archaeology"),
|
|
||||||
"accessDifficultyBonus",
|
|
||||||
container.getModifiedItemAttr("accessDifficultyBonusModifier"), position="post")
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
# accessDifficultyBonusModifierRequiringHacking
|
|
||||||
#
|
|
||||||
# Used by:
|
|
||||||
# Modules named like: Memetic Algorithm Bank (8 of 8)
|
|
||||||
# Implant: Neural Lace 'Blackglass' Net Intrusion 920-40
|
|
||||||
# Implant: Poteque 'Prospector' Environmental Analysis EY-1005
|
|
||||||
# Implant: Poteque 'Prospector' Hacking HC-905
|
|
||||||
type = "passive"
|
|
||||||
|
|
||||||
|
|
||||||
def handler(fit, container, context):
|
|
||||||
fit.modules.filteredItemIncrease(lambda c: c.item.requiresSkill("Hacking"),
|
|
||||||
"accessDifficultyBonus",
|
|
||||||
container.getModifiedItemAttr("accessDifficultyBonusModifier"), position="post")
|
|
||||||
@@ -1,128 +0,0 @@
|
|||||||
# adaptiveArmorHardener
|
|
||||||
#
|
|
||||||
# Used by:
|
|
||||||
# Module: Reactive Armor Hardener
|
|
||||||
from logbook import Logger
|
|
||||||
import eos.config
|
|
||||||
|
|
||||||
pyfalog = Logger(__name__)
|
|
||||||
|
|
||||||
runTime = "late"
|
|
||||||
type = "active"
|
|
||||||
|
|
||||||
|
|
||||||
def handler(fit, module, context):
|
|
||||||
damagePattern = fit.damagePattern
|
|
||||||
# pyfalog.debug("==============================")
|
|
||||||
|
|
||||||
static_adaptive_behavior = eos.config.settings['useStaticAdaptiveArmorHardener']
|
|
||||||
|
|
||||||
if (damagePattern.emAmount == damagePattern.thermalAmount == damagePattern.kineticAmount == damagePattern.explosiveAmount) and static_adaptive_behavior:
|
|
||||||
# pyfalog.debug("Setting adaptivearmorhardener resists to uniform profile.")
|
|
||||||
for attr in ("armorEmDamageResonance", "armorThermalDamageResonance", "armorKineticDamageResonance", "armorExplosiveDamageResonance"):
|
|
||||||
fit.ship.multiplyItemAttr(attr, module.getModifiedItemAttr(attr), stackingPenalties=True, penaltyGroup="preMul")
|
|
||||||
return
|
|
||||||
|
|
||||||
# Skip if there is no damage pattern. Example: projected ships or fleet boosters
|
|
||||||
if damagePattern:
|
|
||||||
|
|
||||||
# Populate a tuple with the damage profile modified by current armor resists.
|
|
||||||
baseDamageTaken = (
|
|
||||||
damagePattern.emAmount * fit.ship.getModifiedItemAttr('armorEmDamageResonance'),
|
|
||||||
damagePattern.thermalAmount * fit.ship.getModifiedItemAttr('armorThermalDamageResonance'),
|
|
||||||
damagePattern.kineticAmount * fit.ship.getModifiedItemAttr('armorKineticDamageResonance'),
|
|
||||||
damagePattern.explosiveAmount * fit.ship.getModifiedItemAttr('armorExplosiveDamageResonance'),
|
|
||||||
)
|
|
||||||
# pyfalog.debug("Damage Adjusted for Armor Resists: %f/%f/%f/%f" % (baseDamageTaken[0], baseDamageTaken[1], baseDamageTaken[2], baseDamageTaken[3]))
|
|
||||||
|
|
||||||
resistanceShiftAmount = module.getModifiedItemAttr(
|
|
||||||
'resistanceShiftAmount') / 100 # The attribute is in percent and we want a fraction
|
|
||||||
RAHResistance = [
|
|
||||||
module.getModifiedItemAttr('armorEmDamageResonance'),
|
|
||||||
module.getModifiedItemAttr('armorThermalDamageResonance'),
|
|
||||||
module.getModifiedItemAttr('armorKineticDamageResonance'),
|
|
||||||
module.getModifiedItemAttr('armorExplosiveDamageResonance'),
|
|
||||||
]
|
|
||||||
|
|
||||||
# Simulate RAH cycles until the RAH either stops changing or enters a loop.
|
|
||||||
# The number of iterations is limited to prevent an infinite loop if something goes wrong.
|
|
||||||
cycleList = []
|
|
||||||
loopStart = -20
|
|
||||||
for num in range(50):
|
|
||||||
# pyfalog.debug("Starting cycle %d." % num)
|
|
||||||
# The strange order is to emulate the ingame sorting when different types have taken the same amount of damage.
|
|
||||||
# This doesn't take into account stacking penalties. In a few cases fitting a Damage Control causes an inaccurate result.
|
|
||||||
damagePattern_tuples = [
|
|
||||||
(0, baseDamageTaken[0] * RAHResistance[0], RAHResistance[0]),
|
|
||||||
(3, baseDamageTaken[3] * RAHResistance[3], RAHResistance[3]),
|
|
||||||
(2, baseDamageTaken[2] * RAHResistance[2], RAHResistance[2]),
|
|
||||||
(1, baseDamageTaken[1] * RAHResistance[1], RAHResistance[1]),
|
|
||||||
]
|
|
||||||
|
|
||||||
# Sort the tuple to drop the highest damage value to the bottom
|
|
||||||
sortedDamagePattern_tuples = sorted(damagePattern_tuples, key=lambda damagePattern: damagePattern[1])
|
|
||||||
|
|
||||||
if sortedDamagePattern_tuples[2][1] == 0:
|
|
||||||
# One damage type: the top damage type takes from the other three
|
|
||||||
# Since the resistances not taking damage will end up going to the type taking damage we just do the whole thing at once.
|
|
||||||
change0 = 1 - sortedDamagePattern_tuples[0][2]
|
|
||||||
change1 = 1 - sortedDamagePattern_tuples[1][2]
|
|
||||||
change2 = 1 - sortedDamagePattern_tuples[2][2]
|
|
||||||
change3 = -(change0 + change1 + change2)
|
|
||||||
elif sortedDamagePattern_tuples[1][1] == 0:
|
|
||||||
# Two damage types: the top two damage types take from the other two
|
|
||||||
# Since the resistances not taking damage will end up going equally to the types taking damage we just do the whole thing at once.
|
|
||||||
change0 = 1 - sortedDamagePattern_tuples[0][2]
|
|
||||||
change1 = 1 - sortedDamagePattern_tuples[1][2]
|
|
||||||
change2 = -(change0 + change1) / 2
|
|
||||||
change3 = -(change0 + change1) / 2
|
|
||||||
else:
|
|
||||||
# Three or four damage types: the top two damage types take from the other two
|
|
||||||
change0 = min(resistanceShiftAmount, 1 - sortedDamagePattern_tuples[0][2])
|
|
||||||
change1 = min(resistanceShiftAmount, 1 - sortedDamagePattern_tuples[1][2])
|
|
||||||
change2 = -(change0 + change1) / 2
|
|
||||||
change3 = -(change0 + change1) / 2
|
|
||||||
|
|
||||||
RAHResistance[sortedDamagePattern_tuples[0][0]] = sortedDamagePattern_tuples[0][2] + change0
|
|
||||||
RAHResistance[sortedDamagePattern_tuples[1][0]] = sortedDamagePattern_tuples[1][2] + change1
|
|
||||||
RAHResistance[sortedDamagePattern_tuples[2][0]] = sortedDamagePattern_tuples[2][2] + change2
|
|
||||||
RAHResistance[sortedDamagePattern_tuples[3][0]] = sortedDamagePattern_tuples[3][2] + change3
|
|
||||||
# pyfalog.debug("Resistances shifted to %f/%f/%f/%f" % ( RAHResistance[0], RAHResistance[1], RAHResistance[2], RAHResistance[3]))
|
|
||||||
|
|
||||||
# See if the current RAH profile has been encountered before, indicating a loop.
|
|
||||||
for i, val in enumerate(cycleList):
|
|
||||||
tolerance = 1e-06
|
|
||||||
if abs(RAHResistance[0] - val[0]) <= tolerance and \
|
|
||||||
abs(RAHResistance[1] - val[1]) <= tolerance and \
|
|
||||||
abs(RAHResistance[2] - val[2]) <= tolerance and \
|
|
||||||
abs(RAHResistance[3] - val[3]) <= tolerance:
|
|
||||||
loopStart = i
|
|
||||||
# pyfalog.debug("Loop found: %d-%d" % (loopStart, num))
|
|
||||||
break
|
|
||||||
if loopStart >= 0:
|
|
||||||
break
|
|
||||||
|
|
||||||
cycleList.append(list(RAHResistance))
|
|
||||||
|
|
||||||
# if loopStart < 0:
|
|
||||||
# pyfalog.error("Reactive Armor Hardener failed to find equilibrium. Damage profile after armor: {0}/{1}/{2}/{3}".format(
|
|
||||||
# baseDamageTaken[0], baseDamageTaken[1], baseDamageTaken[2], baseDamageTaken[3]))
|
|
||||||
|
|
||||||
# Average the profiles in the RAH loop, or the last 20 if it didn't find a loop.
|
|
||||||
loopCycles = cycleList[loopStart:]
|
|
||||||
numCycles = len(loopCycles)
|
|
||||||
average = [0, 0, 0, 0]
|
|
||||||
for cycle in loopCycles:
|
|
||||||
for i in range(4):
|
|
||||||
average[i] += cycle[i]
|
|
||||||
|
|
||||||
for i in range(4):
|
|
||||||
average[i] = round(average[i] / numCycles, 3)
|
|
||||||
|
|
||||||
# Set the new resistances
|
|
||||||
# pyfalog.debug("Setting new resist profile: %f/%f/%f/%f" % ( average[0], average[1], average[2],average[3]))
|
|
||||||
for i, attr in enumerate((
|
|
||||||
'armorEmDamageResonance', 'armorThermalDamageResonance', 'armorKineticDamageResonance',
|
|
||||||
'armorExplosiveDamageResonance')):
|
|
||||||
module.increaseItemAttr(attr, average[i] - module.getModifiedItemAttr(attr))
|
|
||||||
fit.ship.multiplyItemAttr(attr, average[i], stackingPenalties=True, penaltyGroup="preMul")
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
# addToSignatureRadius2
|
|
||||||
#
|
|
||||||
# Used by:
|
|
||||||
# Modules from group: Missile Launcher Bomb (2 of 2)
|
|
||||||
# Modules from group: Shield Extender (36 of 36)
|
|
||||||
type = "passive"
|
|
||||||
|
|
||||||
|
|
||||||
def handler(fit, module, context):
|
|
||||||
fit.ship.increaseItemAttr("signatureRadius", module.getModifiedItemAttr("signatureRadiusAdd"))
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
# afterburnerDurationBonusPostPercentDurationLocationShipModulesRequiringAfterburner
|
|
||||||
#
|
|
||||||
# Used by:
|
|
||||||
# Implants named like: Eifyr and Co. 'Rogue' Afterburner AB (6 of 6)
|
|
||||||
# Implant: Zor's Custom Navigation Link
|
|
||||||
# Skill: Afterburner
|
|
||||||
type = "passive"
|
|
||||||
|
|
||||||
|
|
||||||
def handler(fit, container, context):
|
|
||||||
level = container.level if "skill" in context else 1
|
|
||||||
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Afterburner"),
|
|
||||||
"duration", container.getModifiedItemAttr("durationBonus") * level)
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
# agilityBonus
|
|
||||||
#
|
|
||||||
# Used by:
|
|
||||||
# Subsystems named like: Propulsion Interdiction Nullifier (4 of 4)
|
|
||||||
type = "passive"
|
|
||||||
|
|
||||||
|
|
||||||
def handler(fit, src, context):
|
|
||||||
fit.ship.increaseItemAttr("agility", src.getModifiedItemAttr("agilityBonusAdd"))
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
# agilityMultiplierEffect
|
|
||||||
#
|
|
||||||
# Used by:
|
|
||||||
# Modules from group: Inertial Stabilizer (7 of 7)
|
|
||||||
# Modules from group: Nanofiber Internal Structure (7 of 7)
|
|
||||||
# Modules from group: Reinforced Bulkhead (8 of 8)
|
|
||||||
type = "passive"
|
|
||||||
|
|
||||||
|
|
||||||
def handler(fit, module, context):
|
|
||||||
fit.ship.boostItemAttr("agility",
|
|
||||||
module.getModifiedItemAttr("agilityMultiplier"),
|
|
||||||
stackingPenalties=True)
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
# agilityMultiplierEffectPassive
|
|
||||||
#
|
|
||||||
# Used by:
|
|
||||||
# Modules named like: Polycarbon Engine Housing (8 of 8)
|
|
||||||
type = "passive"
|
|
||||||
|
|
||||||
|
|
||||||
def handler(fit, module, context):
|
|
||||||
fit.ship.boostItemAttr("agility", module.getModifiedItemAttr("agilityBonus"), stackingPenalties=True)
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
# ammoFallofMultiplier
|
|
||||||
#
|
|
||||||
# Used by:
|
|
||||||
# Charges from group: Advanced Artillery Ammo (8 of 8)
|
|
||||||
# Charges from group: Advanced Autocannon Ammo (8 of 8)
|
|
||||||
# Charges from group: Advanced Beam Laser Crystal (8 of 8)
|
|
||||||
# Charges from group: Advanced Blaster Charge (8 of 8)
|
|
||||||
# Charges from group: Advanced Pulse Laser Crystal (8 of 8)
|
|
||||||
# Charges from group: Advanced Railgun Charge (8 of 8)
|
|
||||||
type = "passive"
|
|
||||||
|
|
||||||
|
|
||||||
def handler(fit, module, context):
|
|
||||||
module.multiplyItemAttr("falloff", module.getModifiedChargeAttr("fallofMultiplier") or 1)
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
# ammoInfluenceCapNeed
|
|
||||||
#
|
|
||||||
# Used by:
|
|
||||||
# Items from category: Charge (493 of 947)
|
|
||||||
type = "passive"
|
|
||||||
|
|
||||||
|
|
||||||
def handler(fit, module, context):
|
|
||||||
# Dirty hack to work around cap charges setting cap booster
|
|
||||||
# injection amount to zero
|
|
||||||
rawAttr = module.item.getAttribute("capacitorNeed")
|
|
||||||
if rawAttr is not None and rawAttr >= 0:
|
|
||||||
module.boostItemAttr("capacitorNeed", module.getModifiedChargeAttr("capNeedBonus") or 0)
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
# ammoInfluenceRange
|
|
||||||
#
|
|
||||||
# Used by:
|
|
||||||
# Items from category: Charge (587 of 947)
|
|
||||||
type = "passive"
|
|
||||||
|
|
||||||
|
|
||||||
def handler(fit, module, context):
|
|
||||||
module.multiplyItemAttr("maxRange", module.getModifiedChargeAttr("weaponRangeMultiplier"))
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
# ammoSpeedMultiplier
|
|
||||||
#
|
|
||||||
# Used by:
|
|
||||||
# Charges from group: Festival Charges (23 of 23)
|
|
||||||
# Charges from group: Interdiction Probe (2 of 2)
|
|
||||||
# Charges from group: Structure Festival Charges (3 of 3)
|
|
||||||
# Special Edition Assetss from group: Festival Charges Expired (2 of 2)
|
|
||||||
type = "passive"
|
|
||||||
|
|
||||||
|
|
||||||
def handler(fit, module, context):
|
|
||||||
module.multiplyItemAttr("speed", module.getModifiedChargeAttr("speedMultiplier") or 1)
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
# ammoTrackingMultiplier
|
|
||||||
#
|
|
||||||
# Used by:
|
|
||||||
# Items from category: Charge (182 of 947)
|
|
||||||
# Charges from group: Projectile Ammo (128 of 128)
|
|
||||||
type = "passive"
|
|
||||||
|
|
||||||
|
|
||||||
def handler(fit, module, context):
|
|
||||||
module.multiplyItemAttr("trackingSpeed", module.getModifiedChargeAttr("trackingSpeedMultiplier"))
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
# angelsetbonus
|
|
||||||
#
|
|
||||||
# Used by:
|
|
||||||
# Implants named like: grade Halo (18 of 18)
|
|
||||||
runTime = "early"
|
|
||||||
type = "passive"
|
|
||||||
|
|
||||||
|
|
||||||
def handler(fit, implant, context):
|
|
||||||
fit.appliedImplants.filteredItemMultiply(
|
|
||||||
lambda implant: "signatureRadiusBonus" in implant.itemModifiedAttributes and
|
|
||||||
"implantSetAngel" in implant.itemModifiedAttributes,
|
|
||||||
"signatureRadiusBonus",
|
|
||||||
implant.getModifiedItemAttr("implantSetAngel"))
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
# antiWarpScramblingPassive
|
|
||||||
#
|
|
||||||
# Used by:
|
|
||||||
# Modules from group: Warp Core Stabilizer (8 of 8)
|
|
||||||
type = "passive"
|
|
||||||
|
|
||||||
|
|
||||||
def handler(fit, module, context):
|
|
||||||
fit.ship.increaseItemAttr("warpScrambleStatus", module.getModifiedItemAttr("warpScrambleStrength"))
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
# aoe_beacon_bioluminescence_cloud
|
|
||||||
#
|
|
||||||
# Used by:
|
|
||||||
# Celestials named like: Bioluminescence Cloud (3 of 3)
|
|
||||||
runTime = "early"
|
|
||||||
type = ("projected", "passive", "gang")
|
|
||||||
|
|
||||||
|
|
||||||
def handler(fit, beacon, context, **kwargs):
|
|
||||||
for x in range(1, 3):
|
|
||||||
if beacon.getModifiedItemAttr("warfareBuff{}ID".format(x)):
|
|
||||||
value = beacon.getModifiedItemAttr("warfareBuff{}Value".format(x))
|
|
||||||
id = beacon.getModifiedItemAttr("warfareBuff{}ID".format(x))
|
|
||||||
|
|
||||||
if id:
|
|
||||||
fit.addCommandBonus(id, value, beacon, kwargs['effect'], 'early')
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
# aoe_beacon_caustic_cloud
|
|
||||||
#
|
|
||||||
# Used by:
|
|
||||||
# Celestials named like: Caustic Cloud (3 of 3)
|
|
||||||
runTime = "early"
|
|
||||||
type = ("projected", "passive", "gang")
|
|
||||||
|
|
||||||
|
|
||||||
def handler(fit, beacon, context, **kwargs):
|
|
||||||
for x in range(1, 3):
|
|
||||||
if beacon.getModifiedItemAttr("warfareBuff{}ID".format(x)):
|
|
||||||
value = beacon.getModifiedItemAttr("warfareBuff{}Value".format(x))
|
|
||||||
id = beacon.getModifiedItemAttr("warfareBuff{}ID".format(x))
|
|
||||||
|
|
||||||
if id:
|
|
||||||
fit.addCommandBonus(id, value, beacon, kwargs['effect'], 'early')
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
# aoe_beacon_filament_cloud
|
|
||||||
#
|
|
||||||
# Used by:
|
|
||||||
# Celestials named like: Filament Cloud (3 of 3)
|
|
||||||
runTime = "early"
|
|
||||||
type = ("projected", "passive", "gang")
|
|
||||||
|
|
||||||
|
|
||||||
def handler(fit, beacon, context, **kwargs):
|
|
||||||
for x in range(1, 3):
|
|
||||||
if beacon.getModifiedItemAttr("warfareBuff{}ID".format(x)):
|
|
||||||
value = beacon.getModifiedItemAttr("warfareBuff{}Value".format(x))
|
|
||||||
id = beacon.getModifiedItemAttr("warfareBuff{}ID".format(x))
|
|
||||||
|
|
||||||
if id:
|
|
||||||
fit.addCommandBonus(id, value, beacon, kwargs['effect'], 'early')
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
# archaeologySkillVirusBonus
|
|
||||||
#
|
|
||||||
# Used by:
|
|
||||||
# Modules named like: Emission Scope Sharpener (8 of 8)
|
|
||||||
# Implant: Poteque 'Prospector' Archaeology AC-905
|
|
||||||
# Implant: Poteque 'Prospector' Environmental Analysis EY-1005
|
|
||||||
# Skill: Archaeology
|
|
||||||
type = "passive"
|
|
||||||
|
|
||||||
|
|
||||||
def handler(fit, container, context):
|
|
||||||
level = container.level if "skill" in context else 1
|
|
||||||
fit.modules.filteredItemIncrease(lambda mod: mod.item.requiresSkill("Archaeology"),
|
|
||||||
"virusCoherence", container.getModifiedItemAttr("virusCoherenceBonus") * level)
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
# armorAllRepairSystemsAmountBonusPassive
|
|
||||||
#
|
|
||||||
# Used by:
|
|
||||||
# Implants named like: Agency 'Hardshell' TB Dose (4 of 4)
|
|
||||||
# Implants named like: Exile Booster (4 of 4)
|
|
||||||
# Implant: Antipharmakon Kosybo
|
|
||||||
type = "passive"
|
|
||||||
|
|
||||||
|
|
||||||
def handler(fit, booster, context):
|
|
||||||
fit.modules.filteredItemBoost(
|
|
||||||
lambda mod: mod.item.requiresSkill("Repair Systems") or mod.item.requiresSkill("Capital Repair Systems"),
|
|
||||||
"armorDamageAmount", booster.getModifiedItemAttr("armorDamageAmountBonus") or 0)
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
# armorDamageAmountBonusCapitalArmorRepairers
|
|
||||||
#
|
|
||||||
# Used by:
|
|
||||||
# Modules named like: Auxiliary Nano Pump (8 of 8)
|
|
||||||
type = "passive"
|
|
||||||
|
|
||||||
|
|
||||||
def handler(fit, implant, context):
|
|
||||||
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Capital Repair Systems"),
|
|
||||||
"armorDamageAmount", implant.getModifiedItemAttr("repairBonus"),
|
|
||||||
stackingPenalties=True)
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
# armoredCommandDurationBonus
|
|
||||||
#
|
|
||||||
# Used by:
|
|
||||||
# Skill: Armored Command
|
|
||||||
type = "passive"
|
|
||||||
|
|
||||||
|
|
||||||
def handler(fit, src, context):
|
|
||||||
lvl = src.level
|
|
||||||
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "buffDuration",
|
|
||||||
src.getModifiedItemAttr("durationBonus") * lvl)
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
# armoredCommandMindlink
|
|
||||||
#
|
|
||||||
# Used by:
|
|
||||||
# Implant: Armored Command Mindlink
|
|
||||||
# Implant: Federation Navy Command Mindlink
|
|
||||||
# Implant: Imperial Navy Command Mindlink
|
|
||||||
type = "passive"
|
|
||||||
|
|
||||||
|
|
||||||
def handler(fit, src, context):
|
|
||||||
fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff2Multiplier",
|
|
||||||
src.getModifiedItemAttr("mindlinkBonus"))
|
|
||||||
fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff1Multiplier",
|
|
||||||
src.getModifiedItemAttr("mindlinkBonus"))
|
|
||||||
fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff4Multiplier",
|
|
||||||
src.getModifiedItemAttr("mindlinkBonus"))
|
|
||||||
fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff3Multiplier",
|
|
||||||
src.getModifiedItemAttr("mindlinkBonus"))
|
|
||||||
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "buffDuration",
|
|
||||||
src.getModifiedItemAttr("mindlinkBonus"))
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
# armoredCommandStrengthBonus
|
|
||||||
#
|
|
||||||
# Used by:
|
|
||||||
# Skill: Armored Command Specialist
|
|
||||||
type = "passive"
|
|
||||||
|
|
||||||
|
|
||||||
def handler(fit, src, context):
|
|
||||||
lvl = src.level
|
|
||||||
fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff1Multiplier",
|
|
||||||
src.getModifiedItemAttr("commandStrengthBonus") * lvl)
|
|
||||||
fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff2Multiplier",
|
|
||||||
src.getModifiedItemAttr("commandStrengthBonus") * lvl)
|
|
||||||
fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff4Multiplier",
|
|
||||||
src.getModifiedItemAttr("commandStrengthBonus") * lvl)
|
|
||||||
fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff3Multiplier",
|
|
||||||
src.getModifiedItemAttr("commandStrengthBonus") * lvl)
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
# armorHPBonusAdd
|
|
||||||
#
|
|
||||||
# Used by:
|
|
||||||
# Modules from group: Armor Reinforcer (51 of 51)
|
|
||||||
type = "passive"
|
|
||||||
|
|
||||||
|
|
||||||
def handler(fit, module, context):
|
|
||||||
fit.ship.increaseItemAttr("armorHP", module.getModifiedItemAttr("armorHPBonusAdd"))
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
# armorHPBonusAddPassive
|
|
||||||
#
|
|
||||||
# Used by:
|
|
||||||
# Subsystems from group: Defensive Systems (9 of 12)
|
|
||||||
type = "passive"
|
|
||||||
|
|
||||||
|
|
||||||
def handler(fit, module, context):
|
|
||||||
fit.ship.increaseItemAttr("armorHP", module.getModifiedItemAttr("armorHPBonusAdd") or 0)
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
# armorHPMultiply
|
|
||||||
#
|
|
||||||
# Used by:
|
|
||||||
# Modules from group: Armor Coating (202 of 202)
|
|
||||||
# Modules from group: Armor Plating Energized (187 of 187)
|
|
||||||
type = "passive"
|
|
||||||
|
|
||||||
|
|
||||||
def handler(fit, module, context):
|
|
||||||
fit.ship.multiplyItemAttr("armorHP", module.getModifiedItemAttr("armorHPMultiplier"))
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
# armorReinforcerMassAdd
|
|
||||||
#
|
|
||||||
# Used by:
|
|
||||||
# Modules from group: Armor Reinforcer (51 of 51)
|
|
||||||
type = "passive"
|
|
||||||
|
|
||||||
|
|
||||||
def handler(fit, module, context):
|
|
||||||
fit.ship.increaseItemAttr("mass", module.getModifiedItemAttr("massAddition"))
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
# armorRepair
|
|
||||||
#
|
|
||||||
# Used by:
|
|
||||||
# Modules from group: Armor Repair Unit (108 of 108)
|
|
||||||
runTime = "late"
|
|
||||||
type = "active"
|
|
||||||
|
|
||||||
|
|
||||||
def handler(fit, module, context):
|
|
||||||
amount = module.getModifiedItemAttr("armorDamageAmount")
|
|
||||||
speed = module.getModifiedItemAttr("duration") / 1000.0
|
|
||||||
fit.extraAttributes.increase("armorRepair", amount / speed)
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
# armorRepairAmountBonusSubcap
|
|
||||||
#
|
|
||||||
# Used by:
|
|
||||||
# Implants named like: grade Asklepian (15 of 18)
|
|
||||||
type = "passive"
|
|
||||||
|
|
||||||
|
|
||||||
def handler(fit, src, context):
|
|
||||||
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Repair Systems"),
|
|
||||||
"armorDamageAmount", src.getModifiedItemAttr("armorRepairBonus"))
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
# armorRepairProjectorFalloffBonus
|
|
||||||
#
|
|
||||||
# Used by:
|
|
||||||
# Variations of ship: Navitas (2 of 2)
|
|
||||||
# Ship: Augoror
|
|
||||||
# Ship: Deacon
|
|
||||||
# Ship: Exequror
|
|
||||||
# Ship: Inquisitor
|
|
||||||
type = "passive"
|
|
||||||
|
|
||||||
|
|
||||||
def handler(fit, src, context):
|
|
||||||
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Remote Armor Repairer", "falloffEffectiveness",
|
|
||||||
src.getModifiedItemAttr("falloffBonus"))
|
|
||||||
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Ancillary Remote Armor Repairer",
|
|
||||||
"falloffEffectiveness", src.getModifiedItemAttr("falloffBonus"))
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
# armorRepairProjectorMaxRangeBonus
|
|
||||||
#
|
|
||||||
# Used by:
|
|
||||||
# Variations of ship: Navitas (2 of 2)
|
|
||||||
# Ship: Augoror
|
|
||||||
# Ship: Deacon
|
|
||||||
# Ship: Exequror
|
|
||||||
# Ship: Inquisitor
|
|
||||||
type = "passive"
|
|
||||||
|
|
||||||
|
|
||||||
def handler(fit, src, context):
|
|
||||||
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Remote Armor Repairer", "maxRange",
|
|
||||||
src.getModifiedItemAttr("maxRangeBonus"))
|
|
||||||
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Ancillary Remote Armor Repairer", "maxRange",
|
|
||||||
src.getModifiedItemAttr("maxRangeBonus"))
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
# armorUpgradesMassPenaltyReductionBonus
|
|
||||||
#
|
|
||||||
# Used by:
|
|
||||||
# Skill: Armor Layering
|
|
||||||
type = "passive"
|
|
||||||
|
|
||||||
|
|
||||||
def handler(fit, container, context):
|
|
||||||
level = container.level
|
|
||||||
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Armor Reinforcer",
|
|
||||||
"massAddition", container.getModifiedItemAttr("massPenaltyReduction") * level)
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
# astrogeologyMiningAmountBonusPostPercentMiningAmountLocationShipModulesRequiringMining
|
|
||||||
#
|
|
||||||
# Used by:
|
|
||||||
# Implants named like: Inherent Implants 'Highwall' Mining MX (3 of 3)
|
|
||||||
# Implant: Michi's Excavation Augmentor
|
|
||||||
# Skill: Astrogeology
|
|
||||||
# Skill: Mining
|
|
||||||
type = "passive"
|
|
||||||
|
|
||||||
|
|
||||||
def handler(fit, container, context):
|
|
||||||
level = container.level if "skill" in context else 1
|
|
||||||
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining"),
|
|
||||||
"miningAmount", container.getModifiedItemAttr("miningAmountBonus") * level)
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
# baseMaxScanDeviationModifierModuleOnline2None
|
|
||||||
#
|
|
||||||
# Used by:
|
|
||||||
# Variations of module: Scan Pinpointing Array I (2 of 2)
|
|
||||||
type = "passive"
|
|
||||||
|
|
||||||
|
|
||||||
def handler(fit, module, context):
|
|
||||||
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Astrometrics"),
|
|
||||||
"baseMaxScanDeviation",
|
|
||||||
module.getModifiedItemAttr("maxScanDeviationModifierModule"),
|
|
||||||
stackingPenalties=True)
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
# baseMaxScanDeviationModifierRequiringAstrometrics
|
|
||||||
#
|
|
||||||
# Used by:
|
|
||||||
# Implants named like: Poteque 'Prospector' Astrometric Pinpointing AP (3 of 3)
|
|
||||||
# Skill: Astrometric Pinpointing
|
|
||||||
# Skill: Astrometrics
|
|
||||||
type = "passive"
|
|
||||||
|
|
||||||
|
|
||||||
def handler(fit, container, context):
|
|
||||||
level = container.level if "skill" in context else 1
|
|
||||||
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Astrometrics"),
|
|
||||||
"baseMaxScanDeviation",
|
|
||||||
container.getModifiedItemAttr("maxScanDeviationModifier") * level)
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
# baseSensorStrengthModifierModule
|
|
||||||
#
|
|
||||||
# Used by:
|
|
||||||
# Variations of module: Scan Rangefinding Array I (2 of 2)
|
|
||||||
type = "passive"
|
|
||||||
|
|
||||||
|
|
||||||
def handler(fit, module, context):
|
|
||||||
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Astrometrics"),
|
|
||||||
"baseSensorStrength", module.getModifiedItemAttr("scanStrengthBonusModule"),
|
|
||||||
stackingPenalties=True)
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
# baseSensorStrengthModifierRequiringAstrometrics
|
|
||||||
#
|
|
||||||
# Used by:
|
|
||||||
# Modules from group: Scan Probe Launcher (4 of 7)
|
|
||||||
# Implants named like: Poteque 'Prospector' Astrometric Rangefinding AR (3 of 3)
|
|
||||||
# Implants named like: grade Virtue (10 of 12)
|
|
||||||
# Modules named like: Gravity Capacitor Upgrade (8 of 8)
|
|
||||||
# Skill: Astrometric Rangefinding
|
|
||||||
# Skill: Astrometrics
|
|
||||||
type = "passive"
|
|
||||||
|
|
||||||
|
|
||||||
def handler(fit, container, context):
|
|
||||||
level = container.level if "skill" in context else 1
|
|
||||||
penalized = False if "skill" in context or "implant" in context else True
|
|
||||||
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Astrometrics"),
|
|
||||||
"baseSensorStrength", container.getModifiedItemAttr("scanStrengthBonus") * level,
|
|
||||||
stackingPenalties=penalized)
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user