Compare commits

...

1149 Commits

Author SHA1 Message Date
DarkPhoenix
2de214d7cd Fix orca shield burst bonus 2022-03-31 23:29:19 +03:00
DarkPhoenix
d9d546ca25 Bump version 2022-03-31 23:11:12 +03:00
DarkPhoenix
2c0e6fa73b Update static data to 2024431 2022-03-31 21:14:39 +03:00
DarkPhoenix
86e171f13d Add condenser pack damage profiles 2022-03-30 21:49:25 +03:00
DarkPhoenix
20f222b9bf Split team burner damage patterns into their separate group 2022-03-30 21:28:43 +03:00
Ryan Holmes
01721f9dac Merge pull request #2419 from pyfa-org/issue2410
AppImage
2022-03-30 00:39:19 -04:00
Ryan Holmes
e57b7e5a8f Merge pull request #2422 from nmalaguti/feature/jwt-aud-fix
Skip validation of JWT "aud" claim
2022-03-30 00:38:17 -04:00
Nick Malaguti
4ba037d6e1 Skip validation of JWT "aud" claim
Due to unexpected ESI SSO breakage by adding an "aud" claim, skip
validation of the claim. If in the future CCP specifies which "aud"
claim to verify against, this can be changed to pass the appropriate
"audience" value.

Fixes #2421
2022-03-28 21:36:49 -04:00
Ryan Holmes
f89061a9d8 Merge branch 'master' into issue2410 2022-03-27 09:31:39 -04:00
Ryan Holmes
52b8b4bc6c Merge pull request #2356 from pyfa-org/crowdin_master
New Crowdin updates
2022-03-24 00:07:28 -04:00
Ryan Holmes
51e351d1ba Merge branch 'master' into crowdin_master 2022-03-24 00:07:01 -04:00
blitzmann
518b4abf8c fix crowdin error for PRs 2022-03-23 22:55:22 -04:00
blitzmann
67119fcff1 fix crowdin error for PRs 2022-03-23 22:32:54 -04:00
blitzmann
b1beebda26 Convert makefile to shell commands in appveyor config, various other tweaks 2022-03-23 10:28:04 -04:00
blitzmann
f7ea438ebc Merge tag 'v2.39.2docker' into issue2410 2022-03-23 10:27:12 -04:00
DarkPhoenix
ee6b57ecc1 Rename burner damage profiles and fix sentinel profile 2022-03-19 12:59:53 +03:00
Tomas S
e17fe434ee Update with GTK3 Info 2022-03-17 12:02:19 +01:00
wereii
7cad0b2658 AppImage build 2022-03-16 20:39:55 +01:00
DarkPhoenix
9e06106a1c Save & restore RAH-specific damage pattern through do-undo actions 2022-03-12 13:49:18 +03:00
DarkPhoenix
ae115b640a Allow RAH-specific pattern control 2022-03-12 02:19:03 +03:00
DarkPhoenix
0c292e8d6e Add more burner profiles 2022-03-09 19:20:00 +03:00
DarkPhoenix
68fad93282 Bump version 2022-03-09 00:38:19 +03:00
DarkPhoenix
9067c0b8b3 Update effects 2022-03-09 00:37:01 +03:00
DarkPhoenix
3d4849799b Update static data to 2013787 2022-03-09 00:33:10 +03:00
DarkPhoenix
fc848cd2d0 More mobility data for burners 2022-03-09 00:19:49 +03:00
DarkPhoenix
72b18f7935 Add mobility data to burners 2022-03-05 17:19:00 +03:00
DarkPhoenix
014d389dc8 Update static data to 2010918 2022-03-01 13:11:41 +03:00
DarkPhoenix
bded2d505f Update staticdata to 2008716 2022-02-23 22:24:27 +03:00
DarkPhoenix
532268d4e6 Bump version 2022-02-19 01:43:10 +03:00
DarkPhoenix
6445d4219a Update icons 2022-02-19 01:42:17 +03:00
DarkPhoenix
9e7e994e1d Update effects 2022-02-19 01:39:35 +03:00
DarkPhoenix
209120a0dd Filter out placeholders 2022-02-19 01:14:11 +03:00
DarkPhoenix
0e0d820cb5 Update staticdata to 2006012 and update DB generation script 2022-02-19 00:48:54 +03:00
DarkPhoenix
8cd87929b9 Bump version 2022-02-09 18:20:53 +03:00
Anton Vorobyov
acd853bdde Merge pull request #2403 from shyadow/probesx8
Probes x8 instead of x1000
2022-02-09 18:19:35 +03:00
DarkPhoenix
94b4bed1fe Update effects 2022-02-09 16:23:50 +03:00
DarkPhoenix
01bed745a2 Update staticdata to 2000555 2022-02-09 16:05:00 +03:00
shyadow
40022d5d18 Change multiple-add for Scan Probes to be x8 2022-02-08 20:22:10 +01:00
shyadow
2ad4040ec7 Merge branch 'master' of github.com-shyadow:pyfa-org/Pyfa into master 2022-02-07 22:12:20 +01:00
DarkPhoenix
8cbe094ee6 Fix orca industrial core fuel effect 2022-02-05 04:57:29 +03:00
DarkPhoenix
2e11cbc5ae Bump version 2022-02-05 04:50:53 +03:00
DarkPhoenix
12f191d6d1 Update drug effects 2022-02-05 04:49:34 +03:00
DarkPhoenix
3918c4e3f9 Implement missing AT17 ships' effects 2022-02-05 04:26:30 +03:00
DarkPhoenix
779f1b476c Add missing staticdata updates and remove AT17 prize overrides 2022-02-05 04:01:51 +03:00
DarkPhoenix
61bb1ac225 Update static data to 1998655 2022-02-05 03:37:20 +03:00
DarkPhoenix
c459cbbc92 Remove drifter weapons from search results 2022-02-05 02:11:39 +03:00
DarkPhoenix
66018642f7 Show full burst strength even when burst is offline 2022-02-05 01:56:17 +03:00
DarkPhoenix
4c6d171793 Fix blackops + heated propmod interaction 2022-02-05 01:37:29 +03:00
DarkPhoenix
aa1ecd6bea Add support for implants & boosters as well 2022-02-05 01:25:15 +03:00
DarkPhoenix
be73ffd929 Track changes to underlying modules in stats windows 2022-02-05 00:56:39 +03:00
DarkPhoenix
3024ccd176 Alternate fix for #2400 2022-02-04 23:35:49 +03:00
blitzmann
d12128bc58 Publish "Mutated Drone Specialization" skill, #2399 2022-02-03 21:30:07 -05:00
blitzmann
c295482055 Fix for #2400 2022-02-03 20:53:04 -05:00
DarkPhoenix
b62260cd1f Fix minor issue with dragging modules to cargo on top of other modules 2022-01-23 01:29:15 +03:00
DarkPhoenix
a61e3ebaad Do not hide abyssal mining crystals 2022-01-19 02:43:42 +03:00
DarkPhoenix
9ae78aab0d Update AT ship stats 2022-01-17 20:47:39 +03:00
DarkPhoenix
06b9f8fe08 Bump version 2022-01-15 23:22:44 +03:00
DarkPhoenix
60ab34f6a4 Add AT ship effects 2022-01-15 23:21:46 +03:00
DarkPhoenix
18255d324c Revert changes to mac build for now 2022-01-15 22:00:23 +03:00
DarkPhoenix
51e43f6927 Implement attrib/effect overrides for new AT ships 2022-01-15 21:56:16 +03:00
DarkPhoenix
1208253110 Filter out expired boosters 2022-01-15 06:15:39 +03:00
DarkPhoenix
af4ed86324 Process effect updates and group renames 2022-01-15 06:14:12 +03:00
DarkPhoenix
8b34566d72 Update static data to 1989449 2022-01-15 05:39:43 +03:00
DarkPhoenix
5ab149d9f6 Ceil amount of targets instead of flooring (leshaks with base 7 targets actually have 4 in pochven) 2022-01-11 12:11:25 +03:00
Anton Vorobyov
bb38ba6564 New translations lang.pot (Spanish) 2022-01-10 15:44:16 +03:00
Anton Vorobyov
3f6aa91966 New translations lang.pot (Spanish) 2022-01-09 15:36:47 +03:00
Anton Vorobyov
e86e5933b5 New translations lang.pot (French) 2022-01-04 01:03:43 +03:00
Anton Vorobyov
000c1bc38f New translations lang.pot (French) 2022-01-03 01:04:57 +03:00
DarkPhoenix
feaa52c36b Find out which venv versions are out there 2021-12-11 04:03:35 +03:00
DarkPhoenix
a8e595b470 Update build environment to big sur fix 2021-12-11 01:21:07 +03:00
DarkPhoenix
157e709208 Update build environment to big sur 2021-12-11 00:07:22 +03:00
DarkPhoenix
ae4ec99308 Show mining stats in misc column even for inactive mods/drones 2021-12-09 23:20:02 +03:00
DarkPhoenix
aa7d3a1b7b Fix drone falloff display 2021-12-09 23:10:33 +03:00
DarkPhoenix
a7c2f700b4 Fix drone optimal display 2021-12-09 23:08:53 +03:00
DarkPhoenix
ce4b7f4f20 Update effects 2021-12-09 22:59:08 +03:00
DarkPhoenix
6c86aa95a3 Update static data to 1978193 2021-12-09 22:33:23 +03:00
DarkPhoenix
b52df842d5 Update effects & bump version 2021-12-08 17:01:06 +03:00
DarkPhoenix
4ccc64c7c7 Update static data to 1977292 2021-12-08 16:34:13 +03:00
DarkPhoenix
a0d2b13dd6 Add conversions 2021-12-07 15:11:32 +03:00
DarkPhoenix
7873853f97 Bump version 2021-12-07 14:42:12 +03:00
DarkPhoenix
877990d486 Update static data to 1975905 2021-12-07 14:35:52 +03:00
DarkPhoenix
1019c5f0cd Bump version 2021-12-02 15:05:20 +03:00
DarkPhoenix
b1b4284b6e Add new effects 2021-12-02 15:02:35 +03:00
DarkPhoenix
e0377a084f Update how mining waste is processed, to make it in line with new effects 2021-12-02 14:15:29 +03:00
DarkPhoenix
dbf9875d25 Remove unused effects and rerun effect use script 2021-12-02 13:40:39 +03:00
DarkPhoenix
5d66454e52 Update waste calculation 2021-12-02 13:35:28 +03:00
DarkPhoenix
f5d18fd606 More rename-related updates 2021-12-02 13:23:37 +03:00
DarkPhoenix
145e92b458 Add some rename updates 2021-12-02 13:18:20 +03:00
DarkPhoenix
ca8a8f0785 Handle renamed attributes and groups 2021-12-02 12:55:56 +03:00
DarkPhoenix
dfdcf0a804 Update static data to 1974215 2021-12-02 01:01:03 +03:00
DarkPhoenix
087284e048 Fix missile range column 2021-12-01 23:21:40 +03:00
DarkPhoenix
65741ed03b Superscript 3 in m3 2021-11-29 15:07:26 +03:00
DarkPhoenix
ae3d7ade0f Bump version 2021-11-28 23:46:48 +03:00
DarkPhoenix
789df97017 Merge branch 'master' into singularity 2021-11-28 04:04:56 +03:00
DarkPhoenix
7992ba43b2 Change how command burst misc column info is processed 2021-11-28 00:06:15 +03:00
DarkPhoenix
3595e24ad8 Merge branch 'master' into singularity 2021-11-27 15:35:30 +03:00
Anton Vorobyov
6677435474 Merge pull request #2360 from shyadow/master
Add Command Burst effect in Miscellanea Column
2021-11-27 15:18:47 +03:00
DarkPhoenix
1561d9f581 Fix drone group check 2021-11-27 15:12:52 +03:00
DarkPhoenix
1dee8ae648 Bump version 2021-11-27 00:21:16 +03:00
DarkPhoenix
51ea0c4f31 Fix another issue with restrictions 2021-11-27 00:09:43 +03:00
DarkPhoenix
9d8e338136 Implement multi-level selection menu for mining crystals 2021-11-27 00:05:18 +03:00
DarkPhoenix
48818c9709 Fix turret ammo picker 2021-11-26 21:39:08 +03:00
DarkPhoenix
ca356b413b Add mining waste readout to the misc column, and fix more issues related to the attribute fetching change 2021-11-26 21:27:47 +03:00
DarkPhoenix
a9ef5caf2d Fix attribute fetching, which is needed to calculate mining waste of crystal'd strip miners
This potentially can break many things. Some of those are fixed already, but there might be many more...
2021-11-26 20:43:46 +03:00
DarkPhoenix
22be97f901 Calculate waste stats for fits 2021-11-26 17:19:07 +03:00
DarkPhoenix
06f515ec0b Update icons and renders 2021-11-26 02:45:15 +03:00
DarkPhoenix
c878f4c155 Show yield for new gas hoarding modules 2021-11-26 02:42:08 +03:00
DarkPhoenix
5cf433a3eb Add industrial reconfiguration effect 2021-11-26 02:39:25 +03:00
DarkPhoenix
15191e5752 Add new orca & rorqual effects 2021-11-25 23:07:51 +03:00
DarkPhoenix
9d833d9cac Add new barge/exhumer effects 2021-11-25 22:48:33 +03:00
DarkPhoenix
5767c92985 Add more renames 2021-11-25 22:06:42 +03:00
DarkPhoenix
3246e58278 Process attribute renames, update changed effects, remove unused effects 2021-11-25 21:49:23 +03:00
DarkPhoenix
30546c6e45 Update staticdata to 1971319 2021-11-25 21:25:55 +03:00
DarkPhoenix
37fe587d90 Add/fix event booster effects 2021-11-25 21:00:20 +03:00
DarkPhoenix
09b1b50257 Add AT17 prizes to LE market group 2021-11-25 20:43:37 +03:00
DarkPhoenix
91a507eadd Add new module/charge effects 2021-11-25 20:41:54 +03:00
DarkPhoenix
dd92c3742c Add new barge/exhumer effects 2021-11-25 18:54:29 +03:00
DarkPhoenix
181170d7b0 Add new industrial effects 2021-11-25 17:11:12 +03:00
DarkPhoenix
1156f06db2 Add new orca/rorq effects 2021-11-25 16:57:32 +03:00
DarkPhoenix
90baffe267 Add new porpoise effects 2021-11-24 02:53:31 +03:00
DarkPhoenix
100a1fa01a Add support for new cargo types to UI 2021-11-24 02:29:55 +03:00
DarkPhoenix
d8d854da52 Add new endurance effects 2021-11-24 02:23:13 +03:00
DarkPhoenix
418339d000 Add new prospect effects 2021-11-24 01:33:20 +03:00
DarkPhoenix
24a0f4f53e Process renamed items and attributes 2021-11-24 01:01:40 +03:00
DarkPhoenix
94784bf1e0 Remove unused effects and update by which items effects are used 2021-11-23 20:54:03 +03:00
DarkPhoenix
d894df9819 Fix changed effects 2021-11-23 20:32:26 +03:00
DarkPhoenix
a5e1f3be40 Update static data to 1970121 2021-11-23 20:13:49 +03:00
shyadow
dcb6439444 Merge branch 'pyfa-org:master' into master 2021-11-20 17:34:20 +01:00
shyadow
5918d6371f Merge branch 'master' of github.com-shyadow:pyfa-org/Pyfa into master 2021-11-20 17:33:30 +01:00
Anton Vorobyov
8b1553df03 New translations lang.pot (Korean) 2021-11-04 02:22:53 +03:00
DarkPhoenix
98fa16d921 Bump version and add renamed mutaplasmid conversion 2021-10-28 14:39:57 +03:00
DarkPhoenix
d5795ed5f5 Update static data to 1957858 2021-10-28 14:35:27 +03:00
DarkPhoenix
fe809ed86b Always use base item attributes in mutator 2021-10-27 11:37:14 +03:00
Ryan Holmes
d09db832c3 Merge branch 'master' into master 2021-10-26 20:37:11 -04:00
Ryan Holmes
de4466b037 Merge pull request #2363 from pyfa-org/sso_v2
Sso v2
2021-10-26 17:22:46 -04:00
blitzmann
8ebf478cf7 fix typo in requirements 2021-10-26 16:50:04 -04:00
Ryan Holmes
530a12ea4f Merge branch 'master' into sso_v2 2021-10-26 12:21:10 -04:00
blitzmann
b94f6e8d0a Merge branch 'sso_v2' of https://github.com/pyfa-org/Pyfa into sso_v2 2021-10-26 12:20:20 -04:00
blitzmann
4b83169070 typo fix 2021-10-26 12:20:11 -04:00
DarkPhoenix
a7da9e20a5 Make new option for new mutated item names 2021-10-26 17:48:18 +03:00
Anton Vorobyov
0020dcdf0a Merge branch 'master' into sso_v2 2021-10-26 17:03:24 +03:00
DarkPhoenix
066f697186 Bump version 2021-10-26 13:06:52 +03:00
DarkPhoenix
b2217bb6cd Use short name only if short mutaplasmid name is available 2021-10-26 12:49:27 +03:00
DarkPhoenix
4669b4813c Use short muta + base names for mutated items 2021-10-26 12:22:25 +03:00
DarkPhoenix
a71701932c Do not store mutaplasmids in commands 2021-10-26 11:59:44 +03:00
DarkPhoenix
1be0d306bb Allow imports of mutated drone groups via ctrl-v and additions panel context menus 2021-10-26 11:29:25 +03:00
DarkPhoenix
85f63e237d Allow imports of separate mutated drones from clipboard 2021-10-26 10:19:10 +03:00
DarkPhoenix
acab787a7b Mark mutated drones unpublished 2021-10-26 09:59:32 +03:00
DarkPhoenix
da50c7dcfc Update icons 2021-10-26 09:55:12 +03:00
DarkPhoenix
b0d3469f01 User short mutaplasmid name in mutation panel 2021-10-26 01:31:09 +03:00
DarkPhoenix
148093f6ad Allow import of fits with mutated drones 2021-10-26 01:16:25 +03:00
DarkPhoenix
fab58bdb57 Implement mutated drone export 2021-10-26 00:44:11 +03:00
DarkPhoenix
34b2fdbd1b Store mutation results on window close for undo/redo purposes 2021-10-25 23:43:21 +03:00
DarkPhoenix
ea7a5b3c70 Implement drone mutation support with some exceptions 2021-10-25 23:34:08 +03:00
DarkPhoenix
056685ded5 Make mutaplasmid names short on menu list 2021-10-25 14:08:06 +03:00
DarkPhoenix
60f9e96b19 Add new event booster effects 2021-10-25 13:36:01 +03:00
DarkPhoenix
341950cb42 Add renames for quafe classic 2021-10-25 01:41:41 +03:00
DarkPhoenix
641e982614 Fix quafe apple stacking penalty & run effect used by script 2021-10-25 01:39:01 +03:00
DarkPhoenix
1e534e087a AIR booster stacking penalty fix 2021-10-25 01:32:28 +03:00
DarkPhoenix
0b7b80d0f6 Update bastion effect 2021-10-25 01:21:13 +03:00
DarkPhoenix
27bd9fc55b Update static data to 1954065 2021-10-25 01:18:36 +03:00
blitzmann
533f6f3b24 Handle API exceptions during fit deletion 2021-10-24 14:45:07 -04:00
blitzmann
c18cbe5e59 Handle server timeouts gracefully 2021-10-24 14:30:52 -04:00
blitzmann
adba55eb7d Ensure that the token error dialog spawns n the three most used API calls that might trigger token refresh: fetch skills, fetch fits, and export fit 2021-10-24 14:07:04 -04:00
blitzmann
03c6f7c894 Update token exception message to include button to manage ESI characters 2021-10-24 13:07:32 -04:00
DarkPhoenix
478b2bfe4c Fix cargo amount change command 2021-10-23 16:05:45 +03:00
DarkPhoenix
7e4d8a3074 Allow to change multiple cargo items at once 2021-10-23 04:05:20 +03:00
DarkPhoenix
8497d1f1bb Change how ECM chance is calculated so that sensor strength links are considered 2021-10-21 16:02:04 +03:00
blitzmann
1b5acc36d3 update callback 2021-10-19 00:15:22 -04:00
blitzmann
2a94dcebf8 Remove various preferences related to SSO, move callback to config 2021-10-19 00:15:10 -04:00
blitzmann
7b9e196ca8 additional error handling 2021-10-19 00:02:53 -04:00
blitzmann
f3f7d688ab Properly handle the "manual" mode of copy and paste auth information 2021-10-18 14:06:36 -04:00
blitzmann
17391f119c No longer looks like esipy, removing comment chunk 2021-10-18 13:12:53 -04:00
blitzmann
abd138a015 Pull endpoints from .well-known route
Add request caching (only for meta calls for now)
Add server list (eventually want to support various servers, for now only TQ)
2021-10-18 13:09:16 -04:00
Anton Vorobyov
89d0ee4c77 New translations lang.pot (Korean) 2021-10-18 14:10:48 +03:00
blitzmann
33aa208513 Token validation and various cleanup 2021-10-17 21:01:30 -04:00
blitzmann
1874cbe0c5 Work on the server to handle the new structure 2021-10-16 16:52:23 -04:00
blitzmann
028fb42e18 Starting some tweaks on SSO 2021-10-16 12:46:25 -04:00
shyadow
6b444bfc63 Change all but first ifs to elifs 2021-10-14 18:28:13 +02:00
shyadow
1753d79bc1 Add Command Burst effect in Miscellanea Column 2021-10-14 13:19:00 +02:00
Ryan Holmes
d9b0d2e72a Update README.md 2021-10-13 23:22:39 -04:00
Ryan Holmes
e0234c343e Update README.md 2021-10-13 23:20:57 -04:00
Anton Vorobyov
d7d4643a33 New translations lang.pot (Korean) 2021-10-08 13:37:45 +03:00
Anton Vorobyov
166635d0a1 New translations lang.pot (Korean) 2021-10-07 13:17:56 +03:00
DarkPhoenix
b5816a312c Bump ESI character endpoint version 2021-10-05 16:32:42 +03:00
Anton Vorobyov
076e1999af New translations lang.pot (Korean) 2021-10-01 14:20:45 +03:00
Anton Vorobyov
69caff50c3 New translations lang.pot (Korean) 2021-09-30 13:56:01 +03:00
Anton Vorobyov
0db72f7a8c New translations lang.pot (Korean) 2021-09-29 13:59:34 +03:00
Anton Vorobyov
43ac76ac1f New translations lang.pot (Korean) 2021-09-28 04:36:38 +03:00
Anton Vorobyov
279c912703 New translations lang.pot (Korean) 2021-09-27 04:33:56 +03:00
Anton Vorobyov
e54cec47f2 New translations lang.pot (Korean) 2021-09-26 04:38:53 +03:00
Anton Vorobyov
50293081d1 New translations lang.pot (Russian) 2021-09-11 17:48:52 +03:00
Anton Vorobyov
4f526d7cc4 New translations lang.pot (Russian) 2021-09-10 17:52:14 +03:00
Anton Vorobyov
86cc025812 Merge pull request #2347 from chwons/master
fix text color when using a dark system theme
2021-07-13 15:51:06 +03:00
DarkPhoenix
8072f7e618 Bump version 2021-07-13 15:42:46 +03:00
DarkPhoenix
c34b12d806 Fix ewar booster effect 2021-07-13 15:42:33 +03:00
DarkPhoenix
67760fa2ab Update effects 2021-07-13 15:37:54 +03:00
DarkPhoenix
9a28dd17be Update client data to 1918815 2021-07-13 15:04:05 +03:00
chwons
6e4d1876ad Merge branch 'pyfa-org:master' into master 2021-07-08 07:59:50 +09:00
DarkPhoenix
40b0b27176 Run effects used by 2021-07-02 17:46:55 +03:00
DarkPhoenix
b238422f29 Update static data to 1916151 2021-07-02 17:33:30 +03:00
DarkPhoenix
4e20583b81 Update static data to 1914752 2021-06-28 19:54:32 +03:00
DarkPhoenix
f730cdb86a Bump version 2021-06-27 02:34:11 +03:00
DarkPhoenix
e05471fa6f Update effects 2021-06-27 02:33:48 +03:00
DarkPhoenix
45b517e097 Update static data to 1914498 2021-06-27 02:07:46 +03:00
DarkPhoenix
f27274c9e5 Update staticdata to 1913437 2021-06-27 01:44:52 +03:00
chwons
d32a8f0b61 stop testing 2021-06-23 01:59:28 +09:00
chwons
6a01086cf5 Update itemTraits.py 2021-06-23 01:49:27 +09:00
chwons
e0f65c2370 Update itemAttributes.py 2021-06-23 01:48:59 +09:00
chwons
e7c2f4cff7 Update itemDescription.py 2021-06-23 01:47:57 +09:00
chwons
93cce50d00 testing 2021-06-23 01:26:54 +09:00
DarkPhoenix
d1a2cdc586 Bump version 2021-06-20 04:05:51 +03:00
DarkPhoenix
9dab6b0f92 Bump version 2021-06-15 18:31:09 +03:00
DarkPhoenix
33e3ea6030 Add bubble launcher as an element for wubble alias 2021-06-15 18:27:21 +03:00
DarkPhoenix
9d97b27c3a Implement wubble application via interdiction module 2021-06-15 18:25:25 +03:00
DarkPhoenix
05d156c7f4 Fix covops effects 2021-06-15 18:17:30 +03:00
DarkPhoenix
2ddc4924cc Update metadata to 1910975 2021-06-15 16:30:46 +03:00
DarkPhoenix
e3fef6f1a7 Add custom wubble effect 2021-06-13 02:30:09 +03:00
DarkPhoenix
fbd8d04f68 Fix nullifier duration bonus 2021-06-11 23:53:16 +03:00
DarkPhoenix
23916165bb Bump version 2021-06-11 23:27:11 +03:00
DarkPhoenix
3cca6a6c95 Update effects 2021-06-11 23:26:09 +03:00
DarkPhoenix
a52172a3c6 Update static data to 1910538 2021-06-11 21:26:46 +03:00
Anton Vorobyov
8a2bfb48ce Merge pull request #2339 from pyfa-org/crowdin_master
New Crowdin updates
2021-06-11 21:08:04 +03:00
DarkPhoenix
b42254cb61 Avoid crashes on ctrl-backspace in notes view 2021-06-11 21:04:59 +03:00
DarkPhoenix
91fbcd4eb4 Fix bastion rof bonus 2021-06-11 20:58:17 +03:00
DarkPhoenix
bdc52da05d Remove travis configuration 2021-06-11 20:34:01 +03:00
DarkPhoenix
2bbe17722b Refresh deployment tokens 2021-06-09 13:57:03 +03:00
DarkPhoenix
3ef07b9153 Upload artifacts on tags to github 2021-06-08 22:47:00 +03:00
DarkPhoenix
5af904ca18 Split build process into multiple stages 2021-06-08 21:07:11 +03:00
DarkPhoenix
d3cdc67472 Update path to macos build 2021-06-08 21:01:38 +03:00
DarkPhoenix
0991124d5f Store package as an appveyor artifact 2021-06-08 20:55:10 +03:00
DarkPhoenix
83ea548c4d Try python 3.7
This is the version we used on Travis
2021-06-08 20:43:09 +03:00
DarkPhoenix
4752eee09d Fix venv path 2021-06-08 20:36:47 +03:00
DarkPhoenix
53d2d1fabe Activate python 3.8 2021-06-08 20:34:29 +03:00
DarkPhoenix
f600868eb2 Move sleep to init 2021-06-08 20:29:03 +03:00
DarkPhoenix
f6a489fa22 Add sleep to give some time to debug 2021-06-08 20:24:26 +03:00
DarkPhoenix
2962a89919 Enable macOS debugging 2021-06-08 20:19:50 +03:00
DarkPhoenix
c3d53f56a9 Preinstall pathlib on macOS 2021-06-08 19:46:43 +03:00
DarkPhoenix
3257abbeff Remove linking of gettext for macOS
Apprently, it's already linked on appveyor images
2021-06-08 19:36:48 +03:00
DarkPhoenix
5858d96deb Set python version to 3.8 for mac before building 2021-06-08 19:36:18 +03:00
DarkPhoenix
94d1eae464 Move macOS build scripts to appveyor configuration 2021-06-08 19:29:34 +03:00
DarkPhoenix
b7e2b782c9 Move all windows-specific code under windows section 2021-06-08 19:02:37 +03:00
DarkPhoenix
05e5958d36 Make PYTHON variable image-specific to test if it works 2021-06-08 18:54:07 +03:00
Anton Vorobyov
e9364fe65a New translations lang.pot (Russian) 2021-05-27 14:31:12 +03:00
Anton Vorobyov
b3b71896ae New translations lang.pot (Russian) 2021-05-26 14:22:49 +03:00
Anton Vorobyov
00d20f53d0 New translations lang.pot (Russian) 2021-05-19 21:03:26 +03:00
DarkPhoenix
88e4f7a77e Set SQLAlchemy version to last known working 2021-05-19 03:40:09 +03:00
DarkPhoenix
5763954f51 Fix bastion falloff bonus 2021-05-19 03:29:25 +03:00
DarkPhoenix
4fbfbcc84e Bump version 2021-05-19 03:12:20 +03:00
DarkPhoenix
1f2c20e95b Update icons 2021-05-19 03:11:13 +03:00
DarkPhoenix
d7aa744bc1 Do not activate WCS and nullifiers by default 2021-05-19 02:55:20 +03:00
DarkPhoenix
1117c786ff Add new nullifier/WCS effects 2021-05-19 02:52:29 +03:00
DarkPhoenix
6a715f6678 Update effects for metaliminal storms 2021-05-19 02:27:39 +03:00
DarkPhoenix
5e93235b02 Add jargon entries for SRS and wubble 2021-05-19 01:35:32 +03:00
DarkPhoenix
271b915ce6 Fix sig suppressor effect #2 2021-05-19 01:30:58 +03:00
DarkPhoenix
52667da64a Fix sig suppressor effect 2021-05-19 01:29:20 +03:00
DarkPhoenix
82f0fea1bf Run effectUsedBy script 2021-05-19 01:28:48 +03:00
DarkPhoenix
751823177b Update static data to 1903677 2021-05-19 01:19:49 +03:00
DarkPhoenix
ac7e8a1efa Fix msgformat 2021-05-19 00:40:29 +03:00
Anton Vorobyov
066a12e912 Merge pull request #2337 from pyfa-org/crowdin_master
New Crowdin updates
2021-05-19 00:23:35 +03:00
Anton Vorobyov
ea64657fba New translations lang.pot (Chinese Simplified) 2021-05-18 20:34:57 +03:00
Anton Vorobyov
17b94001e0 New translations lang.pot (Turkish) 2021-05-18 20:34:56 +03:00
Anton Vorobyov
cc481f9f29 New translations lang.pot (Russian) 2021-05-18 20:34:55 +03:00
Anton Vorobyov
f73bb14990 New translations lang.pot (Korean) 2021-05-18 20:34:53 +03:00
Anton Vorobyov
a5ed992cd2 New translations lang.pot (Japanese) 2021-05-18 20:34:52 +03:00
Anton Vorobyov
f7e792411a New translations lang.pot (Italian) 2021-05-18 20:34:51 +03:00
Anton Vorobyov
fc71d7b706 New translations lang.pot (Spanish) 2021-05-18 20:34:50 +03:00
Anton Vorobyov
162055d5ce New translations lang.pot (French) 2021-05-18 20:34:48 +03:00
DarkPhoenix
5cc87a439f Merge branch 'i18n' 2021-05-12 22:39:41 +03:00
Anton Vorobyov
d2d3a42adf Merge pull request #2331 from pyfa-org/crowdin_l10n
New Crowdin updates
2021-05-12 22:38:37 +03:00
Ryan Holmes
e43cb74906 New translations lang.pot (Korean) 2021-04-07 20:23:31 -04:00
DarkPhoenix
ace00d495a Add stacking penalty for bastion missile range bonus 2021-03-27 19:40:34 +03:00
DarkPhoenix
729b39e351 Define backref for boosters manually 2021-03-27 19:37:11 +03:00
DarkPhoenix
5ae1e64a76 Revert "Remove backref to owner since it seems to be unused, but it crashed attempts to remove items which have been removed from static data"
This reverts commit 8ee900a90e.
2021-03-27 18:51:15 +03:00
DarkPhoenix
88a9ce03ba Merge branch 'i18n' 2021-03-26 17:18:22 +03:00
Anton Vorobyov
cbd3d8cc1b Merge pull request #2319 from pyfa-org/crowdin_l10n
New Crowdin updates
2021-03-26 17:17:06 +03:00
Anton Vorobyov
65a126b4c5 Merge pull request #2312 from sajuukthanatoskhar/EFS_Information_Export_add_on
Adding more information to EFS Export
2021-03-25 15:47:56 +03:00
DarkPhoenix
241c744d40 Fixed stacking penalization of heat rof bonus 2021-03-25 15:37:09 +03:00
DarkPhoenix
8ee900a90e Remove backref to owner since it seems to be unused, but it crashed attempts to remove items which have been removed from static data 2021-03-25 15:13:18 +03:00
DarkPhoenix
1c415fbe06 Mark objects dirty so that they get removed, instead of setting their item ID to 0 2021-03-25 15:06:28 +03:00
DarkPhoenix
304aebbccf Update static data to 1889623 2021-03-25 14:31:41 +03:00
DarkPhoenix
18d9f6a542 Fix repr for boosters with no item 2021-03-25 13:52:40 +03:00
DarkPhoenix
cdc6e046b0 Fix #2308 2021-03-25 13:15:48 +03:00
DarkPhoenix
59bb9670b6 Multiple fixes related to sig suppressor 2021-03-25 12:49:50 +03:00
DarkPhoenix
9321a78a61 Update effects 2021-03-25 12:35:40 +03:00
DarkPhoenix
53191714f2 Exclude TSB from list of projectable mods for EFS purposes 2021-03-24 17:47:54 +03:00
DarkPhoenix
b17347156c Update static data to 1888542 2021-03-24 17:40:26 +03:00
DarkPhoenix
66ace06194 Run effect used by script 2021-03-23 18:44:06 +03:00
DarkPhoenix
e8697c6131 Update static data to 1888542 2021-03-23 18:38:26 +03:00
Ryan Holmes
7a6e87330d New translations lang.pot (Russian) 2021-03-02 10:09:27 -05:00
Ryan Holmes
2acf92160e New translations lang.pot (Russian) 2021-03-02 09:11:19 -05:00
DarkPhoenix
6e49f6fd7a Bump version 2021-02-23 21:34:06 +03:00
DarkPhoenix
6e56230a0e Update staticdata to 1878176 2021-02-23 21:28:30 +03:00
DarkPhoenix
01282d103a Add extraction filaments as well 2021-02-20 21:24:11 +03:00
DarkPhoenix
a0317d5b3c Bump version 2021-02-20 20:23:04 +03:00
DarkPhoenix
a697c1890a Adjust effects 2021-02-20 20:22:44 +03:00
DarkPhoenix
aaf719437f Update static data to 1877575 2021-02-20 20:09:24 +03:00
DarkPhoenix
3d6e703b7c Trigger travis builds only on tags 2021-02-19 21:17:34 +03:00
DarkPhoenix
ce26b22752 Add filament market group 2021-02-19 20:44:57 +03:00
DarkPhoenix
6b30b2859e Allow adding items to cargo via double-click 2021-02-19 20:39:10 +03:00
DarkPhoenix
961258618a Make filaments searchable 2021-02-19 20:36:11 +03:00
DarkPhoenix
f07684875f Use proper attribute for missiles 2021-02-19 10:37:52 +03:00
DarkPhoenix
538954f4a6 Fix vargur damage bonus 2021-02-19 02:16:57 +03:00
DarkPhoenix
30e73ed795 Fix bastion stacking penalties on RoF 2021-02-18 23:38:24 +03:00
DarkPhoenix
d216eb3e55 Bump version 2021-02-18 22:15:00 +03:00
DarkPhoenix
c25fa0f632 Update bastion effect 2021-02-18 22:11:51 +03:00
DarkPhoenix
8a115be0bd Update effect uses 2021-02-18 21:03:53 +03:00
DarkPhoenix
b897ee5146 Update static data to 1876339 2021-02-18 20:41:40 +03:00
Ryan Holmes
7b3bda5816 New translations lang.pot (Korean) 2021-01-27 11:06:59 -05:00
Ryan Holmes
f62e16288d New translations lang.pot (Korean) 2021-01-27 09:54:33 -05:00
Ryan Holmes
9aad8860a9 New translations lang.pot (Korean) 2021-01-26 23:57:54 -05:00
Ryan Holmes
f8dd1f27a9 New translations lang.pot (Korean) 2021-01-26 22:46:04 -05:00
DarkPhoenix
09bc2da863 Limit subwarp speed to 100 for warp considerations 2021-01-26 00:33:43 +03:00
Ryan Holmes
f531c144af New translations lang.pot (Chinese Simplified) 2021-01-23 21:52:34 -05:00
Gareth Williams
dfd08fa8e2 MOD: EFS Export Version Number to 0.05
ADD: OptimalSignatureRadius is now exported with each *turret*
2021-01-23 17:55:00 +01:00
Gareth Williams
e430848be9 ADD: Added the ability for EFS exports to contain the following information: shieldrecharge rate, inertia, energy|neutraliser Resistance 2021-01-09 22:34:29 +01:00
Anton Vorobyov
2d645b8f91 Merge pull request #2301 from pyfa-org/i18n
I18n
2020-12-08 20:00:30 +03:00
DarkPhoenix
e33cb80608 Bump version 2020-12-08 19:56:43 +03:00
Anton Vorobyov
fcc8c3eeb9 Merge pull request #2300 from pyfa-org/crowdin_l10n
New Crowdin updates
2020-12-08 19:51:37 +03:00
DarkPhoenix
c29d567255 Change default jargon definitions for missile types, they now include launchers as well 2020-12-08 19:46:13 +03:00
DarkPhoenix
e85b618cbc Add set of hacks to rename caustic to tachyon 2020-12-08 19:40:48 +03:00
DarkPhoenix
008b6a887d Rename Caustic to Tachyon 2020-12-08 19:30:21 +03:00
DarkPhoenix
05ab2d47c7 Fix error handler in a situation when gamedata metadata is not available 2020-12-08 19:16:05 +03:00
DarkPhoenix
75b611c94a Fix salvaging spec effect 2020-12-08 18:01:57 +03:00
Ryan Holmes
356d594637 New translations lang.pot (French) 2020-12-08 09:14:01 -05:00
Ryan Holmes
03f4bc1d75 New translations lang.pot (French) 2020-12-08 08:13:23 -05:00
DarkPhoenix
226c5d7fad Update static data to 1860847 2020-12-08 15:09:51 +03:00
DarkPhoenix
0c3998b640 Add new effects 2020-12-05 16:39:58 +03:00
DarkPhoenix
942f9a35ee Update static data to 1859379 2020-12-05 14:27:19 +03:00
DarkPhoenix
031a3c2d3e Fix IDs of new damage patterns 2020-11-27 18:03:18 +03:00
DarkPhoenix
ed7df2f1a7 Bump version 2020-11-26 15:57:15 +03:00
DarkPhoenix
fb2ad2cc10 Update static data to 1853874 2020-11-26 15:56:40 +03:00
DarkPhoenix
34bce4e083 Bump version 2020-11-24 14:56:39 +03:00
DarkPhoenix
fc45b43295 Update static data to 1852074 2020-11-24 14:53:32 +03:00
DarkPhoenix
4160b08388 Fix mining preservation charge 2020-11-24 13:08:25 +03:00
DarkPhoenix
95c25b274c Add logging to help with no localization debugging 2020-11-24 13:01:52 +03:00
DarkPhoenix
690e5caf52 Bump version 2020-11-19 15:33:11 +03:00
DarkPhoenix
95ada5d61d Fix misc column for ewar drones 2020-11-19 14:54:44 +03:00
DarkPhoenix
976820a7c0 Merge branch 'master' into singularity 2020-11-19 14:21:08 +03:00
DarkPhoenix
8ab14bcfcc Attempt to fix unavailable graphs 2020-11-19 14:20:41 +03:00
DarkPhoenix
6e8798eb08 Adjust effects 2020-11-19 14:01:03 +03:00
DarkPhoenix
15af830dca Update staticdata to 1848209 2020-11-19 13:36:14 +03:00
Anton Vorobyov
ce728243b0 Merge pull request #2287 from porowns/master
Fix typo in ECM targetting view
2020-11-19 12:50:19 +03:00
DarkPhoenix
2696b480fa Fix mistype 2020-11-17 19:20:25 +03:00
Kaleb
ca7a3cae9c Fix typo in ECM targetting view 2020-11-13 13:11:50 -05:00
DarkPhoenix
10613c9070 Consider a market group as non-final when it has sub-market groups 2020-11-11 22:37:49 +03:00
DarkPhoenix
a692b1fe74 Bump version 2020-11-11 00:30:49 +03:00
DarkPhoenix
0592d2c3f4 Update staticdata to 1842315 2020-11-11 00:24:19 +03:00
DarkPhoenix
301a3473cc Update static data to 1837348 2020-11-04 01:35:44 +03:00
DarkPhoenix
4887852de4 Update po template after merge 2020-10-28 15:29:11 +03:00
DarkPhoenix
6b3bf1f7a8 Merge branch 'master' of github.com:pyfa-org/Pyfa into master 2020-10-28 15:28:49 +03:00
DarkPhoenix
a4d163fc89 Update po template 2020-10-28 15:28:33 +03:00
Anton Vorobyov
e88e3996a8 Merge pull request #2282 from Neugeniko/master
Added new target and damage profiles for invasion drifters, rogue drones and sleepers.
2020-10-28 15:27:25 +03:00
Neugeniko
a36dd20890 Add new invasion target profiles for drifters, rogue drones and sleepers. 2020-10-28 20:41:02 +11:00
Neugeniko
c9f37457ab Add new invasion damage profiles for drifters, rogue drones and sleepers. 2020-10-28 20:39:35 +11:00
DarkPhoenix
4709cd78f3 Update contribution readme 2020-10-28 00:03:24 +03:00
DarkPhoenix
5ac5b3f5e4 Bump version 2020-10-27 23:05:40 +03:00
DarkPhoenix
da78c24d9b Do not persist messages across generation sessions 2020-10-27 22:56:02 +03:00
DarkPhoenix
566c87fa59 Avoid crash when .mo files are not generated 2020-10-27 22:42:03 +03:00
DarkPhoenix
e6d7a140cf Add message for case when mo files are not generated 2020-10-27 22:39:21 +03:00
DarkPhoenix
89e496f3af Move language setting to top 2020-10-27 22:17:06 +03:00
DarkPhoenix
d650284ae1 Update english translations 2020-10-27 21:42:18 +03:00
DarkPhoenix
b41d7d2603 Merge branch 'i18n' into master 2020-10-27 21:36:24 +03:00
Anton Vorobyov
33c20bfc3b Merge pull request #2239 from pyfa-org/crowdin_l10n
New Crowdin updates
2020-10-27 21:34:47 +03:00
DarkPhoenix
6f0778ad94 Update po template 2020-10-27 21:27:20 +03:00
DarkPhoenix
7ce39a0dac Update staticdata to 1832008 2020-10-27 15:55:38 +03:00
DarkPhoenix
7de7a17bae Update effect list 2020-10-27 13:25:47 +03:00
DarkPhoenix
50b8c3fc61 Avoid subtraction of attacker radius for ships with AoE bursts twice 2020-10-27 00:37:24 +03:00
DarkPhoenix
dab5d1f211 Fix item diff script 2020-10-25 00:11:56 +03:00
DarkPhoenix
5141a30e22 Update static data to 1830766 2020-10-23 21:27:18 +03:00
DarkPhoenix
1172e5011e Merge branch 'i18n' into singularity 2020-10-23 19:53:36 +03:00
DarkPhoenix
ea2141b719 Update effects 2020-10-22 17:58:10 +03:00
DarkPhoenix
9f7fdfc800 Update static data to 1828662 2020-10-22 13:53:57 +03:00
DarkPhoenix
de63b11a80 Rename invasion profiles 2020-10-22 12:38:55 +03:00
DarkPhoenix
514d11e6f2 Bump pyfa version 2020-10-13 18:38:29 +03:00
DarkPhoenix
9d759054ca Add migrations/conversions 2020-10-13 18:33:21 +03:00
DarkPhoenix
c162d96cc0 Rename triglavian profiles 2020-10-13 18:27:05 +03:00
DarkPhoenix
3c22397377 Update staticdata 2020-10-13 17:58:41 +03:00
DarkPhoenix
20e605c30f Make quantum cores invalid modules for pyfa fits 2020-09-24 16:51:40 +03:00
DarkPhoenix
9601887855 Add ability to import modules from ESI directly into pyfa 2020-09-23 17:24:40 +03:00
DarkPhoenix
0a7f5b6092 Bump version 2020-09-23 14:35:18 +03:00
DarkPhoenix
e27ea79bc6 Add recalculation after unloading charge 2020-09-23 13:48:24 +03:00
DarkPhoenix
adb0609eb8 Update staticdata to 1810288 2020-09-23 13:35:18 +03:00
DarkPhoenix
39b08baff0 Change damage pattern order 2020-09-23 13:30:07 +03:00
DarkPhoenix
e4388bf835 Merge branch 'singularity' into master 2020-09-23 13:09:35 +03:00
Anton Vorobyov
b9cae5165b Merge pull request #2273 from Neugeniko/master
Addresses Issue #2265. Damage and Target Profiles for new abyssal NPC's.
2020-09-23 12:39:10 +03:00
Neugeniko
06e6589209 Edit Concord Profiles due to CCP patch updates. 2020-09-22 21:56:26 +10:00
Ryan Holmes
9f21b251ff Fix string escape (again) 2020-09-21 20:43:49 -04:00
Ryan Holmes
7d7a499070 Escape string 2020-09-21 20:35:49 -04:00
Ryan Holmes
e18a3bfab5 #2272 info blurb 2020-09-21 20:35:20 -04:00
blitzmann
a80b7c098a Use natural sort for dictionaries when dumping staticdata 2020-09-20 12:57:23 -04:00
Ryan Holmes
1580a8ddc9 New translations lang.pot (Chinese Simplified) 2020-09-17 11:09:10 -04:00
DarkPhoenix
385215b717 Bump version 2020-09-17 01:31:45 +03:00
DarkPhoenix
d09913d61b Add 2 new fax effects 2020-09-17 01:31:24 +03:00
DarkPhoenix
b9059835ce Update static data to 1806850 2020-09-17 01:13:08 +03:00
DarkPhoenix
7af28b497c Fix several bugs related to siege mod rep cycle time mutations 2020-09-16 16:16:44 +03:00
DarkPhoenix
5cf555b8ce Bump version 2020-09-16 14:11:29 +03:00
DarkPhoenix
a25bb1aaa3 Update static data to 1805757 2020-09-16 14:05:33 +03:00
DarkPhoenix
24a54c9cec Merge branch 'singularity' into master 2020-09-16 14:04:02 +03:00
Neugeniko
0b9e6bf55d Damage Changes from sisi to tq for "Depths of the Abyss' patch. 2020-09-15 21:31:08 +10:00
Neugeniko
41070ce22e Fixed text error with menu names. 2020-09-14 09:24:19 +10:00
Neugeniko
7b5aab8a35 Merge pull request #2 from Neugeniko/Issue-#2265,-Update-Target-profiles
Update target profiles for Abyss npcs and add new profiles for new ab…
2020-09-13 20:15:26 +10:00
Neugeniko
c9fe6c959d Update target profiles for Abyss npcs and add new profiles for new abyss npc, Issue #2265 2020-09-13 20:15:09 +10:00
Neugeniko
67cf3a3a26 Merge pull request #1 from Neugeniko/Issue-#2265,-Update-Damage-profiles
Update damage profiles for Abyss npcs and add new profiles for new ab…
2020-09-13 19:33:10 +10:00
Neugeniko
c644d528e9 Update damage profiles for Abyss npcs and add new profiles for new abyss npcs, see Issue: #2265 2020-09-13 19:31:27 +10:00
DarkPhoenix
c1302ce7ee Fix version 2020-09-10 04:38:52 +03:00
DarkPhoenix
e4ea9c2ab9 Bump version 2020-09-10 04:38:20 +03:00
DarkPhoenix
cca5781ca8 Bump version 2020-09-10 04:30:20 +03:00
DarkPhoenix
9760d2c4a5 Merge branch 'master' into singularity 2020-09-10 04:26:42 +03:00
DarkPhoenix
4efa9a6961 Always return some value in mutators 2020-09-10 04:25:37 +03:00
DarkPhoenix
b27d4bcbbd Merge branch 'master' into singularity 2020-09-10 03:57:34 +03:00
DarkPhoenix
7bff295012 Add some jargon definitions for capital mods with numbers 2020-09-10 03:55:52 +03:00
DarkPhoenix
01249dfd9c Validate presence of mutaplasmid item 2020-09-10 03:28:36 +03:00
DarkPhoenix
0cc2668c1c Update warfare buffs for caustic cloud 2020-09-10 02:00:15 +03:00
DarkPhoenix
dfad734a0a Update staticdata to 1801413 2020-09-10 01:01:31 +03:00
DarkPhoenix
66c901a2b0 Bump version 2020-09-09 16:38:29 +03:00
DarkPhoenix
3673e7f39e Take measures against ESI spamming error 2020-09-09 16:35:57 +03:00
Anton Vorobyov
124b35d108 Merge pull request #2264 from jrdn/delete_all_esi_fits
Delete all of a character's fits via ESI
2020-09-09 15:12:01 +03:00
DarkPhoenix
5181e8e95a Reduce point radius for linux when scaled up 2020-09-09 15:00:35 +03:00
Anton Vorobyov
48cc4ce9d4 Merge pull request #2260 from YaarPodshipnik/fix-vector-picker-dpi-scaling
Fix HiDPI scaling issues in VectorPicker
2020-09-09 14:22:21 +03:00
Anton Vorobyov
07ff43b576 Merge branch 'master' into fix-vector-picker-dpi-scaling 2020-09-09 14:09:16 +03:00
DarkPhoenix
70969cc9fb Fix structure ewar skill not affecting ECM batteries 2020-09-09 13:56:06 +03:00
DarkPhoenix
d23bf2fa4c Add SBA tiericide conversions 2020-09-09 13:33:07 +03:00
DarkPhoenix
04c8659922 Update static data to 1800064 2020-09-09 13:21:16 +03:00
Jordan Greenberg
5e3b92699b Delete all of a character's fits via ESI 2020-09-05 15:32:52 -07:00
Yaar Podshipnik
1755ab4c3f Fix HiDPI scaling issues in VectorPicker
VectorPicker was DPI unware, so when asking GetClientSize() it would
get the actual size multiplied by the scaling factor of GetContentScaleFactor().
This made the widget seem to be cut off and display only the upper left
quarter of the circle.
Similarly, when choosing the font size for the percentages, it wouldn't
scale the maximum size, resulting in very large text.
2020-08-26 17:01:20 +01:00
DarkPhoenix
26ec741094 Bump version 2020-08-25 14:21:49 +03:00
DarkPhoenix
530f7510d5 Update staticdata to 1792574 2020-08-25 14:18:27 +03:00
DarkPhoenix
37ab704cd7 Merge branch 'singularity' into master 2020-08-25 14:17:21 +03:00
DarkPhoenix
683ad3802f Unmutate destination module when moving from cargo 2020-08-25 00:46:51 +03:00
DarkPhoenix
c882351886 Make propmod speed bonuses penalizable as postMul chain, fixes black hole + propmod speeds 2020-08-21 00:15:32 +03:00
DarkPhoenix
9d71215c52 Bump version 2020-08-20 16:31:00 +03:00
DarkPhoenix
d2a0605abe Update static data to 1790093 2020-08-20 16:27:05 +03:00
DarkPhoenix
bee6652cc6 Bump version 2020-08-18 00:51:34 +03:00
DarkPhoenix
047ef75960 Update effects 2020-08-18 00:51:11 +03:00
DarkPhoenix
32e57f8138 Update staticdata to 1788518 2020-08-18 00:40:07 +03:00
DarkPhoenix
e891804b64 Allow more than 1 system-wide effect 2020-08-17 13:36:52 +03:00
DarkPhoenix
7d42b89726 Merge branch 'master' into singularity 2020-08-17 11:43:50 +03:00
Anton Vorobyov
6561996327 Merge pull request #2256 from m-sasha/perf
Re-render tab when its disabled state changes.
2020-08-17 11:42:35 +03:00
Alexander Maryanovsky
0d2ad6eac2 No longer recomputing all tab sizes when a single tab changes its disabled state. 2020-08-16 18:00:01 +03:00
Alexander Maryanovsky
3b2bfd01c5 Re-render tab when it's disabled state changes. 2020-08-16 17:45:34 +03:00
DarkPhoenix
a317dab9a8 Bump version 2020-08-15 03:56:54 +03:00
DarkPhoenix
db6d8b93e9 Re-enable meta storm menu 2020-08-15 03:39:48 +03:00
DarkPhoenix
7d585c1a62 Merge branch 'master' into singularity
# Conflicts:
#	staticdata/fsd_lite/evetypes.json
#	staticdata/phobos/metadata.json
2020-08-15 03:34:45 +03:00
DarkPhoenix
c45e84470e Add storm effects 2020-08-15 03:27:47 +03:00
DarkPhoenix
ab3b40e136 Rerun effectUsedBy 2020-08-15 02:08:09 +03:00
DarkPhoenix
b566a8bfa6 Update static data to 1786419 2020-08-15 02:06:19 +03:00
DarkPhoenix
ccb395d592 Add standard outpost rig effect 2020-08-13 13:17:50 +03:00
DarkPhoenix
3d039724c9 Relax ship name format restrictions even further 2020-08-13 01:18:03 +03:00
DarkPhoenix
d0e7e7eed5 Merge branch 'master' of github.com:pyfa-org/Pyfa into master 2020-08-13 01:16:08 +03:00
DarkPhoenix
a387bc8d09 Fix import of ship/structure names containing single quotes 2020-08-13 01:15:41 +03:00
Anton Vorobyov
94344bd432 Merge pull request #2254 from Neugeniko/Issue/2253
Updated 'Retaliating Caldari Entities' target profile
2020-08-12 12:14:48 +03:00
DarkPhoenix
a976fb33f0 Fix defender velocity hardwiring 2020-08-12 00:11:52 +03:00
DarkPhoenix
f850fdf0d5 Bump version 2020-08-11 19:59:46 +03:00
DarkPhoenix
0fcbedba45 Disable metaliminal storm context menu 2020-08-11 19:58:44 +03:00
DarkPhoenix
5707bddacd Update staticdata to 1782806 2020-08-11 19:56:33 +03:00
Neugeniko
8ed9257dfa Updated 'Retaliating Caldari Entities' target profile
2020/08/12 Eve Online Patch increased Em Resists for some npc in the group of  'Retaliating Caldari Entities'
The groups target profile has been recalculated and edited.
2020-08-12 02:56:13 +10:00
DarkPhoenix
056ae590cf Merge branch 'master' into singularity 2020-08-10 17:39:37 +03:00
DarkPhoenix
7733fd38c2 Do not set read-only property 2020-08-10 17:39:08 +03:00
DarkPhoenix
fef78c971f Add effects used by metaliminal storms
Most of them are stubs, so just 2 so far
2020-08-05 22:57:45 +03:00
DarkPhoenix
e52ceacdb9 Add liminal storms to projection menu 2020-08-05 22:46:43 +03:00
DarkPhoenix
85b2d7af8d Add item conversions
Based on my own assumptions about them
2020-08-05 22:21:27 +03:00
DarkPhoenix
3e658a31bb Process renamed items 2020-08-05 21:54:56 +03:00
DarkPhoenix
0d2a4d4d44 Update metadata to 1780302 2020-08-05 21:40:59 +03:00
Ryan Holmes
c08216252e New translations lang.pot (Chinese Simplified) 2020-08-04 11:03:34 -04:00
blitzmann
02219846a7 Merge branch 'i18n' into crowdin_l10n 2020-08-02 22:57:24 -04:00
blitzmann
ff0af7cce7 Add check to see if crowdin API key is available 2020-08-02 22:25:15 -04:00
blitzmann
0e8316192b data updates 2020-08-02 20:20:25 -04:00
blitzmann
c0f8099f49 Merge branch 'master' into i18n
# Conflicts:
#	staticdata/fsd_binary/typedogma.json
#	staticdata/fsd_lite/evetypes.json
#	staticdata/phobos/metadata.0.json
#	staticdata/phobos/traits.json
2020-08-02 18:54:31 -04:00
Ryan Holmes
32a03dedaa I18n - Fixes for translations during build (#2251) 2020-08-02 18:52:55 -04:00
Ryan Holmes
5ae43fc648 New translations lang.pot (Spanish) 2020-08-02 13:10:03 -04:00
Ryan Holmes
d1f26dd4db New translations lang.pot (Turkish) 2020-08-02 13:10:01 -04:00
Ryan Holmes
c595e4426e New translations lang.pot (Chinese Simplified) 2020-08-02 13:09:59 -04:00
Ryan Holmes
abe7fa4ac1 New translations lang.pot (Russian) 2020-08-02 13:09:57 -04:00
Ryan Holmes
fd5d6ffca5 New translations lang.pot (Korean) 2020-08-02 13:09:55 -04:00
Ryan Holmes
abaa7f395d New translations lang.pot (Japanese) 2020-08-02 13:09:53 -04:00
Ryan Holmes
8e3893d6c6 New translations lang.pot (Italian) 2020-08-02 13:09:52 -04:00
Ryan Holmes
5de808456e New translations lang.pot (French) 2020-08-02 13:09:50 -04:00
Ryan Holmes
362d081338 Merge pull request #2240 from zhaoweny/i18n
I18n: more string annotations (mostly tooltips)
2020-08-02 13:07:11 -04:00
Anton Vorobyov
bc2cdcdea7 Merge pull request #2249 from m-sasha/undup-tab-selection
_TabsContainer.CheckTabSelected no longer posts PageChanged event twice
2020-07-31 11:59:38 +03:00
Anton Vorobyov
30ad554cc0 Merge pull request #2248 from m-sasha/no-half-second-sleep
Do not sleep for half a second in OpenFitsThread.run()
2020-07-31 11:59:06 +03:00
Anton Vorobyov
4a85fd5d1b Merge pull request #2247 from m-sasha/fast-tabs-resize
Ignore _TabRenderer.SetSize when new size is the same as old size
2020-07-31 11:58:35 +03:00
Alexander Maryanovsky
4d8dbe74bd _TabsContainer.CheckTabSelected no longer posts PageChanged event twice 2020-07-30 16:03:00 +03:00
Alexander Maryanovsky
016f2b44ff Do not sleep for half a second in OpenFitsThread.run() 2020-07-30 11:35:30 +03:00
Alexander Maryanovsky
676794baed Ignore _TabRenderer.SetSize when new size is the same as old size. 2020-07-30 11:31:00 +03:00
Anton Vorobyov
ca488089fd Merge pull request #2231 from fonsuiplaysvideogames/master
Updates to Abyss room-wide and localized weather effects
2020-07-30 08:04:42 +03:00
Anton Vorobyov
96f9b9a719 Merge branch 'master' into master 2020-07-30 08:04:09 +03:00
fonsuiplaysvideogames
508572e08b Update fit.py 2020-07-29 21:33:15 -04:00
DarkPhoenix
f91e0b2e23 Do not crash on ship w/o labels and w/o slots 2020-07-29 09:02:46 +03:00
DarkPhoenix
93ae9e0891 Bump version 2020-07-28 17:39:20 +03:00
DarkPhoenix
2ed5dbc3c7 Avoid using newer matplotlib 2020-07-28 17:12:15 +03:00
DarkPhoenix
bca8ba3114 Bump version 2020-07-28 15:58:15 +03:00
DarkPhoenix
174ac97682 Implement new CS effects 2020-07-28 15:57:55 +03:00
DarkPhoenix
2d9e873d42 Update staticdata to 1777281 2020-07-28 15:21:08 +03:00
zhaoweny
7bc4e5b34c i18n: update lang.pot for plural strings 2020-07-27 15:31:37 +08:00
zhaoweny
9450f4a915 i18n: improve wording for locale/README.md 2020-07-27 15:21:42 +08:00
zhaoweny
1a9ebf0772 i18n: add detail for excluding a folder (virtualenv) when generating new lang.pot
Because in-tree virtualenv folder is developer-defined, we can't really predict and provide specific commands for excluding virtualenv.
2020-07-27 14:51:26 +08:00
zhaoweny
1c7036e612 i18n: update locale/README.md to reflect current usage of xgettext 2020-07-27 09:59:59 +08:00
zhaoweny
87cb9713f9 i18n: update lang.pot 2020-07-26 18:20:05 +08:00
zhaoweny
d3f0d9d41a i18n: use named placeholder to allow Russian translation to rearrange word order 2020-07-26 18:20:05 +08:00
zhaoweny
b0d088ab31 i18n: update lang.pot for more Tooltip annotation 2020-07-26 18:20:05 +08:00
zhaoweny
47bda45516 i18n: more Tooltip annotation 2020-07-26 18:20:05 +08:00
zhaoweny
95d18dc3ab i18n: update lang.pot for annotating SensorStr scanType 2020-07-26 18:20:05 +08:00
zhaoweny
be5f8ce37d i18n: annotate for SensorStr scanType 2020-07-26 18:20:05 +08:00
DarkPhoenix
33377357f6 Change dampener definition to include named variations 2020-07-26 10:36:55 +03:00
blitzmann
107856ab0e Add hyperlink to README for translations 2020-07-25 20:14:11 -04:00
Ryan Holmes
c1f9b1d0d7 Fix version dump when tag ahas a + in it 2020-07-25 17:53:42 -04:00
Ryan Holmes
469c255bbf Show progress of translations 2020-07-25 17:39:28 -04:00
Ryan Holmes
bec9eb2224 Add translation progress dump to travis and remove the data dump to console 2020-07-25 16:54:15 -04:00
Ryan Holmes
7dc362fef9 fix api key environment variable name 2020-07-25 16:42:29 -04:00
Ryan Holmes
80781c7eff debugging why build is failing 2020-07-25 16:33:14 -04:00
Ryan Holmes
fca78a72e4 Create translation progress dump script and add to appveryor build (travis soon) 2020-07-25 16:27:42 -04:00
Ryan Holmes
7438fb72bc revert compile_lang, don't have access to config here... will need to fix 2020-07-25 12:40:12 -04:00
Ryan Holmes
b6f24ec4c2 New translations lang.pot (Spanish) 2020-07-24 22:58:23 -04:00
Ryan Holmes
db2bd22ddc New Crowdin updates (#2238)
* New translations lang.pot (Turkish)

* New translations lang.pot (Turkish)

* New translations lang.pot (Turkish)

* New translations lang.pot (Italian)

* New translations lang.pot (Italian)

* New translations lang.pot (Chinese Simplified)
2020-07-24 22:23:02 -04:00
blitzmann
bbcedbf2cb Better description for language flag 2020-07-24 22:22:11 -04:00
blitzmann
937adb68d7 Show user-friendly name for the eos lang dropdown as well 2020-07-24 22:14:54 -04:00
blitzmann
1fe83ddcdf Finish up the dynamic translations 2020-07-24 21:59:38 -04:00
Ryan Holmes
ac1e6fe5b7 Starting to generate list of languages dynamically 2020-07-24 20:42:47 -04:00
Ryan Holmes
cb99151f69 New Crowdin updates (#2236)
* New translations lang.pot (Russian)

* New translations lang.pot (Russian)

* New translations lang.pot (Russian)

* New translations lang.pot (Italian)

* New translations lang.pot (Russian)

* New translations lang.pot (Russian)

* New translations lang.pot (Russian)

* New translations lang.pot (Japanese)

* New translations lang.pot (Korean)

* New translations lang.pot (Italian)

* New translations lang.pot (Russian)

* New translations lang.pot (Italian)

* New translations lang.pot (Japanese)

* New translations lang.pot (Japanese)

* New translations lang.pot (Russian)

* New translations lang.pot (Italian)

* New translations lang.pot (Russian)

* New translations lang.pot (Italian)

* New translations lang.pot (Korean)

* New translations lang.pot (Korean)

* New translations lang.pot (Italian)

* New translations lang.pot (Italian)

* New translations lang.pot (Russian)

* New translations lang.pot (Russian)

* New translations lang.pot (Russian)

* New translations lang.pot (Russian)

* New translations lang.pot (Russian)

* New translations lang.pot (French)

* New translations lang.pot (Italian)

* New translations lang.pot (Japanese)

* New translations lang.pot (Korean)

* New translations lang.pot (Russian)

* New translations lang.pot (Chinese Simplified)

* New translations lang.pot (Russian)

* New translations lang.pot (Russian)
2020-07-22 23:48:47 -04:00
blitzmann
0b850808cb Update README to remove reference to 'only support EVE languages' 2020-07-22 23:43:24 -04:00
blitzmann
ffb14a2393 Add ability to set eos language separate from pyfa language 2020-07-22 23:41:38 -04:00
blitzmann
f1feb8cebe Fix typo 2020-07-22 00:23:55 -04:00
blitzmann
507cc09a2f update data 2020-07-19 21:33:35 -04:00
blitzmann
6990a71d14 Merge remote-tracking branch 'origin/master' into i18n
# Conflicts:
#	staticdata/fsd_binary/marketgroups.json
#	staticdata/fsd_binary/typedogma.json
#	staticdata/fsd_lite/evegroups.json
#	staticdata/fsd_lite/evetypes.json
#	staticdata/phobos/metadata.0.json
2020-07-19 21:33:14 -04:00
blitzmann
2239428b71 Merge branch 'i18n' of https://github.com/pyfa-org/Pyfa into i18n 2020-07-19 21:03:03 -04:00
blitzmann
ce755e393c Merge branch 'i18n' of https://github.com/zhaoweny/Pyfa into i18n 2020-07-19 20:52:16 -04:00
blitzmann
d07e46099b Update localization README 2020-07-19 20:52:07 -04:00
Ryan Holmes
d11e9c52c0 Merge pull request #2235 from pyfa-org/crowdin_l10n
New Crowdin updates
2020-07-19 20:48:23 -04:00
DarkPhoenix
3ee0ee7e40 Fix drone sorting in projected view 2020-07-19 23:31:00 +03:00
Ryan Holmes
57aaed4140 New translations lang.pot (Chinese Simplified) 2020-07-19 14:18:22 -04:00
Ryan Holmes
af76103957 New translations lang.pot (Chinese Simplified) 2020-07-19 14:04:35 -04:00
Ryan Holmes
fdaf682dbd New translations lang.pot (Russian) 2020-07-19 14:04:33 -04:00
Ryan Holmes
26e1ab47f2 New translations lang.pot (Korean) 2020-07-19 14:04:31 -04:00
Ryan Holmes
f1ffd369d0 New translations lang.pot (Japanese) 2020-07-19 14:04:29 -04:00
Ryan Holmes
9ac8969d43 New translations lang.pot (Italian) 2020-07-19 14:04:27 -04:00
Ryan Holmes
7bc4a1d334 New translations lang.pot (French) 2020-07-19 14:04:26 -04:00
Ryan Holmes
bb9866e175 Merge pull request #2232 from zhaoweny/i18n
i18n: string annotation for graph panels
2020-07-19 14:03:06 -04:00
zhaoweny
d81e25fc50 i18n/zh_CN: update translation for graph pannels 2020-07-17 12:48:40 +08:00
zhaoweny
6b11fd0a91 i18n: string annotation for graph panels 2020-07-17 12:44:47 +08:00
fonsuiplaysvideogames
2f02747b29 Update fit.py 2020-07-16 16:24:08 -04:00
fonsuiplaysvideogames
78b176a135 Update fit.py 2020-07-16 16:20:24 -04:00
DarkPhoenix
52063beea9 BCS affect defender missiles now 2020-07-16 00:40:04 +03:00
DarkPhoenix
03a55d94e9 Bump version 2020-07-14 20:29:47 +03:00
DarkPhoenix
db256f57d1 Adjust ANP jargon entries 2020-07-14 20:29:27 +03:00
DarkPhoenix
8de6d78be0 Update effect docstrings 2020-07-14 20:25:45 +03:00
DarkPhoenix
902a00d37d Add conversions 2020-07-14 20:24:42 +03:00
DarkPhoenix
523cb1467e Merge branch 'issue_2205' 2020-07-14 20:21:23 +03:00
DarkPhoenix
f75de70d79 Update staticdata to 1769230 2020-07-14 20:00:23 +03:00
DarkPhoenix
4b635f4d21 Remove www to make dropdown shorter on gtk 2020-07-14 17:31:19 +03:00
DarkPhoenix
63632e09b3 Group market sources up to avoid cross-server price data confusion 2020-07-14 17:25:30 +03:00
Ryan Holmes
8a5b2b3e48 Merge pull request #2227 from pyfa-org/crowdin_l10n
New Crowdin updates
2020-07-12 21:16:07 -04:00
Ryan Holmes
99d1655c20 New translations lang.pot (Chinese Simplified) 2020-07-12 21:11:16 -04:00
Ryan Holmes
91c30832b3 New translations lang.pot (Russian) 2020-07-12 21:11:15 -04:00
Ryan Holmes
390f198310 New translations lang.pot (Korean) 2020-07-12 21:11:13 -04:00
Ryan Holmes
316265e19d New translations lang.pot (Japanese) 2020-07-12 21:11:11 -04:00
Ryan Holmes
8754326446 New translations lang.pot (Italian) 2020-07-12 21:11:09 -04:00
Ryan Holmes
443769badd New translations lang.pot (French) 2020-07-12 21:11:08 -04:00
Ryan Holmes
0cee45e33d Update Crowdin configuration file 2020-07-11 18:37:23 -04:00
Ryan Holmes
9d02845875 Update Crowdin configuration file 2020-07-11 18:33:57 -04:00
blitzmann
5f97ba6931 Merge branch 'i18n' of https://github.com/pyfa-org/Pyfa into i18n 2020-07-11 18:25:43 -04:00
blitzmann
ca7358d2fc Include .pot file in epo 2020-07-11 18:25:30 -04:00
Ryan Holmes
dcdd50a91b Update Crowdin configuration file 2020-07-11 18:01:10 -04:00
Ryan Holmes
9895339c13 Merge pull request #2222 from zhaoweny/i18n
i18n: more annotation, more translation
2020-07-11 12:27:45 -04:00
blitzmann
04f9c1c9f8 Move the pyfa app initialization back to where it was 2020-07-11 12:27:09 -04:00
zhaoweny
e536aa5f1c i18n: special comments to mark strings containing '%' mean literally '%', not part of escape sequence
see also:
* https://github.com/vslavik/poedit/issues/645
* https://www.gnu.org/software/gettext/manual/html_node/PO-Files.html
* https://www.gnu.org/software/gettext/manual/html_node/c_002dformat-Flag.html
2020-07-11 21:12:57 +08:00
zhaoweny
b82030090c i18n/zh_CN: update translation for damagePattern and targetProfiles 2020-07-11 16:09:45 +08:00
zhaoweny
9868b219d7 i18n: deferred translation and category annotation for targetProfile and damagePattern
* use deferred translation annotation, to annotate strings in eos.db and translate on the GUI side

  As @blitzmann said in #2222, we should not include wx as a dependency for eos module.

* category annotation _c for taking '[]' out of string literals
2020-07-11 16:09:02 +08:00
Zhao Wen Yuan
f9b3defd4b Merge pull request #1 from pyfa-org/zhaoweny-i18n
Update to translations for damage profiles
2020-07-11 14:54:22 +08:00
blitzmann
7451ce147e Create a dummy translation annotation for damage patterns, and do translations on the GUI side 2020-07-10 23:36:52 -04:00
zhaoweny
2203767fde i18n/zh_CN: update Chinese translation for damage pattern and target profile 2020-07-09 10:17:57 +08:00
zhaoweny
067f60bfcd i18n: separate strings for simpler translation 2020-07-08 17:51:55 +08:00
zhaoweny
d4c9423c77 i18n: annotate targetProfile.py, damagePattern.py
Also init PyfaApp earlier for targetProfile.py and damagePattern.py localization
2020-07-08 14:42:05 +08:00
zhaoweny
ffbae4826d i18n/zh_CN: tweaks for Chinese translation 2020-07-07 17:13:47 +08:00
zhaoweny
da2ec4d759 i18n/zh_CN: update translation for service slot 2020-07-07 16:37:13 +08:00
zhaoweny
5a45863432 i18n: add translation mapping for service slot 2020-07-07 16:37:13 +08:00
zhaoweny
605addd0d6 i18n/zh_CN: update translation for pyfa_gauge.py 2020-07-07 16:37:13 +08:00
zhaoweny
5f45709118 i18n: annotate pyfa_gauge.py 2020-07-07 16:37:12 +08:00
blitzmann
76674435c9 Remove default for language 2020-07-05 15:22:08 -04:00
blitzmann
dcdf69d658 Merge branch 'master' into i18n 2020-07-05 14:10:04 -04:00
blitzmann
47e428d57e Fix publicity and group checking 2020-07-05 14:00:46 -04:00
blitzmann
95af9660b6 Move mainframe import below app init, which will allow us to set up locale correctly before anything else loads 2020-07-05 14:00:18 -04:00
blitzmann
6e7b8cca24 Fix missing damage type icons for missle selector 2020-07-05 13:42:23 -04:00
Ryan Holmes
af62a7273f Merge pull request #2220 from zhaoweny/i18n
i18n: return compiled_data in _readData
2020-07-02 09:00:33 -04:00
zhaoweny
6cdb4a7dc1 i18n: return compiled_data in _readData 2020-07-02 16:13:39 +08:00
blitzmann
469f0a9be7 Skills were attempting to match English names with translated names 2020-07-01 20:49:06 -04:00
blitzmann
832ad5bc6b Merge commit '5d95877d2c856bc7336d2032ecb2a4d5c24c75c6' into i18n2 2020-07-01 20:47:50 -04:00
Joshua Pierce
bc77f24471 Translating Typo To English 2020-07-01 20:45:40 -04:00
zhaoweny
5d95877d2c i18n: use rsplit on typeName to avoid explict for-loop 2020-07-01 21:45:21 +08:00
zhaoweny
3642ff8cee i18n: update zh_CN translation 2020-07-01 11:48:55 +08:00
zhaoweny
8cc770467e i18n: fix crash on right clicking a Tactical Destroyer fit 2020-07-01 11:48:37 +08:00
zhaoweny
85e779469f i18n: more annotation 2020-07-01 11:48:06 +08:00
blitzmann
29f6ac0d99 Initialize other langauges, update README to provide more information, simplify the langauge codes 2020-06-30 23:33:34 -04:00
Ryan Holmes
4d49512a7e Merge pull request #2217 from zhaoweny/i18n
i18n: sort zh_CN translation, more zh_CN translation
2020-06-30 09:32:45 -04:00
zhaoweny
dae13f934e i18n/zh_CN: update translation 2020-06-30 17:21:02 +08:00
zhaoweny
bf99132f2f i18n: more string annotation 2020-06-30 17:19:40 +08:00
zhaoweny
25a694bd69 i18n: annotate copySelectDialog.py 2020-06-30 16:37:36 +08:00
zhaoweny
038b7ce931 i18n: fix 'Level Not learned' issue in #2202 (comment) 2020-06-30 15:15:15 +08:00
zhaoweny
8625dea833 i18n: update locale/README.md to include sort detail; sort zh_CN lang.po 2020-06-30 11:50:19 +08:00
Ryan Holmes
6f3049f39b Merge pull request #2208 from pyfa-org/i18n_db
i18n Localization - Database
2020-06-29 22:32:29 -04:00
blitzmann
52b74d9950 Some export fixes 2020-06-29 22:15:26 -04:00
blitzmann
dc60c3d68b Update the dump script with latest phobos changes (including custom write in case phobos doesn't merge it in) 2020-06-29 22:06:03 -04:00
blitzmann
6612724beb Fix market group generation to be language agnostic 2020-06-29 21:26:58 -04:00
blitzmann
c242a18a34 Ensure that languages that are set are actually supported 2020-06-29 21:10:10 -04:00
blitzmann
732634fefa Updated the string manipulation texts to be prefixed, allowing for better context management if needed for things like "Effects", "System Effects". Added en translations to handle them on the English side. Updated the zh translations using merge:
`msgmerge --update locale/zh_CN/LC_MESSAGES/lang.p o locale/lang.pot`
2020-06-29 20:57:17 -04:00
blitzmann
10a4f62b78 Merge branch 'i18n_db' of https://github.com/pyfa-org/Pyfa into i18n_db 2020-06-29 20:13:18 -04:00
Ryan Holmes
df78eb5781 Merge pull request #2214 from copyliu/cevemarket
add market source ceve-market
2020-06-29 20:07:44 -04:00
blitzmann
c39ae080fb Update db-update to be language agnostic (further testing required) 2020-06-29 20:03:33 -04:00
Ryan Holmes
e5e8a570db Merge pull request #2213 from zhaoweny/i18n_db
i18n_db: sort and (slightly) update zh_CN translation
2020-06-29 11:01:16 -04:00
Copy Liu
c1a5828d6b add market source ceve-market 2020-06-29 20:58:46 +08:00
zhaoweny
eaea671f4e i18n_db: remove git-lfs from Travis CI config 2020-06-29 15:38:10 +08:00
zhaoweny
1145a27e04 i18n_db: update shipBrowser to use displayName 2020-06-29 15:31:50 +08:00
zhaoweny
b18205a184 i18n_db: sort and (slightly) update zh_CN translation 2020-06-29 10:51:43 +08:00
blitzmann
9703ce70d9 Merge branch 'i18n' into i18n_db 2020-06-28 17:57:20 -04:00
blitzmann
47dd6f7bba Add annotations to the rack separators in the fitting view 2020-06-28 17:56:57 -04:00
blitzmann
3db9c637e0 Force EFT export to English variant 2020-06-28 17:41:28 -04:00
blitzmann
77d233ca1c Fix environmental variables for localizations 2020-06-28 13:38:33 -04:00
blitzmann
b225093b1f Fix exception when opening WH projection menu (still have other issues that need to be worked out tho) 2020-06-28 11:57:39 -04:00
blitzmann
4a6a3fc6ea Keep name on group, create displayName property to point to translated version 2020-06-28 00:54:51 -04:00
blitzmann
2fd4168ab7 Fix two group name issues 2020-06-28 00:40:59 -04:00
Ryan Holmes
22498e5605 Merge pull request #2211 from zhaoweny/i18n_db
i18n_db: fix ValueError exception when name should be groupName
2020-06-28 00:18:01 -04:00
zhaoweny
f515536d1a i18n_db: fix ValueError exception when name should be groupName 2020-06-28 11:42:27 +08:00
blitzmann
5c7e3fe782 Remove LSF, convert staticdata files to split files 2020-06-27 22:27:28 -04:00
blitzmann
546a3b6e01 Fix turrent ammo type grouping (using English names for grouping). Also made missile damage type grouping translation-aware 2020-06-27 21:06:16 -04:00
blitzmann
1d0c890c23 Fix the preferences not loading / saving correctly (needed to be done after paths were set) 2020-06-27 21:05:00 -04:00
blitzmann
5377210b89 Merge branch 'master' of https://github.com/pyfa-org/Pyfa 2020-06-26 23:30:51 -04:00
blitzmann
1936255f2c Merge branch 'issue2209' 2020-06-26 23:30:40 -04:00
blitzmann
b3e5763cfc Fix for #2209 - Flip the None check to happen first instead of second, otherwise if Notes is None then the first check will fail due to trying to len(None) 2020-06-26 23:30:15 -04:00
DarkPhoenix
7442f315c9 Do not activate scanners by default 2020-06-26 21:30:43 +03:00
blitzmann
427b1189f7 travis fix? 2020-06-25 22:37:31 -04:00
blitzmann
19da89a467 Fixes to some translations 2020-06-25 22:23:12 -04:00
blitzmann
be4f6241a9 more tweaks 2020-06-25 22:06:57 -04:00
blitzmann
03e29e02b1 update unit to include all the translations 2020-06-25 22:04:04 -04:00
blitzmann
853b65d6aa Some clean up and commenting out unused columns [I think] 2020-06-25 22:01:27 -04:00
blitzmann
45dae7a031 Remove index on items.description(lol) 2020-06-25 21:51:24 -04:00
blitzmann
4130852e76 Remove old todo 2020-06-25 21:47:26 -04:00
blitzmann
3d238a1cc1 Reset forced language to English 2020-06-25 21:36:09 -04:00
blitzmann
977ae98ea6 more conversions on the traits 2020-06-25 21:35:26 -04:00
blitzmann
dac35597ea Update traits 2020-06-25 21:32:43 -04:00
blitzmann
0a1489719b Rename the descriptions so as not to confuse the synonym 2020-06-25 21:18:25 -04:00
blitzmann
4ff63e5fc4 Create deferred descriptions for each of the item description languages, and then one synonym to rule them all 2020-06-25 21:09:32 -04:00
blitzmann
c3389fb19b Add all languages to market groups 2020-06-25 21:08:37 -04:00
blitzmann
42706f35c6 Update various instances where Category name wasn't displaying the translated version (in context menus) 2020-06-25 20:32:49 -04:00
blitzmann
c7ec87b979 Fix a couple of bugs by referencing raw names, and include all translations for types table 2020-06-25 19:23:28 -04:00
blitzmann
66140f092b More tweaks to dynamically generate the columns, and a fix for attribute display names 2020-06-25 00:40:29 -04:00
blitzmann
8be4b0dd91 Introduce way to dynamically map the languages, helps reduce the typing and also should "just work" if there's any new languages added 2020-06-25 00:09:36 -04:00
blitzmann
a2f65e9d46 categoryName > name (this was caused by having name not be the literal data rather than the synonym) 2020-06-24 23:59:58 -04:00
blitzmann
c040ff26c0 Update schema and db_update to support translations.
(language setting from preferences is borked for some reason)
2020-06-24 23:33:24 -04:00
blitzmann
92ecd8c844 Add the other files 2020-06-24 23:28:08 -04:00
blitzmann
60c8a66bea Add the other directories 2020-06-24 23:28:00 -04:00
blitzmann
b1ff13e370 Add fsd_binary 2020-06-24 23:27:16 -04:00
blitzmann
50b0df7d6d Track fsd_binary 2020-06-24 23:26:50 -04:00
blitzmann
281e30f78a Remove static data 2020-06-24 23:23:37 -04:00
blitzmann
821378e2c3 Add LFS, because... translations are heafty 2020-06-24 23:01:23 -04:00
blitzmann
433c9555bf Removed the util function - this would actually break older migrations if we added things that didn't exist in future iterations. Each migration should be self-contained 2020-06-24 15:18:43 -04:00
blitzmann
d1345fc71e Fixes #2205 - runs conversions on modules.baseItemID since the creation of baseItemID. Also, centralized the function to convert modules so that in case we ever add other things that need to be converted, they are all in one spot. 2020-06-24 15:09:51 -04:00
blitzmann
d4106a7229 fix mac spec file to include locales 2020-06-24 13:28:58 -04:00
blitzmann
079256a99f some tweaks to get the languages to compile 2020-06-24 13:01:36 -04:00
blitzmann
bcd7e6d56d Add language compilation to build process (maybe?) 2020-06-24 12:51:19 -04:00
blitzmann
e56db0d129 add script to automatically generate all languages 2020-06-24 12:47:11 -04:00
blitzmann
6035d26dd0 add msgfmt.py to scripts/ to have a way to produce localizations 2020-06-24 12:11:24 -04:00
Ryan Holmes
61127dc4d3 Merge pull request #2204 from zhaoweny/i18n
i18n: annotate Preferences, update zh_CN translations
2020-06-24 09:48:47 -04:00
zhaoweny
f4f0dc775c i18n/zh_CN: update translation for context menus 2020-06-23 16:45:30 +08:00
zhaoweny
d33976a2ad i18n: more annotation for context menus, stats view tooltips 2020-06-23 15:55:28 +08:00
zhaoweny
1c4de8b259 i18n: annotate gui/builtinContextMenus 2020-06-23 15:55:16 +08:00
zhaoweny
1079371ecf i18n/zh_CN: update translation for unified wildcard annotations 2020-06-23 01:36:00 +08:00
zhaoweny
85c3158e98 i18n: unify FileDialog wildcard string annotations 2020-06-23 01:16:42 +08:00
zhaoweny
2d97f0952e i18n/zh_CN: translation for item stat views 2020-06-22 20:04:21 +08:00
zhaoweny
396640e5b1 i18n: annotate item stat views 2020-06-22 20:03:53 +08:00
zhaoweny
aa8a03b18b i18n/zh_CN: update translation 2020-06-22 17:56:35 +08:00
zhaoweny
889e901cbd i18n: improve string literal annotations
1. annotate more strings for statsViews, itemStats
2. fix raw title and description for preferences
3. fix crash on opening AttributeEditor, characterEditor
2020-06-22 17:55:58 +08:00
zhaoweny
07696ce0ed i18n: fix typos in locale/README.md 2020-06-22 10:16:48 +08:00
zhaoweny
ffdbab87e9 i18n/zh_CN: lots of new strings for Preferences, more translations 2020-06-21 17:23:46 +08:00
zhaoweny
4eda1a1d66 i18n: minor tweaks for easier translation
1. tweaks for characterEditor.py and patternEditor.py according to PR discussion.
2. fix for "Recent Fits" label not translated
2020-06-21 17:11:58 +08:00
zhaoweny
29ec297acb i18n: annotate string literals in gui/builtinPreferenceViews 2020-06-21 16:06:43 +08:00
zhaoweny
7cd7d475db i18n: minor tweaks for locale/README.md 2020-06-21 15:35:41 +08:00
blitzmann
02aa4eb2b4 Add UI to change language 2020-06-20 15:46:30 -04:00
blitzmann
7cf9326311 Add language to eos config
This should be accessible now when table mapping occurs, so we should be able to use it to determine which column to pull from for translation values.
2020-06-20 15:31:10 -04:00
blitzmann
2214269cca Bring language selection to the forefront before wx app initialization 2020-06-20 15:20:56 -04:00
blitzmann
9b73f1c221 Final conversion of _t() 2020-06-20 15:13:33 -04:00
blitzmann
339367f730 More _t() conversion 2020-06-20 15:09:42 -04:00
blitzmann
68f7e6c709 Start converting _() to _t() 2020-06-20 15:07:14 -04:00
Ryan Holmes
eb657e804c Merge pull request #2203 from zhaoweny/i18n
I18n: more zh_CN translations and a better locale/README.md
2020-06-20 15:01:13 -04:00
blitzmann
413bc5d46c Minor tweaks per PR review 2020-06-20 14:59:49 -04:00
zhaoweny
e4e49cef54 i18n: extend locale/README.md and add translation workflow detail 2020-06-20 21:43:47 +08:00
zhaoweny
49cf03759b i18n: fix unresolved reference Inspection errors 2020-06-20 17:57:01 +08:00
zhaoweny
a05e7dbad1 i18n/zh_CN: add more UI translation 2020-06-20 17:42:52 +08:00
zhaoweny
d28665f172 i18n/zh_CN: add lots of UI translation 2020-06-20 16:03:42 +08:00
Ryan Holmes
1848acc1ba Merge pull request #2201 from pyfa-org/i18n_zhaoweny
I18n - zhaoweny
2020-06-19 23:22:40 -04:00
blitzmann
d9e5349edd Move supported languages to the LocaleSettings class, and use the command parameter as an override 2020-06-19 22:44:09 -04:00
blitzmann
d939b0e565 rename pyfa.po to lang.po 2020-06-19 22:27:35 -04:00
blitzmann
c290e9e23a Add various references 2020-06-19 22:25:23 -04:00
zhaoweny
ce0d8b1247 i18n/zh_CN: add more translation for builtinStatsViews
(cherry picked from commit 97c1c597c8c75450350123ed471f082e5ad8dabd)
2020-06-19 22:20:37 -04:00
zhaoweny
834e4a3d6e i18n: implement LocaleSettings, use wx.GetTranslation instead of gettext
(cherry picked from commit 3648a5c8c7b230a4b695a29ebfaad3877e7b644f)
2020-06-19 22:19:20 -04:00
zhaoweny
3d9b4c11d4 i18n/zh_CN: reformat code and ignore _
1. update `Pyfa_Inspections.xml` to ignore `_`

i18n module `gettext` will do a `gettext.install(...)` to put `_` function into `builtin` module. Currently PyCharm does not recognize this as a function and report as unresolved reference.

2. reformat code to remove padding for vertical dicts

3. update `Pyfa_CodeStyle.xml` to not pad vertical dicts

(cherry picked from commit e5a570a0078f05fe34c473841af6b7746e06bfca)
2020-06-19 22:17:49 -04:00
zhaoweny
2d3a661442 i18n/zh_CN: update zh_CN translation for lots of StatsViews
(cherry picked from commit bdac2f825fba27000c87f20de8e09e61ae1376d2)
2020-06-19 22:17:21 -04:00
zhaoweny
343f4a5196 i18n/zh_CN: basic gettext i18n setup and demo for zh_CN translation
(cherry picked from commit 8fe58a95f7d9c08d792b8e17605700607662f7c9)
2020-06-19 22:15:44 -04:00
blitzmann
31fd480fb0 Fix some missing references 2020-06-19 21:54:30 -04:00
blitzmann
8e83adf7db Merge tag 'v2.22.1' into i18n
# Conflicts:
#	gui/esiFittings.py
2020-06-19 21:51:45 -04:00
blitzmann
7fa638f62b More localization 2020-06-19 11:35:37 -04:00
DarkPhoenix
31cae0e54b Bump version 2020-06-18 17:28:02 +03:00
DarkPhoenix
b2cc3ae600 Update static data to 1751741 2020-06-18 13:52:46 +03:00
DarkPhoenix
62d1d6a06d Avoid crash when nothing is selected 2020-06-18 06:04:23 +03:00
DarkPhoenix
83fa567321 Pass primitives around instead of SQL Alchemy objects 2020-06-18 05:33:12 +03:00
DarkPhoenix
9b315b5870 Fix a few mistypes 2020-06-17 21:53:24 +03:00
DarkPhoenix
3094fd32fc Add destructible beacons to projected effects 2020-06-17 17:07:34 +03:00
DarkPhoenix
c3f1824a84 Rename charge export button 2020-06-16 21:06:42 +03:00
DarkPhoenix
b558ae3810 Add an option not to export charges to ESI 2020-06-15 23:38:07 +03:00
DarkPhoenix
c38f05902a Add alternate redo shortcut 2020-06-15 17:20:20 +03:00
DarkPhoenix
181e1e1e30 Make scanners active and expose data to misc column 2020-06-15 16:37:52 +03:00
DarkPhoenix
3fc26254ae bump version 2020-06-15 16:14:53 +03:00
DarkPhoenix
ca8822c455 Fix upwell ships' race 2020-06-15 16:14:20 +03:00
DarkPhoenix
4c55d32086 Update icons 2020-06-15 15:54:28 +03:00
DarkPhoenix
c13817708e Add vorton damage booster effect 2020-06-15 15:20:59 +03:00
DarkPhoenix
39cab4be72 Remove DB TDs from seen items 2020-06-15 15:00:36 +03:00
DarkPhoenix
f80473d073 Update static data to 1748920 2020-06-15 14:50:58 +03:00
DarkPhoenix
0d9be1b174 Merge branch 'singularity' 2020-06-15 14:42:13 +03:00
DarkPhoenix
83b0b3ff0e Fix jargon definitions for missile damage types 2020-06-13 12:33:06 +03:00
DarkPhoenix
c4b490907d Add alternate DNA format import 2020-06-12 02:20:32 +03:00
DarkPhoenix
f89fe4d5d7 Fix merge issue 2020-06-09 19:11:11 +03:00
DarkPhoenix
81dced03bf Disable ammo graph 2020-06-09 19:07:23 +03:00
DarkPhoenix
27d3bea8d8 Merge branch 'ammo_graph' into singularity
# Conflicts:
#	gui/targetProfileEditor.py
2020-06-09 19:04:20 +03:00
DarkPhoenix
7eaeea325d Bump version 2020-06-09 10:54:21 +03:00
DarkPhoenix
9695526352 Update staticdata to 1744483 2020-06-09 10:51:28 +03:00
DarkPhoenix
4f35bc38db Fix structure module drag'n'drop 2020-06-09 01:56:31 +03:00
blitzmann
2490d3de92 More localization 2020-06-07 13:44:15 -04:00
DarkPhoenix
d9c7c1a0fd Fix burst projectors' misc column 2020-06-06 19:22:55 +03:00
DarkPhoenix
a94e66e5ed Invuln - alias for old invulns 2020-06-06 02:25:08 +03:00
DarkPhoenix
e9c41c6439 Add renders of 2/3 of new ships 2020-06-06 01:42:57 +03:00
DarkPhoenix
b24ceb95f5 Add missing icons 2020-06-05 22:22:03 +03:00
DarkPhoenix
6ba6e0f69b Implement skill and booster effects, and bump version 2020-06-06 01:09:55 +03:00
DarkPhoenix
9aca49e539 Add damage mod effect and gun application readout 2020-06-06 00:25:19 +03:00
DarkPhoenix
01f1cdb175 Implement gun effect and its support in graphs 2020-06-06 00:11:36 +03:00
DarkPhoenix
c1356906bb Add ship effects 2020-06-05 23:36:39 +03:00
DarkPhoenix
8229537e5f Run effect used by script 2020-06-05 22:50:34 +03:00
DarkPhoenix
6cfa2161ec Add conversions for amplifier tiericide 2020-06-05 22:47:13 +03:00
DarkPhoenix
fa3cf46786 Update static data to 1742848 2020-06-05 22:28:25 +03:00
DarkPhoenix
ac40dfd018 Update version again 2020-05-26 16:07:01 +03:00
DarkPhoenix
4c32d559bb Bump version 2020-05-26 16:04:51 +03:00
DarkPhoenix
02ee12586f Adjust jargon definitions to new realities 2020-05-26 16:03:48 +03:00
DarkPhoenix
496f15a83e Add conversions/migrations 2020-05-26 15:50:52 +03:00
DarkPhoenix
acf6af8f95 Update effect definitions and headers to all the renamed items 2020-05-26 15:30:50 +03:00
DarkPhoenix
225b00b9ff Update staticdata to 1733182 and update DB generation script 2020-05-26 15:22:08 +03:00
blitzmann
c3ceebcec7 remove old test string 2020-05-16 15:10:01 -04:00
blitzmann
5177768962 Start working through the files, adding translation strings 2020-05-16 14:42:29 -04:00
blitzmann
fdd06ed3d6 Add some boilerplate code to add support for localization 2020-05-15 21:55:48 -04:00
DarkPhoenix
7f2b7415ea Disable evemarketdata price source 2020-05-14 15:10:04 +03:00
DarkPhoenix
d1d9ae4dac Add fuzzwork as price data source 2020-05-14 15:09:38 +03:00
DarkPhoenix
ac7ccfb6a3 Change how double-clicking ammo behaves 2020-05-04 19:18:14 +03:00
DarkPhoenix
05c2c41762 Destroy effective label when switching from firepower to mining view, and add it back to correct position when needed 2020-05-04 13:46:52 +03:00
DarkPhoenix
10f5133282 Revert "Disable system theme for listctrls on windows"
This reverts commit 42658a8167.
2020-04-27 13:55:30 +03:00
DarkPhoenix
42658a8167 Disable system theme for listctrls on windows 2020-04-26 23:27:28 +03:00
DarkPhoenix
6dbf38dbb4 Revert "Switch to wxpython 4.1.0". Refer to #2179 for more info on
reasons

This reverts commit 15fe8f6261.
2020-04-25 17:37:29 +03:00
DarkPhoenix
64306e2366 Align attribute icons the same way as input boxes in target profile
editor
2020-04-25 17:29:29 +03:00
DarkPhoenix
f259df85cd Fix editors 2020-04-25 17:27:29 +03:00
DarkPhoenix
1090589cb2 Fix preferences for wx 4.1.0 2020-04-25 17:19:05 +03:00
DarkPhoenix
29e09fcdda Fix abyssal item slider on windows 2020-04-25 12:36:45 +03:00
DarkPhoenix
36fd4aeaff Redo fix for 2176 2020-04-25 15:14:27 +03:00
DarkPhoenix
c46a59e3b1 Fix gauge bars 2020-04-25 09:57:49 +03:00
DarkPhoenix
8a3a21972a Fix error dialog layout 2020-04-25 09:40:50 +03:00
DarkPhoenix
6839669e04 Remove a few flags the new wx complains about 2020-04-25 09:36:40 +03:00
DarkPhoenix
5645e20505 Do not float auxiliary windows on parent if there is no parent 2020-04-25 09:29:14 +03:00
DarkPhoenix
15fe8f6261 Switch to wxpython 4.1.0 2020-04-25 09:26:47 +03:00
DarkPhoenix
855fafa94d Bump version 2020-04-24 15:35:52 +03:00
DarkPhoenix
4e10335ae7 Revert "Try resetting locale on wx 4.0.6 as an attempt to work around #2174"
This reverts commit ea07bbf4f9.
2020-04-24 15:10:33 +03:00
DarkPhoenix
21ea9ce579 "Move" attributes at DB generation time 2020-04-24 15:10:00 +03:00
DarkPhoenix
ea07bbf4f9 Try resetting locale on wx 4.0.6 as an attempt to work around #2174 2020-04-24 11:37:27 +03:00
DarkPhoenix
8eed6fbe21 Ignore mutations with 0 base value 2020-04-23 19:47:57 +03:00
DarkPhoenix
0859f2fbe9 Hide limited synth boosters 2020-04-22 02:27:20 +03:00
DarkPhoenix
71ba33edeb Fix tail clear method 2020-04-21 19:00:44 +03:00
DarkPhoenix
ce80d92b35 Limit amount of fits returned by search by 100 2020-04-21 11:16:06 +03:00
DarkPhoenix
f17cf9b736 Fix undo for fill with module (which was caused by module add command not being undone properly in case of failure) 2020-04-20 16:36:16 +03:00
DarkPhoenix
98579c774b Change shortcuts for undo/redo from built-in wx standards to ctrl-z and ctrl-y 2020-04-20 14:42:32 +03:00
DarkPhoenix
509fa279e7 Initialize session container with main thread session 2020-04-17 19:15:47 +03:00
DarkPhoenix
091ee87761 Update char list when new character gets added 2020-04-17 13:31:15 +03:00
DarkPhoenix
c0c20cc92e Remove skill refresh button 2020-04-17 13:27:51 +03:00
DarkPhoenix
1341f7bca1 Make some jargon replacements a bit looser 2020-04-17 11:27:28 +03:00
DarkPhoenix
fe93db1d4b Round booster side-effect context menu 2020-04-17 00:25:33 +03:00
DarkPhoenix
5db97ea773 Bump version 2020-04-16 22:46:45 +03:00
DarkPhoenix
1758e4f320 Rework fix for projection, so that it does not recalculate projected fits too early 2020-04-16 22:25:10 +03:00
DarkPhoenix
1a897c0419 Pass search results as set of item IDs 2020-04-16 15:13:32 +03:00
DarkPhoenix
32db3e3179 Create scoped gamedata sessions 2020-04-16 15:01:00 +03:00
DarkPhoenix
d830a8957a Bump version 2020-04-15 16:54:24 +03:00
DarkPhoenix
652ea48223 Accept 0 free slots for guns which have already been fitted 2020-04-15 16:53:03 +03:00
DarkPhoenix
8c25b2b8f5 Make sure it's impossible to add extra cap boosters via dragging too 2020-04-15 15:40:54 +03:00
DarkPhoenix
db4c56be8e Bump version 2020-04-15 15:27:19 +03:00
DarkPhoenix
f3bcffe2f9 Implement fax cap booster limit 2020-04-15 15:23:49 +03:00
DarkPhoenix
bc5786d099 Update static data to 1706308 2020-04-15 14:50:55 +03:00
DarkPhoenix
5959fe5daf Ignore case when searching for implant sets to allow some inconsistencies on CCP side 2020-04-14 15:28:40 +03:00
DarkPhoenix
649d338bb1 Split implants and boosters in EFT and multibuy exports 2020-04-14 11:36:08 +03:00
DarkPhoenix
dcb058a718 Update existing character if character with the same name exists 2020-04-13 12:52:02 +03:00
DarkPhoenix
1772bb5e7f Merge branch 'master' into singularity 2020-04-13 12:45:25 +03:00
Anton Vorobyov
30bd0adb06 Merge pull request #2159 from soro/auto_create_char
Regarding #2045: automatically create a character for a new sso character
2020-04-12 22:49:01 +03:00
DarkPhoenix
44dfcf771c Ensure projected fits get recalculated, since they get checkStates'd anyway 2020-04-11 02:26:17 +03:00
DarkPhoenix
a1f8a7a930 Fix escaping in regex search 2020-04-11 01:33:53 +03:00
Soeren Roerden
b22887dfad automatically create a character for a new sso character and add button to retrieve all chars directly linked to existing sso characters 2020-04-10 21:12:38 +02:00
DarkPhoenix
28137fa3f4 Remove excessive space 2020-04-10 13:11:04 +03:00
DarkPhoenix
9cbdc6055d Add search timer to attribute overrides to prevent hangs 2020-04-10 11:52:57 +03:00
DarkPhoenix
fc93c61fcf Add more entries 2020-04-10 11:31:17 +03:00
DarkPhoenix
3fa2e7ebd1 More additions to jargon 2020-04-10 06:07:33 +03:00
DarkPhoenix
818628da0c Finalize new jargon dictionary 2020-04-10 05:53:24 +03:00
DarkPhoenix
adf90a8263 Split basic and regex search functions 2020-04-10 00:57:41 +03:00
DarkPhoenix
362923ac64 Overhaul jargon dictionary (some entries are still missing) 2020-04-09 23:29:51 +03:00
DarkPhoenix
7d73838ce1 Change the way user definitions are used 2020-04-08 14:06:49 +03:00
DarkPhoenix
b3278ca9ec Tokenize regexp requests taking into consideration regexp context 2020-04-08 13:24:48 +03:00
DarkPhoenix
5707914ad5 Make use of regex for search 2020-04-08 03:17:25 +03:00
DarkPhoenix
9b697b24d8 Implement regexp function for gamedata 2020-04-08 02:34:28 +03:00
DarkPhoenix
098b088da6 Bump version 2020-04-07 21:39:24 +03:00
DarkPhoenix
14d62f31e1 Add conversions for names 2020-04-07 21:37:40 +03:00
DarkPhoenix
e7b1f55d08 Update static data to 1702461 2020-04-07 20:59:07 +03:00
DarkPhoenix
8f501896a1 Give separate axis selector and axis label names to make it clearer what the graphs do 2020-04-07 20:37:28 +03:00
DarkPhoenix
befcb9b874 Un-hide and un-fuck bumping graphs 2020-04-07 20:14:06 +03:00
DarkPhoenix
c70afa9a4c Hide experimental bumping graphs 2020-04-07 18:25:40 +03:00
DarkPhoenix
029c7dd4c2 Recalculate fit when overrides are toggled 2020-04-07 18:05:43 +03:00
DarkPhoenix
1f83dba1ac Add missing hidden imports to platform-specific specifications 2020-04-07 18:00:17 +03:00
DarkPhoenix
6eae3405fd Add hidden import for newer version of setuptools 2020-04-07 17:39:24 +03:00
DarkPhoenix
c0e1d9e4de Attempt to use newer version of setuptools 2020-04-07 17:09:59 +03:00
DarkPhoenix
7c0bd7aa88 Leave only setuptools override 2020-04-07 16:41:28 +03:00
DarkPhoenix
394583584c Remove some of hardcoded libraries for MacOS X build 2020-04-07 15:08:30 +03:00
DarkPhoenix
4112e2aa6b Debugging Mac OS X build, step 1 2020-04-07 14:17:55 +03:00
DarkPhoenix
aa19e0da72 Add bunch of experimental graphs related to bumping 2020-04-07 13:46:28 +03:00
DarkPhoenix
bba9be1598 Add momentum graphs 2020-04-07 10:50:12 +03:00
DarkPhoenix
17998916b4 Bump version 2020-04-06 22:24:06 +03:00
DarkPhoenix
543089bcd9 Add context menu support for predefined implant sets 2020-04-06 22:20:28 +03:00
DarkPhoenix
7f35c78a65 Add data about implant sets to eve.db 2020-04-06 21:01:20 +03:00
DarkPhoenix
b25798dd83 Update effects 2020-04-06 20:14:19 +03:00
DarkPhoenix
744b9ff78a Updated staticdata to 1701385 2020-04-06 19:19:06 +03:00
DarkPhoenix
8dc1457ebb Change the way zip file is generated 2020-04-06 15:09:06 +03:00
DarkPhoenix
982ad54fab Merge branch 'Metallicow-optimize-images' 2020-04-06 15:29:10 +03:00
DarkPhoenix
912192cd7d Merge branch 'optimize-images' of https://github.com/Metallicow/Pyfa into Metallicow-optimize-images 2020-04-06 15:26:32 +03:00
DarkPhoenix
74cd6d48da Show propmpt to creade damage patterns when there're none 2020-04-06 11:14:21 +03:00
DarkPhoenix
3467a7fe3f Update static data to 1684558 2020-03-11 17:01:34 +03:00
DarkPhoenix
eb269a05ed Bump version 2020-03-10 19:39:22 +03:00
DarkPhoenix
c63cf4b3b0 Add cap booster conversions 2020-03-10 19:19:13 +03:00
DarkPhoenix
11ed94454d Update staticdata to 1683031 2020-03-10 19:07:42 +03:00
DarkPhoenix
8df07645da Show RR power for uncharged ancillary RRs 2020-03-06 00:50:18 +03:00
DarkPhoenix
5666fdd250 Bump version 2020-02-27 15:24:45 +03:00
DarkPhoenix
0b55ae40fc Update staticdata to 1675961 2020-02-27 15:09:54 +03:00
DarkPhoenix
3726e84697 Add dbuffcollections (just to have overview of changed fleet buffs when stuff changes) 2020-02-20 13:31:18 +03:00
DarkPhoenix
a0c4341102 Fix ECM range rigs affecting ECM bursts with penalty 2020-02-17 23:42:48 +03:00
Metallicow
cd6b1038e8 optimize all pngs 2020-02-13 14:58:27 -06:00
DarkPhoenix
10583fd506 Update fighter additions tab icon 2020-02-13 17:25:40 +03:00
DarkPhoenix
713694be56 Bump version 2020-02-13 17:00:58 +03:00
DarkPhoenix
f50293cf77 Revert to wxpython 4.0.6 once again (see issue 2136) 2020-02-13 16:57:23 +03:00
DarkPhoenix
7c88fa477f Bump version 2020-02-13 12:20:03 +03:00
DarkPhoenix
f8df540fad Use newer wxpython version once again 2020-02-13 12:19:37 +03:00
DarkPhoenix
61a01805cc Reset locale and remove resolution data from all the images (wxpython phoenix issue #1515) 2020-02-13 12:14:26 +03:00
Metallicow
a93915cf04 trim trailing whitespace 2020-02-12 16:14:12 -06:00
DarkPhoenix
0f74c97fbf Temporarily revert python version to 3.7 2020-02-12 18:31:44 +03:00
DarkPhoenix
29713b69dc Fix the hack 2020-02-12 18:25:17 +03:00
DarkPhoenix
cc3c2cb9c8 Add hack to build wx on windows 2020-02-12 18:22:31 +03:00
DarkPhoenix
b36a3959da Revert wxpython version to 4.0.6 2020-02-12 18:18:18 +03:00
DarkPhoenix
3b26bfc0e9 Add missing entries to migration 2020-02-11 17:02:00 +03:00
DarkPhoenix
c59b621963 Add capacitor transfer tiericide conversion entries 2020-02-11 16:52:15 +03:00
DarkPhoenix
977a8fa329 Update staticdata to 1663997 2020-02-11 16:20:08 +03:00
DarkPhoenix
a5226fee83 Merge branch 'singularity' 2020-02-11 16:08:46 +03:00
DarkPhoenix
aec5a46452 Fix shebang line 2020-02-05 09:43:10 +03:00
DarkPhoenix
588f9a4490 Re-use market service item sorting for attribute overrides 2020-02-04 01:48:13 +03:00
DarkPhoenix
4ba948c6f9 Merge branch 'master' into ammo_graph
# Conflicts:
#	gui/builtinMarketBrowser/itemView.py
#	service/market.py
2020-02-04 01:43:52 +03:00
DarkPhoenix
e4069a3988 Update AppVeyor deploy oauth key after repo transfer 2020-02-04 00:41:03 +03:00
DarkPhoenix
6527f9e11e Attempt to terminate threads when pyfa is closed 2020-02-03 17:12:23 +03:00
DarkPhoenix
9ddfcc894f Make sure not to catch keyboard interrupts and system exits 2020-02-03 16:12:41 +03:00
DarkPhoenix
f22a4f13e5 Allow pasting non-mutated modules via ctrl-v 2020-02-03 15:54:22 +03:00
DarkPhoenix
fc9337df67 Add more info prints to mac os packaging scripts 2020-02-03 13:37:27 +03:00
DarkPhoenix
98a8332cfa Change the way we update pip for mac os builds 2020-02-03 13:33:26 +03:00
DarkPhoenix
a78eedad48 Remove unused files 2020-02-03 13:30:04 +03:00
DarkPhoenix
85e1a1bd06 Use newer image version for mac builds 2020-02-03 13:25:51 +03:00
DarkPhoenix
25b2c04309 Do not set environment variable as it's not used anymore 2020-02-03 13:23:56 +03:00
DarkPhoenix
54a84d0e42 Do not attempt to install custom python version, just use system default 2020-02-03 13:23:13 +03:00
DarkPhoenix
af0a4d1def Update travis deployment key 2020-02-03 13:11:48 +03:00
DarkPhoenix
97cdde84ce Uninstall not when setup just begins, but right before install begins 2020-02-03 06:17:28 +03:00
DarkPhoenix
a65129b277 Fix function definition order in inno setup script 2020-02-03 06:06:25 +03:00
DarkPhoenix
3b40a49918 Uninstall previous pyfa version unconditionally 2020-02-03 05:59:29 +03:00
DarkPhoenix
d95345b476 Insist on uninstalling old pyfa versions when next one is about to be installed 2020-02-02 23:48:06 +03:00
DarkPhoenix
2ba2d95017 Change clone depth to 400 on appveyor 2020-02-02 14:46:26 +03:00
DarkPhoenix
4d0ffedcc8 Install in 64 bit mode on Windows 2020-02-02 11:07:20 +03:00
DarkPhoenix
84abde4fc5 Some OCD in echoes + testing if build is started after webhook removal 2020-02-02 04:22:41 +03:00
DarkPhoenix
8897f1e4b1 Use my credentials when uploading builds to releases 2020-02-02 04:08:10 +03:00
DarkPhoenix
6f33cacb7a Clean up appveyor file 2020-02-02 03:54:52 +03:00
DarkPhoenix
b1a8c0ad09 Disable appveyor's RDP setup as it's a security hole 2020-02-02 03:40:20 +03:00
DarkPhoenix
13ed635803 We do not care if all PR builds will be built 2020-02-02 03:38:43 +03:00
DarkPhoenix
5a44909ebf Remove appveyor environment preparation which is not needed anymore 2020-02-02 03:13:57 +03:00
DarkPhoenix
6cbb80693d Switch to powershell echo 2020-02-02 03:04:53 +03:00
DarkPhoenix
0b90d254f9 Try another approach in echo'ing 2020-02-02 02:53:18 +03:00
DarkPhoenix
641d36205c Remove quotes from echo commands 2020-02-02 02:47:14 +03:00
DarkPhoenix
6e38b6ea4d Fix path to windows SDK once again 2020-02-02 02:38:26 +03:00
DarkPhoenix
84b4d8fabb Change windows SDK location 2020-02-02 02:03:23 +03:00
DarkPhoenix
c0e1f7e746 Fix mistype 2020-02-01 15:36:33 +03:00
DarkPhoenix
d734c12168 Try to find out when listing windows SDKs fails 2020-02-01 15:34:14 +03:00
DarkPhoenix
340d94eb3e Specify image in apprveyor file 2020-02-01 15:09:47 +03:00
DarkPhoenix
9b0b31648c Add debugging prints to see appveyor's OS version 2020-02-01 02:38:00 +03:00
DarkPhoenix
8346358c37 Fix appveyor script slashes 2020-02-01 02:24:09 +03:00
DarkPhoenix
b935700a12 Bump INNO setup version 2020-02-01 01:56:26 +03:00
DarkPhoenix
2f4a5a4830 Fix improper use of operator "is" when comparing to literal values 2020-01-31 18:16:36 +03:00
DarkPhoenix
e951ce8e9d Bump version 2020-01-31 18:06:28 +03:00
DarkPhoenix
fc6d42f483 Update effects 2020-01-31 17:59:50 +03:00
DarkPhoenix
7df1431ad2 Update staticdata to 1657583 2020-01-31 17:40:51 +03:00
DarkPhoenix
4d4680961e Bump versions of multiple packages 2020-01-31 16:08:24 +03:00
DarkPhoenix
1cae99b812 Remove VC 9.0 tools manifest
We're using newer version now, and we will see later if dependency section in manifest is needed
2020-01-31 16:07:26 +03:00
DarkPhoenix
3727d19311 In attribute editor what is you see is what you get now 2020-01-31 10:32:18 +03:00
DarkPhoenix
c45c093f4f Return search results as list to avoid various issues 2020-01-31 10:04:35 +03:00
DarkPhoenix
b613d3e312 Use market sorting in attribute override editor 2020-01-31 00:37:35 +03:00
DarkPhoenix
4cff9247b0 Always include ships and citadels in override editor output 2020-01-31 00:13:40 +03:00
DarkPhoenix
b9a26ec28d Do not return items from all the categories in overrides editor searches 2020-01-31 00:00:05 +03:00
DarkPhoenix
245f81e888 Actually make appveyor to use 64 bit python 2020-01-30 13:55:30 +03:00
DarkPhoenix
204c1ba9f2 Switch windows build to be 64 bits 2020-01-30 13:45:49 +03:00
DarkPhoenix
a7a4be133c Bump pyinstaller version once again 2020-01-30 13:04:49 +03:00
DarkPhoenix
14f9a46d7a Remove MPL hook 2020-01-30 11:59:04 +03:00
DarkPhoenix
003dd040dc Bump matplotlib version 2020-01-30 11:51:27 +03:00
DarkPhoenix
97cf53217e Bump python version to 3.8 2020-01-30 11:09:11 +03:00
DarkPhoenix
7c1805826d Fix mino and apostle RR effects affecting subcap modules 2020-01-29 16:11:30 +03:00
DarkPhoenix
d6a2b4dfc4 Fix projected system-wide effects menu after multiple renames 2020-01-29 15:58:02 +03:00
DarkPhoenix
d70952520a Update effects 2020-01-29 15:41:55 +03:00
DarkPhoenix
a57867b806 Update static data to 1655541 2020-01-29 15:18:07 +03:00
DarkPhoenix
17d7583cfc Remove overrides which are not needed 2020-01-29 12:54:23 +03:00
DarkPhoenix
55ad95081b Fix alternate tree display in case units did not have display names 2020-01-28 15:16:48 +03:00
DarkPhoenix
da8da1759f Bump version 2020-01-28 14:59:20 +03:00
DarkPhoenix
b4e758b9ee Add new and old but missing conversions 2020-01-28 14:58:54 +03:00
DarkPhoenix
3ba41db699 Update data to 1655072 2020-01-28 14:39:35 +03:00
DarkPhoenix
cfc47cf483 When learning skill in character editor, learn all the prerequisites 2020-01-27 11:44:20 +03:00
DarkPhoenix
38c8be995b Add even more condensed tooltip version (not used yet) 2020-01-24 17:56:07 +03:00
DarkPhoenix
492207700b Force pyinstaller to 3.3 on mac as well 2020-01-21 22:17:29 +03:00
DarkPhoenix
b69adefbf8 Restore matplotlib pyinstaller hook which is still needed in 3.3 2020-01-21 22:00:08 +03:00
DarkPhoenix
361299f51e Bump version 2020-01-21 21:51:02 +03:00
DarkPhoenix
025091c3f6 Roll back pyinstaller to 3.3 for windows builds 2020-01-21 21:50:10 +03:00
DarkPhoenix
a54d70036b Bump version number 2020-01-21 15:32:16 +03:00
DarkPhoenix
9e67b5962c Update the way we update pip on appveyor 2020-01-21 15:31:48 +03:00
Anton Vorobyov
ed0c080163 Merge pull request #2123 from Neugeniko/master
Update Invasion Damage Profiles due to npc changes.
2020-01-21 15:26:53 +03:00
DarkPhoenix
eacc97160b Merge branch 'master' into ammo_graph 2020-01-21 15:25:39 +03:00
Neugeniko
21389d84fc Update Invasion Damage Profiles due to npc changes.
#2122
2020-01-21 23:17:37 +11:00
DarkPhoenix
0fe54631ad Update staticdata to 1651039 2020-01-21 15:00:21 +03:00
DarkPhoenix
36ea5200f2 Remove pyinstaller from requirements as it's needed just for packaging 2020-01-21 13:33:17 +03:00
DarkPhoenix
d9934160a6 Compatibility with pyinstaller 2020-01-21 13:30:48 +03:00
DarkPhoenix
b9c92c48d3 Bump wx version 2020-01-17 17:15:21 +03:00
DarkPhoenix
6d23df6156 Fix on-effect resistance definition 2020-01-17 16:08:55 +03:00
DarkPhoenix
a913ab72c2 Bump version of pyinstaller to avoid security issues 2020-01-17 11:14:28 +03:00
DarkPhoenix
f6d33a2ac1 Use effect IDs rather than effect amount when deciding if we should rebuild abilities 2020-01-16 19:02:10 +03:00
DarkPhoenix
5a3fec33a7 Bump version 2020-01-16 16:26:19 +03:00
DarkPhoenix
95734aca2c Add fighter AB effect 2020-01-16 16:19:37 +03:00
DarkPhoenix
377254be9d Add nirvana effects 2020-01-16 16:09:17 +03:00
DarkPhoenix
ad9905a5e0 Add shield booster tieritice conversions 2020-01-16 15:46:30 +03:00
DarkPhoenix
6fc5a9699e Add slave as jargon entry for amulets 2020-01-16 15:39:54 +03:00
DarkPhoenix
0ee86f4836 Add slaves to renames dictionary 2020-01-16 15:34:56 +03:00
DarkPhoenix
85593367f7 Remove single-precition workaround and update data to 1646988 2020-01-16 15:19:39 +03:00
DarkPhoenix
465ea61b06 Merge branch 'singularity' 2020-01-16 14:57:44 +03:00
DarkPhoenix
2d5507e951 Force faction structure neut to be variation of t1 item 2020-01-08 00:47:56 +03:00
DarkPhoenix
84e20be153 Add auto targeting system to list of mods which do not get online by default 2020-01-04 19:32:31 +03:00
DarkPhoenix
60080c02b0 Fix red giant ecm bonus 2020-01-02 22:11:16 +03:00
DarkPhoenix
2a0d4179d0 Make script effect passive 2020-01-02 22:03:05 +03:00
DarkPhoenix
6dd55e0cd4 Move sansha incursion and sleeper profile one level up, as there are not too many 2019-12-28 21:48:56 +03:00
Anton Vorobyov
0adc6e0c6f Merge pull request #2113 from Neugeniko/master
Add Abyssal/Invasion Damage and Target Profiles.
2019-12-28 21:45:51 +03:00
Neugeniko
e155356701 Add Invasion & Abyssal Target Profiles 2019-12-29 00:28:48 +11:00
Neugeniko
ef44b50980 Add Abyssal & Invasion Damage Profile 2019-12-29 00:26:39 +11:00
DarkPhoenix
6785d16c87 More mods not activated by default 2019-12-28 04:22:11 +03:00
DarkPhoenix
76c670b7d5 Do not activate several categories of modules on fitting 2019-12-28 03:53:02 +03:00
DarkPhoenix
717303a72f Allow to put separate charges into citadel cargo 2019-12-27 12:39:37 +03:00
DarkPhoenix
e13125b174 Enable sigamp tooltip for citadels 2019-12-27 12:35:51 +03:00
DarkPhoenix
8aa7964b81 Do not show effective cap stats for ships with 100% neut resist (citadels) 2019-12-27 12:27:30 +03:00
DarkPhoenix
5e5f23d296 Show DG standup ECM as variation of t1 module 2019-12-26 12:28:01 +03:00
DarkPhoenix
05837f99ff Change order of structure meta levels 2019-12-26 12:26:25 +03:00
DarkPhoenix
70f620c2c3 Fix unit name processing 2019-12-25 01:33:43 +03:00
DarkPhoenix
10ffb987ea Detect concord race and show appropriate icon 2019-12-23 17:02:34 +03:00
DarkPhoenix
13ed785551 Drop tank stats on factor reload toggle 2019-12-23 13:26:23 +03:00
DarkPhoenix
69d2e38647 Use modified item attributes to show ancillary tank modules reload time 2019-12-23 13:17:08 +03:00
DarkPhoenix
c45abbdbcf Use new table as source for skill requirement data 2019-12-19 20:44:17 +03:00
DarkPhoenix
2655b001a9 Add new skillrequirement file 2019-12-19 19:56:23 +03:00
DarkPhoenix
3c7f4edb1b Fix replacements generation 2019-12-19 16:41:49 +03:00
DarkPhoenix
663cbab401 More updates to the script, and data update as well 2019-12-19 16:24:45 +03:00
DarkPhoenix
897ca9ca43 Update DB script to be able to work with sisi data 2019-12-19 15:56:57 +03:00
DarkPhoenix
b31a071859 Fix crashes related to target profiles / damage patterns 2019-12-17 13:10:41 +03:00
DarkPhoenix
8c2788fd78 Do not show implant set editor if user cancels addition of an implant set 2019-12-16 18:06:03 +03:00
DarkPhoenix
dee8fa400c Allow to override menu visibility settings with control key 2019-12-16 16:29:42 +03:00
DarkPhoenix
2339327473 Do some renames 2019-12-16 13:59:41 +03:00
Anton Vorobyov
f6f9239dd5 Merge pull request #2103 from Gochim/issue/2073
Issue #2073: Added ability to add a list of implants from implant view to a new implant set
2019-12-16 13:55:00 +03:00
DarkPhoenix
cd4c8c8c10 Do not save changes when changes were reverted 2019-12-16 13:42:49 +03:00
DarkPhoenix
c25eda8b64 Do not save mutated values when user did not touch them (and they were altered due to rounding) 2019-12-16 13:11:18 +03:00
Gochim
b6edf0e034 Issue #2073: Fixed a stuck in character editor 2019-12-12 18:03:45 +02:00
Gochim
af4277fc7e Issue #2073: Fixed name of a popup menu item for implant tab 2019-12-12 17:57:03 +02:00
Gochim
acd774abe5 Issue #2073: Fixed Codacy warning 2019-12-12 14:02:31 +02:00
Gochim
9081353634 Issue #2073: Final tweaks on implementation 2019-12-11 07:51:31 +02:00
Gochim
9c7fa37a72 Issue #2073: Added functionality to add all implants in fit implant tab to implant set list 2019-12-10 18:14:13 +02:00
Gochim
d92e11893a Issue #2073: Added mechanism to pass implant list to implant set manager 2019-12-10 08:50:21 +02:00
Gochim
f55ab00bf5 Fixed error message text in EntityEditor 2019-12-10 08:18:01 +02:00
DarkPhoenix
36265aa2a3 Add comment about built-in pattern order 2019-12-07 15:52:10 +03:00
DarkPhoenix
bfedcdbffd Simplify code a little 2019-12-07 15:42:26 +03:00
DarkPhoenix
a5d10c4a76 Change sorting of built-in damage profiles 2019-12-07 15:41:07 +03:00
DarkPhoenix
78423f67dd Merge branch 'master' into ammo_graph 2019-12-07 03:34:26 +03:00
DarkPhoenix
2962ce1945 Ensure 0%-100% spoolup is sorted properly 2019-12-07 03:07:49 +03:00
DarkPhoenix
0063d2955e Add info on invasion ships 2019-12-07 02:53:48 +03:00
DarkPhoenix
9787a18666 Fix issue with inability to right-click user-defined target profiles in graph window 2019-12-07 02:47:24 +03:00
DarkPhoenix
d8cd3197b5 Show short profile name in graphs 2019-12-07 02:43:53 +03:00
DarkPhoenix
e07c4f65ab Allow to have square brackets in profile name 2019-12-07 02:19:24 +03:00
DarkPhoenix
c3108f773a Use uniform as default resists profile, if no resist profile can be fetched 2019-12-07 01:29:15 +03:00
DarkPhoenix
a400821268 Remove "database defaults" 2019-12-07 01:21:46 +03:00
DarkPhoenix
ca4bac07da Implement switching between user and builtin patterns 2019-12-07 01:17:41 +03:00
DarkPhoenix
0038eacc3f Add database migration to add new columns which are used to refer builting profiles 2019-12-07 00:50:35 +03:00
DarkPhoenix
4bd633a0d4 Process names on pattern objects themselves 2019-12-06 23:24:29 +03:00
DarkPhoenix
ee837f0b04 Add comment about ideal target profile 2019-12-06 20:07:08 +03:00
DarkPhoenix
016e18cc89 Set negative IDs to all hardcoded entities 2019-12-06 20:04:56 +03:00
DarkPhoenix
1d6ac53183 Make target profile adder multi-level 2019-12-06 20:00:53 +03:00
DarkPhoenix
1e32dfa463 Always show target resist switcher 2019-12-06 19:35:20 +03:00
DarkPhoenix
4431753570 Reimplement switcher to use built-ins too and be multi-level 2019-12-06 17:59:44 +03:00
DarkPhoenix
6fdb57318c Expose builtin target profiles to service 2019-12-06 17:14:12 +03:00
DarkPhoenix
649db019de Add builtin fetcher for target profiles 2019-12-06 16:58:58 +03:00
DarkPhoenix
1d528be0ef Add target profile builtins as well 2019-12-06 14:45:46 +03:00
DarkPhoenix
2d6f6df83d Store built-in damage patterns in dictionary rather than in list 2019-12-06 14:25:27 +03:00
DarkPhoenix
7fa998f276 Avoid duplicating names in context menu 2019-12-06 02:59:14 +03:00
DarkPhoenix
6a3157a4c8 Add hardcoded damage patterns to damage pattern definition module 2019-12-06 02:47:11 +03:00
DarkPhoenix
74efa50576 Add multi-level menu support for incoming damage patterns 2019-12-06 02:11:32 +03:00
DarkPhoenix
61e32d0b1b Merge branch 'ammo_graph' of github.com:pyfa-org/Pyfa into ammo_graph 2019-12-06 01:09:16 +03:00
DarkPhoenix
bf04d26a7b Remove duplicate code 2019-12-06 01:08:11 +03:00
DarkPhoenix
e99be78c54 Partially rework damage pattern switcher 2019-12-05 20:04:50 +03:00
DarkPhoenix
e386232de1 Gather data on damage patterns into single object 2019-12-05 10:44:26 +03:00
DarkPhoenix
a15896044d Show cap delta detailed info even when fit has no cap use 2019-12-05 10:14:30 +03:00
DarkPhoenix
72c74513ce Add excessive effective cap gain to tooltip 2019-12-04 22:21:25 +03:00
DarkPhoenix
34d6d13cb2 Avoid extra indentation in non-spool version of the tooltip 2019-12-03 20:01:25 +03:00
DarkPhoenix
323bb8e064 Change layout once again 2019-12-03 19:57:40 +03:00
DarkPhoenix
b628f8ef9b Change layout of the tooltip 2019-12-03 19:45:49 +03:00
DarkPhoenix
1900216d36 Show spoolup part before damage distribution 2019-12-03 16:31:21 +03:00
DarkPhoenix
c045ed81c1 Do not show spoolup mark when fit has no spoolup weapons 2019-12-03 16:21:16 +03:00
DarkPhoenix
39edec60e3 Add damage type info to firepower panel tooltips 2019-12-03 16:15:05 +03:00
Gochim
259214e907 Issue #2073: Added popup menu item 2019-12-03 09:23:54 +02:00
DarkPhoenix
2e1c53392d Keep hull energizers online upon addition 2019-12-02 11:41:13 +03:00
DarkPhoenix
7a77d12686 Use built-in curl timeouts instead of coreutils 2019-11-27 16:35:22 +03:00
DarkPhoenix
763a7714ed Use proper timeout on Mac OS 2019-11-27 16:06:31 +03:00
DarkPhoenix
e89f35d654 Do not fail Mac OS build if upload fails 2019-11-27 15:56:57 +03:00
DarkPhoenix
3196751b7a Bump version 2019-11-27 15:11:26 +03:00
DarkPhoenix
f221024974 Update staticdata to 1618828 2019-11-27 15:10:49 +03:00
DarkPhoenix
66cab8a0d4 Bump version 2019-11-26 16:55:15 +03:00
DarkPhoenix
daa49a8cd4 Correctly detect zirnitra as amarr 2019-11-26 16:54:47 +03:00
DarkPhoenix
b8601ff240 Update icons/renders 2019-11-26 16:50:51 +03:00
DarkPhoenix
5df3eeca64 Add zirnitra renders 2019-11-26 16:34:28 +03:00
DarkPhoenix
274e4ac2ca Update effects 2019-11-26 16:27:06 +03:00
DarkPhoenix
f6485251ca Update staticdata to 1617768 2019-11-26 15:00:40 +03:00
DarkPhoenix
c03fb70def Merge branch 'singularity' 2019-11-26 14:55:51 +03:00
DarkPhoenix
2217aff5ab Add button to add custom drone groups 2019-11-26 14:53:47 +03:00
DarkPhoenix
400d0aaa22 Rework graph ammo picker to be dialog instead of frame 2019-11-25 16:54:25 +03:00
DarkPhoenix
bfc928934c Put entries into different sizers 2019-11-25 15:48:04 +03:00
DarkPhoenix
395c17270e Merge branch 'master' into ammo_graph 2019-11-25 13:56:16 +03:00
DarkPhoenix
8652a2891b Do not attempt to change skill levels of built-in all0/5 chars 2019-11-22 15:46:29 +03:00
DarkPhoenix
a09af0a9eb Propagate keys to upper windows if they were not processed in here 2019-11-22 15:45:01 +03:00
Anton Vorobyov
c7d4b93fba Merge pull request #2094 from RussianCow/level-keyboard-shortcuts
Add keyboard shortcuts to character level editor
2019-11-22 15:01:26 +03:00
Sasha
745c0db530 Add keyboard shortcuts to character level editor 2019-11-21 18:10:56 -08:00
DarkPhoenix
aa56ab8d6c Bump version 2019-11-21 13:01:14 +03:00
DarkPhoenix
043c430221 Update staticdata to 1610407 2019-11-21 13:00:36 +03:00
DarkPhoenix
dc5cc5855e Show range of bursts projectors, taking ship/citadel radius taken into consideration 2019-11-18 15:18:20 +03:00
DarkPhoenix
59d6266e2b Do not crash on exception classes without message attribute 2019-11-17 21:24:41 +03:00
DarkPhoenix
7495ba67f8 Revert change made for debugging 2019-11-17 17:40:07 +03:00
DarkPhoenix
e649683a4d Accept XML headers with extra info 2019-11-17 17:34:49 +03:00
DarkPhoenix
bec58a5772 Do not crash on cargo fetch failures 2019-11-17 17:29:16 +03:00
DarkPhoenix
e8f9ae8a9c Do not use hires assets on wxGTK 2019-11-16 19:22:37 +03:00
DarkPhoenix
679382e220 Do not separate number and unit in range tooltip, for consistency with other fields 2019-11-15 20:19:23 +03:00
DarkPhoenix
1b40467775 Merge branch 'master' into ammo_graph 2019-11-15 20:17:02 +03:00
DarkPhoenix
7f86782f54 Change range column tooltip for missiles 2019-11-15 13:23:27 +03:00
DarkPhoenix
f80b7d972f Implement hidden flight time bonus 2019-11-15 12:59:57 +03:00
Anton Vorobyov
c0bd489c1b Merge pull request #2085 from Gochim/master
Possible fix for #2084
2019-11-14 12:38:08 +03:00
Gochim
41e4c2107d Fix for #2084 2019-11-14 11:36:59 +02:00
Gochim
cfc95c272a Possible fix for #2084 2019-11-14 10:35:16 +02:00
DarkPhoenix
27e3e53868 Merge branch 'master' into ammo_graph 2019-11-13 22:03:01 +03:00
DarkPhoenix
f778f9ceae Take ship radius in consideration when displaying missile range in range column 2019-11-13 20:51:14 +03:00
DarkPhoenix
7fb6170bcb Implement missile "falloff" support in graphs 2019-11-13 20:28:52 +03:00
DarkPhoenix
00b9884c68 Remove unnecessary color setting, as aux window already does it for us 2019-11-13 19:56:49 +03:00
DarkPhoenix
b05bd04801 Do not allow to open more than 1 ammo pickers 2019-11-13 18:45:43 +03:00
DarkPhoenix
7ba5585b83 Set min window size of ammo picker as well 2019-11-13 17:07:32 +03:00
DarkPhoenix
9386ba3fb9 Fix initial window sizing 2019-11-13 17:03:45 +03:00
DarkPhoenix
1321e70035 Add info on drones/fighters to ammo picker 2019-11-13 16:46:47 +03:00
DarkPhoenix
e2d943b0b0 Gray out items for inactive radiobuttons 2019-11-13 16:33:23 +03:00
DarkPhoenix
424b769ba9 Fix ammo section labels 2019-11-13 16:14:00 +03:00
DarkPhoenix
3671f10c9c Resize ammo picker window 2019-11-13 16:08:06 +03:00
DarkPhoenix
935ecd0ea7 Make structure scrollable 2019-11-13 15:55:02 +03:00
DarkPhoenix
92e15ece72 Add structure with checkboxes to ammo switcher window 2019-11-13 15:20:43 +03:00
DarkPhoenix
a5c1875a29 Make sure to cache ammo set to reuse it when applying ammo to multiple modules 2019-11-13 13:48:13 +03:00
DarkPhoenix
9146c0f2c6 Use ammo service to generate ammo switcher context menu 2019-11-13 13:31:40 +03:00
DarkPhoenix
a917207e07 Reimplement some of logic used in ammo picker in a service package 2019-11-12 19:39:52 +03:00
DarkPhoenix
c21b92945f Merge branch 'master' into ammo_graph 2019-11-12 16:58:34 +03:00
DarkPhoenix
fa37428cd3 Update staticdata to 1573560935 2019-11-12 15:39:57 +03:00
DarkPhoenix
2a2d9d3456 Fix circular import issues for tests 2019-11-12 14:59:43 +03:00
Anton Vorobyov
a91efb681f Merge pull request #2079 from Gochim/minor_fixes
Several minor fixes in different parts of code
2019-11-12 00:07:31 +03:00
Gochim
386e05be8f Fixed method naming in stats.py classes 2019-11-11 19:19:31 +02:00
Anton Vorobyov
65e7bf609d Merge pull request #2078 from Gochim/master
Added central place to get damage types and ehp sources. Added tests
2019-11-11 16:40:22 +03:00
Gochim
7a58d97652 Merge remote-tracking branch 'origin/minor_fixes' into minor_fixes
# Conflicts:
#	eos/utils/stats.py
2019-11-10 17:36:47 +02:00
Gochim
c5118da417 Fixed small issue with #2078 by removing unnecessary prints 2019-11-10 17:35:48 +02:00
Gochim
13b505525d Refactored shipstats.py to use common damage profile and hull type names. Reduced code complexity 2019-11-10 17:27:46 +02:00
Gochim
b682dec363 Second fix for #2076 - use Abstract collections from .abc module. Fixes future issue 2019-11-10 17:27:46 +02:00
Gochim
6aa98e2214 Fixed test_aboutData test 2019-11-10 17:27:46 +02:00
Gochim
8fba988222 Added central place to get damage types and ehp sources. Added tests 2019-11-10 17:27:46 +02:00
Gochim
24a82efe50 Refactored shipstats.py to use common damage profile and hull type names. Reduced code complexity 2019-11-10 17:27:07 +02:00
Gochim
8054fa9267 Second fix for #2076 - use Abstract collections from .abc module. Fixes future issue 2019-11-09 23:27:28 +02:00
Gochim
a0e39a3725 Fixed test_aboutData test 2019-11-09 22:53:52 +02:00
Gochim
185d6d0c51 Merge remote-tracking branch 'origin/master' 2019-11-08 08:35:29 +02:00
Gochim
1975e96848 Added central place to get damage types and ehp sources. Added tests 2019-11-08 08:34:45 +02:00
Gochim
ab37d228ea Added central place to get damage types and ehp sources. Added tests 2019-11-08 08:34:22 +02:00
DarkPhoenix
049bd10797 Merge branch 'master' into ammo_graph 2019-11-07 19:02:12 +03:00
DarkPhoenix
f48483d754 Merge branch 'master' of github.com:pyfa-org/Pyfa 2019-11-07 18:36:10 +03:00
DarkPhoenix
e6cfd33435 Fix #2076 - use collection ABCs from .abc module 2019-11-07 18:35:14 +03:00
Anton Vorobyov
c29126ce1d Merge pull request #2077 from Gochim/master
Added instructions to run tests for the project
2019-11-07 18:34:58 +03:00
Gochim
c52170b731 Fixed path for bash script call 2019-11-07 14:45:29 +02:00
Gochim
6607dd31bf Merge remote-tracking branch 'origin/master' 2019-11-07 14:27:36 +02:00
Gochim
c6c74be38d Added instructions to run tests for the project 2019-11-07 14:27:14 +02:00
Gochim
41c6062ff9 Added instructions to run tests for the project 2019-11-07 14:26:41 +02:00
DarkPhoenix
bbb96a73b7 Add alias file for trig dread-related items 2019-11-06 23:09:28 +03:00
DarkPhoenix
9eb3b9e017 Do not use evepraisal info unless there are orders up 2019-11-04 02:50:55 +03:00
DarkPhoenix
6b3e94729c Update staticdata and bump version 2019-10-31 15:05:14 +03:00
DarkPhoenix
b486276167 Merge branch 'master' into ammo_graph 2019-10-31 11:29:31 +03:00
DarkPhoenix
fb48f2b5d4 Do not attempt to add entries to name maps if entity fetch failed 2019-10-30 16:46:34 +03:00
DarkPhoenix
cfffc77777 Change format name 2019-10-30 16:13:15 +03:00
DarkPhoenix
f7089f358d Fix stats export parenthesis 2019-10-30 16:12:33 +03:00
DarkPhoenix
06c4f2ce46 Bump version 2019-10-30 16:09:08 +03:00
Anton Vorobyov
83eb0abd92 Merge pull request #2070 from pyfa-org/json_in_repo
Store text staticdata in repo instead of binary
2019-10-30 16:05:55 +03:00
DarkPhoenix
4199b33c47 Merge branch 'master' into json_in_repo 2019-10-30 15:47:47 +03:00
Anton Vorobyov
23cd4bff5a Merge pull request #2069 from Gochim/master
[Updated] Implemented copying the currently open fit stats to the clipboard.
2019-10-30 15:44:53 +03:00
DarkPhoenix
b65f95fe77 Make sure to avoid doing DB updates avoiding sqlalchemy, as we're re-using the same session for pyfa now 2019-10-30 15:39:21 +03:00
DarkPhoenix
32160c94e1 Add extra metadata field which we use during gamedata DB checks 2019-10-30 15:26:26 +03:00
DarkPhoenix
ac02fba98b Move useless category IDs closer to context 2019-10-30 15:18:08 +03:00
DarkPhoenix
cde0108cba Change logging a little so info about DB being rebuilt is always printed to stdout 2019-10-30 15:16:13 +03:00
DarkPhoenix
39dc7e4a46 Compose DB out of data stored externally 2019-10-30 14:56:51 +03:00
Gochim
9943f784a8 Fixed code auto-checks for pull request 2019-10-30 13:34:54 +02:00
Gochim
88ce45f29e Merge remote-tracking branch 'origin/master' 2019-10-30 11:46:25 +02:00
Gochim
7157e876ca Fixed issue with mainFrame after merging 2019-10-30 11:46:03 +02:00
Gochim
0cf88cf7ca Added stats that were more or less agreed on in [Issue #2065] 2019-10-30 11:39:21 +02:00
Gochim
10dfdc3627 Added UI for new type of copying data about fit to the clipboard 2019-10-30 11:39:21 +02:00
Gochim
76bdefcda6 Fixed wording in contributing.md 2019-10-30 11:39:21 +02:00
Gochim
1c2c8cc5f9 Added UI for new type of copying data about fit to the clipboard 2019-10-30 11:39:21 +02:00
Alexander Maryanovsky
58f853de5b Implemented copying the currently open fit stats to the clipboard. 2019-10-30 11:39:21 +02:00
Gochim
c052297bf7 Added stats that were more or less agreed on in [Issue #2065] 2019-10-30 09:17:38 +02:00
DarkPhoenix
9e78cd1076 Fix drag-n-dropping module from market into specific empty slot 2019-10-28 13:23:40 +03:00
DarkPhoenix
79f4deacea Show hidden graphs on ctrl-alt-g 2019-10-28 12:56:34 +03:00
Gochim
ff42c4c711 Added UI for new type of copying data about fit to the clipboard 2019-10-26 18:36:38 +03:00
DarkPhoenix
02d31d49d8 Implement graph types to pick best ECM burst + damps ship 2019-10-26 00:30:45 +03:00
DarkPhoenix
64f47fcc24 Do not choke on fits for unknown ships 2019-10-25 01:00:32 +03:00
DarkPhoenix
0ceb8acd64 Rename some fields for the hidden graph 2019-10-24 23:15:38 +03:00
DarkPhoenix
78579e2e13 Adjust ECM burst + damp graph 2019-10-24 23:13:20 +03:00
DarkPhoenix
cf4e1d3935 Consistency fixes 2019-10-24 14:09:08 +03:00
DarkPhoenix
d1be0bb680 Add scanres vs locktime on active fit graph as experimental feature 2019-10-24 13:52:34 +03:00
Gochim
384d9f4614 Fixed wording in contributing.md 2019-10-21 13:22:00 +03:00
Gochim
47434c68f9 Added UI for new type of copying data about fit to the clipboard 2019-10-20 15:25:06 +03:00
Gochim
af88afb6b5 Merge branch 'export-stats' of https://github.com/m-sasha/PyfaAT into m-sasha-export-stats
# Conflicts:
#	gui/mainFrame.py
#	gui/mainMenuBar.py
2019-10-20 14:48:00 +03:00
DarkPhoenix
e4df215e47 Change order of market group sorting for ammo picker 2019-10-18 12:10:02 +03:00
DarkPhoenix
33cb332978 Plug market sort into module grouping 2019-10-17 18:21:03 +03:00
DarkPhoenix
7c8f4d51bb Move sorting from item view to market service 2019-10-17 17:39:44 +03:00
DarkPhoenix
66bf7b3dc6 Merge branch 'master' into ammo_graph 2019-10-17 17:19:40 +03:00
DarkPhoenix
8bb1d43d0c Show module groups / drone header / fighter header in dialog 2019-10-11 13:23:18 +03:00
DarkPhoenix
3d70ca941c Change ammo picker window to be auxiliary frame rather than modal dialog 2019-10-10 19:10:28 +03:00
DarkPhoenix
04a1e95730 Get list of fighters and drones as well 2019-10-10 16:03:56 +03:00
DarkPhoenix
9fddb64ef9 Get list of damage dealer mods with ammo data 2019-10-10 15:40:39 +03:00
DarkPhoenix
e883035120 Do not show menu item for non-dps graphs 2019-10-10 13:38:42 +03:00
DarkPhoenix
4d2da8e52e Merge branch 'master' into ammo_graph 2019-10-10 12:41:30 +03:00
DarkPhoenix
c1df335f08 Add context menu item which will handle fit duplication 2019-10-08 15:39:16 +03:00
Alexander Maryanovsky
b2c718d614 Implemented copying the currently open fit stats to the clipboard. 2018-09-08 20:14:34 +03:00
1485 changed files with 4268047 additions and 11071 deletions

View File

@@ -1,135 +1,214 @@
environment:
global:
# SDK v7.0 MSVC Express 2008's SetEnv.cmd script will fail if the
# /E:ON and /V:ON options are not enabled in the batch script intepreter
# See: http://stackoverflow.com/a/13751649/163740
CMD_IN_ENV: "cmd /E:ON /V:ON /C .\\appveyor\\run_with_env.cmd"
image:
- Visual Studio 2019
- Ubuntu
- macos
clone_depth: 1
for:
-
matrix:
only:
- image: Ubuntu
environment:
APPVEYOR_SSH_KEY: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDJDW/+oYNGOiPvwuwAL9tc/LQgg58aosIVpMYfepQZ20V+VZnHpZh8IRDA8Jo5xht19p2PksA+hFgqA0kpKtrSkuiWdE8rATQItfk4gf7yB0yGasJGGQZYazy9k/9XtmYkq2HHOOeEqdxvrICddJQ88MLCLT9lJENSUP/YS/yGcjZFXVxE11pTeIcqlCRU+3eYa1v7BeNvXIKNhZoK5orXWrtuH3cy8jrSns/u70aYfJ6B2jA8CnWnDbuvpeQtEY61SQqlKUsSArNa8NAsXj41wr3Ar9gAG9330w7EMTqlutk8HZO35uHI0q5qinUhaQYufPPrVkb2L/N+ZCfu0fnh appveyor"
APPIMAGE_TOOL: appimagetool-x86_64.AppImage
PYTHON_APPIMAGE: python3.7.13-cp37-cp37m-manylinux2014_x86_64.AppImage
DEPLOY_DIR: AppDir/opt/pyfa
# APPVEYOR_SSH_BLOCK: true
cache:
- /home/appveyor/.cache/pip -> requirements.txt
init:
- sh: curl -sflL 'https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-ssh.sh' | bash -e -
install:
- sh: git fetch --prune --unshallow # to fix the version dump issues
- sh: sudo DEBIAN_FRONTEND=noninteractive apt-get -y update
- sh: sudo DEBIAN_FRONTEND=noninteractive apt-get -y install python3.7-dev libgtk-3-dev python3-pip libwebkit2gtk-4.0-dev
before_build:
- sh: mkdir build && cd build
- sh: curl -LO https://github.com/AppImage/AppImageKit/releases/download/13/$APPIMAGE_TOOL && chmod +x $APPIMAGE_TOOL
- sh: curl -LO https://github.com/niess/python-appimage/releases/download/python3.7/$PYTHON_APPIMAGE && chmod +x $PYTHON_APPIMAGE
build_script:
# Prepare Python base AppImage, stripping Python metadata
- sh: ./$PYTHON_APPIMAGE --appimage-extract
- sh: mv squashfs-root AppDir
- sh: rm AppDir/python*.desktop
- sh: rm AppDir/usr/share/applications/*.desktop
- sh: rm AppDir/usr/share/metainfo/*.appdata.xml
- sh: mkdir -p $DEPLOY_DIR
# run install pyfa packages and any other requirements
matrix:
- sh: AppDir/usr/bin/python -s -m pip install -U pip setuptools==41.6.0 wheel pathlib2
- sh: AppDir/usr/bin/python -s -m pip install -r ../requirements.txt
- PYTHON: "C:\\Python36"
PYTHON_VERSION: "3.6.x"
PYTHON_ARCH: "32"
init:
- ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
install:
# If there is a newer build queued for the same PR, cancel this one.
# The AppVeyor 'rollout builds' option is supposed to serve the same
# purpose but it is problematic because it tends to cancel builds pushed
# directly to master instead of just PR builds (or the converse).
# credits: JuliaLang developers.
- ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_NUMBER -ne ((Invoke-RestMethod `
https://ci.appveyor.com/api/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/history?recordsNumber=50).builds | `
Where-Object pullRequestId -eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) { `
throw "There are newer queued builds for this pull request, failing early." }
# Run scripts to prep pyfa data and build database
- sh: cd ../
- sh: find locale/ -type f -name "*.po" -exec msgen "{}" -o "{}" \;
- sh: build/AppDir/usr/bin/python scripts/compile_lang.py
- sh: build/AppDir/usr/bin/python scripts/dump_crowdin_progress.py
- sh: build/AppDir/usr/bin/python db_update.py
- sh: export PYFA_VERSION="$(python3.7 scripts/dump_version.py)"
- ECHO "Filesystem root:"
- ps: "ls \"C:/\""
# Copy pyfa files to host
- sh: cp -r eos graphs gui imgs locale service utils eve.db config.py pyfa.py db_update.py README.md LICENSE version.yml ./build/$DEPLOY_DIR
- sh: find ./build/$DEPLOY_DIR | grep -E "(__pycache__|\.pyc|\.pyo$)" | xargs rm -rf
- ECHO "Filesystem projects root:"
- ps: "ls \"C:\\projects\\\""
# Copy static AppImage files
- sh: cd dist_assets/linux
- sh: cp AppRun pyfa.desktop ../../build/AppDir/
- sh: cp pyfa.desktop ../../build/AppDir/usr/share/applications/
- sh: cp org.pyfa.pyfa.appdata.xml ../../build/AppDir/usr/share/metainfo/
- sh: chmod +x pyfa && cp pyfa ../../build/AppDir/usr/bin
- sh: cd ../../
- ECHO "Filesystem pyfa root:"
- ps: "ls \"C:\\projects\\$env:APPVEYOR_PROJECT_SLUG\""
# Package it all up
- sh: mkdir dist
- sh: ./build/$APPIMAGE_TOOL build/AppDir dist/pyfa-$PYFA_VERSION-linux.AppImage
after_build:
- sh: ls -la build
artifacts:
- path: dist/pyfa-$PYFA_VERSION-linux.AppImage
deploy:
tag: $PYFA_VERSION
release: pyfa $PYFA_VERSION
description: 'Release description'
provider: GitHub
auth_token:
secure: M94o0xMtzxrvlKpqMcXU2KfbJdd3aYJ3UxWzePUz/pkT1/Ojiis052CiLsLVyzJg
draft: true
force_update: false
# deploy on tag push only
on:
APPVEYOR_REPO_TAG: true
-
matrix:
only:
- image: Visual Studio 2019
environment:
PYTHON: "C:\\Python37-x64"
# Should be enabled only for build process debugging
# init:
# - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
cache:
- C:\users\appveyor\appdata\local\pip\cache\ -> requirements.txt
install:
- cmd: git fetch --prune --unshallow # to fix the version dump issues
- ECHO "Installed SDKs:"
- ps: "ls \"C:/Program Files/Microsoft SDKs/Windows\""
- ps: echo("OS version:")
- ps: "[System.Environment]::OSVersion.Version"
# Prepend newly installed Python to the PATH of this build (this cannot be
# done from inside the powershell script as it would require to restart
# the parent CMD process).
- "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
- ps: echo("Filesystem - root:")
- ps: "ls \"C:\\\""
- "python --version"
- "python -c \"import struct; print(struct.calcsize('P') * 8)\""
- ps: echo("Filesystem - projects root:")
- ps: "ls \"C:\\projects\\\""
# Upgrade to the latest version of pip to avoid it displaying warnings
# about it being out of date.
- "pip install --disable-pip-version-check --user --upgrade pip"
- ps: echo("Filesystem - pyfa root:")
- ps: "ls \"C:\\projects\\$env:APPVEYOR_PROJECT_SLUG\\\""
# Install the build dependencies of the project. If some dependencies contain
# compiled extensions and are not provided as pre-built wheel packages,
# pip will build them from source using the MSVC compiler matching the
# target Python version and architecture
- ECHO "Install pip requirements:"
- "python -m pip install -r requirements.txt"
- "python -m pip install PyInstaller"
- ps: echo("Filesystem - installed SDKs:")
- ps: "ls \"C:\\Program Files (x86)\\Windows Kits\\\""
before_build:
# directory that will contain the built files
- ps: $env:PYFA_DIST_DIR = "c:\projects\$env:APPVEYOR_PROJECT_SLUG\dist"
- ps: $env:PYFA_VERSION = (python ./scripts/dump_version.py)
- ps: echo("pyfa version ")
- ps: echo ($env:PYFA_VERSION)
# Prepend newly installed Python to the PATH of this build (this cannot be
# done from inside the powershell script as it would require to restart
# the parent CMD process).
- cmd: "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
- cmd: "appveyor DownloadFile https://github.com/mlocati/gettext-iconv-windows/releases/download/v0.20.2-v1.16/gettext0.20.2-iconv1.16-shared-64.zip"
- cmd: "7z x gettext0.20.2-iconv1.16-shared-64.zip -ogettext"
- cmd: "SET PATH=gettext;%PATH%"
build_script:
- ECHO "Build pyfa:"
- cmd: "python --version"
- cmd: "python -c \"import struct; print(struct.calcsize('P') * 8)\""
##########
# PyInstaller - create binaries for pyfa
##########
# Build command for PyInstaller
- "python -m PyInstaller --noupx --clean --windowed --noconsole -y pyfa.spec"
# Copy over manifest (See pyfa-org/pyfa#1622)
- ps: xcopy /y dist_assets\win\pyfa.exe.manifest $env:PYFA_DIST_DIR\pyfa\
# Not really sure if this is needed, but why not
- ps: xcopy /y dist_assets\win\Microsoft.VC90.CRT.manifest $env:PYFA_DIST_DIR\pyfa\
# Upgrade to the latest version of pip to avoid it displaying warnings
# about it being out of date.
- cmd: "python -m pip install --upgrade pip"
##########
# InnoScript EXE building
# This is in a separate script because I don't feel like copying over the logic to AppVeyor script right now...
##########
- "python dist_assets/win/dist.py"
- ps: dir $env:PYFA_DIST_DIR/
#- ECHO "Build pyfa (Debug):"
#- copy C:\projects\pyfa\dist_assets\win\pyfa_debug.spec C:\projects\pyfa\pyfa_debug.spec
#- "pyinstaller.exe --clean --noconfirm --windowed --upx-dir=C:\\projects\\pyfa\\scripts\\upx.exe C:\\projects\\pyfa\\pyfa_debug.spec"
build: on
after_build:
- ps: "ls \"./\""
#- ps: "ls \"C:\\projects\\pyfa\\build\\pyfa\\\""
# - ps: "ls \"C:\\projects\\$env:APPVEYOR_PROJECT_SLUG\\build\\exe.win32-2.7\\\""
# Zip
# APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_NUMBER
#- 7z a build.zip -r C:\projects\pyfa\build\pyfa\*.*
- ps: 7z a "pyfa-$env:PYFA_VERSION-win.zip" -r "$env:PYFA_DIST_DIR\pyfa\*.*"
#- 7z a pyfa_debug.zip -r C:\projects\pyfa\dist\pyfa_debug\*.*
on_success:
# Do nothing right now
test_script:
#- tox
#- "py.test --cov=./"
# Run the project tests
# - "%CMD_IN_ENV% python C:/projects/eve-gnosis/setup.py nosetests"
after_test:
# If tests are successful, create binary packages for the project.
# - "%CMD_IN_ENV% python setup.py bdist_wheel"
# - "%CMD_IN_ENV% python setup.py bdist_wininst"
# - "%CMD_IN_ENV% python setup.py bdist_msi"
# - ps: "ls dist"
artifacts:
# Archive the generated packages in the ci.appveyor.com build report.
- path: pyfa*-win.zip
- path: pyfa*-win.exe
#- path: pyfa_debug.zip
# name: Pyfa_debug
deploy:
tag: $(pyfa_version)
release: pyfa $(pyfa_version)
description: 'Release description'
provider: GitHub
auth_token:
secure: BfNHO66ff5hVx2O2ORbl49X0U/5h2V2T0IuRZDwm7fd1HvsVluF0wRCbl29oRp1M
draft: true
on:
APPVEYOR_REPO_TAG: true # deploy on tag push only
#on_success:
# - TODO: upload the content of dist/*.whl to a public wheelhouse
#
# Install the build dependencies of the project. If some dependencies contain
# compiled extensions and are not provided as pre-built wheel packages,
# pip will build them from source using the MSVC compiler matching the
# target Python version and architecture
- ps: echo("Install pip requirements:")
# This one is needed to build wxpython 4.0.6 on windows
- cmd: "python -m pip install pathlib2"
- cmd: "python -m pip install -r requirements.txt"
- cmd: "python -m pip install PyInstaller==3.6"
before_build:
# directory that will contain the built files
- ps: $env:PYFA_DIST_DIR = "c:\projects\$env:APPVEYOR_PROJECT_SLUG\dist"
- ps: $env:PYFA_VERSION = (python ./scripts/dump_version.py)
- ps: echo("pyfa version $env:PYFA_VERSION")
build_script:
- ps: echo("Build pyfa:")
- ps: Get-ChildItem locale/*.po -Recurse -File| Foreach {msgen $_.fullname -o $_.fullname}
# Build language files
- cmd: "python scripts/compile_lang.py"
# Dump language progress
- cmd: "python scripts/dump_crowdin_progress.py"
# Build gamedata DB
- cmd: "python db_update.py"
# Build command for PyInstaller
- cmd: "python -m PyInstaller --noupx --clean --windowed --noconsole -y pyfa.spec"
# Copy over manifest (See pyfa-org/pyfa#1622)
- ps: xcopy /y dist_assets\win\pyfa.exe.manifest $env:PYFA_DIST_DIR\pyfa\
# InnoScript EXE building. This is in a separate script because I don't feel like copying over the logic to AppVeyor script right now...
- cmd: "python dist_assets/win/dist.py"
- ps: dir $env:PYFA_DIST_DIR/
after_build:
- ps: "ls \"./\""
- ps: 7z a "pyfa-$env:PYFA_VERSION-win.zip" -r "$env:PYFA_DIST_DIR\pyfa\*"
artifacts:
- path: pyfa*-win.zip
- path: pyfa*-win.exe
deploy:
tag: $(pyfa_version)
release: pyfa $(pyfa_version)
description: 'Release description'
provider: GitHub
auth_token:
secure: M94o0xMtzxrvlKpqMcXU2KfbJdd3aYJ3UxWzePUz/pkT1/Ojiis052CiLsLVyzJg
draft: true
force_update: false
# deploy on tag push only
on:
APPVEYOR_REPO_TAG: true
-
matrix:
only:
- image: macos
environment:
APPVEYOR_SSH_KEY: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDJDW/+oYNGOiPvwuwAL9tc/LQgg58aosIVpMYfepQZ20V+VZnHpZh8IRDA8Jo5xht19p2PksA+hFgqA0kpKtrSkuiWdE8rATQItfk4gf7yB0yGasJGGQZYazy9k/9XtmYkq2HHOOeEqdxvrICddJQ88MLCLT9lJENSUP/YS/yGcjZFXVxE11pTeIcqlCRU+3eYa1v7BeNvXIKNhZoK5orXWrtuH3cy8jrSns/u70aYfJ6B2jA8CnWnDbuvpeQtEY61SQqlKUsSArNa8NAsXj41wr3Ar9gAG9330w7EMTqlutk8HZO35uHI0q5qinUhaQYufPPrVkb2L/N+ZCfu0fnh appveyor"
cache:
- /Users/appveyor/Library/Caches/pip/ -> requirements.txt
init:
# - sh: curl -sflL 'https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-ssh.sh' | bash -e -
- sh: source ~/venv3.7/bin/activate
install:
- sh: git fetch --prune --unshallow # to fix the version dump issues
- sh: bash scripts/osx-setup.sh
build_script:
- sh: bash scripts/osx-translations.sh
- sh: python3 scripts/compile_lang.py
- sh: python3 scripts/dump_crowdin_progress.py
- sh: python3 db_update.py
after_build:
- sh: export PYFA_VERSION="$(python3 scripts/dump_version.py)"
- sh: bash scripts/osx-package.sh
# on_finish:
# - sh: export APPVEYOR_SSH_BLOCK=true
# - sh: curl -sflL 'https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-ssh.sh' | bash -e -
artifacts:
- path: dist/pyfa*-mac.zip
before_deploy:
- sh: export RELEASE_PKG_FILE=$(ls *.deb)
- sh: echo "deploying $RELEASE_PKG_FILE to GitHub releases"
deploy:
tag: $PYFA_VERSION
release: pyfa $PYFA_VERSION
description: 'Release description'
provider: GitHub
auth_token:
secure: M94o0xMtzxrvlKpqMcXU2KfbJdd3aYJ3UxWzePUz/pkT1/Ojiis052CiLsLVyzJg
draft: true
force_update: false
# deploy on tag push only
on:
APPVEYOR_REPO_TAG: true

6
.gitattributes vendored
View File

@@ -1,11 +1,9 @@
# Set the default behavior, in case people don't have core.autocrlf set.
* text=auto
# Explicitly declare text files you want to always be normalized and converted
# to native line endings on checkout.
# *.c text
# *.h text
# Declare files that will always have CRLF line endings on checkout.
# Source files
# ============
@@ -15,7 +13,6 @@
*.pyw text eol=crlf
*.pyx text eol=crlf
pyfa.py text eol=lf
# Denote all files that are truly binary and should not be modified.
# Binary files
# ============
@@ -25,12 +22,10 @@ pyfa.py text eol=lf
*.pyc binary
*.pyd binary
*.pyo binary
# Note: .db, .p, and .pkl files are associated
# with the python modules ``pickle``, ``dbm.*``,
# ``shelve``, ``marshal``, ``anydbm``, & ``bsddb``
# (among others).
# Denote all files that are truly binary and should not be modified.
# Image files
# ============
@@ -38,3 +33,4 @@ pyfa.py text eol=lf
*.jpg binary
*.icns binary
*.ico binary

2
.gitignore vendored
View File

@@ -67,7 +67,6 @@ coverage.xml
# Translations
*.mo
*.pot
# Django stuff:
*.log
@@ -123,3 +122,4 @@ gitversion
*.swp
*.fsdbinary
/locale/progress.json

View File

@@ -1,31 +0,0 @@
os: linux
language: python
git:
depth: 400
python:
- 3.6
matrix:
include:
- os: osx
osx_image: xcode7.3
language: generic
env: PYTHON=3.6.1
before_install:
- bash scripts/setup-osx.sh
install:
- export PYFA_VERSION="$(python3 scripts/dump_version.py)"
- bash scripts/package-osx.sh
before_deploy:
- export RELEASE_PKG_FILE=$(ls *.deb)
- echo "deploying $RELEASE_PKG_FILE to GitHub releases"
deploy:
provider: releases
api_key:
secure: Xfu0xApoB0zUPLXl29aYUulVC3iA4/3bXQwwADKCfAKZwxgNon4dLbO7Rie5/7Ukf2POL0KwmRaQGN3kOr+XSoIVTE4M5sXxnhiaaLGKQ+48hDizLE6JuXcZGJvkxUaghaTzIdCwHsG7VGBsPfQgfGsjJcfBp8tFNLmRyM/Jpsr8T6BR2MxtBIEUVy8zrOWFNZqnmWrY2pWMsB9fYt3JFNdpqeIgRAYqbBsBcZQ1MngLTi3ztuYS5IaF+lk06RrnBlHmUsJu/5nCvIpvPvD0i2BLZ3Uu0+Fn+8QWUgjJEL9MNseXZMXynu05xd8YRk7Ajc9CUrzQIIbAktyteYp85kE3pUJHmrMLcXhh7nqkwttR5/47Zwa3OLJLJFKBxMx6wY5jFkJjkV08850B7aWrmTFl/Eqc3Q5nZMuiEt3wFRbjxHi9h1mTN/fkxfRRHg8u3ENGPR+ZPiFC3J18qtks/B/hsKjjHvZP1i79OYlET4V/zyLyyQkCbpDaARQANuotLYJyZ7tH+KWEyRsvTi0M9Yev9mNNw6aI4vzh4HfkEhvcvnWnYwckPj1dnjQ573Qpw0Z9wsconoWfHAn+hBDt3+YLMrrFZl++mCRskHH1mZChX3aGMDi49zD0kfxBUkYPOAhguc6PwudBxHUZP+O6T/SoHylff6EizCE/k5dGeAk=
file_glob: true
file: "dist/pyfa-*.zip"
skip_cleanup: true
draft: true
on:
tags: true
repo: pyfa-org/Pyfa

View File

@@ -2,9 +2,9 @@
## Requirements
- Python 3.6
- Python 3.7
- Git CLI installed
- Python, pip and git are all available as command-line commands (add to path if needed)
- 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.
@@ -12,21 +12,21 @@ Virtual environment will be created in *PyfaEnv* folder. Project will be cloned
## Setting up the project manually
Clone the repo
Clone the repository
```
git clone <repo> PyfaDEV
```
Create virtual environment
Create the virtual environment
```
python -m venv PyfaEnv
```
Activate virtual environment
Activate the virtual environment
```
For cmd.exe: PyfaEnv\scripts\activate.bat
For bash: source <venv>/bin/activate
For bash: source <venv>/Scripts/activate
```
> For other OS check [Python documentation](https://docs.python.org/3/library/venv.html)
@@ -36,27 +36,32 @@ 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 libs from *requirements.txt* are installed
Check that the libs from *requirements.txt* are installed
```
pip list
```
Build translations and database:
```
python scripts\compile_lang.py
python db_update.py
```
Test that the project is starting properly
```
python PyfaDEV\pyfa.py
```
## Setting up the project with PyCharm/IntelliJ
Install PyCharm / Other IntelliJ product with Python plugin
After launching select *Check out from Version Control* -> *GIt*
After launching - select *Check out from Version Control* -> *GIt*
![welcome](https://user-images.githubusercontent.com/54093496/66862580-d8edab00-ef99-11e9-94e2-e93d7043e620.png)
Login to GitHub, paste repo URL and seect the folder to which to clone the project, press *Clone*.
Login to GitHub, paste the repo URL and select the folder to which to clone the project into, press *Clone*.
![Clone](https://user-images.githubusercontent.com/54093496/66862748-38e45180-ef9a-11e9-9f68-4903baf47385.png)
@@ -68,7 +73,7 @@ Press on options and add new virtual environment.
![venv](https://user-images.githubusercontent.com/54093496/66862833-67622c80-ef9a-11e9-94fa-47cca0158d29.png)
Open project tree view and double-click on *requirements.txt*. Press *Install requirements*. Install all requirements.
Open project tree view and double-click on the *requirements.txt*. Press *Install requirements*. Install all requirements.
![Reqs](https://user-images.githubusercontent.com/54093496/66862870-7a74fc80-ef9a-11e9-9b18-e64be42c49b8.png)
@@ -76,4 +81,27 @@ Create new *Run Configuration*. Set correct *Script path* and *Python interprete
![Run configuraion](https://user-images.githubusercontent.com/54093496/66862970-b4460300-ef9a-11e9-9fb4-20e24759904b.png)
Test that the project is starting properly.
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).

View File

@@ -3,7 +3,6 @@
<option name="RIGHT_MARGIN" value="165" />
<Python>
<option name="NEW_LINE_AFTER_COLON" value="true" />
<option name="DICT_ALIGNMENT" value="2" />
<option name="DICT_NEW_LINE_AFTER_LEFT_BRACE" value="true" />
<option name="DICT_NEW_LINE_BEFORE_RIGHT_BRACE" value="true" />
<option name="USE_CONTINUATION_INDENT_FOR_ARGUMENTS" value="true" />

View File

@@ -1,54 +1,61 @@
<profile version="1.0">
<option name="myName" value="Pyfa" />
<inspection_tool class="IgnoreUnusedEntry" enabled="false" level="UNUSED ENTRY" enabled_by_default="false" />
<inspection_tool class="InconsistentLineSeparators" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="ProblematicWhitespace" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PyBehaveInspection" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PyClassicStyleClassInspection" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PyCompatibilityInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ourVersions">
<value>
<list size="1">
<item index="0" class="java.lang.String" itemvalue="2.7" />
<profile version="1.0">
<option name="myName" value="Pyfa" />
<inspection_tool class="IgnoreUnusedEntry" enabled="false" level="UNUSED ENTRY" enabled_by_default="false" />
<inspection_tool class="InconsistentLineSeparators" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="ProblematicWhitespace" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PyBehaveInspection" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PyClassicStyleClassInspection" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PyCompatibilityInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ourVersions">
<value>
<list size="1">
<item index="0" class="java.lang.String" itemvalue="2.7" />
</list>
</value>
</option>
</inspection_tool>
<inspection_tool class="PyMissingTypeHintsInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoredPackages">
<value>
<list size="1">
<item index="0" class="java.lang.String" itemvalue="wxPython" />
</list>
</value>
</option>
</inspection_tool>
<inspection_tool class="PyPep8Inspection" enabled="true" level="TYPO" enabled_by_default="true">
<option name="ignoredErrors">
<list>
<option value="E203" />
<option value="E127" />
<option value="E128" />
<option value="E126" />
</list>
</value>
</option>
</inspection_tool>
<inspection_tool class="PyMissingTypeHintsInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoredPackages">
<value>
<list size="1">
<item index="0" class="java.lang.String" itemvalue="wxPython" />
</option>
</inspection_tool>
<inspection_tool class="PyPep8NamingInspection" enabled="true" level="TYPO" enabled_by_default="true">
<option name="ignoredErrors">
<list>
<option value="N802" />
<option value="N806" />
<option value="N803" />
<option value="N814" />
</list>
</value>
</option>
</inspection_tool>
<inspection_tool class="PyPep8Inspection" enabled="true" level="TYPO" enabled_by_default="true">
<option name="ignoredErrors">
<list>
<option value="E203" />
<option value="E127" />
<option value="E128" />
<option value="E126" />
</list>
</option>
</inspection_tool>
<inspection_tool class="PyPep8NamingInspection" enabled="true" level="TYPO" enabled_by_default="true">
<option name="ignoredErrors">
<list>
<option value="N802" />
<option value="N806" />
<option value="N803" />
<option value="N814" />
</list>
</option>
</inspection_tool>
<inspection_tool class="PyShadowingBuiltinsInspection" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PyShadowingNamesInspection" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
<option name="processCode" value="true" />
<option name="processLiterals" value="true" />
<option name="processComments" value="true" />
</inspection_tool>
</profile>
</option>
</inspection_tool>
<inspection_tool class="PyShadowingBuiltinsInspection" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PyShadowingNamesInspection" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PyUnresolvedReferencesInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoredIdentifiers">
<list>
<option value="_" />
</list>
</option>
</inspection_tool>
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
<option name="processCode" value="true" />
<option name="processLiterals" value="true" />
<option name="processComments" value="true" />
</inspection_tool>
</profile>

View File

@@ -49,6 +49,8 @@ def DBInMemory_test():
gamedata_version = gamedata_session.execute(
"SELECT `field_value` FROM `metadata` WHERE `field_name` LIKE 'client_build'"
).fetchone()[0]
except (KeyboardInterrupt, SystemExit):
raise
except Exception as e:
print("Missing gamedata version.")
gamedata_version = None
@@ -72,7 +74,7 @@ def DBInMemory_test():
# noinspection PyPep8
#from eos.db.gamedata import alphaClones, attribute, category, effect, group, icon, item, marketGroup, metaData, metaGroup, queries, traits, unit
# noinspection PyPep8
#from eos.db.saveddata import booster, cargo, character, crest, damagePattern, databaseRepair, drone, fighter, fit, implant, implantSet, loadDefaultDatabaseValues, miscData, module, override, price, queries, skill, targetProfile, user
#from eos.db.saveddata import booster, cargo, character, crest, damagePattern, databaseRepair, drone, fighter, fit, implant, implantSet, miscData, module, override, price, queries, skill, targetProfile, user
# If using in memory saveddata, you'll want to reflect it so the data structure is good.
if saveddata_connectionstring == "sqlite:///:memory:":

View File

@@ -39,8 +39,13 @@ loggingLevel = None
logging_setup = None
cipher = None
clientHash = None
experimentalFeatures = None
version = None
language = None
API_CLIENT_ID = '095d8cd841ac40b581330919b49fe746'
ESI_CACHE = 'esi_cache'
SSO_CALLBACK = 'https://pyfa-org.github.io/Pyfa/callback'
LOGLEVEL_MAP = {
"critical": CRITICAL,
@@ -50,6 +55,8 @@ LOGLEVEL_MAP = {
"debug": DEBUG,
}
CATALOG = 'lang'
slotColourMap = {
FittingSlot.LOW: wx.Colour(250, 235, 204), # yellow = low slots
FittingSlot.MED: wx.Colour(188, 215, 241), # blue = mid slots
@@ -103,6 +110,8 @@ def defPaths(customSavePath=None):
global cipher
global clientHash
global version
global experimentalFeatures
global language
pyfalog.debug("Configuring Pyfa")
@@ -168,6 +177,10 @@ def defPaths(customSavePath=None):
logPath = os.path.join(savePath, logFile)
experimentalFeatures = getattr(configforced, "experimentalFeatures", experimentalFeatures)
if experimentalFeatures is None:
experimentalFeatures = False
# DON'T MODIFY ANYTHING BELOW
import eos.config
@@ -178,9 +191,15 @@ def defPaths(customSavePath=None):
eos.config.gamedata_connectionstring = "sqlite:///" + gameDB + "?check_same_thread=False"
# initialize the settings
from service.settings import EOSSettings
from service.settings import EOSSettings, LocaleSettings
eos.config.settings = EOSSettings.getInstance().EOSSettings # this is kind of confusing, but whatever
# set langauge, taking the passed argument or falling back to what's saved in the settings
localeSettings = LocaleSettings.getInstance()
language = language or localeSettings.get('locale')
# sets the lang for eos, using the mapped langauge.
eos.config.set_lang(localeSettings.get_eos_locale())
def defLogging():
global debug
@@ -226,6 +245,8 @@ def defLogging():
# reset=False,
)
])
except (KeyboardInterrupt, SystemExit):
raise
except:
print("Critical error attempting to setup logging. Falling back to console only.")
logging_setup = NestedSetup([

3
crowdin.yml Normal file
View File

@@ -0,0 +1,3 @@
files:
- source: /locale/*.pot
translation: /locale/%locale_with_underscore%/LC_MESSAGES/lang.po

650
db_update.py Normal file
View File

@@ -0,0 +1,650 @@
#!/usr/bin/env python3
#======================================================================
# Copyright (C) 2012 Diego Duclos
#
# This file is part of eos.
#
# eos is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as
# published by the Free Software Foundation, either version 3 of
# the License, or (at your option) any later version.
#
# eos is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with eos. If not, see <http://www.gnu.org/licenses/>.
#======================================================================
import functools
import itertools
import json
import os
import re
import sqlite3
import sys
import sqlalchemy.orm
from sqlalchemy import or_, and_
# todo: need to set the EOS language to en, becasuse this assumes it's being run within an English context
# Need to know what that would do if called from pyfa
ROOT_DIR = os.path.realpath(os.path.dirname(__file__))
DB_PATH = os.path.join(ROOT_DIR, 'eve.db')
JSON_DIR = os.path.join(ROOT_DIR, 'staticdata')
if ROOT_DIR not in sys.path:
sys.path.insert(0, ROOT_DIR)
GAMEDATA_SCHEMA_VERSION = 4
def db_needs_update():
"""True if needs, false if it does not, none if we cannot check it."""
try:
with open(os.path.join(JSON_DIR, 'phobos', 'metadata.0.json')) as f:
data_version = next((r['field_value'] for r in json.load(f) if r['field_name'] == 'client_build'))
except (KeyboardInterrupt, SystemExit):
raise
# If we have no source data - return None; should not update in this case
except:
return None
if not os.path.isfile(DB_PATH):
print('Gamedata DB not found')
return True
db_data_version = None
db_schema_version = None
try:
db = sqlite3.connect(DB_PATH)
cursor = db.cursor()
cursor.execute('SELECT field_value FROM metadata WHERE field_name = \'client_build\'')
for row in cursor:
db_data_version = int(row[0])
cursor.execute('SELECT field_value FROM metadata WHERE field_name = \'schema_version\'')
for row in cursor:
db_schema_version = int(row[0])
cursor.close()
db.close()
except (KeyboardInterrupt, SystemExit):
raise
except:
print('Error when fetching gamedata DB metadata')
return True
if data_version != db_data_version:
print('Gamedata DB data version mismatch: needed {}, DB has {}'.format(data_version, db_data_version))
return True
if GAMEDATA_SCHEMA_VERSION != db_schema_version:
print('Gamedata DB schema version mismatch: needed {}, DB has {}'.format(GAMEDATA_SCHEMA_VERSION, db_schema_version))
return True
return False
def update_db():
print('Building gamedata DB...')
if os.path.isfile(DB_PATH):
os.remove(DB_PATH)
import eos.db
import eos.gamedata
import eos.config
# Create the database tables
eos.db.gamedata_meta.create_all()
def _readData(minerName, jsonName, keyIdName=None):
compiled_data = None
for i in itertools.count(0):
try:
with open(os.path.join(JSON_DIR, minerName, '{}.{}.json'.format(jsonName, i)), encoding='utf-8') as f:
rawData = json.load(f)
if i == 0:
compiled_data = {} if type(rawData) == dict else []
if type(rawData) == dict:
compiled_data.update(rawData)
else:
compiled_data.extend(rawData)
except FileNotFoundError:
break
if not keyIdName:
return compiled_data
# IDs in keys, rows in values
data = []
for k, v in compiled_data.items():
row = {}
row.update(v)
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_binary', 'types', keyIdName='typeID')
for row in data:
if (
# Apparently people really want Civilian modules available
(row['typeName_en-us'].startswith('Civilian') and "Shuttle" not in row['typeName_en-us']) or
row['typeName_en-us'] == 'Capsule' or
row['groupID'] == 4033 # destructible effect beacons
):
row['published'] = True
# Nearly useless and clutter search results too much
elif (
row['typeName_en-us'].startswith('Limited Synth ') or
row['typeName_en-us'].startswith('Expired ') or
row['typeName_en-us'].endswith(' Filament') and (
"'Needlejack'" not in row['typeName_en-us'] and
"'Devana'" not in row['typeName_en-us'] and
"'Pochven'" not in row['typeName_en-us'] and
"'Extraction'" not in row['typeName_en-us'] and
"'Krai Veles'" not in row['typeName_en-us'] and
"'Krai Perun'" not in row['typeName_en-us'] and
"'Krai Svarog'" not in row['typeName_en-us']
)
):
row['published'] = False
newData = []
for row in data:
if (
row['published'] or
# group Ship Modifiers, for items like tactical t3 ship modes
row['groupID'] == 1306 or
# Micro Bombs (Fighters)
row['typeID'] in (41549, 41548, 41551, 41550) or
# Abyssal weather (environment)
row['groupID'] in (
1882,
1975,
1971,
1983) # the "container" for the abyssal environments
):
newData.append(row)
map = {'typeName_en-us': 'typeName', 'description_en-us': '_description'}
map.update({'description'+v: '_description'+v for (k, v) in eos.config.translation_mapping.items() if k != 'en'})
_addRows(newData, eos.gamedata.Item, fieldMap=map)
return newData
def processEveGroups():
print('processing evegroups')
data = _readData('fsd_binary', 'groups', keyIdName='groupID')
map = {'groupName_en-us': 'name'}
map.update({'groupName'+v: 'name'+v for (k, v) in eos.config.translation_mapping.items() if k != 'en'})
_addRows(data, eos.gamedata.Group, fieldMap=map)
return data
def processEveCategories():
print('processing evecategories')
data = _readData('fsd_binary', 'categories', keyIdName='categoryID')
map = { 'categoryName_en-us': 'name' }
map.update({'categoryName'+v: 'name'+v for (k, v) in eos.config.translation_mapping.items() if k != 'en'})
_addRows(data, eos.gamedata.Category, fieldMap=map)
def processDogmaAttributes():
print('processing dogmaattributes')
data = _readData('fsd_binary', 'dogmaattributes', keyIdName='attributeID')
map = {
'displayName_en-us': 'displayName',
# 'tooltipDescription_en-us': 'tooltipDescription'
}
_addRows(data, eos.gamedata.AttributeInfo, fieldMap=map)
def processDogmaTypeAttributes(eveTypesData):
print('processing dogmatypeattributes')
data = _readData('fsd_binary', 'typedogma', keyIdName='typeID')
eveTypeIds = set(r['typeID'] for r in eveTypesData)
newData = []
seenKeys = set()
def checkKey(key):
if key in seenKeys:
return False
seenKeys.add(key)
return True
for typeData in data:
if typeData['typeID'] not in eveTypeIds:
continue
for row in typeData.get('dogmaAttributes', ()):
row['typeID'] = typeData['typeID']
if checkKey((row['typeID'], row['attributeID'])):
newData.append(row)
for row in eveTypesData:
for attrId, attrName in {4: 'mass', 38: 'capacity', 161: 'volume', 162: 'radius'}.items():
if attrName in row and checkKey((row['typeID'], attrId)):
newData.append({'typeID': row['typeID'], 'attributeID': attrId, 'value': row[attrName]})
_addRows(newData, eos.gamedata.Attribute)
return newData
def processDynamicItemAttributes():
print('processing dynamicitemattributes')
data = _readData('fsd_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',
'displayName_en-us': 'displayName'
})
def processMarketGroups():
print('processing marketgroups')
data = _readData('fsd_binary', 'marketgroups', keyIdName='marketGroupID')
map = {
'name_en-us': 'marketGroupName',
'description_en-us': '_description',
}
map.update({'name'+v: 'marketGroupName'+v for (k, v) in eos.config.translation_mapping.items() if k != 'en'})
map.update({'description' + v: '_description' + v for (k, v) in eos.config.translation_mapping.items() if k != 'en'})
_addRows(data, eos.gamedata.MarketGroup, fieldMap=map)
def processMetaGroups():
print('processing metagroups')
data = _readData('fsd_binary', 'metagroups', keyIdName='metaGroupID')
map = {'name_en-us': 'metaGroupName'}
map.update({'name' + v: 'metaGroupName' + v for (k, v) in eos.config.translation_mapping.items() if k != 'en'})
_addRows(data, eos.gamedata.MetaGroup, fieldMap=map)
def processCloneGrades():
print('processing clonegrades')
data = _readData('fsd_lite', 'clonegrades')
newData = []
# December, 2017 - CCP decided to use only one set of skill levels for alpha clones. However, this is still
# represented in the data as a skillset per race. To ensure that all skills are the same, we store them in a way
# that we can check to make sure all races have the same skills, as well as skill levels
check = {}
for ID in data:
for skill in data[ID]['skills']:
newData.append({
'alphaCloneID': int(ID),
'alphaCloneName': 'Alpha Clone',
'typeID': skill['typeID'],
'level': skill['level']})
if ID not in check:
check[ID] = {}
check[ID][int(skill['typeID'])] = int(skill['level'])
if not functools.reduce(lambda a, b: a if a == b else False, [v for _, v in check.items()]):
raise Exception('Alpha Clones not all equal')
newData = [x for x in newData if x['alphaCloneID'] == 1]
if len(newData) == 0:
raise Exception('Alpha Clone processing failed')
tmp = []
for row in newData:
if row['alphaCloneID'] not in tmp:
cloneParent = eos.gamedata.AlphaClone()
setattr(cloneParent, 'alphaCloneID', row['alphaCloneID'])
setattr(cloneParent, 'alphaCloneName', row['alphaCloneName'])
eos.db.gamedata_session.add(cloneParent)
tmp.append(row['alphaCloneID'])
_addRows(newData, eos.gamedata.AlphaCloneSkill)
def processTraits():
print('processing traits')
data = _readData('phobos', 'traits')
def convertSection(sectionData):
sectionLines = []
headerText = '<b>{}</b>'.format(sectionData['header'])
sectionLines.append(headerText)
for bonusData in sectionData['bonuses']:
prefix = '{} '.format(bonusData['number']) if 'number' in bonusData else ''
bonusText = '{}{}'.format(prefix, bonusData['text'].replace('\u00B7', '\u2022 '))
sectionLines.append(bonusText)
sectionLine = '<br />\n'.join(sectionLines)
return sectionLine
newData = []
for row in data:
try:
newRow = {
'typeID': row['typeID'],
}
for (k, v) in eos.config.translation_mapping.items():
if v == '':
v = '_en-us'
typeLines = []
traitData = row['traits{}'.format(v)]
for skillData in sorted(traitData.get('skills', ()), key=lambda i: i['header']):
typeLines.append(convertSection(skillData))
if 'role' in traitData:
typeLines.append(convertSection(traitData['role']))
if 'misc' in traitData:
typeLines.append(convertSection(traitData['misc']))
traitLine = '<br />\n<br />\n'.join(typeLines)
newRow['traitText{}'.format(v)] = traitLine
newData.append(newRow)
except:
pass
_addRows(newData, eos.gamedata.Traits, fieldMap={'traitText_en-us': 'traitText'})
def processMetadata():
print('processing metadata')
data = _readData('phobos', 'metadata')
_addRows(data, eos.gamedata.MetaData)
def processReqSkills(eveTypesData):
print('processing requiredskillsfortypes')
def composeReqSkills(raw):
reqSkills = {}
for skillTypeID, skillLevel in raw.items():
reqSkills[int(skillTypeID)] = skillLevel
return reqSkills
eveTypeIds = set(r['typeID'] for r in eveTypesData)
data = _readData('fsd_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_en-us', '')
# Regular sets matching
m = re.match('(?P<grade>(High|Mid|Low)-grade) (?P<set>\w+) (?P<implant>(Alpha|Beta|Gamma|Delta|Epsilon|Omega))', typeName, re.IGNORECASE)
if m:
implantSets.setdefault((m.group('grade'), m.group('set')), set()).add(row['typeID'])
# Special set matching
for setHandle, (setName, implantPattern) in specials.items():
pattern = '(?P<set>{}) (?P<implant>{})'.format(setName, implantPattern)
m = re.match(pattern, typeName)
if m:
implantSets.setdefault((None, setHandle), set()).add(row['typeID'])
break
data = []
for (gradeName, setName), implants in implantSets.items():
if len(implants) < 2:
continue
implants = ','.join('{}'.format(tid) for tid in sorted(implants))
row = {'setName': setName, 'gradeName': gradeName, 'implants': implants}
data.append(row)
_addRows(data, eos.gamedata.ImplantSet)
eveTypesData = processEveTypes()
eveGroupsData = processEveGroups()
processEveCategories()
processDogmaAttributes()
dogmaTypeAttributesData = processDogmaTypeAttributes(eveTypesData)
processDynamicItemAttributes()
processDogmaEffects()
dogmaTypeEffectsData = processDogmaTypeEffects(eveTypesData)
processDogmaUnits()
processMarketGroups()
processMetaGroups()
processCloneGrades()
processTraits()
processMetadata()
eos.db.gamedata_session.flush()
processReqSkills(eveTypesData)
processReplacements(eveTypesData, eveGroupsData, dogmaTypeAttributesData, dogmaTypeEffectsData)
processImplantSets(eveTypesData)
# Add schema version to prevent further updates
metadata_schema_version = eos.gamedata.MetaData()
metadata_schema_version.field_name = 'schema_version'
metadata_schema_version.field_value = GAMEDATA_SCHEMA_VERSION
eos.db.gamedata_session.add(metadata_schema_version)
eos.db.gamedata_session.flush()
# CCP still has 5 subsystems assigned to T3Cs, even though only 4 are available / usable. They probably have some
# old legacy requirement or assumption that makes it difficult for them to change this value in the data. But for
# pyfa, we can do it here as a post-processing step
for attr in eos.db.gamedata_session.query(eos.gamedata.Attribute).filter(eos.gamedata.Attribute.ID == 1367).all():
attr.value = 4.0
for item in eos.db.gamedata_session.query(eos.gamedata.Item).filter(or_(
eos.gamedata.Item.name.like('%abyssal%'),
eos.gamedata.Item.name.like('%mutated%'),
eos.gamedata.Item.name.like('%_PLACEHOLDER%'),
# Drifter weapons are published for some reason
eos.gamedata.Item.name.in_(('Lux Kontos', 'Lux Xiphos'))
)).all():
if 'Asteroid Mining Crystal' in item.name:
continue
if 'Mutated Drone Specialization' in item.name:
continue
item.published = False
for x in [
30 # Apparel
]:
cat = eos.db.gamedata_session.query(eos.gamedata.Category).filter(eos.gamedata.Category.ID == x).first()
print ('Removing Category: {}'.format(cat.name))
eos.db.gamedata_session.delete(cat)
# Unused normally, can be useful for customizing items
def _hardcodeAttribs(typeID, attrMap):
for attrName, value in attrMap.items():
try:
attr = eos.db.gamedata_session.query(eos.gamedata.Attribute).filter(and_(
eos.gamedata.Attribute.name == attrName, eos.gamedata.Attribute.typeID == typeID)).one()
except sqlalchemy.orm.exc.NoResultFound:
attrInfo = eos.db.gamedata_session.query(eos.gamedata.AttributeInfo).filter(eos.gamedata.AttributeInfo.name == attrName).one()
attr = eos.gamedata.Attribute()
attr.ID = attrInfo.ID
attr.typeID = typeID
attr.value = value
eos.db.gamedata_session.add(attr)
else:
attr.value = value
def _hardcodeEffects(typeID, effectMap):
item = eos.db.gamedata_session.query(eos.gamedata.Item).filter(eos.gamedata.Item.ID == typeID).one()
item.effects.clear()
for effectID, effectName in effectMap.items():
effect = eos.gamedata.Effect()
effect.effectID = effectID
effect.effectName = effectName
item.effects[effectName] = effect
eos.db.gamedata_session.commit()
eos.db.gamedata_engine.execute('VACUUM')
print('done')
if __name__ == '__main__':
update_db()

19
dist_assets/linux/AppRun Normal file
View File

@@ -0,0 +1,19 @@
#! /bin/bash -i
# Export APPRUN if running from an extracted image
self="$(readlink -f -- $0)"
here="${self%/*}"
APPDIR="${APPDIR:-${here}}"
# Export TCl/Tk
export TCL_LIBRARY="${APPDIR}/usr/share/tcltk/tcl8.4"
export TK_LIBRARY="${APPDIR}/usr/share/tcltk/tk8.4"
export TKPATH="${TK_LIBRARY}"
# Export SSL certificate
export SSL_CERT_FILE="${APPDIR}/opt/_internal/certs.pem"
# Call the entry point
#! /bin/bash -i
${APPDIR}/usr/bin/pyfa "$@"

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop-application">
<id>org.pyfa.pyfa</id>
<metadata_license>MIT</metadata_license>
<project_license>GPL-3</project_license>
<name>Pyfa</name>
<summary>Pyfa </summary>
<description>
<p> Python Fitting Assitant for EVE Online
</p>
</description>
<launchable type="desktop-id">pyfa.desktop</launchable>
<url type="homepage">https://github.com/pyfa-org/Pyfa</url>
<provides>
<binary>pyfa</binary>
</provides>
</component>

3
dist_assets/linux/pyfa Normal file
View File

@@ -0,0 +1,3 @@
#! /bin/bash
${APPDIR}/usr/bin/python3.7 -s "${APPDIR}/opt/pyfa/pyfa.py" "$@"

View File

@@ -1,70 +0,0 @@
# -*- mode: python -*-
import os
from itertools import chain
import subprocess
label = subprocess.check_output([
"git", "describe", "--tags"]).strip()
with open('gitversion', 'w+') as f:
f.write(label.decode())
block_cipher = None
added_files = [
( 'imgs/gui/*.png', 'imgs/gui' ),
( 'imgs/gui/*.gif', 'imgs/gui' ),
( 'imgs/icons/*.png', 'imgs/icons' ),
( 'imgs/renders/*.png', 'imgs/renders' ),
( 'dist_assets/win/pyfa.ico', '.' ),
( 'dist_assets/cacert.pem', '.' ),
( 'eve.db', '.' ),
( 'README.md', '.' ),
( 'LICENSE', '.' ),
( 'gitversion', '.' ),
]
import_these = []
# Walk directories that do dynamic importing
paths = ('eos/effects', 'eos/db/migrations', 'service/conversions')
for root, folders, files in chain.from_iterable(os.walk(path) for path in paths):
for file_ in files:
if file_.endswith(".py") and not file_.startswith("_"):
mod_name = "{}.{}".format(
root.replace("/", "."),
file_.split(".py")[0],
)
import_these.append(mod_name)
a = Analysis(['pyfa.py'],
pathex=[],
binaries=[],
datas=added_files,
hiddenimports=import_these,
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
exclude_binaries=True,
name='pyfa',
debug=False,
strip=False,
upx=True,
console=True )
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
name='pyfa')

View File

@@ -0,0 +1,7 @@
[Desktop Entry]
Type=Application
Name=Pyfa
Exec=pyfa
Comment=Python Fitting Assistant for EVE: Online
Icon=python
Categories=Game;

View File

@@ -20,6 +20,7 @@ added_files = [
('../../imgs/renders/*.png', 'imgs/renders'),
('../../dist_assets/win/pyfa.ico', '.'),
('../../service/jargon/*.yaml', 'service/jargon'),
('../../locale', 'locale'),
(requests.certs.where(), '.'), # is this needed anymore?
('../../eve.db', '.'),
('../../README.md', '.'),
@@ -30,7 +31,8 @@ added_files = [
import_these = [
'numpy.core._dtype_ctypes', # https://github.com/pyinstaller/pyinstaller/issues/3982
'sqlalchemy.ext.baked' # windows build doesn't launch without if when using sqlalchemy 1.3.x
'sqlalchemy.ext.baked', # windows build doesn't launch without if when using sqlalchemy 1.3.x
'pkg_resources.py2_warn' # issue 2156
]
icon = os.path.join(os.getcwd(), "dist_assets", "mac", "pyfa.icns")

View File

@@ -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

View File

@@ -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>

View File

@@ -14,7 +14,7 @@ with open("version.yml", 'r') as file:
os.environ["PYFA_DIST_DIR"] = os.path.join(os.getcwd(), 'dist')
os.environ["PYFA_VERSION"] = version
iscc = "C:\Program Files (x86)\Inno Setup 5\ISCC.exe" # inno script location via wine
iscc = "C:\Program Files (x86)\Inno Setup 6\ISCC.exe"
source = os.path.join(os.environ["PYFA_DIST_DIR"], "pyfa")

View File

@@ -15,10 +15,6 @@
#define MyAppURL "https://github.com/pyfa-org/Pyfa/"
#define MyAppExeName "pyfa.exe"
; What version starts with the new structure (1.x.0). This is used to determine if we run directory structure cleanup
#define MajorVersionFlag 2
#define MinorVersionFlag 0
#ifndef MyOutputFile
#define MyOutputFile LowerCase(StringChange(MyAppName+'-'+MyAppVersion+'-win', " ", "-"))
#endif
@@ -30,7 +26,6 @@
#endif
[Setup]
; NOTE: The value of AppId uniquely identifies this application.
; Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
@@ -41,6 +36,9 @@ AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
ArchitecturesAllowed=x64
ArchitecturesInstallIn64BitMode=x64
CloseApplications=yes
DefaultDirName={pf}\{#MyAppName}
DefaultGroupName={#MyAppName}
AllowNoIcons=yes
@@ -49,7 +47,6 @@ OutputDir={#MyOutputDir}
OutputBaseFilename={#MyOutputFile}
SetupIconFile={#MyAppDir}\pyfa.ico
SolidCompression=yes
CloseApplications=yes
[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"
@@ -83,6 +80,7 @@ Type: files; Name: "{app}\*.pyc"
[Code]
/////////////////////////////////////////////////////////////////////
function IsAppRunning(const FileName : string): Boolean;
var
FSWbemLocator: Variant;
@@ -99,6 +97,7 @@ begin
FSWbemLocator := Unassigned;
end;
/////////////////////////////////////////////////////////////////////
procedure RemoveFromVirtualStore;
var
VirtualStore,FileName,FilePath:String;
@@ -115,11 +114,12 @@ begin
end;
end;
/////////////////////////////////////////////////////////////////////
function PrepareToInstall(var NeedsRestart: Boolean): String;
begin
if(IsAppRunning( 'pyfa.exe' )) then
begin
Result := 'Please close pyfa before continuing. When closed, please go back to the previous step and continue.';
Result := 'Please close pyfa before continuing. When closed, please go back to the previous step and continue. If you have named this installer pyfa.exe, please rename it and restart installation';
end
else
begin
@@ -127,54 +127,61 @@ begin
end
end;
function GetUninstallString: string;
/////////////////////////////////////////////////////////////////////
function GetUninstallString(): String;
var
sUnInstPath: string;
sUnInstPath: String;
sUnInstallString: String;
begin
Result := '';
sUnInstPath := ExpandConstant('Software\Microsoft\Windows\CurrentVersion\Uninstall\{{3DA39096-C08D-49CD-90E0-1D177F32C8AA}_is1'); //Your App GUID/ID
sUnInstallString := '';
if not RegQueryStringValue(HKLM, sUnInstPath, 'UninstallString', sUnInstallString) then
RegQueryStringValue(HKCU, sUnInstPath, 'UninstallString', sUnInstallString);
if not RegQueryStringValue(HKCU, sUnInstPath, 'UninstallString', sUnInstallString) then
if not RegQueryStringValue(HKLM32, sUnInstPath, 'UninstallString', sUnInstallString) then
RegQueryStringValue(HKCU32, sUnInstPath, 'UninstallString', sUnInstallString);
Result := sUnInstallString;
end;
function IsUpgrade: Boolean;
/////////////////////////////////////////////////////////////////////
function UnInstallOldVersion(): Integer;
var
sUnInstallString: String;
iResultCode: Integer;
begin
// Return Values:
// 1 - uninstall string is empty
// 2 - error executing the UnInstallString
// 3 - successfully executed the UnInstallString
// default return value
Result := 0;
// get the uninstall string of the old app
sUnInstallString := GetUninstallString();
if sUnInstallString <> '' then begin
sUnInstallString := RemoveQuotes(sUnInstallString);
if Exec(sUnInstallString, '/SILENT /NORESTART /SUPPRESSMSGBOXES','', SW_HIDE, ewWaitUntilTerminated, iResultCode) then
Result := 3
else
Result := 2;
end else
Result := 1;
end;
/////////////////////////////////////////////////////////////////////
function IsUpgrade(): Boolean;
begin
Result := (GetUninstallString() <> '');
end;
function InitializeSetup: Boolean;
var
V: Integer;
iResultCode: Integer;
sUnInstallString: string;
iOldVersionMajor: Cardinal;
iOldVersionMinor: Cardinal;
/////////////////////////////////////////////////////////////////////
procedure CurStepChanged(CurStep: TSetupStep);
begin
Result := True; // in case when no previous version is found
if RegValueExists(HKEY_LOCAL_MACHINE,'Software\Microsoft\Windows\CurrentVersion\Uninstall\{3DA39096-C08D-49CD-90E0-1D177F32C8AA}_is1', 'UninstallString') then //Your App GUID/ID
if (CurStep=ssInstall) then
begin
RegQueryDWordValue(HKEY_LOCAL_MACHINE,
'Software\Microsoft\Windows\CurrentVersion\Uninstall\{3DA39096-C08D-49CD-90E0-1D177F32C8AA}_is1',
'MajorVersion', iOldVersionMajor);
RegQueryDWordValue(HKEY_LOCAL_MACHINE,
'Software\Microsoft\Windows\CurrentVersion\Uninstall\{3DA39096-C08D-49CD-90E0-1D177F32C8AA}_is1',
'MinorVersion', iOldVersionMinor);
if (iOldVersionMajor < {#MajorVersionFlag}) or ((iOldVersionMajor = {#MajorVersionFlag}) and (iOldVersionMinor < {#MinorVersionFlag})) then // If old version with old structure is installed.
if (IsUpgrade()) then
begin
V := MsgBox(ExpandConstant('An old version of pyfa was detected. Due to recent changes in the application structure, you must uninstall the previous version first. This will not affect your user data (saved fittings, characters, etc.). Do you want to uninstall now?'), mbInformation, MB_YESNO); //Custom Message if App installed
if V = IDYES then
begin
sUnInstallString := GetUninstallString();
sUnInstallString := RemoveQuotes(sUnInstallString);
Exec(ExpandConstant(sUnInstallString), '', '', SW_SHOW, ewWaitUntilTerminated, iResultCode);
Result := True; //if you want to proceed after uninstall
//Exit; //if you want to quit after uninstall
end
else
Result := False; //when older version present and not uninstalled
UnInstallOldVersion();
end;
end;
end;

View File

@@ -8,11 +8,6 @@
</requestedPrivileges>
</security>
</trustInfo>
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Microsoft.VC90.CRT" version="9.0.21022.8" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
</dependentAssembly>
</dependency>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>

View File

@@ -20,7 +20,6 @@ added_files = [
('../../service/jargon/*.yaml', 'service/jargon'),
('../../dist_assets/win/pyfa.ico', '.'),
('../../dist_assets/win/pyfa.exe.manifest', '.'),
('../../dist_assets/win/Microsoft.VC90.CRT.manifest', '.'),
(requests.certs.where(), '.'), # is this needed anymore?
('../../eve.db', '.'),
('../../README.md', '.'),
@@ -30,7 +29,8 @@ added_files = [
import_these = [
'numpy.core._dtype_ctypes', # https://github.com/pyinstaller/pyinstaller/issues/3982
'sqlalchemy.ext.baked' # windows build doesn't launch without if when using sqlalchemy 1.3.x
'sqlalchemy.ext.baked', # windows build doesn't launch without if when using sqlalchemy 1.3.x
'pkg_resources.py2_warn' # issue 2156
]
# Walk directories that do dynamic importing

View File

@@ -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',
)

View File

@@ -18,6 +18,38 @@
# =============================================================================
import math
# Just copy-paste penalization chain calculation code (with some modifications,
# as multipliers arrive in different form) in here to not make actual attribute
# calculations slower than they already are due to extra function calls
def calculateMultiplier(multipliers):
"""
multipliers: dictionary in format:
{stacking group name: [(mult, resist attr ID), (mult, resist attr ID)]}
"""
val = 1
for penalizedMultipliers in multipliers.values():
# A quick explanation of how this works:
# 1: Bonuses and penalties are calculated seperately, so we'll have to filter each of them
l1 = [v[0] for v in penalizedMultipliers if v[0] > 1]
l2 = [v[0] for v in penalizedMultipliers if v[0] < 1]
# 2: The most significant bonuses take the smallest penalty,
# This means we'll have to sort
abssort = lambda _val: -abs(_val - 1)
l1.sort(key=abssort)
l2.sort(key=abssort)
# 3: The first module doesn't get penalized at all
# Any module after the first takes penalties according to:
# 1 + (multiplier - 1) * math.exp(- math.pow(i, 2) / 7.1289)
for l in (l1, l2):
for i in range(len(l)):
bonus = l[i]
val *= 1 + (bonus - 1) * math.exp(- i ** 2 / 7.1289)
return val
def calculateRangeFactor(srcOptimalRange, srcFalloffRange, distance, restrictedRange=True):
"""Range strength/chance factor, applicable to guns, ewar, RRs, etc."""
if distance is None:
@@ -31,3 +63,9 @@ def calculateRangeFactor(srcOptimalRange, srcFalloffRange, distance, restrictedR
return 1
else:
return 0
def calculateLockTime(srcScanRes, tgtSigRadius):
if not srcScanRes or not tgtSigRadius:
return None
return min(40000 / srcScanRes / math.asinh(tgtSigRadius) ** 2, 30 * 60)

View File

@@ -13,6 +13,24 @@ saveddataCache = True
gamedata_version = ""
gamedata_date = ""
gamedata_connectionstring = 'sqlite:///' + realpath(join(dirname(abspath(__file__)), "..", "eve.db"))
lang = ""
# Maps supported langauges to their suffix in the database
translation_mapping = {
"en": "",
"fr": "_fr",
# "it": "_it",
"ja": "_ja",
"ko": "_ko",
"ru": "_ru",
"zh": "_zh",
}
def set_lang(i18n_lang):
global lang
lang = translation_mapping.get(i18n_lang, translation_mapping.get("en"))
pyfalog.debug("Gamedata connection string: {0}", gamedata_connectionstring)
if istravis is True or hasattr(sys, '_called_from_test'):

View File

@@ -17,24 +17,37 @@
# along with eos. If not, see <http://www.gnu.org/licenses/>.
# ===============================================================================
import re
import threading
from sqlalchemy import MetaData, create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy import MetaData, create_engine, event
from sqlalchemy.orm import sessionmaker, scoped_session
from . import migration
from eos import config
from logbook import Logger
pyfalog = Logger(__name__)
pyfalog.info("Initializing database")
pyfalog.info("Gamedata connection: {0}", config.gamedata_connectionstring)
pyfalog.info("Saveddata connection: {0}", config.saveddata_connectionstring)
class ReadOnlyException(Exception):
pass
def re_fn(expr, item):
try:
reg = re.compile(expr, re.IGNORECASE)
except (SystemExit, KeyboardInterrupt):
raise
except:
return False
return reg.search(item) is not None
pyfalog.debug('Initializing gamedata')
gamedata_connectionstring = config.gamedata_connectionstring
if callable(gamedata_connectionstring):
@@ -42,9 +55,26 @@ if callable(gamedata_connectionstring):
else:
gamedata_engine = create_engine(gamedata_connectionstring, echo=config.debug)
@event.listens_for(gamedata_engine, 'connect')
def create_functions(dbapi_connection, connection_record):
dbapi_connection.create_function('regexp', 2, re_fn)
gamedata_meta = MetaData()
gamedata_meta.bind = gamedata_engine
gamedata_session = sessionmaker(bind=gamedata_engine, autoflush=False, expire_on_commit=False)()
GamedataSession = scoped_session(sessionmaker(bind=gamedata_engine, autoflush=False, expire_on_commit=False))
gamedata_session = GamedataSession()
gamedata_sessions = {threading.get_ident(): gamedata_session}
def get_gamedata_session():
thread_id = threading.get_ident()
if thread_id not in gamedata_sessions:
gamedata_sessions[thread_id] = GamedataSession()
return gamedata_sessions[thread_id]
pyfalog.debug('Getting gamedata version')
# This should be moved elsewhere, maybe as an actual query. Current, without try-except, it breaks when making a new
@@ -56,6 +86,8 @@ try:
config.gamedata_date = gamedata_session.execute(
"SELECT `field_value` FROM `metadata` WHERE `field_name` LIKE 'dump_time'"
).fetchone()[0]
except (KeyboardInterrupt, SystemExit):
raise
except Exception as e:
pyfalog.warning("Missing gamedata version.")
pyfalog.critical(e)
@@ -82,11 +114,11 @@ sd_lock = threading.RLock()
pyfalog.debug('Importing gamedata DB scheme')
# Import all the definitions for all our database stuff
# noinspection PyPep8
from eos.db.gamedata import alphaClones, attribute, category, effect, group, item, marketGroup, metaData, metaGroup, queries, traits, unit, dynamicAttributes
from eos.db.gamedata import alphaClones, attribute, category, effect, group, item, marketGroup, metaData, metaGroup, queries, traits, unit, dynamicAttributes, implantSet
pyfalog.debug('Importing saveddata DB scheme')
# noinspection PyPep8
from eos.db.saveddata import booster, cargo, character, damagePattern, databaseRepair, drone, fighter, fit, implant, implantSet, loadDefaultDatabaseValues, \
miscData, mutator, module, override, price, queries, skill, targetProfile, user
from eos.db.saveddata import booster, cargo, character, damagePattern, databaseRepair, drone, fighter, fit, implant, implantSet, \
miscData, mutatorMod, mutatorDrone, module, override, price, queries, skill, targetProfile, user
pyfalog.debug('Importing gamedata queries')
# noinspection PyPep8

View File

@@ -1,2 +1,2 @@
__all__ = ["attribute", "category", "effect", "group", "metaData", "dynamicAttributes",
"item", "marketGroup", "metaGroup", "unit", "alphaClones"]
"item", "marketGroup", "metaGroup", "unit", "alphaClones", "implantSet"]

View File

@@ -23,7 +23,7 @@ from sqlalchemy.orm import relation, mapper, synonym, deferred
from eos.db import gamedata_meta
from eos.gamedata import Attribute, AttributeInfo, Unit
import eos.config
typeattributes_table = Table("dgmtypeattribs", gamedata_meta,
Column("value", Float),
Column("typeID", Integer, ForeignKey("invtypes.typeID"), primary_key=True, index=True),
@@ -36,11 +36,11 @@ attributes_table = Table("dgmattribs", gamedata_meta,
Column("maxAttributeID", Integer, ForeignKey("dgmattribs.attributeID")),
Column("description", Unicode),
Column("published", Boolean),
Column("displayName", String),
*[Column("displayName{}".format(lang), String) for lang in eos.config.translation_mapping.values()],
Column("highIsGood", Boolean),
Column("iconID", Integer),
Column("attributeCategory", Integer),
Column("tooltipDescription", Integer),
# Column("tooltipDescription", Integer), # deprecated...?
Column("unitID", Integer, ForeignKey("dgmunits.unitID")))
mapper(Attribute, typeattributes_table,
@@ -51,14 +51,14 @@ mapper(AttributeInfo, attributes_table,
"unit" : relation(Unit),
"ID" : synonym("attributeID"),
"name" : synonym("attributeName"),
"description": deferred(attributes_table.c.description)
"description": deferred(attributes_table.c.description),
})
Attribute.ID = association_proxy("info", "attributeID")
Attribute.name = association_proxy("info", "attributeName")
Attribute.description = association_proxy("info", "description")
Attribute.published = association_proxy("info", "published")
Attribute.displayName = association_proxy("info", "displayName")
Attribute.displayName = association_proxy("info", "displayName{}".format(eos.config.lang))
Attribute.highIsGood = association_proxy("info", "highIsGood")
Attribute.iconID = association_proxy("info", "iconID")
Attribute.icon = association_proxy("info", "icon")

View File

@@ -22,17 +22,18 @@ from sqlalchemy.orm import deferred, mapper, synonym
from eos.db import gamedata_meta
from eos.gamedata import Category
import eos.config
categories_table = Table("invcategories", gamedata_meta,
Column("categoryID", Integer, primary_key=True),
Column("categoryName", String),
Column("description", String),
*[Column("name{}".format(lang), String) for lang in eos.config.translation_mapping.values()],
# Column("description", String), # deprecated
Column("published", Boolean),
Column("iconID", Integer))
mapper(Category, categories_table,
properties={
"ID" : synonym("categoryID"),
"name" : synonym("categoryName"),
"description": deferred(categories_table.c.description)
"displayName": synonym("name{}".format(eos.config.lang)),
# "description": deferred(categories_table.c.description) # deprecated
})

View File

@@ -22,11 +22,12 @@ from sqlalchemy.orm import relation, mapper, synonym, deferred, backref
from eos.db import gamedata_meta
from eos.gamedata import Category, Group
import eos.config
groups_table = Table("invgroups", gamedata_meta,
Column("groupID", Integer, primary_key=True),
Column("groupName", String),
Column("description", String),
*[Column("name{}".format(lang), String) for lang in eos.config.translation_mapping.values()],
# Column("description", String), # deprecated
Column("published", Boolean),
Column("categoryID", Integer, ForeignKey("invcategories.categoryID")),
Column("iconID", Integer))
@@ -35,6 +36,6 @@ mapper(Group, groups_table,
properties={
"category" : relation(Category, backref=backref("groups", cascade="all,delete")),
"ID" : synonym("groupID"),
"name" : synonym("groupName"),
"description": deferred(groups_table.c.description)
"displayName" : synonym("name{}".format(eos.config.lang)),
# "description": deferred(groups_table.c.description) # deprecated
})

View 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")})

View File

@@ -17,7 +17,7 @@
# along with eos. If not, see <http://www.gnu.org/licenses/>.
# ===============================================================================
from sqlalchemy import Boolean, Column, Float, ForeignKey, Integer, String, Table
from sqlalchemy import Boolean, Column, ForeignKey, Integer, String, Table
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.orm import backref, deferred, mapper, relation, synonym
from sqlalchemy.orm.collections import attribute_mapped_collection
@@ -27,15 +27,14 @@ from eos.db.gamedata.dynamicAttributes import dynamicApplicable_table
from eos.db.gamedata.effect import typeeffects_table
from eos.gamedata import Attribute, DynamicItem, Effect, Group, Item, Traits, MetaGroup
import eos.config
items_table = Table("invtypes", gamedata_meta,
Column("typeID", Integer, primary_key=True),
Column("typeName", String, index=True),
Column("description", String),
*[Column("typeName{}".format(lang), String, index=True) for lang in eos.config.translation_mapping.values()],
*[Column("typeDescription{}".format(lang), String) for lang in eos.config.translation_mapping.values()],
Column("raceID", Integer),
Column("factionID", Integer),
Column("volume", Float),
Column("mass", Float),
Column("capacity", Float),
Column("published", Boolean),
Column("marketGroupID", Integer, ForeignKey("invmarketgroups.marketGroupID")),
Column("iconID", Integer),
@@ -44,27 +43,40 @@ items_table = Table("invtypes", gamedata_meta,
Column("metaLevel", Integer),
Column("metaGroupID", Integer, ForeignKey("invmetagroups.metaGroupID"), index=True),
Column("variationParentTypeID", Integer, ForeignKey("invtypes.typeID"), index=True),
Column("replacements", String))
Column("replacements", String),
Column("reqskills", String),
Column("requiredfor", String),
)
from .traits import traits_table # noqa
mapper(Item, items_table,
properties={
"group" : relation(Group, backref=backref("items", cascade="all,delete")),
props = {
"group": relation(Group, backref=backref("items", cascade="all,delete")),
"_Item__attributes": relation(Attribute, cascade='all, delete, delete-orphan', collection_class=attribute_mapped_collection('name')),
"effects": relation(Effect, secondary=typeeffects_table, collection_class=attribute_mapped_collection('name')),
"metaGroup" : relation(MetaGroup, backref=backref("items", cascade="all,delete")),
"varParent" : relation(Item, backref=backref("varChildren", cascade="all,delete"), remote_side=items_table.c.typeID),
"ID" : synonym("typeID"),
"name" : synonym("typeName"),
"description" : deferred(items_table.c.description),
"traits" : relation(Traits,
primaryjoin=traits_table.c.typeID == items_table.c.typeID,
uselist=False),
"mutaplasmids": relation(DynamicItem,
primaryjoin=dynamicApplicable_table.c.applicableTypeID == items_table.c.typeID,
secondaryjoin=dynamicApplicable_table.c.typeID == DynamicItem.typeID,
secondary=dynamicApplicable_table,
backref="applicableItems")})
"metaGroup": relation(MetaGroup, backref=backref("items", cascade="all,delete")),
"varParent": relation(Item, backref=backref("varChildren", cascade="all,delete"), remote_side=items_table.c.typeID),
"ID": synonym("typeID"),
"name": synonym("typeName{}".format(eos.config.lang)),
"description" : synonym("_description{}".format(eos.config.lang)),
"traits": relation(
Traits,
primaryjoin=traits_table.c.typeID == items_table.c.typeID,
uselist=False
),
"mutaplasmids": relation(
DynamicItem,
primaryjoin=dynamicApplicable_table.c.applicableTypeID == items_table.c.typeID,
secondaryjoin=dynamicApplicable_table.c.typeID == DynamicItem.typeID,
secondary=dynamicApplicable_table,
backref="applicableItems"
)
}
# Create deferred columns shadowing all the description fields. The literal `description` property will dynamically
# be assigned as synonym to one of these
props.update({'_description' + v: deferred(items_table.c['typeDescription' + v]) for (k, v) in eos.config.translation_mapping.items()})
mapper(Item, items_table, properties=props)
Item.category = association_proxy("group", "category")

View File

@@ -22,22 +22,34 @@ from sqlalchemy.orm import relation, mapper, synonym, deferred
from eos.db import gamedata_meta
from eos.gamedata import Item, MarketGroup
import eos.config
marketgroups_table = Table("invmarketgroups", gamedata_meta,
Column("marketGroupID", Integer, primary_key=True),
Column("marketGroupName", String),
Column("description", String),
*[Column("marketGroupName{}".format(lang), String) for lang in eos.config.translation_mapping.values()],
*[Column("marketGroupDescription{}".format(lang), String) for lang in eos.config.translation_mapping.values()],
Column("hasTypes", Boolean),
Column("parentGroupID", Integer,
ForeignKey("invmarketgroups.marketGroupID", initially="DEFERRED", deferrable=True)),
ForeignKey("invmarketgroups.marketGroupID", initially="DEFERRED", deferrable=True)),
Column("iconID", Integer))
mapper(MarketGroup, marketgroups_table,
properties={
"items" : relation(Item, backref="marketGroup"),
"parent" : relation(MarketGroup, backref="children",
remote_side=[marketgroups_table.c.marketGroupID]),
"ID" : synonym("marketGroupID"),
"name" : synonym("marketGroupName"),
"description": deferred(marketgroups_table.c.description)
})
props = {
"items": relation(Item, backref="marketGroup"),
"parent": relation(MarketGroup, backref="children", remote_side=[marketgroups_table.c.marketGroupID]),
"ID": synonym("marketGroupID"),
"name": synonym("marketGroupName{}".format(eos.config.lang)),
"description": synonym("_description{}".format(eos.config.lang)),
}
# Create deferred columns shadowing all the description fields. The literal `description` property will dynamically
# be assigned as synonym to one of these
# this is mostly here to allow the db_update to be language-agnostic
# todo: determine if we ever use market group descriptions... can we just get with of these?
props.update({'_description' + v: deferred(marketgroups_table.c['marketGroupDescription' + v]) for (k, v) in eos.config.translation_mapping.items()})
mapper(
MarketGroup,
marketgroups_table,
properties=props
)

View File

@@ -22,12 +22,20 @@ from sqlalchemy.orm import mapper, synonym
from eos.db import gamedata_meta
from eos.gamedata import MetaGroup
import eos.config
metagroups_table = Table("invmetagroups", gamedata_meta,
Column("metaGroupID", Integer, primary_key=True),
Column("metaGroupName", String))
metagroups_table = Table(
"invmetagroups",
gamedata_meta,
Column("metaGroupID", Integer, primary_key=True),
*[Column("metaGroupName{}".format(lang), String) for lang in eos.config.translation_mapping.values()],
)
mapper(MetaGroup, metagroups_table,
properties={
"ID" : synonym("metaGroupID"),
"name": synonym("metaGroupName")})
mapper(
MetaGroup,
metagroups_table,
properties={
"ID" : synonym("metaGroupID"),
"name": synonym("metaGroupName{}".format(eos.config.lang))
}
)

View File

@@ -22,11 +22,11 @@ from sqlalchemy.orm import aliased, exc, join
from sqlalchemy.sql import and_, or_, select
import eos.config
from eos.db import gamedata_session
from eos.db import get_gamedata_session
from eos.db.gamedata.item import items_table
from eos.db.gamedata.group import groups_table
from eos.db.util import processEager, processWhere
from eos.gamedata import AlphaClone, Attribute, AttributeInfo, Category, DynamicItem, Group, Item, MarketGroup, MetaData, MetaGroup
from eos.gamedata import AlphaClone, Attribute, AttributeInfo, Category, DynamicItem, Group, Item, MarketGroup, MetaData, MetaGroup, ImplantSet
cache = {}
configVal = getattr(eos.config, "gamedataCache", None)
@@ -64,7 +64,7 @@ else:
return deco
def sqlizeString(line):
def sqlizeNormalString(line):
# Escape backslashes first, as they will be as escape symbol in queries
# Then escape percent and underscore signs
# Finally, replace generic wildcards with sql-style wildcards
@@ -79,28 +79,39 @@ itemNameMap = {}
def getItem(lookfor, eager=None):
if isinstance(lookfor, int):
if eager is None:
item = gamedata_session.query(Item).get(lookfor)
item = get_gamedata_session().query(Item).get(lookfor)
else:
item = gamedata_session.query(Item).options(*processEager(eager)).filter(Item.ID == lookfor).first()
item = get_gamedata_session().query(Item).options(*processEager(eager)).filter(Item.ID == lookfor).first()
elif isinstance(lookfor, str):
if lookfor in itemNameMap:
id = itemNameMap[lookfor]
if eager is None:
item = gamedata_session.query(Item).get(id)
item = get_gamedata_session().query(Item).get(id)
else:
item = gamedata_session.query(Item).options(*processEager(eager)).filter(Item.ID == id).first()
item = get_gamedata_session().query(Item).options(*processEager(eager)).filter(Item.ID == id).first()
else:
# Item names are unique, so we can use first() instead of one()
item = gamedata_session.query(Item).options(*processEager(eager)).filter(Item.name == lookfor).first()
itemNameMap[lookfor] = item.ID
item = get_gamedata_session().query(Item).options(*processEager(eager)).filter(Item.typeName == lookfor).first()
if item is not None:
itemNameMap[lookfor] = item.ID
else:
raise TypeError("Need integer or string as argument")
return item
@cachedQuery(1, "itemIDs")
def getItems(itemIDs, eager=None):
if not isinstance(itemIDs, (tuple, list, set)) or not all(isinstance(t, int) for t in itemIDs):
raise TypeError("Need iterable of integers as argument")
if eager is None:
items = get_gamedata_session().query(Item).filter(Item.ID.in_(itemIDs)).all()
else:
items = get_gamedata_session().query(Item).options(*processEager(eager)).filter(Item.ID.in_(itemIDs)).all()
return items
def getMutaplasmid(lookfor, eager=None):
if isinstance(lookfor, int):
item = gamedata_session.query(DynamicItem).filter(DynamicItem.ID == lookfor).first()
item = get_gamedata_session().query(DynamicItem).filter(DynamicItem.ID == lookfor).first()
else:
raise TypeError("Need integer as argument")
return item
@@ -108,7 +119,7 @@ def getMutaplasmid(lookfor, eager=None):
def getItemWithBaseItemAttribute(lookfor, baseItemID, eager=None):
# A lot of this is described in more detail in #1597
item = gamedata_session.query(Item).get(lookfor)
item = get_gamedata_session().query(Item).get(lookfor)
base = getItem(baseItemID)
# we have to load all attributes for this object, otherwise we'll lose access to them when we expunge.
@@ -124,7 +135,7 @@ def getItemWithBaseItemAttribute(lookfor, baseItemID, eager=None):
# Expunge the item form the session. This is required to have different Abyssal / Base combinations loaded in memory.
# Without expunging it, once one Abyssal Web is created, SQLAlchmey will use it for all others. We don't want this,
# we want to generate a completely new object to work with
gamedata_session.expunge(item)
get_gamedata_session().expunge(item)
return item
@@ -147,7 +158,7 @@ def getItems(lookfor, eager=None):
if len(toGet) > 0:
# Get items that aren't currently cached, and store them in the cache
items = gamedata_session.query(Item).filter(Item.ID.in_(toGet)).all()
items = get_gamedata_session().query(Item).filter(Item.ID.in_(toGet)).all()
for item in items:
cache[(item.ID, None)] = item
results += items
@@ -161,9 +172,9 @@ def getItems(lookfor, eager=None):
def getAlphaClone(lookfor, eager=None):
if isinstance(lookfor, int):
if eager is None:
item = gamedata_session.query(AlphaClone).get(lookfor)
item = get_gamedata_session().query(AlphaClone).get(lookfor)
else:
item = gamedata_session.query(AlphaClone).options(*processEager(eager)).filter(AlphaClone.ID == lookfor).first()
item = get_gamedata_session().query(AlphaClone).options(*processEager(eager)).filter(AlphaClone.ID == lookfor).first()
else:
raise TypeError("Need integer as argument")
return item
@@ -171,7 +182,7 @@ def getAlphaClone(lookfor, eager=None):
def getAlphaCloneList(eager=None):
eager = processEager(eager)
clones = gamedata_session.query(AlphaClone).options(*eager).all()
clones = get_gamedata_session().query(AlphaClone).options(*eager).all()
return clones
@@ -182,20 +193,21 @@ groupNameMap = {}
def getGroup(lookfor, eager=None):
if isinstance(lookfor, int):
if eager is None:
group = gamedata_session.query(Group).get(lookfor)
group = get_gamedata_session().query(Group).get(lookfor)
else:
group = gamedata_session.query(Group).options(*processEager(eager)).filter(Group.ID == lookfor).first()
group = get_gamedata_session().query(Group).options(*processEager(eager)).filter(Group.ID == lookfor).first()
elif isinstance(lookfor, str):
if lookfor in groupNameMap:
id = groupNameMap[lookfor]
if eager is None:
group = gamedata_session.query(Group).get(id)
group = get_gamedata_session().query(Group).get(id)
else:
group = gamedata_session.query(Group).options(*processEager(eager)).filter(Group.ID == id).first()
group = get_gamedata_session().query(Group).options(*processEager(eager)).filter(Group.ID == id).first()
else:
# Group names are unique, so we can use first() instead of one()
group = gamedata_session.query(Group).options(*processEager(eager)).filter(Group.name == lookfor).first()
groupNameMap[lookfor] = group.ID
group = get_gamedata_session().query(Group).options(*processEager(eager)).filter(Group.name == lookfor).first()
if group is not None:
groupNameMap[lookfor] = group.ID
else:
raise TypeError("Need integer or string as argument")
return group
@@ -208,23 +220,24 @@ categoryNameMap = {}
def getCategory(lookfor, eager=None):
if isinstance(lookfor, int):
if eager is None:
category = gamedata_session.query(Category).get(lookfor)
category = get_gamedata_session().query(Category).get(lookfor)
else:
category = gamedata_session.query(Category).options(*processEager(eager)).filter(
category = get_gamedata_session().query(Category).options(*processEager(eager)).filter(
Category.ID == lookfor).first()
elif isinstance(lookfor, str):
if lookfor in categoryNameMap:
id = categoryNameMap[lookfor]
if eager is None:
category = gamedata_session.query(Category).get(id)
category = get_gamedata_session().query(Category).get(id)
else:
category = gamedata_session.query(Category).options(*processEager(eager)).filter(
category = get_gamedata_session().query(Category).options(*processEager(eager)).filter(
Category.ID == id).first()
else:
# Category names are unique, so we can use first() instead of one()
category = gamedata_session.query(Category).options(*processEager(eager)).filter(
category = get_gamedata_session().query(Category).options(*processEager(eager)).filter(
Category.name == lookfor).first()
categoryNameMap[lookfor] = category.ID
if category is not None:
categoryNameMap[lookfor] = category.ID
else:
raise TypeError("Need integer or string as argument")
return category
@@ -237,39 +250,40 @@ metaGroupNameMap = {}
def getMetaGroup(lookfor, eager=None):
if isinstance(lookfor, int):
if eager is None:
metaGroup = gamedata_session.query(MetaGroup).get(lookfor)
metaGroup = get_gamedata_session().query(MetaGroup).get(lookfor)
else:
metaGroup = gamedata_session.query(MetaGroup).options(*processEager(eager)).filter(
metaGroup = get_gamedata_session().query(MetaGroup).options(*processEager(eager)).filter(
MetaGroup.ID == lookfor).first()
elif isinstance(lookfor, str):
if lookfor in metaGroupNameMap:
id = metaGroupNameMap[lookfor]
if eager is None:
metaGroup = gamedata_session.query(MetaGroup).get(id)
metaGroup = get_gamedata_session().query(MetaGroup).get(id)
else:
metaGroup = gamedata_session.query(MetaGroup).options(*processEager(eager)).filter(
metaGroup = get_gamedata_session().query(MetaGroup).options(*processEager(eager)).filter(
MetaGroup.ID == id).first()
else:
# MetaGroup names are unique, so we can use first() instead of one()
metaGroup = gamedata_session.query(MetaGroup).options(*processEager(eager)).filter(
MetaGroup.name == lookfor).first()
metaGroupNameMap[lookfor] = metaGroup.ID
metaGroup = get_gamedata_session().query(MetaGroup).options(*processEager(eager)).filter(
MetaGroup.metaGroupName == lookfor).first()
if metaGroup is not None:
metaGroupNameMap[lookfor] = metaGroup.ID
else:
raise TypeError("Need integer or string as argument")
return metaGroup
def getMetaGroups():
return gamedata_session.query(MetaGroup).all()
return get_gamedata_session().query(MetaGroup).all()
@cachedQuery(1, "lookfor")
def getMarketGroup(lookfor, eager=None):
if isinstance(lookfor, int):
if eager is None:
marketGroup = gamedata_session.query(MarketGroup).get(lookfor)
marketGroup = get_gamedata_session().query(MarketGroup).get(lookfor)
else:
marketGroup = gamedata_session.query(MarketGroup).options(*processEager(eager)).filter(
marketGroup = get_gamedata_session().query(MarketGroup).options(*processEager(eager)).filter(
MarketGroup.ID == lookfor).first()
else:
raise TypeError("Need integer as argument")
@@ -281,7 +295,7 @@ def getMarketTreeNodeIds(rootNodeIds):
addedIds = set(rootNodeIds)
while addedIds:
allIds.update(addedIds)
addedIds = {mg.ID for mg in gamedata_session.query(MarketGroup).filter(MarketGroup.parentGroupID.in_(addedIds))}
addedIds = {mg.ID for mg in get_gamedata_session().query(MarketGroup).filter(MarketGroup.parentGroupID.in_(addedIds))}
return allIds
@@ -295,7 +309,7 @@ def getItemsByCategory(filter, where=None, eager=None):
raise TypeError("Need integer or string as argument")
filter = processWhere(filter, where)
return gamedata_session.query(Item).options(*processEager(eager)).join(Item.group, Group.category).filter(
return get_gamedata_session().query(Item).options(*processEager(eager)).join(Item.group, Group.category).filter(
filter).all()
@@ -310,9 +324,9 @@ def searchItems(nameLike, where=None, join=None, eager=None):
if not hasattr(join, "__iter__"):
join = (join,)
items = gamedata_session.query(Item).options(*processEager(eager)).join(*join)
items = get_gamedata_session().query(Item).options(*processEager(eager)).join(*join)
for token in nameLike.split(' '):
token_safe = "%{0}%".format(sqlizeString(token))
token_safe = "%{0}%".format(sqlizeNormalString(token))
if where is not None:
items = items.filter(and_(Item.name.like(token_safe, escape="\\"), where))
else:
@@ -321,14 +335,35 @@ def searchItems(nameLike, where=None, join=None, eager=None):
return items
@cachedQuery(3, "tokens", "where", "join")
def searchItemsRegex(tokens, where=None, join=None, eager=None):
if not isinstance(tokens, (tuple, list)) or not all(isinstance(t, str) for t in tokens):
raise TypeError("Need tuple or list of strings as argument")
if join is None:
join = tuple()
if not hasattr(join, "__iter__"):
join = (join,)
items = get_gamedata_session().query(Item).options(*processEager(eager)).join(*join)
for token in tokens:
if where is not None:
items = items.filter(and_(Item.name.op('regexp')(token), where))
else:
items = items.filter(Item.name.op('regexp')(token))
items = items.limit(100).all()
return items
@cachedQuery(3, "where", "nameLike", "join")
def searchSkills(nameLike, where=None, eager=None):
if not isinstance(nameLike, str):
raise TypeError("Need string as argument")
items = gamedata_session.query(Item).options(*processEager(eager)).join(Item.group, Group.category)
items = get_gamedata_session().query(Item).options(*processEager(eager)).join(Item.group, Group.category)
for token in nameLike.split(' '):
token_safe = "%{0}%".format(sqlizeString(token))
token_safe = "%{0}%".format(sqlizeNormalString(token))
if where is not None:
items = items.filter(and_(Item.name.like(token_safe, escape="\\"), Category.ID == 16, where))
else:
@@ -348,7 +383,7 @@ def getVariations(itemids, groupIDs=None, where=None, eager=None):
itemfilter = or_(*(items_table.c.variationParentTypeID == itemid for itemid in itemids))
filter = processWhere(itemfilter, where)
vars = gamedata_session.query(Item).options(*processEager(eager)).filter(filter).all()
vars = get_gamedata_session().query(Item).options(*processEager(eager)).filter(filter).all()
if vars:
return vars
@@ -356,7 +391,7 @@ def getVariations(itemids, groupIDs=None, where=None, eager=None):
itemfilter = or_(*(groups_table.c.groupID == groupID for groupID in groupIDs))
filter = processWhere(itemfilter, where)
joinon = items_table.c.groupID == groups_table.c.groupID
vars = gamedata_session.query(Item).options(*processEager(eager)).join((groups_table, joinon)).filter(
vars = get_gamedata_session().query(Item).options(*processEager(eager)).join((groups_table, joinon)).filter(
filter).all()
return vars
@@ -371,7 +406,7 @@ def getAttributeInfo(attr, eager=None):
else:
raise TypeError("Need integer or string as argument")
try:
result = gamedata_session.query(AttributeInfo).options(*processEager(eager)).filter(filter).one()
result = get_gamedata_session().query(AttributeInfo).options(*processEager(eager)).filter(filter).one()
except exc.NoResultFound:
result = None
return result
@@ -380,7 +415,7 @@ def getAttributeInfo(attr, eager=None):
@cachedQuery(1, "field")
def getMetaData(field):
if isinstance(field, str):
data = gamedata_session.query(MetaData).get(field)
data = get_gamedata_session().query(MetaData).get(field)
else:
raise TypeError("Need string as argument")
return data
@@ -399,12 +434,12 @@ def directAttributeRequest(itemIDs, attrIDs):
and_(Attribute.attributeID.in_(attrIDs), Item.typeID.in_(itemIDs)),
from_obj=[join(Attribute, Item)])
result = gamedata_session.execute(q).fetchall()
result = get_gamedata_session().execute(q).fetchall()
return result
def getAbyssalTypes():
return set([r.resultingTypeID for r in gamedata_session.query(DynamicItem.resultingTypeID).distinct()])
return set([r.resultingTypeID for r in get_gamedata_session().query(DynamicItem.resultingTypeID).distinct()])
@cachedQuery(1, "itemID")
@@ -412,9 +447,9 @@ def getDynamicItem(itemID, eager=None):
try:
if isinstance(itemID, int):
if eager is None:
result = gamedata_session.query(DynamicItem).filter(DynamicItem.ID == itemID).one()
result = get_gamedata_session().query(DynamicItem).filter(DynamicItem.ID == itemID).one()
else:
result = gamedata_session.query(DynamicItem).options(*processEager(eager)).filter(DynamicItem.ID == itemID).one()
result = get_gamedata_session().query(DynamicItem).options(*processEager(eager)).filter(DynamicItem.ID == itemID).one()
else:
raise TypeError("Need integer as argument")
except exc.NoResultFound:
@@ -422,23 +457,7 @@ def getDynamicItem(itemID, eager=None):
return result
def getRequiredFor(itemID, attrMapping):
Attribute1 = aliased(Attribute)
Attribute2 = aliased(Attribute)
skillToLevelClauses = []
for attrSkill, attrLevel in attrMapping.items():
skillToLevelClauses.append(and_(Attribute1.attributeID == attrSkill, Attribute2.attributeID == attrLevel))
queryOr = or_(*skillToLevelClauses)
q = select((Attribute2.typeID, Attribute2.value),
and_(Attribute1.value == itemID, queryOr),
from_obj=[
join(Attribute1, Attribute2, Attribute1.typeID == Attribute2.typeID)
])
result = gamedata_session.execute(q).fetchall()
return result
@cachedQuery(1, "lookfor")
def getAllImplantSets():
implantSets = get_gamedata_session().query(ImplantSet).all()
return implantSets

View File

@@ -1,11 +1,21 @@
from sqlalchemy import Column, Table, Integer, String, ForeignKey
from sqlalchemy.orm import mapper
from sqlalchemy.orm import mapper, synonym
from eos.db import gamedata_meta
from eos.gamedata import Traits
import eos.config
traits_table = Table("invtraits", gamedata_meta,
Column("typeID", Integer, ForeignKey("invtypes.typeID"), primary_key=True),
Column("traitText", String))
traits_table = Table(
"invtraits",
gamedata_meta,
Column("typeID", Integer, ForeignKey("invtypes.typeID"), primary_key=True),
*[Column("traitText{}".format(lang), String) for lang in eos.config.translation_mapping.values()],
)
mapper(Traits, traits_table)
mapper(
Traits,
traits_table,
properties={
"display": synonym("traitText{}".format(eos.config.lang)),
}
)

View File

@@ -22,11 +22,13 @@ from sqlalchemy.orm import mapper, synonym
from eos.db import gamedata_meta
from eos.gamedata import Unit
import eos.config
groups_table = Table("dgmunits", gamedata_meta,
Column("unitID", Integer, primary_key=True),
Column("unitName", String),
Column("displayName", String))
*[Column("displayName{}".format(lang), String) for lang in eos.config.translation_mapping.values()],
)
mapper(Unit, groups_table,
properties={

View File

@@ -8,43 +8,25 @@ many upgrade files as there are database versions (version 5 would include
upgrade files 1-5)
"""
import pkgutil
import re
from eos.utils.pyinst_support import iterNamespace
updates = {}
appVersion = 0
prefix = __name__ + "."
# load modules to work based with and without pyinstaller
# from: https://github.com/webcomics/dosage/blob/master/dosagelib/loader.py
# see: https://github.com/pyinstaller/pyinstaller/issues/1905
# load modules using iter_modules()
# (should find all filters in normal build, but not pyinstaller)
module_names = [m[1] for m in pkgutil.iter_modules(__path__, prefix)]
# special handling for PyInstaller
importers = map(pkgutil.get_importer, __path__)
toc = set()
for i in importers:
if hasattr(i, 'toc'):
toc |= i.toc
for elm in toc:
if elm.startswith(prefix):
module_names.append(elm)
for modname in module_names:
for modName in iterNamespace(__name__, __path__):
# loop through python files, extracting update number and function, and
# adding it to a list
modname_tail = modname.rsplit('.', 1)[-1]
module = __import__(modname, fromlist=True)
modname_tail = modName.rsplit('.', 1)[-1]
m = re.match("^upgrade(?P<index>\d+)$", modname_tail)
if not m:
continue
index = int(m.group("index"))
appVersion = max(appVersion, index)
module = __import__(modName, fromlist=True)
upgrade = getattr(module, "upgrade", False)
if upgrade:
updates[index] = upgrade

View File

@@ -33,9 +33,13 @@ def upgrade(saveddata_engine):
try:
saveddata_session.execute(commandFits_table.insert(),
{"boosterID": value, "boostedID": boosted, "active": 1})
except (KeyboardInterrupt, SystemExit):
raise
except Exception:
pass
saveddata_session.commit()
except (KeyboardInterrupt, SystemExit):
raise
except:
# Shouldn't fail unless you have updated database without the old fleet schema and manually modify the database version
# If it does, simply fail. Fleet data migration isn't critically important here

View File

@@ -4235,6 +4235,8 @@ def upgrade(saveddata_engine):
# And last but not least, delete the last subsystem
saveddata_engine.execute("DELETE FROM modules WHERE ID = ?", (oldModules[4][0],))
except (KeyboardInterrupt, SystemExit):
raise
except:
# if something fails, fuck it, we tried. It'll default to the generic conversion below
continue

View File

@@ -6,16 +6,16 @@ Allow use of floats in damage pattern values
tmpTable = """
CREATE TABLE "damagePatternsTemp" (
"ID" INTEGER NOT NULL,
"name" VARCHAR,
"emAmount" FLOAT,
"thermalAmount" FLOAT,
"kineticAmount" FLOAT,
"explosiveAmount" FLOAT,
"ownerID" INTEGER,
"created" DATETIME,
"modified" DATETIME,
PRIMARY KEY ("ID"),
"ID" INTEGER NOT NULL,
"name" VARCHAR,
"emAmount" FLOAT,
"thermalAmount" FLOAT,
"kineticAmount" FLOAT,
"explosiveAmount" FLOAT,
"ownerID" INTEGER,
"created" DATETIME,
"modified" DATETIME,
PRIMARY KEY ("ID"),
FOREIGN KEY("ownerID") REFERENCES users ("ID")
)
"""

View 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;")

View 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))

View 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))

View File

@@ -0,0 +1,42 @@
"""
Migration 38
- Armor hardener tiericide
"""
CONVERSIONS = {
16357: ( # Experimental Enduring EM Armor Hardener I
16353, # Upgraded Armor EM Hardener I
),
16365: ( # Experimental Enduring Explosive Armor Hardener I
16361, # Upgraded Armor Explosive Hardener I
),
16373: ( # Experimental Enduring Kinetic Armor Hardener I
16369, # Upgraded Armor Kinetic Hardener I
),
16381: ( # Experimental Enduring Thermal Armor Hardener I
16377, # Upgraded Armor Thermal Hardener I
),
16359: ( # Prototype Compact EM Armor Hardener I
16355, # Limited Armor EM Hardener I
),
16367: ( # Prototype Compact Explosive Armor Hardener I
16363, # Limited Armor Explosive Hardener I
),
16375: ( # Prototype Compact Kinetic Armor Hardener I
16371, # Limited Armor Kinetic Hardener I
),
16383: ( # Prototype Compact Thermal Armor Hardener I
16379, # Limited Armor Thermal Hardener I
)
}
def upgrade(saveddata_engine):
# Convert modules
for replacement_item, list in CONVERSIONS.items():
for retired_item in list:
saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?',
(replacement_item, retired_item))
saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?',
(replacement_item, retired_item))

View File

@@ -0,0 +1,34 @@
"""
Migration 39
- Shield amplifier tiericide
- CCP getting rid of DB TDs due to exploits
"""
CONVERSIONS = {
1798: ( # 'Basic' EM Shield Amplifier
9562, # Supplemental EM Ward Amplifier
),
1804: ( # 'Basic' Explosive Shield Amplifier
9574, # Supplemental Explosive Deflection Amplifier
),
1802: ( # 'Basic' Kinetic Shield Amplifier
9570, # Supplemental Kinetic Deflection Amplifier
),
1800: ( # 'Basic' Thermal Shield Amplifier
9566, # Supplemental Thermal Dissipation Amplifier
),
22933: ( # 'Investor' Tracking Disruptor I
32416, # Dark Blood Tracking Disruptor
)
}
def upgrade(saveddata_engine):
# Convert modules
for replacement_item, list in CONVERSIONS.items():
for retired_item in list:
saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?',
(replacement_item, retired_item))
saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?',
(replacement_item, retired_item))

View File

@@ -0,0 +1,18 @@
"""
Migration 40
Imports all item conversions since Migration 28 and runs them against module.baseItemID. This column seems to have been
forgotten about since it's been added.
"""
from .upgrade36 import CONVERSIONS as u36
from .upgrade37 import CONVERSIONS as u37
from .upgrade38 import CONVERSIONS as u38
from .upgrade39 import CONVERSIONS as u39
def upgrade(saveddata_engine):
for conversions in [u36, u37, u38, u39]:
for replacement_item, list in conversions.items():
for retired_item in list:
saveddata_engine.execute('UPDATE "modules" SET "baseItemID" = ? WHERE "baseItemID" = ?',
(replacement_item, retired_item))

View File

@@ -0,0 +1,50 @@
"""
Migration 41
- Resistance plating tiericide
"""
CONVERSIONS = {
16345: ( # Upgraded Layered Coating I
16347, # Limited Layered Plating I
16349, # 'Scarab' Layered Plating I
16351, # 'Grail' Layered Plating I
),
16305: ( # Upgraded Multispectrum Coating I
16307, # Limited Adaptive Nano Plating I
16309, # 'Collateral' Adaptive Nano Plating I
16311, # 'Refuge' Adaptive Nano Plating I
),
16329: ( # Upgraded EM Coating I
16331, # Limited EM Plating I
16333, # 'Contour' EM Plating I
16335, # 'Spiegel' EM Plating I
),
16321: ( # Upgraded Explosive Coating I
16323, # Limited Explosive Plating I
16325, # Experimental Explosive Plating I
16319, # 'Aegis' Explosive Plating I
),
16313: ( # Upgraded Kinetic Coating I
16315, # Limited Kinetic Plating I
16317, # Experimental Kinetic Plating I
16327, # 'Element' Kinetic Plating I
),
16337: ( # Upgraded Thermal Coating I
16339, # Limited Thermal Plating I
16341, # Experimental Thermal Plating I
16343, # Prototype Thermal Plating I
)
}
def upgrade(saveddata_engine):
# Convert modules
for replacement_item, list in CONVERSIONS.items():
for retired_item in list:
saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?',
(replacement_item, retired_item))
saveddata_engine.execute('UPDATE "modules" SET "baseItemID" = ? WHERE "baseItemID" = ?',
(replacement_item, retired_item))
saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?',
(replacement_item, retired_item))

View File

@@ -0,0 +1,50 @@
"""
Migration 42
- Resistance membrane tiericide
"""
CONVERSIONS = {
16391: ( # Compact Multispectrum Energized Membrane
16389, # Experimental Energized Adaptive Nano Membrane I
16387, # Limited Energized Adaptive Nano Membrane I
16385, # Upgraded Energized Adaptive Nano Membrane I
),
16423: ( # Compact Layered Energized Membrane
16421, # Experimental Energized Armor Layering Membrane I
16419, # Limited Energized Armor Layering Membrane I
16417, # Upgraded Energized Armor Layering Membrane I
),
16415: ( # Compact EM Energized Membrane
16413, # Experimental Energized EM Membrane I
16411, # Limited Energized EM Membrane I
16409, # Upgraded Energized EM Membrane I
),
16407: ( # Compact Explosive Energized Membrane
16405, # Experimental Energized Explosive Membrane I
16403, # Limited Energized Explosive Membrane I
16401, # Upgraded Energized Explosive Membrane I
),
16399: ( # Compact Kinetic Energized Membrane
16397, # Experimental Energized Kinetic Membrane I
16395, # Limited Energized Kinetic Membrane I
16393, # Upgraded Energized Kinetic Membrane I
),
16431: ( # Compact Thermal Energized Membrane
16429, # Experimental Energized Thermal Membrane I
16427, # Limited Energized Thermal Membrane I
16425, # Upgraded Energized Thermal Membrane I
)
}
def upgrade(saveddata_engine):
# Convert modules
for replacement_item, list in CONVERSIONS.items():
for retired_item in list:
saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?',
(replacement_item, retired_item))
saveddata_engine.execute('UPDATE "modules" SET "baseItemID" = ? WHERE "baseItemID" = ?',
(replacement_item, retired_item))
saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?',
(replacement_item, retired_item))

View File

@@ -0,0 +1,26 @@
"""
Migration 43
- Shield booster amplifier tiericide
"""
CONVERSIONS = {
16533: ( # Stalwart Restrained Shield Boost Amplifier
16531, # 5a Prototype Shield Support I
),
16535: ( # Copasetic Compact Shield Boost Amplifier
16529, # Ionic Field Accelerator I
),
}
def upgrade(saveddata_engine):
# Convert modules
for replacement_item, list in CONVERSIONS.items():
for retired_item in list:
saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?',
(replacement_item, retired_item))
saveddata_engine.execute('UPDATE "modules" SET "baseItemID" = ? WHERE "baseItemID" = ?',
(replacement_item, retired_item))
saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?',
(replacement_item, retired_item))

View File

@@ -0,0 +1,25 @@
"""
Migration 44
- Signal distortion amplifier tiericide
"""
CONVERSIONS = {
25565: ( # Hypnos Compact Signal Distortion Amplifier I
25571, # Initiated Signal Distortion Amplifier I
25569, # Induced Signal Distortion Amplifier I
25567, # Compulsive Signal Distortion Amplifier I
),
}
def upgrade(saveddata_engine):
# Convert modules
for replacement_item, list in CONVERSIONS.items():
for retired_item in list:
saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?',
(replacement_item, retired_item))
saveddata_engine.execute('UPDATE "modules" SET "baseItemID" = ? WHERE "baseItemID" = ?',
(replacement_item, retired_item))
saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?',
(replacement_item, retired_item))

View File

@@ -0,0 +1,18 @@
"""
Migration 45
- Drone mutaplasmid support
"""
import sqlalchemy
def upgrade(saveddata_engine):
try:
saveddata_engine.execute("SELECT baseItemID FROM drones LIMIT 1")
except sqlalchemy.exc.DatabaseError:
saveddata_engine.execute("ALTER TABLE drones ADD COLUMN baseItemID INTEGER;")
try:
saveddata_engine.execute("SELECT mutaplasmidID FROM drones LIMIT 1")
except sqlalchemy.exc.DatabaseError:
saveddata_engine.execute("ALTER TABLE drones ADD COLUMN mutaplasmidID INTEGER;")

View File

@@ -0,0 +1,68 @@
"""
Migration 46
- Mining crystal changes
"""
CONVERSIONS = {
60276: ( # Simple Asteroid Mining Crystal Type A I
18066, # Veldspar Mining Crystal I
18062, # Scordite Mining Crystal I
18060, # Pyroxeres Mining Crystal I
18058, # Plagioclase Mining Crystal I
),
60281: ( # Simple Asteroid Mining Crystal Type A II
18618, # Veldspar Mining Crystal II
18616, # Scordite Mining Crystal II
18614, # Pyroxeres Mining Crystal II
18612, # Plagioclase Mining Crystal II
),
60285: ( # Coherent Asteroid Mining Crystal Type A I
18056, # Omber Mining Crystal I
18052, # Kernite Mining Crystal I
18050, # Jaspet Mining Crystal I
18048, # Hemorphite Mining Crystal I
18046, # Hedbergite Mining Crystal I
),
60288: ( # Coherent Asteroid Mining Crystal Type A II
18610, # Omber Mining Crystal II
18604, # Jaspet Mining Crystal II
18606, # Kernite Mining Crystal II
18600, # Hedbergite Mining Crystal II
18602, # Hemorphite Mining Crystal II
),
60291: ( # Variegated Asteroid Mining Crystal Type A I
18044, # Gneiss Mining Crystal I
18042, # Dark Ochre Mining Crystal I
18040, # Crokite Mining Crystal I
),
60294: ( # Variegated Asteroid Mining Crystal Type A II
18598, # Gneiss Mining Crystal II
18596, # Dark Ochre Mining Crystal II
18594, # Crokite Mining Crystal II
),
60297: ( # Complex Asteroid Mining Crystal Type A I
18038, # Bistot Mining Crystal I
18036, # Arkonor Mining Crystal I
18064, # Spodumain Mining Crystal I
),
60300: ( # Complex Asteroid Mining Crystal Type A II
18592, # Bistot Mining Crystal II
18590, # Arkonor Mining Crystal II
18624, # Spodumain Mining Crystal II
),
}
def upgrade(saveddata_engine):
# Convert modules
for replacement_item, list in CONVERSIONS.items():
for retired_item in list:
saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?',
(replacement_item, retired_item))
saveddata_engine.execute('UPDATE "modules" SET "baseItemID" = ? WHERE "baseItemID" = ?',
(replacement_item, retired_item))
saveddata_engine.execute('UPDATE "modules" SET "chargeID" = ? WHERE "chargeID" = ?',
(replacement_item, retired_item))
saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?',
(replacement_item, retired_item))

View File

@@ -1,7 +1,8 @@
__all__ = [
"character",
"fit",
"mutator",
"mutatorMod",
"mutatorDrone",
"module",
"user",
"skill",
@@ -13,6 +14,5 @@ __all__ = [
"miscData",
"targetProfile",
"override",
"implantSet",
"loadDefaultDatabaseValues"
"implantSet"
]

View File

@@ -24,11 +24,12 @@ import datetime
from eos.db import saveddata_meta
from eos.saveddata.booster import Booster
from eos.saveddata.boosterSideEffect import BoosterSideEffect
from eos.saveddata.fit import Fit
boosters_table = Table("boosters", saveddata_meta,
Column("ID", Integer, primary_key=True),
Column("itemID", Integer),
Column("fitID", Integer, ForeignKey("fits.ID"), nullable=False),
Column("fitID", Integer, ForeignKey("fits.ID"), nullable=False, index=True),
Column("active", Boolean),
Column("created", DateTime, nullable=True, default=datetime.datetime.now),
Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now),
@@ -44,6 +45,7 @@ booster_side_effect_table = Table("boosterSideEffects", saveddata_meta,
mapper(Booster, boosters_table,
properties={
"owner": relation(Fit),
"_Booster__sideEffects": relation(
BoosterSideEffect,
backref="booster",

View File

@@ -24,16 +24,20 @@ import datetime
from eos.db import saveddata_meta
from eos.saveddata.damagePattern import DamagePattern
damagePatterns_table = Table("damagePatterns", saveddata_meta,
Column("ID", Integer, primary_key=True),
Column("name", String),
Column("emAmount", Float),
Column("thermalAmount", Float),
Column("kineticAmount", Float),
Column("explosiveAmount", Float),
Column("ownerID", ForeignKey("users.ID"), nullable=True),
Column("created", DateTime, nullable=True, default=datetime.datetime.now),
Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now)
)
damagePatterns_table = Table(
'damagePatterns',
saveddata_meta,
Column('ID', Integer, primary_key=True),
Column('name', String),
Column('emAmount', Float),
Column('thermalAmount', Float),
Column('kineticAmount', Float),
Column('explosiveAmount', Float),
Column('ownerID', ForeignKey('users.ID'), nullable=True),
Column('created', DateTime, nullable=True, default=datetime.datetime.now),
Column('modified', DateTime, nullable=True, onupdate=datetime.datetime.now))
mapper(DamagePattern, damagePatterns_table)
mapper(
DamagePattern,
damagePatterns_table,
properties={'rawName': damagePatterns_table.c.name})

View File

@@ -18,27 +18,35 @@
# ===============================================================================
from sqlalchemy import Table, Column, Integer, Float, ForeignKey, Boolean, DateTime
from sqlalchemy.orm import mapper, relation
from sqlalchemy.orm import mapper, relation, synonym
from sqlalchemy.orm.collections import attribute_mapped_collection
import datetime
from eos.db import saveddata_meta
from eos.saveddata.drone import Drone
from eos.saveddata.fit import Fit
from eos.saveddata.mutator import MutatorDrone
drones_table = Table("drones", saveddata_meta,
Column("groupID", Integer, primary_key=True),
Column("fitID", Integer, ForeignKey("fits.ID"), nullable=False, index=True),
Column("itemID", Integer, nullable=False),
Column("baseItemID", Integer, nullable=True),
Column("mutaplasmidID", Integer, nullable=True),
Column("amount", Integer, nullable=False),
Column("amountActive", Integer, nullable=False),
Column("projected", Boolean, default=False),
Column("created", DateTime, nullable=True, default=datetime.datetime.now),
Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now),
Column("projectionRange", Float, nullable=True)
)
Column("projectionRange", Float, nullable=True))
mapper(Drone, drones_table,
properties={
"owner": relation(Fit)
}
)
"ID": synonym("groupID"),
"owner": relation(Fit),
"mutators": relation(
MutatorDrone,
backref="item",
cascade="all,delete-orphan",
collection_class=attribute_mapped_collection('attrID'))})

View File

@@ -53,8 +53,10 @@ fits_table = Table("fits", saveddata_meta,
Column("timestamp", Integer, nullable=False),
Column("characterID", ForeignKey("characters.ID"), nullable=True),
Column("damagePatternID", ForeignKey("damagePatterns.ID"), nullable=True),
Column("builtinDamagePatternID", Integer, nullable=True),
Column("booster", Boolean, nullable=False, index=True, default=0),
Column("targetResistsID", ForeignKey("targetResists.ID"), nullable=True),
Column("builtinTargetResistsID", Integer, nullable=True),
Column("modeID", Integer, nullable=True),
Column("implantLocation", Integer, nullable=False),
Column("notes", String, nullable=True),
@@ -189,7 +191,6 @@ mapper(es_Fit, fits_table,
Booster,
collection_class=HandledBoosterList,
cascade='all, delete, delete-orphan',
backref='owner',
single_parent=True),
"_Fit__drones": relation(
Drone,
@@ -233,8 +234,10 @@ mapper(es_Fit, fits_table,
"_Fit__character": relation(
Character,
backref="fits"),
"_Fit__damagePattern": relation(DamagePattern),
"_Fit__targetProfile": relation(TargetProfile),
"_Fit__userDamagePattern": relation(DamagePattern),
"_Fit__builtinDamagePatternID": fits_table.c.builtinDamagePatternID,
"_Fit__userTargetProfile": relation(TargetProfile),
"_Fit__builtinTargetProfileID": fits_table.c.builtinTargetResistsID,
"projectedOnto": projectedFitSourceRel,
"victimOf": relationship(
ProjectedFit,

View File

@@ -1,248 +0,0 @@
# ===============================================================================
# Copyright (C) 2010 Diego Duclos
#
# This file is part of pyfa.
#
# pyfa is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# pyfa is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with pyfa. If not, see <http://www.gnu.org/licenses/>.
# ===============================================================================
import eos.db
from eos.saveddata.damagePattern import DamagePattern as es_DamagePattern
from eos.saveddata.targetProfile import TargetProfile as es_TargetProfile
class ImportError(Exception):
pass
class DefaultDatabaseValues:
def __init__(self):
pass
instance = None
@classmethod
def importDamageProfileDefaults(cls):
damageProfileList = [["Uniform", 25, 25, 25, 25],
["[Generic]EM", 1, 0, 0, 0],
["[Generic]Thermal", 0, 1, 0, 0],
["[Generic]Kinetic", 0, 0, 1, 0],
["[Generic]Explosive", 0, 0, 0, 1],
["[NPC][Asteroid] Blood Raiders", 5067, 4214, 0, 0],
["[Bombs]Electron Bomb", 6400, 0, 0, 0],
["[Bombs]Scorch Bomb", 0, 6400, 0, 0],
["[Bombs]Concussion Bomb", 0, 0, 6400, 0],
["[Bombs]Shrapnel Bomb", 0, 0, 0, 6400],
["[Frequency Crystals][T2] Conflagration", 7.7, 7.7, 0, 0],
["[Frequency Crystals][T2] Scorch", 9, 2, 0, 0],
["[Frequency Crystals][T2] Gleam", 7, 7, 0, 0],
["[Frequency Crystals][T2] Aurora", 5, 3, 0, 0],
["[Frequency Crystals]Multifrequency", 7, 5, 0, 0],
["[Frequency Crystals]Gamma", 7, 4, 0, 0],
["[Frequency Crystals]Xray", 6, 4, 0, 0],
["[Frequency Crystals]Ultraviolet", 6, 3, 0, 0],
["[Frequency Crystals]Standard", 5, 3, 0, 0],
["[Frequency Crystals]Infrared", 5, 2, 0, 0],
["[Frequency Crystals]Microwave", 4, 2, 0, 0],
["[Frequency Crystals]Radio", 5, 0, 0, 0],
["[Hybrid Charges][T2] Void", 0, 7.7, 7.7, 0],
["[Hybrid Charges][T2] Null", 0, 6, 5, 0],
["[Hybrid Charges][T2] Javelin", 0, 8, 6, 0],
["[Hybrid Charges][T2] Spike", 0, 4, 4, 0],
["[Hybrid Charges]Antimatter", 0, 5, 7, 0],
["[Hybrid Charges]Plutonium", 0, 5, 6, 0],
["[Hybrid Charges]Uranium", 0, 4, 6, 0],
["[Hybrid Charges]Thorium", 0, 4, 5, 0],
["[Hybrid Charges]Lead", 0, 3, 5, 0],
["[Hybrid Charges]Iridium", 0, 3, 4, 0],
["[Hybrid Charges]Tungsten", 0, 2, 4, 0],
["[Hybrid Charges]Iron", 0, 2, 3, 0],
["[Missiles]Mjolnir", 1, 0, 0, 0],
["[Missiles]Inferno", 0, 1, 0, 0],
["[Missiles]Scourge", 0, 0, 1, 0],
["[Missiles]Nova", 0, 0, 0, 1],
["[Missiles][Structure] Standup Missile", 1, 1, 1, 1],
["[Projectile Ammo][T2] Hail", 0, 0, 3.3, 12.1],
["[Projectile Ammo][T2] Barrage", 0, 0, 5, 6],
["[Projectile Ammo][T2] Quake", 0, 0, 5, 9],
["[Projectile Ammo][T2] Tremor", 0, 0, 3, 5],
["[Projectile Ammo]EMP", 9, 0, 1, 2],
["[Projectile Ammo]Phased Plasma", 0, 10, 2, 0],
["[Projectile Ammo]Fusion", 0, 0, 2, 10],
["[Projectile Ammo]Depleted Uranium", 0, 3, 2, 3],
["[Projectile Ammo]Titanium Sabot", 0, 0, 6, 2],
["[Projectile Ammo]Proton", 3, 0, 2, 0],
["[Projectile Ammo]Carbonized Lead", 0, 0, 4, 1],
["[Projectile Ammo]Nuclear", 0, 0, 1, 4],
# Different sizes of plasma do different damage, the values here are
# average of proportions across sizes
["[Exotic Plasma][T2] Occult", 0, 55863, 0, 44137],
["[Exotic Plasma][T2] Mystic", 0, 66319, 0, 33681],
["[Exotic Plasma]Tetryon", 0, 69208, 0, 30792],
["[Exotic Plasma]Baryon", 0, 59737, 0, 40263],
["[Exotic Plasma]Meson", 0, 60519, 0, 39481],
["[NPC][Burner] Cruor (Blood Raiders)", 90, 90, 0, 0],
["[NPC][Burner] Dramiel (Angel)", 55, 0, 20, 96],
["[NPC][Burner] Daredevil (Serpentis)", 0, 110, 154, 0],
["[NPC][Burner] Succubus (Sanshas Nation)", 135, 30, 0, 0],
["[NPC][Burner] Worm (Guristas)", 0, 0, 228, 0],
["[NPC][Burner] Enyo", 0, 147, 147, 0],
["[NPC][Burner] Hawk", 0, 0, 247, 0],
["[NPC][Burner] Jaguar", 36, 0, 50, 182],
["[NPC][Burner] Vengeance", 232, 0, 0, 0],
["[NPC][Burner] Ashimmu (Blood Raiders)", 260, 100, 0, 0],
["[NPC][Burner] Talos", 0, 413, 413, 0],
["[NPC][Burner] Sentinel", 0, 75, 0, 90],
["[NPC][Asteroid] Angel Cartel", 1838, 562, 2215, 3838],
["[NPC][Deadspace] Angel Cartel", 369, 533, 1395, 3302],
["[NPC][Deadspace] Blood Raiders", 6040, 5052, 10, 15],
["[NPC][Asteroid] Guristas", 0, 1828, 7413, 0],
["[NPC][Deadspace] Guristas", 0, 1531, 9680, 0],
["[NPC][Asteroid] Rogue Drone", 394, 666, 1090, 1687],
["[NPC][Deadspace] Rogue Drone", 276, 1071, 1069, 871],
["[NPC][Asteroid] Sanshas Nation", 5586, 4112, 0, 0],
["[NPC][Deadspace] Sanshas Nation", 3009, 2237, 0, 0],
["[NPC][Asteroid] Serpentis", 0, 5373, 4813, 0],
["[NPC][Deadspace] Serpentis", 0, 3110, 1929, 0],
["[NPC][Mission] Amarr Empire", 4464, 3546, 97, 0],
["[NPC][Mission] Caldari State", 0, 2139, 4867, 0],
["[NPC][Mission] CONCORD", 336, 134, 212, 412],
["[NPC][Mission] Gallente Federation", 9, 3712, 2758, 0],
["[NPC][Mission] Khanid", 612, 483, 43, 6],
["[NPC][Mission] Minmatar Republic", 1024, 388, 1655, 4285],
["[NPC][Mission] Mordus Legion", 25, 262, 625, 0],
["[NPC][Mission] Thukker", 0, 52, 10, 79],
["[NPC][Other] Sleepers", 1472, 1472, 1384, 1384],
["[NPC][Other] Sansha Incursion", 1682, 1347, 3678, 3678]]
for damageProfileRow in damageProfileList:
name, em, therm, kin, exp = damageProfileRow
damageProfile = eos.db.getDamagePattern(name)
if damageProfile is None:
damageProfile = es_DamagePattern(em, therm, kin, exp)
damageProfile.name = name
eos.db.add(damageProfile)
else:
damageProfile.emAmount = em
damageProfile.thermalAmount = therm
damageProfile.kineticAmount = kin
damageProfile.explosiveAmount = exp
eos.db.commit()
@classmethod
def importTargetProfileDefaults(cls):
targetProfileList = [["Uniform (25%)", 0.25, 0.25, 0.25, 0.25],
["Uniform (50%)", 0.50, 0.50, 0.50, 0.50],
["Uniform (75%)", 0.75, 0.75, 0.75, 0.75],
["Uniform (90%)", 0.90, 0.90, 0.90, 0.90],
["[T1 Resist]Shield", 0.0, 0.20, 0.40, 0.50],
["[T1 Resist]Armor", 0.50, 0.45, 0.25, 0.10],
["[T1 Resist]Hull", 0.33, 0.33, 0.33, 0.33],
["[T1 Resist]Shield (+T2 DCU)", 0.125, 0.30, 0.475, 0.562],
["[T1 Resist]Armor (+T2 DCU)", 0.575, 0.532, 0.363, 0.235],
["[T1 Resist]Hull (+T2 DCU)", 0.598, 0.598, 0.598, 0.598],
["[T2 Resist]Amarr (Shield)", 0.0, 0.20, 0.70, 0.875],
["[T2 Resist]Amarr (Armor)", 0.50, 0.35, 0.625, 0.80],
["[T2 Resist]Caldari (Shield)", 0.20, 0.84, 0.76, 0.60],
["[T2 Resist]Caldari (Armor)", 0.50, 0.8625, 0.625, 0.10],
["[T2 Resist]Gallente (Shield)", 0.0, 0.60, 0.85, 0.50],
["[T2 Resist]Gallente (Armor)", 0.50, 0.675, 0.8375, 0.10],
["[T2 Resist]Minmatar (Shield)", 0.75, 0.60, 0.40, 0.50],
["[T2 Resist]Minmatar (Armor)", 0.90, 0.675, 0.25, 0.10],
["[NPC][Asteroid] Angel Cartel", 0.54, 0.42, 0.37, 0.32],
["[NPC][Asteroid] Blood Raiders", 0.34, 0.39, 0.45, 0.52],
["[NPC][Asteroid] Guristas", 0.55, 0.35, 0.3, 0.48],
["[NPC][Asteroid] Rogue Drones", 0.35, 0.38, 0.44, 0.49],
["[NPC][Asteroid] Sanshas Nation", 0.35, 0.4, 0.47, 0.53],
["[NPC][Asteroid] Serpentis", 0.49, 0.38, 0.29, 0.51],
["[NPC][Deadspace] Angel Cartel", 0.59, 0.48, 0.4, 0.32],
["[NPC][Deadspace] Blood Raiders", 0.31, 0.39, 0.47, 0.56],
["[NPC][Deadspace] Guristas", 0.57, 0.39, 0.31, 0.5],
["[NPC][Deadspace] Rogue Drones", 0.42, 0.42, 0.47, 0.49],
["[NPC][Deadspace] Sanshas Nation", 0.31, 0.39, 0.47, 0.56],
["[NPC][Deadspace] Serpentis", 0.49, 0.38, 0.29, 0.56],
["[NPC][Mission] Amarr Empire", 0.34, 0.38, 0.42, 0.46],
["[NPC][Mission] Caldari State", 0.51, 0.38, 0.3, 0.51],
["[NPC][Mission] CONCORD", 0.47, 0.46, 0.47, 0.47],
["[NPC][Mission] Gallente Federation", 0.51, 0.38, 0.31, 0.52],
["[NPC][Mission] Khanid", 0.51, 0.42, 0.36, 0.4],
["[NPC][Mission] Minmatar Republic", 0.51, 0.46, 0.41, 0.35],
["[NPC][Mission] Mordus Legion", 0.32, 0.48, 0.4, 0.62],
["[NPC][Other] Sleeper", 0.61, 0.61, 0.61, 0.61],
["[NPC][Other] Sansha Incursion", 0.65, 0.63, 0.64, 0.65],
["[NPC][Burner] Cruor (Blood Raiders)", 0.8, 0.73, 0.69, 0.67],
["[NPC][Burner] Dramiel (Angel)", 0.35, 0.48, 0.61, 0.68],
["[NPC][Burner] Daredevil (Serpentis)", 0.69, 0.59, 0.59, 0.43],
["[NPC][Burner] Succubus (Sanshas Nation)", 0.35, 0.48, 0.61, 0.68],
["[NPC][Burner] Worm (Guristas)", 0.48, 0.58, 0.69, 0.74],
["[NPC][Burner] Enyo", 0.58, 0.72, 0.86, 0.24],
["[NPC][Burner] Hawk", 0.3, 0.86, 0.79, 0.65],
["[NPC][Burner] Jaguar", 0.78, 0.65, 0.48, 0.56],
["[NPC][Burner] Vengeance", 0.66, 0.56, 0.75, 0.86],
["[NPC][Burner] Ashimmu (Blood Raiders)", 0.8, 0.76, 0.68, 0.7],
["[NPC][Burner] Talos", 0.68, 0.59, 0.59, 0.43],
["[NPC][Burner] Sentinel", 0.58, 0.45, 0.52, 0.66]]
for targetProfileRow in targetProfileList:
name = targetProfileRow[0]
em = targetProfileRow[1]
therm = targetProfileRow[2]
kin = targetProfileRow[3]
exp = targetProfileRow[4]
try:
maxVel = targetProfileRow[5]
except IndexError:
maxVel = None
try:
sigRad = targetProfileRow[6]
except IndexError:
sigRad = None
try:
radius = targetProfileRow[7]
except:
radius = None
targetProfile = eos.db.getTargetProfile(name)
if targetProfile is None:
targetProfile = es_TargetProfile(em, therm, kin, exp, maxVel, sigRad, radius)
targetProfile.name = name
eos.db.add(targetProfile)
else:
targetProfile.emAmount = em
targetProfile.thermalAmount = therm
targetProfile.kineticAmount = kin
targetProfile.explosiveAmount = exp
targetProfile.maxVelocity = maxVel
targetProfile.signatureRadius = sigRad
targetProfile.radius = radius
eos.db.commit()
@classmethod
def importRequiredDefaults(cls):
damageProfileList = [["Uniform", 25, 25, 25, 25]]
for damageProfileRow in damageProfileList:
name, em, therm, kin, exp = damageProfileRow
damageProfile = eos.db.getDamagePattern(name)
if damageProfile is None:
damageProfile = es_DamagePattern(em, therm, kin, exp)
damageProfile.name = name
eos.db.add(damageProfile)
else:
damageProfile.emAmount = em
damageProfile.thermalAmount = therm
damageProfile.kineticAmount = kin
damageProfile.explosiveAmount = exp
eos.db.commit()

View File

@@ -18,14 +18,14 @@
# ===============================================================================
from sqlalchemy import Table, Column, Integer, Float, ForeignKey, CheckConstraint, Boolean, DateTime
from sqlalchemy.orm.collections import attribute_mapped_collection
from sqlalchemy.orm import relation, mapper
from sqlalchemy.orm.collections import attribute_mapped_collection
import datetime
from eos.db import saveddata_meta
from eos.saveddata.module import Module
from eos.saveddata.mutator import Mutator
from eos.saveddata.fit import Fit
from eos.saveddata.mutator import MutatorModule
modules_table = Table("modules", saveddata_meta,
Column("ID", Integer, primary_key=True),
@@ -45,13 +45,12 @@ modules_table = Table("modules", saveddata_meta,
Column("projectionRange", Float, nullable=True),
CheckConstraint('("dummySlot" = NULL OR "itemID" = NULL) AND "dummySlot" != "itemID"'))
mapper(Module, modules_table,
properties={
"owner": relation(Fit),
"mutators": relation(
Mutator,
backref="module",
MutatorModule,
backref="item",
cascade="all,delete-orphan",
collection_class=attribute_mapped_collection('attrID')
)
})
collection_class=attribute_mapped_collection('attrID'))})

View File

@@ -23,13 +23,14 @@ from sqlalchemy import Column, DateTime, Float, ForeignKey, Integer, Table
from sqlalchemy.orm import mapper
from eos.db import saveddata_meta
from eos.saveddata.mutator import Mutator
from eos.saveddata.mutator import MutatorDrone
mutator_table = Table("mutators", saveddata_meta,
Column("moduleID", Integer, ForeignKey("modules.ID"), primary_key=True, index=True),
Column("attrID", Integer, primary_key=True, index=True),
Column("value", Float, nullable=False),
Column("created", DateTime, nullable=True, default=datetime.datetime.now),
Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now))
mutatorDrones_table = Table(
"mutatorsDrones", saveddata_meta,
Column("groupID", Integer, ForeignKey("drones.groupID"), primary_key=True, index=True),
Column("attrID", Integer, primary_key=True, index=True),
Column("value", Float, nullable=False),
Column("created", DateTime, nullable=True, default=datetime.datetime.now),
Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now))
mapper(Mutator, mutator_table)
mapper(MutatorDrone, mutatorDrones_table)

View File

@@ -0,0 +1,36 @@
# ===============================================================================
# Copyright (C) 2010 Diego Duclos
#
# This file is part of eos.
#
# eos is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# eos is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with eos. If not, see <http://www.gnu.org/licenses/>.
# ===============================================================================
import datetime
from sqlalchemy import Column, DateTime, Float, ForeignKey, Integer, Table
from sqlalchemy.orm import mapper
from eos.db import saveddata_meta
from eos.saveddata.mutator import MutatorModule
mutatorMods_table = Table(
"mutators", saveddata_meta,
Column("moduleID", Integer, ForeignKey("modules.ID"), primary_key=True, index=True),
Column("attrID", Integer, primary_key=True, index=True),
Column("value", Float, nullable=False),
Column("created", DateTime, nullable=True, default=datetime.datetime.now),
Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now))
mapper(MutatorModule, mutatorMods_table)

View File

@@ -413,7 +413,7 @@ def getDamagePattern(lookfor, eager=None):
eager = processEager(eager)
with sd_lock:
pattern = saveddata_session.query(DamagePattern).options(*eager).filter(
DamagePattern.name == lookfor).first()
DamagePattern.rawName == lookfor).first()
else:
raise TypeError("Need integer or string as argument")
return pattern
@@ -434,7 +434,7 @@ def getTargetProfile(lookfor, eager=None):
eager = processEager(eager)
with sd_lock:
pattern = saveddata_session.query(TargetProfile).options(*eager).filter(
TargetProfile.name == lookfor).first()
TargetProfile.rawName == lookfor).first()
else:
raise TypeError("Need integer or string as argument")
return pattern
@@ -470,7 +470,7 @@ def searchFits(nameLike, where=None, eager=None):
filter = processWhere(Fit.name.like(nameLike, escape="\\"), where)
eager = processEager(eager)
with sd_lock:
fits = removeInvalid(saveddata_session.query(Fit).options(*eager).filter(filter).all())
fits = removeInvalid(saveddata_session.query(Fit).options(*eager).filter(filter).limit(100).all())
return fits
@@ -560,6 +560,8 @@ def commit():
with sd_lock:
try:
saveddata_session.commit()
except (KeyboardInterrupt, SystemExit):
raise
except Exception:
saveddata_session.rollback()
exc_info = sys.exc_info()
@@ -570,6 +572,8 @@ def flush():
with sd_lock:
try:
saveddata_session.flush()
except (KeyboardInterrupt, SystemExit):
raise
except Exception:
saveddata_session.rollback()
exc_info = sys.exc_info()

View File

@@ -24,23 +24,28 @@ import datetime
from eos.db import saveddata_meta
from eos.saveddata.targetProfile import TargetProfile
targetProfiles_table = Table("targetResists", saveddata_meta,
Column("ID", Integer, primary_key=True),
Column("name", String),
Column("emAmount", Float),
Column("thermalAmount", Float),
Column("kineticAmount", Float),
Column("explosiveAmount", Float),
Column("maxVelocity", Float, nullable=True),
Column("signatureRadius", Float, nullable=True),
Column("radius", Float, nullable=True),
Column("ownerID", ForeignKey("users.ID"), nullable=True),
Column("created", DateTime, nullable=True, default=datetime.datetime.now),
Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now)
)
mapper(TargetProfile, targetProfiles_table,
properties={
"_maxVelocity": targetProfiles_table.c.maxVelocity,
"_signatureRadius": targetProfiles_table.c.signatureRadius,
"_radius": targetProfiles_table.c.radius})
targetProfiles_table = Table(
'targetResists',
saveddata_meta,
Column('ID', Integer, primary_key=True),
Column('name', String),
Column('emAmount', Float),
Column('thermalAmount', Float),
Column('kineticAmount', Float),
Column('explosiveAmount', Float),
Column('maxVelocity', Float, nullable=True),
Column('signatureRadius', Float, nullable=True),
Column('radius', Float, nullable=True),
Column('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})

View File

@@ -19,6 +19,7 @@
from logbook import Logger
from sqlalchemy.orm.attributes import flag_dirty
from sqlalchemy.orm.collections import collection
@@ -108,10 +109,7 @@ class HandledList(list):
def remove(self, thing):
# We must flag it as modified, otherwise it not be removed from the database
# @todo: flag_modified isn't in os x skel. need to rebuild to include
# flag_modified(thing, "itemID")
if thing.isInvalid: # see GH issue #324
thing.itemID = 0
flag_dirty(thing)
list.remove(self, thing)
def sort(self, *args, **kwargs):

File diff suppressed because it is too large Load Diff

View File

@@ -18,7 +18,8 @@
# ===============================================================================
from collections import OrderedDict
import json
import re
from logbook import Logger
from sqlalchemy.orm import reconstructor
@@ -32,6 +33,10 @@ from .eqBase import EqBase
pyfalog = Logger(__name__)
def _t(x):
return x
class Effect(EqBase):
"""
The effect handling class, it is used to proxy and load effect handler code,
@@ -145,6 +150,12 @@ class Effect(EqBase):
return self.__effectDef is not None
@property
def dealsDamage(self):
if not self.__generated:
self.__generateHandler()
return self.__dealsDamage
def isType(self, type):
"""
Check if this effect is of the passed type
@@ -166,6 +177,7 @@ class Effect(EqBase):
self.__handler = getattr(effectDef, "handler", eos.effects.BaseEffect.handler)
self.__runTime = getattr(effectDef, "runTime", "normal")
self.__activeByDefault = getattr(effectDef, "activeByDefault", True)
self.__dealsDamage = effectDef.dealsDamage
effectType = getattr(effectDef, "type", None)
effectType = effectType if isinstance(effectType, tuple) or effectType is None else (effectType,)
self.__type = effectType
@@ -174,6 +186,7 @@ class Effect(EqBase):
self.__handler = eos.effects.DummyEffect.handler
self.__runTime = "normal"
self.__activeByDefault = True
self.__dealsDamage = False
self.__type = None
pyfalog.debug("ImportError generating handler: {0}", e)
except AttributeError as e:
@@ -181,12 +194,16 @@ class Effect(EqBase):
self.__handler = eos.effects.DummyEffect.handler
self.__runTime = "normal"
self.__activeByDefault = True
self.__dealsDamage = False
self.__type = None
pyfalog.error("AttributeError generating handler: {0}", e)
except (KeyboardInterrupt, SystemExit):
raise
except Exception as e:
self.__handler = eos.effects.DummyEffect.handler
self.__runTime = "normal"
self.__activeByDefault = True
self.__dealsDamage = False
self.__type = None
pyfalog.critical("Exception generating handler:")
pyfalog.critical(e)
@@ -199,45 +216,20 @@ class Effect(EqBase):
try:
return self.__effectDef.get(key, None)
except (KeyboardInterrupt, SystemExit):
raise
except:
return getattr(self.__effectDef, key, None)
class Item(EqBase):
MOVE_ATTRS = (4, # Mass
38, # Capacity
161) # Volume
MOVE_ATTR_INFO = None
ABYSSAL_TYPES = None
@classmethod
def getMoveAttrInfo(cls):
info = getattr(cls, "MOVE_ATTR_INFO", None)
if info is None:
cls.MOVE_ATTR_INFO = info = []
for id in cls.MOVE_ATTRS:
info.append(eos.db.getAttributeInfo(id))
return info
def moveAttrs(self):
self.__moved = True
for info in self.getMoveAttrInfo():
val = getattr(self, info.name, 0)
if val != 0:
attr = Attribute()
attr.info = info
attr.value = val
self.__attributes[info.name] = attr
@reconstructor
def init(self):
self.__race = None
self.__requiredSkills = None
self.__requiredFor = None
self.__moved = False
self.__offensive = None
self.__assistive = None
self.__overrides = None
@@ -258,10 +250,11 @@ class Item(EqBase):
return shortName
@property
def attributes(self):
if not self.__moved:
self.moveAttrs()
def customName(self):
return re.sub(_t('Caustic'), _t('Tachyon'), self.name)
@property
def attributes(self):
return self.__attributes
@property
@@ -314,50 +307,26 @@ class Item(EqBase):
eos.db.saveddata_session.delete(override)
eos.db.commit()
srqIDMap = {182: 277, 183: 278, 184: 279, 1285: 1286, 1289: 1287, 1290: 1288}
@property
def requiredSkills(self):
if self.__requiredSkills is None:
requiredSkills = OrderedDict()
self.__requiredSkills = requiredSkills
# Map containing attribute IDs we may need for required skills
# { requiredSkillX : requiredSkillXLevel }
combinedAttrIDs = set(self.srqIDMap.keys()).union(set(self.srqIDMap.values()))
# Map containing result of the request
# { attributeID : attributeValue }
skillAttrs = {}
# Get relevant attribute values from db (required skill IDs and levels) for our item
for attrInfo in eos.db.directAttributeRequest((self.ID,), tuple(combinedAttrIDs)):
attrID = attrInfo[1]
attrVal = attrInfo[2]
skillAttrs[attrID] = attrVal
# Go through all attributeID pairs
for srqIDAtrr, srqLvlAttr in self.srqIDMap.items():
# Check if we have both in returned result
if srqIDAtrr in skillAttrs and srqLvlAttr in skillAttrs:
skillID = int(skillAttrs[srqIDAtrr])
skillLvl = skillAttrs[srqLvlAttr]
# Fetch item from database and fill map
item = eos.db.getItem(skillID)
requiredSkills[item] = skillLvl
self.__requiredSkills = {}
if self.reqskills:
for skillTypeID, skillLevel in json.loads(self.reqskills).items():
skillItem = eos.db.getItem(int(skillTypeID))
if skillItem:
self.__requiredSkills[skillItem] = skillLevel
return self.__requiredSkills
@property
def requiredFor(self):
if self.__requiredFor is None:
self.__requiredFor = dict()
# Map containing attribute IDs we may need for required skills
# Get relevant attribute values from db (required skill IDs and levels) for our item
q = eos.db.getRequiredFor(self.ID, self.srqIDMap)
for itemID, lvl in q:
# Fetch item from database and fill map
item = eos.db.getItem(itemID)
self.__requiredFor[item] = lvl
self.__requiredFor = {}
if self.requiredfor:
for typeID, skillLevel in json.loads(self.requiredfor).items():
requiredForItem = eos.db.getItem(int(typeID))
if requiredForItem:
self.__requiredFor[requiredForItem] = skillLevel
return self.__requiredFor
factionMap = {
@@ -373,7 +342,8 @@ class Item(EqBase):
500016: "sisters",
500018: "mordu",
500019: "sansha",
500020: "serpentis"
500020: "serpentis",
500026: "triglavian"
}
@property
@@ -381,7 +351,11 @@ class Item(EqBase):
if self.__race is None:
try:
if self.category.categoryName == 'Structure':
if (
self.category.name == 'Structure' or
# Here until CCP puts their shit together
self.name in ("Thunderchild", "Stormbringer", "Skybreaker")
):
self.__race = "upwell"
else:
self.__race = self.factionMap[self.factionID]
@@ -399,6 +373,7 @@ class Item(EqBase):
9 : "guristas", # Caldari + Gallente
10 : "angelserp", # Minmatar + Gallente, final race depends on the order of skills
12 : "sisters", # Amarr + Gallente
15 : "concord",
16 : "jove",
32 : "sansha", # Incrusion Sansha
128: "ore",
@@ -456,7 +431,7 @@ class Item(EqBase):
def requiresSkill(self, skill, level=None):
for s, l in self.requiredSkills.items():
if isinstance(skill, str):
if s.name == skill and (level is None or l == level):
if s.typeName == skill and (level is None or l == level):
return True
elif isinstance(skill, int) and (level is None or l == level):
@@ -514,6 +489,10 @@ class Item(EqBase):
def isCharge(self):
return self.category.name == 'Charge'
@property
def isCommodity(self):
return self.category.name == 'Commodity'
@property
def isDrone(self):
return self.category.name == 'Drone'
@@ -539,8 +518,8 @@ class Item(EqBase):
return False
def __repr__(self):
return "Item(ID={}, name={}) at {}".format(
self.ID, self.name, hex(id(self))
return "Item(ID={}, name={}, display={}) at {}".format(
self.ID, self.typeName, self.name, hex(id(self))
)
@@ -588,7 +567,18 @@ class Group(EqBase):
class DynamicItem(EqBase):
pass
@property
def shortName(self):
name = self.item.customName
keywords = ('Decayed', 'Gravid', 'Unstable', 'Radical')
for kw in keywords:
if name.startswith(f'{kw} '):
name = kw
m = re.match('(?P<mutagrade>\S+) (?P<dronetype>\S+) Drone (?P<mutatype>\S+) Mutaplasmid', name)
if m:
name = '{} {}'.format(m.group('mutagrade'), m.group('mutatype'))
return name
class DynamicItemAttribute(EqBase):
@@ -740,5 +730,15 @@ class Unit(EqBase):
return value
class Traits(EqBase):
pass
class ImplantSet(EqBase):
@property
def fullName(self):
if not self.gradeName:
return self.setName
return '{} {}'.format(self.gradeName, self.setName)

View File

@@ -17,7 +17,8 @@
# along with eos. If not, see <http://www.gnu.org/licenses/>.
# ===============================================================================
import collections
from collections.abc import MutableMapping
from copy import copy
from math import exp
@@ -70,33 +71,33 @@ class ItemAttrShortcut:
def getModifiedItemAttr(self, key, default=0):
return_value = self.itemModifiedAttributes.get(key)
return return_value or default
return return_value if return_value is not None else default
def getModifiedItemAttrExtended(self, key, extraMultipliers=None, ignoreAfflictors=(), default=0):
return_value = self.itemModifiedAttributes.getExtended(key, extraMultipliers=extraMultipliers, ignoreAfflictors=ignoreAfflictors)
return return_value or default
return return_value if return_value is not None else default
def getItemBaseAttrValue(self, key, default=0):
return_value = self.itemModifiedAttributes.getOriginal(key)
return return_value or default
return return_value if return_value is not None else default
class ChargeAttrShortcut:
def getModifiedChargeAttr(self, key, default=0):
return_value = self.chargeModifiedAttributes.get(key)
return return_value or default
return return_value if return_value is not None else default
def getModifiedChargeAttrExtended(self, key, extraMultipliers=None, ignoreAfflictors=(), default=0):
return_value = self.chargeModifiedAttributes.getExtended(key, extraMultipliers=extraMultipliers, ignoreAfflictors=ignoreAfflictors)
return return_value or default
return return_value if return_value is not None else default
def getChargeBaseAttrValue(self, key, default=0):
return_value = self.chargeModifiedAttributes.getOriginal(key)
return return_value or default
return return_value if return_value is not None else default
class ModifiedAttributeDict(collections.MutableMapping):
class ModifiedAttributeDict(MutableMapping):
overrides_enabled = False
class CalculationPlaceholder:

View File

@@ -159,6 +159,6 @@ class Booster(HandledItem, ItemAttrShortcut):
sideEffect.active = sideEffectStates[sideEffect.effectID]
def __repr__(self):
return "Booster(ID={}, name={}) at {}".format(
self.item.ID, self.item.name, hex(id(self))
)
if self.item is not None:
return f"Booster(ID={self.item.ID}, name={self.item.name}) at {hex(id(self))}"
return f"Booster(ID={self.itemID}) at {hex(id(self))}"

View File

@@ -21,6 +21,9 @@ from logbook import Logger
from sqlalchemy.orm import reconstructor
from eos.utils.round import roundToPrec
pyfalog = Logger(__name__)
@@ -56,9 +59,8 @@ class BoosterSideEffect:
@property
def name(self):
return "{0}% {1}".format(
self.booster.getModifiedItemAttr(self.attr),
self.__effect.getattr('displayName') or self.__effect.name,
)
roundToPrec(self.booster.getModifiedItemAttr(self.attr), 5),
self.__effect.getattr('displayName') or self.__effect.name)
@property
def attr(self):

View File

@@ -96,7 +96,7 @@ class Character:
if cls.__itemNameMap is None:
map = {}
for skill in cls.getSkillList():
map[skill.name] = skill
map[skill.typeName] = skill
cls.__itemNameMap = map

View File

@@ -18,21 +18,204 @@
# ===============================================================================
import re
from collections import OrderedDict
from sqlalchemy.orm import reconstructor
import eos.db
def _t(x):
return x
def _c(x):
return '[' + x + ']'
# Order is significant here - UI uses order as-is for built-in patterns
BUILTINS = OrderedDict([
(-1, (_t('Uniform'), 25, 25, 25, 25)),
(-2, (_c(_t('Generic')) + _t('EM'), 1, 0, 0, 0)),
(-3, (_c(_t('Generic')) + _t('Thermal'), 0, 1, 0, 0)),
(-4, (_c(_t('Generic')) + _t('Kinetic'), 0, 0, 1, 0)),
(-5, (_c(_t('Generic')) + _t('Explosive'), 0, 0, 0, 1)),
(-6, (_c(_t('Frequency Crystals')) + '|' + _t('[T2] Aurora'), 5, 3, 0, 0)),
(-7, (_c(_t('Frequency Crystals')) + '|' + _t('[T2] Scorch'), 9, 2, 0, 0)),
(-8, (_c(_t('Frequency Crystals')) + _t('Radio'), 5, 0, 0, 0)),
(-9, (_c(_t('Frequency Crystals')) + _t('Microwave'), 4, 2, 0, 0)),
(-10, (_c(_t('Frequency Crystals')) + _t('Infrared'), 5, 2, 0, 0)),
(-11, (_c(_t('Frequency Crystals')) + _t('Standard'), 5, 3, 0, 0)),
(-12, (_c(_t('Frequency Crystals')) + _t('Ultraviolet'), 6, 3, 0, 0)),
(-13, (_c(_t('Frequency Crystals')) + _t('Xray'), 6, 4, 0, 0)),
(-14, (_c(_t('Frequency Crystals')) + _t('Gamma'), 7, 4, 0, 0)),
(-15, (_c(_t('Frequency Crystals')) + _t('Multifrequency'), 7, 5, 0, 0)),
(-16, (_c(_t('Frequency Crystals')) + '|' + _t('[T2] Gleam'), 7, 7, 0, 0)),
(-17, (_c(_t('Frequency Crystals')) + '|' + _t('[T2] Conflagration'), 7.7, 7.7, 0, 0)),
# Different sizes of plasma do different damage ratios, the values here
# are average of ratios across sizes
(-18, (_c(_t('Exotic Plasma')) + '|' + _t('[T2] Mystic'), 0, 66319, 0, 33681)),
(-19, (_c(_t('Exotic Plasma')) + _t('Meson'), 0, 60519, 0, 39481)),
(-20, (_c(_t('Exotic Plasma')) + _t('Baryon'), 0, 59737, 0, 40263)),
(-21, (_c(_t('Exotic Plasma')) + _t('Tetryon'), 0, 69208, 0, 30792)),
(-22, (_c(_t('Exotic Plasma')) + '|' + _t('[T2] Occult'), 0, 55863, 0, 44137)),
# Different sizes of packs do different damage ratios, the values here
# are average of ratios across sizes
(-23, (_c(_t('Condenser Packs')) + '|' + _t('[T2] StrikeSnipe'), 51817, 0, 48183, 0)),
(-24, (_c(_t('Condenser Packs')) + _t('MesmerFlux'), 76476, 0, 23524, 0)),
(-25, (_c(_t('Condenser Packs')) + _t('SlamBolt'), 23376, 0, 76624, 0)),
(-26, (_c(_t('Condenser Packs')) + _t('BlastShot'), 19820, 0, 80180, 0)),
(-27, (_c(_t('Condenser Packs')) + _t('GalvaSurge'), 80206, 0, 19794, 0)),
(-28, (_c(_t('Condenser Packs')) + '|' + _t('[T2] ElectroPunch'), 0, 50547, 0, 49453)),
(-29, (_c(_t('Hybrid Charges')) + '|' + _t('[T2] Spike'), 0, 4, 4, 0)),
(-30, (_c(_t('Hybrid Charges')) + '|' + _t('[T2] Null'), 0, 6, 5, 0)),
(-31, (_c(_t('Hybrid Charges')) + _t('Iron'), 0, 2, 3, 0)),
(-32, (_c(_t('Hybrid Charges')) + _t('Tungsten'), 0, 2, 4, 0)),
(-33, (_c(_t('Hybrid Charges')) + _t('Iridium'), 0, 3, 4, 0)),
(-34, (_c(_t('Hybrid Charges')) + _t('Lead'), 0, 3, 5, 0)),
(-35, (_c(_t('Hybrid Charges')) + _t('Thorium'), 0, 4, 5, 0)),
(-36, (_c(_t('Hybrid Charges')) + _t('Uranium'), 0, 4, 6, 0)),
(-37, (_c(_t('Hybrid Charges')) + _t('Plutonium'), 0, 5, 6, 0)),
(-38, (_c(_t('Hybrid Charges')) + _t('Antimatter'), 0, 5, 7, 0)),
(-39, (_c(_t('Hybrid Charges')) + '|' + _t('[T2] Javelin'), 0, 8, 6, 0)),
(-40, (_c(_t('Hybrid Charges')) + '|' + _t('[T2] Void'), 0, 7.7, 7.7, 0)),
(-41, (_c(_t('Projectile Ammo')) + '|' + _t('[T2] Tremor'), 0, 0, 3, 5)),
(-42, (_c(_t('Projectile Ammo')) + '|' + _t('[T2] Barrage'), 0, 0, 5, 6)),
(-43, (_c(_t('Projectile Ammo')) + _t('Carbonized Lead'), 0, 0, 4, 1)),
(-44, (_c(_t('Projectile Ammo')) + _t('Nuclear'), 0, 0, 1, 4)),
(-45, (_c(_t('Projectile Ammo')) + _t('Proton'), 3, 0, 2, 0)),
(-46, (_c(_t('Projectile Ammo')) + _t('Depleted Uranium'), 0, 3, 2, 3)),
(-47, (_c(_t('Projectile Ammo')) + _t('Titanium Sabot'), 0, 0, 6, 2)),
(-48, (_c(_t('Projectile Ammo')) + _t('EMP'), 9, 0, 1, 2)),
(-49, (_c(_t('Projectile Ammo')) + _t('Phased Plasma'), 0, 10, 2, 0)),
(-50, (_c(_t('Projectile Ammo')) + _t('Fusion'), 0, 0, 2, 10)),
(-51, (_c(_t('Projectile Ammo')) + '|' + _t('[T2] Quake'), 0, 0, 5, 9)),
(-52, (_c(_t('Projectile Ammo')) + '|' + _t('[T2] Hail'), 0, 0, 3.3, 12.1)),
(-53, (_c(_t('Missiles')) + _t('Mjolnir'), 1, 0, 0, 0)),
(-54, (_c(_t('Missiles')) + _t('Inferno'), 0, 1, 0, 0)),
(-55, (_c(_t('Missiles')) + _t('Scourge'), 0, 0, 1, 0)),
(-56, (_c(_t('Missiles')) + _t('Nova'), 0, 0, 0, 1)),
(-57, (_c(_t('Bombs')) + _t('Electron Bomb'), 6400, 0, 0, 0)),
(-58, (_c(_t('Bombs')) + _t('Scorch Bomb'), 0, 6400, 0, 0)),
(-59, (_c(_t('Bombs')) + _t('Concussion Bomb'), 0, 0, 6400, 0)),
(-60, (_c(_t('Bombs')) + _t('Shrapnel Bomb'), 0, 0, 0, 6400)),
# Source: ticket #2067 and #2265
(-61, (_c(_t('NPC')) + _c(_t('Abyssal')) + _t('All'), 126, 427, 218, 230)),
(-62, (_c(_t('NPC')) + _c(_t('Abyssal')) + _t('Angel'), 450, 72, 80, 398)),
(-63, (_c(_t('NPC')) + _c(_t('Abyssal')) + _t('Concord'), 53, 559, 94, 295)),
(-64, (_c(_t('NPC')) + _c(_t('Abyssal')) + _t('Drifter'), 250, 250, 250, 250)),
(-65, (_c(_t('NPC')) + _c(_t('Abyssal')) + _t('Drones'), 250, 250, 250, 250)),
(-66, (_c(_t('NPC')) + _c(_t('Abyssal')) + _t('Overmind'), 0, 410, 590, 0)),
(-67, (_c(_t('NPC')) + _c(_t('Abyssal')) + _t('Sansha'), 569, 431, 0, 0)),
(-68, (_c(_t('NPC')) + _c(_t('Abyssal')) + _t('Seeker'), 402, 402, 98, 98)),
(-69, (_c(_t('NPC')) + _c(_t('Abyssal')) + _t('Sleeper'), 313, 313, 187, 187)),
(-70, (_c(_t('NPC')) + _c(_t('Abyssal')) + _t('Triglavian'), 0, 615, 0, 385)),
(-71, (_c(_t('NPC')) + _c(_t('Asteroid')) + _t('Angel Cartel'), 1838, 562, 2215, 3838)),
(-72, (_c(_t('NPC')) + _c(_t('Asteroid')) + _t('Blood Raiders'), 5067, 4214, 0, 0)),
(-73, (_c(_t('NPC')) + _c(_t('Asteroid')) + _t('Guristas'), 0, 1828, 7413, 0)),
(-74, (_c(_t('NPC')) + _c(_t('Asteroid')) + _t('Rogue Drone'), 394, 666, 1090, 1687)),
(-75, (_c(_t('NPC')) + _c(_t('Asteroid')) + _t('Sanshas Nation'), 5586, 4112, 0, 0)),
(-76, (_c(_t('NPC')) + _c(_t('Asteroid')) + _t('Serpentis'), 0, 5373, 4813, 0)),
(-77, (_c(_t('NPC')) + _c(_t('Burner')) + _c(_t('Team')) + _t('Enyo'), 0, 147, 147, 0)),
(-78, (_c(_t('NPC')) + _c(_t('Burner')) + _c(_t('Team')) + _t('Hawk'), 0, 0, 247, 0)),
(-79, (_c(_t('NPC')) + _c(_t('Burner')) + _c(_t('Team')) + _t('Jaguar'), 36, 0, 50, 182)),
(-80, (_c(_t('NPC')) + _c(_t('Burner')) + _c(_t('Team')) + _t('Vengeance'), 232, 0, 0, 0)),
(-81, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Cruor'), 90, 90, 0, 0)),
(-82, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Dramiel'), 55, 0, 20, 96)),
(-83, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Daredevil'), 0, 110, 154, 0)),
(-84, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Succubus'), 135, 30, 0, 0)),
(-85, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Worm'), 0, 0, 228, 0)),
(-86, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Ashimmu'), 260, 100, 0, 0)),
(-87, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Talos'), 0, 413, 413, 0)),
(-88, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Sentinel'), 0, 0, 75, 90)),
(-89, (_c(_t('NPC')) + _c(_t('Deadspace')) + _t('Angel Cartel'), 369, 533, 1395, 3302)),
(-90, (_c(_t('NPC')) + _c(_t('Deadspace')) + _t('Blood Raiders'), 6040, 5052, 10, 15)),
(-91, (_c(_t('NPC')) + _c(_t('Deadspace')) + _t('Guristas'), 0, 1531, 9680, 0)),
(-92, (_c(_t('NPC')) + _c(_t('Deadspace')) + _t('Rogue Drone'), 276, 1071, 1069, 871)),
(-93, (_c(_t('NPC')) + _c(_t('Deadspace')) + _t('Sanshas Nation'), 3009, 2237, 0, 0)),
(-94, (_c(_t('NPC')) + _c(_t('Deadspace')) + _t('Serpentis'), 0, 3110, 1929, 0)),
# Source: ticket #2067
(-95, (_c(_t('NPC')) + _c(_t('Invasion')) + _c(_t('Triglavian Entities')) + _t('Dread'), 0, 417, 0, 583)),
(-96, (_c(_t('NPC')) + _c(_t('Invasion')) + _c(_t('Triglavian Entities')) + _t('Normal Subcaps'), 0, 610, 0, 390)),
# To avoid errors on msgfmt, we have to mark that '0%' is meaning literally 0% with no-python-format.
# See also: https://github.com/vslavik/poedit/issues/645
(-97, (_c(_t('NPC')) + _c(_t('Invasion')) + _c(_t('Triglavian Entities')) +
# xgettext:no-python-format
_t('Subcaps w/missiles 0% spool up'), 367, 155, 367, 112)),
(-98, (_c(_t('NPC')) + _c(_t('Invasion')) + _c(_t('Triglavian Entities')) +
# xgettext:no-python-format
_t('Subcaps w/missiles 50% spool up'), 291, 243, 291, 175)),
(-99, (_c(_t('NPC')) + _c(_t('Invasion')) + _c(_t('Triglavian Entities')) +
# xgettext:no-python-format
_t('Subcaps w/missiles 100% spool up'), 241, 301, 241, 217)),
(-100, (_c(_t('NPC')) + _c(_t('Invasion')) + _c(_t('Amarr EDENCOM Entities')) + _t('Dread/Subcaps'), 583, 417, 0, 0)),
(-101, (_c(_t('NPC')) + _c(_t('Invasion')) + _c(_t('Caldari EDENCOM Entities')) + _t('Dread'), 1000, 0, 0, 0)),
(-102, (_c(_t('NPC')) + _c(_t('Invasion')) + _c(_t('Caldari EDENCOM Entities')) + _t('Subcaps'), 511, 21, 29, 440)),
(-103, (_c(_t('NPC')) + _c(_t('Invasion')) + _c(_t('Gallente EDENCOM Entities')) + _t('Dread/Subcaps'), 0, 417, 583, 0)),
(-104, (_c(_t('NPC')) + _c(_t('Invasion')) + _c(_t('Minmatar EDENCOM Entities')) + _t('Dread'), 0, 0, 583, 417)),
(-105, (_c(_t('NPC')) + _c(_t('Invasion')) + _c(_t('Minmatar EDENCOM Entities')) + _t('Subcaps'), 302, 136, 328, 234)),
(-106, (_c(_t('NPC')) + _c(_t('Invasion')) + _t('Drifter Entities'), 250, 250, 250, 250)),
(-107, (_c(_t('NPC')) + _c(_t('Invasion')) + _t('Sleeper Entities'), 265, 265, 235, 235)),
(-108, (_c(_t('NPC')) + _c(_t('Invasion')) + _t('Rogue Drone Entities'), 250, 250, 250, 250)),
(-109, (_c(_t('NPC')) + _c(_t('Mission')) + _t('Amarr Empire'), 4464, 3546, 97, 0)),
(-110, (_c(_t('NPC')) + _c(_t('Mission')) + _t('Caldari State'), 0, 2139, 4867, 0)),
(-111, (_c(_t('NPC')) + _c(_t('Mission')) + _t('CONCORD'), 336, 134, 212, 412)),
(-112, (_c(_t('NPC')) + _c(_t('Mission')) + _t('Gallente Federation'), 9, 3712, 2758, 0)),
(-113, (_c(_t('NPC')) + _c(_t('Mission')) + _t('Khanid'), 612, 483, 43, 6)),
(-114, (_c(_t('NPC')) + _c(_t('Mission')) + _t('Minmatar Republic'), 1024, 388, 1655, 4285)),
(-115, (_c(_t('NPC')) + _c(_t('Mission')) + _t('Mordus Legion'), 25, 262, 625, 0)),
(-116, (_c(_t('NPC')) + _c(_t('Mission')) + _t('Thukker'), 0, 52, 10, 79)),
(-117, (_c(_t('NPC')) + _t('Sansha Incursion'), 1682, 1347, 3678, 3678)),
(-118, (_c(_t('NPC')) + _t('Sleepers'), 1472, 1472, 1384, 1384))])
class DamagePattern:
DAMAGE_TYPES = ("em", "thermal", "kinetic", "explosive")
DAMAGE_TYPES = ('em', 'thermal', 'kinetic', 'explosive')
_builtins = None
def __init__(self, *args, **kwargs):
self.builtin = False
self.update(*args, **kwargs)
@reconstructor
def init(self):
self.builtin = False
def update(self, emAmount=25, thermalAmount=25, kineticAmount=25, explosiveAmount=25):
self.emAmount = emAmount
self.thermalAmount = thermalAmount
self.kineticAmount = kineticAmount
self.explosiveAmount = explosiveAmount
@classmethod
def getBuiltinList(cls):
if cls._builtins is None:
cls.__generateBuiltins()
return list(cls._builtins.values())
@classmethod
def getBuiltinById(cls, id):
if cls._builtins is None:
cls.__generateBuiltins()
return cls._builtins.get(id)
@classmethod
def getDefaultBuiltin(cls):
if cls._builtins is None:
cls.__generateBuiltins()
return cls._builtins.get(-1)
@classmethod
def __generateBuiltins(cls):
cls._builtins = OrderedDict()
for id, (rawName, em, therm, kin, explo) in BUILTINS.items():
pattern = DamagePattern(emAmount=em, thermalAmount=therm, kineticAmount=kin, explosiveAmount=explo)
pattern.ID = id
pattern.rawName = rawName
pattern.builtin = True
cls._builtins[id] = pattern
def calculateEhp(self, fit):
ehp = {}
for (type, attr) in (('shield', 'shieldCapacity'), ('armor', 'armorHP'), ('hull', 'hp')):
@@ -48,7 +231,8 @@ class DamagePattern:
"armorRepair": "armor",
"armorRepairPreSpool": "armor",
"armorRepairFullSpool": "armor",
"hullRepair": "hull"}
"hullRepair": "hull"
}
ereps = {}
for field in tankInfo:
if field in typeMap:
@@ -72,12 +256,21 @@ class DamagePattern:
return amount / (specificDivider or 1)
importMap = {
"em" : "em",
"em": "em",
"therm": "thermal",
"kin" : "kinetic",
"exp" : "explosive"
"kin": "kinetic",
"exp": "explosive"
}
@classmethod
def oneType(cls, damageType, amount=100):
pattern = DamagePattern()
pattern.update(amount if damageType == "em" else 0,
amount if damageType == "thermal" else 0,
amount if damageType == "kinetic" else 0,
amount if damageType == "explosive" else 0)
return pattern
@classmethod
def importPatterns(cls, text):
lines = re.split('[\n\r]+', text)
@@ -89,7 +282,7 @@ class DamagePattern:
lookup = {}
current = eos.db.getDamagePatternList()
for pattern in current:
lookup[pattern.name] = pattern
lookup[pattern.rawName] = pattern
for line in lines:
try:
@@ -98,6 +291,8 @@ class DamagePattern:
line = line.split('#', 1)[0] # allows for comments
type, data = line.rsplit('=', 1)
type, data = type.strip(), data.split(',')
except (KeyboardInterrupt, SystemExit):
raise
except:
# Data isn't in correct format, continue to next line
continue
@@ -112,6 +307,8 @@ class DamagePattern:
for index, val in enumerate(data):
try:
fields["%sAmount" % cls.DAMAGE_TYPES[index]] = int(val)
except (KeyboardInterrupt, SystemExit):
raise
except:
continue
@@ -122,7 +319,7 @@ class DamagePattern:
eos.db.save(pattern)
else:
pattern = DamagePattern(**fields)
pattern.name = name.strip()
pattern.rawName = name.strip()
eos.db.save(pattern)
patterns.append(pattern)
@@ -138,11 +335,41 @@ class DamagePattern:
out += "# Values are in following format:\n"
out += "# DamageProfile = [name],[EM amount],[Thermal amount],[Kinetic amount],[Explosive amount]\n\n"
for dp in patterns:
out += cls.EXPORT_FORMAT % (dp.name, dp.emAmount, dp.thermalAmount, dp.kineticAmount, dp.explosiveAmount)
out += cls.EXPORT_FORMAT % (dp.rawName, dp.emAmount, dp.thermalAmount, dp.kineticAmount, dp.explosiveAmount)
return out.strip()
@property
def name(self):
return self.rawName
@property
def fullName(self):
categories, tail = self.__parseRawName()
return '{}{}'.format(''.join('[{}]'.format(c) for c in categories), tail)
@property
def shortName(self):
return self.__parseRawName()[1]
@property
def hierarchy(self):
return self.__parseRawName()[0]
def __parseRawName(self):
categories = []
remainingName = self.rawName.strip() if self.rawName else ''
while True:
start, end = remainingName.find('['), remainingName.find(']')
if start == -1 or end == -1:
return categories, remainingName
splitter = remainingName.find('|')
if splitter != -1 and splitter == start - 1:
return categories, remainingName[1:]
categories.append(remainingName[start + 1:end])
remainingName = remainingName[end + 1:].strip()
def __deepcopy__(self, memo):
p = DamagePattern(self.emAmount, self.thermalAmount, self.kineticAmount, self.explosiveAmount)
p.name = "%s copy" % self.name
p.rawName = "%s copy" % self.rawName
return p

View File

@@ -25,6 +25,8 @@ from sqlalchemy.orm import reconstructor, validates
import eos.db
from eos.effectHandlerHelpers import HandledCharge, HandledItem
from eos.modifiedAttributeDict import ChargeAttrShortcut, ItemAttrShortcut, ModifiedAttributeDict
from eos.saveddata.mutatedMixin import MutatedMixin, MutaError
from eos.saveddata.mutator import MutatorDrone
from eos.utils.cycles import CycleInfo
from eos.utils.default import DEFAULT
from eos.utils.stats import DmgTypes, RRTypes
@@ -33,12 +35,13 @@ from eos.utils.stats import DmgTypes, RRTypes
pyfalog = Logger(__name__)
class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut, MutatedMixin):
MINING_ATTRIBUTES = ("miningAmount",)
def __init__(self, item):
def __init__(self, item, baseItem=None, mutaplasmid=None):
"""Initialize a drone from the program"""
self.__item = item
self._item = item
self._mutaInit(baseItem=baseItem, mutaplasmid=mutaplasmid)
if self.isInvalid:
raise ValueError("Passed item is not a Drone")
@@ -53,14 +56,19 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
@reconstructor
def init(self):
"""Initialize a drone from the database and validate"""
self.__item = None
self._item = None
if self.itemID:
self.__item = eos.db.getItem(self.itemID)
if self.__item is None:
self._item = eos.db.getItem(self.itemID)
if self._item is None:
pyfalog.error("Item (id: {0}) does not exist", self.itemID)
return
try:
self._mutaReconstruct()
except MutaError:
return
if self.isInvalid:
pyfalog.error("Item (id: {0}) is not a Drone", self.itemID)
return
@@ -72,15 +80,18 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
self.__charge = None
self.__baseVolley = None
self.__baseRRAmount = None
self.__miningyield = None
self.__miningYield = None
self.__miningWaste = None
self.__itemModifiedAttributes = ModifiedAttributeDict()
self.__itemModifiedAttributes.original = self.__item.attributes
self.__itemModifiedAttributes.overrides = self.__item.overrides
self.__itemModifiedAttributes.original = self._item.attributes
self.__itemModifiedAttributes.overrides = self._item.overrides
self.__chargeModifiedAttributes = ModifiedAttributeDict()
# pheonix todo: check the attribute itself, not the modified. this will always return 0 now.
self._mutaLoadMutators(mutatorClass=MutatorDrone)
self.__itemModifiedAttributes.mutators = self.mutators
chargeID = self.getModifiedItemAttr("entityMissileTypeID", None)
if chargeID is not None:
if chargeID:
charge = eos.db.getItem(int(chargeID))
self.__charge = charge
self.__chargeModifiedAttributes.original = charge.attributes
@@ -96,11 +107,17 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
@property
def isInvalid(self):
return self.__item is None or self.__item.category.name != "Drone"
if self._item is None:
return True
if self._item.category.name != "Drone":
return True
if self._mutaIsInvalid:
return True
return False
@property
def item(self):
return self.__item
return self._item
@property
def charge(self):
@@ -111,9 +128,9 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
if self.hasAmmo:
cycleTime = self.getModifiedItemAttr("missileLaunchDuration", 0)
else:
for attr in ("speed", "duration"):
cycleTime = self.getModifiedItemAttr(attr, None)
if cycleTime is not None:
for attr in ("speed", "duration", "durationHighisGood"):
cycleTime = self.getModifiedItemAttr(attr)
if cycleTime:
break
if cycleTime is None:
return 0
@@ -220,35 +237,49 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
def getCycleParameters(self, reloadOverride=None):
cycleTime = self.cycleTime
if cycleTime == 0:
if not cycleTime:
return None
return CycleInfo(self.cycleTime, 0, math.inf, False)
@property
def miningStats(self):
if self.__miningyield is None:
if self.mines is True and self.amountActive > 0:
getter = self.getModifiedItemAttr
cycleParams = self.getCycleParameters()
if cycleParams is None:
self.__miningyield = 0
else:
cycleTime = cycleParams.averageTime
volley = sum([getter(d) for d in self.MINING_ATTRIBUTES]) * self.amountActive
self.__miningyield = volley / (cycleTime / 1000.0)
else:
self.__miningyield = 0
def getMiningYPS(self, ignoreState=False):
if not ignoreState and self.amountActive <= 0:
return 0
if self.__miningYield is None:
self.__miningYield, self.__miningWaste = self.__calculateMining()
return self.__miningYield
return self.__miningyield
def getMiningWPS(self, ignoreState=False):
if not ignoreState and self.amountActive <= 0:
return 0
if self.__miningWaste is None:
self.__miningYield, self.__miningWaste = self.__calculateMining()
return self.__miningWaste
def __calculateMining(self):
if self.mines is True:
getter = self.getModifiedItemAttr
cycleParams = self.getCycleParameters()
if cycleParams is None:
yps = 0
else:
cycleTime = cycleParams.averageTime
yield_ = sum([getter(d) for d in self.MINING_ATTRIBUTES]) * self.amount
yps = yield_ / (cycleTime / 1000.0)
wasteChance = self.getModifiedItemAttr("miningWasteProbability")
wasteMult = self.getModifiedItemAttr("miningWastedVolumeMultiplier")
wps = yps * max(0, min(1, wasteChance / 100)) * wasteMult
return yps, wps
else:
return 0, 0
@property
def maxRange(self):
attrs = ("shieldTransferRange", "powerTransferRange",
"energyDestabilizationRange", "empFieldRange",
"ecmBurstRange", "maxRange")
"ecmBurstRange", "maxRange", "ECMRangeOptimal")
for attr in attrs:
maxRange = self.getModifiedItemAttr(attr, None)
if maxRange is not None:
maxRange = self.getModifiedItemAttr(attr)
if maxRange:
return maxRange
if self.charge is not None:
delay = self.getModifiedChargeAttr("explosionDelay")
@@ -263,8 +294,8 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
def falloff(self):
attrs = ("falloff", "falloffEffectiveness")
for attr in attrs:
falloff = self.getModifiedItemAttr(attr, None)
if falloff is not None:
falloff = self.getModifiedItemAttr(attr)
if falloff:
return falloff
@validates("ID", "itemID", "chargeID", "amount", "amountActive")
@@ -285,7 +316,8 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
def clear(self):
self.__baseVolley = None
self.__baseRRAmount = None
self.__miningyield = None
self.__miningYield = None
self.__miningWaste = None
self.itemModifiedAttributes.clear()
self.chargeModifiedAttributes.clear()
@@ -337,10 +369,11 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
effect.handler(fit, self, ("droneCharge",), projectionRange, effect=effect)
def __deepcopy__(self, memo):
copy = Drone(self.item)
copy = Drone(self.item, self.baseItem, self.mutaplasmid)
copy.amount = self.amount
copy.amountActive = self.amountActive
copy.projectionRange = self.projectionRange
self._mutaApplyMutators(mutatorClass=MutatorDrone, targetInstance=copy)
return copy
def rebase(self, item):
@@ -348,19 +381,28 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
amountActive = self.amountActive
projectionRange = self.projectionRange
Drone.__init__(self, item)
Drone.__init__(self, item, self.baseItem, self.mutaplasmid)
self.amount = amount
self.amountActive = amountActive
self.projectionRange = projectionRange
self._mutaApplyMutators(mutatorClass=MutatorDrone)
def fits(self, fit):
fitDroneGroupLimits = set()
for i in range(1, 3):
groneGrp = fit.ship.getModifiedItemAttr("allowedDroneGroup%d" % i, None)
if groneGrp is not None:
groneGrp = fit.ship.getModifiedItemAttr("allowedDroneGroup%d" % i)
if groneGrp:
fitDroneGroupLimits.add(int(groneGrp))
if len(fitDroneGroupLimits) == 0:
return True
if self.item.groupID in fitDroneGroupLimits:
return True
return False
def canDealDamage(self, ignoreState=False):
if self.item is None:
return False
for effect in self.item.effects.values():
if effect.dealsDamage and (ignoreState or self.amountActive > 0):
return True
return False

View File

@@ -99,7 +99,7 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
self.__itemModifiedAttributes = ModifiedAttributeDict()
self.__chargeModifiedAttributes = ModifiedAttributeDict()
if len(self.abilities) != len(self.item.effects):
if {a.effectID for a in self.abilities} != {e.ID for e in self.item.effects.values()}:
self.__abilities = []
for ability in self.__getAbilities():
self.__abilities.append(ability)
@@ -441,3 +441,15 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
return False
return True
def canDealDamage(self, ignoreState=False, ignoreAbilityState=False):
if self.item is None:
return False
if not self.active and not ignoreState:
return False
for ability in self.abilities:
if not ability.active and not ignoreAbilityState:
continue
if ability.effect.dealsDamage:
return True
return False

View File

@@ -21,27 +21,34 @@ import datetime
import time
from copy import deepcopy
from itertools import chain
from math import asinh, log, sqrt
from math import ceil, log, sqrt
from logbook import Logger
from sqlalchemy.orm import reconstructor, validates
import eos.db
from eos import capSim
from eos.calc import calculateLockTime, calculateMultiplier
from eos.const import CalcType, FitSystemSecurity, FittingHardpoint, FittingModuleState, FittingSlot, ImplantLocation
from eos.effectHandlerHelpers import (
HandledBoosterList, HandledDroneCargoList, HandledImplantList,
HandledModuleList, HandledProjectedDroneList, HandledProjectedModList)
from eos.saveddata.character import Character
from eos.saveddata.citadel import Citadel
from eos.saveddata.damagePattern import DamagePattern
from eos.saveddata.module import Module
from eos.saveddata.ship import Ship
from eos.saveddata.targetProfile import TargetProfile
from eos.utils.float import floatUnerr
from eos.utils.stats import DmgTypes, RRTypes
pyfalog = Logger(__name__)
def _t(x):
return x
class FitLite:
def __init__(self, id=None, name=None, shipID=None, shipName=None, shipNameShort=None):
@@ -132,9 +139,11 @@ class Fit:
self.__weaponVolleyMap = {}
self.__remoteRepMap = {}
self.__minerYield = None
self.__droneYield = None
self.__minerWaste = None
self.__droneWaste = None
self.__droneDps = None
self.__droneVolley = None
self.__droneYield = None
self.__sustainableTank = None
self.__effectiveSustainableTank = None
self.__effectiveTank = None
@@ -148,7 +157,7 @@ class Fit:
self.factorReload = False
self.boostsFits = set()
self.gangBoosts = None
self.ecmProjectedStr = 1
self.__ecmProjectedList = []
self.commandBonuses = {}
def clearFactorReloadDependentData(self):
@@ -162,14 +171,29 @@ class Fit:
self.__capUsed = None
self.__capRecharge = None
self.__savedCapSimData.clear()
# Ancillary tank modules affect this
self.__sustainableTank = None
self.__effectiveSustainableTank = None
@property
def targetProfile(self):
return self.__targetProfile
if self.__userTargetProfile is not None:
return self.__userTargetProfile
if self.__builtinTargetProfileID is not None:
return TargetProfile.getBuiltinById(self.__builtinTargetProfileID)
return None
@targetProfile.setter
def targetProfile(self, targetProfile):
self.__targetProfile = targetProfile
if targetProfile is None:
self.__userTargetProfile = None
self.__builtinTargetProfileID = None
elif targetProfile.builtin:
self.__userTargetProfile = None
self.__builtinTargetProfileID = targetProfile.ID
else:
self.__userTargetProfile = targetProfile
self.__builtinTargetProfileID = None
self.__weaponDpsMap = {}
self.__weaponVolleyMap = {}
self.__droneDps = None
@@ -177,11 +201,25 @@ class Fit:
@property
def damagePattern(self):
return self.__damagePattern
if self.__userDamagePattern is not None:
return self.__userDamagePattern
if self.__builtinDamagePatternID is not None:
pattern = DamagePattern.getBuiltinById(self.__builtinDamagePatternID)
if pattern is not None:
return pattern
return DamagePattern.getDefaultBuiltin()
@damagePattern.setter
def damagePattern(self, damagePattern):
self.__damagePattern = damagePattern
if damagePattern is None:
self.__userDamagePattern = None
self.__builtinDamagePatternID = None
elif damagePattern.builtin:
self.__userDamagePattern = None
self.__builtinDamagePatternID = damagePattern.ID
else:
self.__userDamagePattern = damagePattern
self.__builtinDamagePatternID = None
self.__ehp = None
self.__effectiveTank = None
@@ -329,25 +367,44 @@ class Fit:
@property
def minerYield(self):
if self.__minerYield is None:
self.calculateMiningStats()
self.calculatemining()
return self.__minerYield
@property
def minerWaste(self):
if self.__minerWaste is None:
self.calculatemining()
return self.__minerWaste
@property
def droneYield(self):
if self.__droneYield is None:
self.calculateMiningStats()
self.calculatemining()
return self.__droneYield
@property
def droneWaste(self):
if self.__droneWaste is None:
self.calculatemining()
return self.__droneWaste
@property
def totalYield(self):
return self.droneYield + self.minerYield
@property
def totalWaste(self):
return self.droneWaste + self.minerWaste
@property
def maxTargets(self):
return min(self.extraAttributes["maxTargetsLockedFromSkills"],
self.ship.getModifiedItemAttr("maxLockedTargets"))
maxTargets = min(self.extraAttributes["maxTargetsLockedFromSkills"],
self.ship.getModifiedItemAttr("maxLockedTargets"))
return ceil(floatUnerr(maxTargets))
@property
def maxTargetRange(self):
@@ -361,20 +418,24 @@ class Fit:
@property
def scanType(self):
maxStr = -1
type = None
for scanType in ("Magnetometric", "Ladar", "Radar", "Gravimetric"):
type_ = None
for scanType in (_t("Magnetometric"), _t("Ladar"), _t("Radar"), _t("Gravimetric")):
currStr = self.ship.getModifiedItemAttr("scan%sStrength" % scanType)
if currStr > maxStr:
maxStr = currStr
type = scanType
type_ = scanType
elif currStr == maxStr:
type = "Multispectral"
type_ = _t("Multispectral")
return type
return type_
@property
def jamChance(self):
return (1 - self.ecmProjectedStr) * 100
sensors = self.scanStrength
retainLockChance = 1
for jamStr in self.__ecmProjectedList:
retainLockChance *= 1 - min(1, jamStr / sensors)
return (1 - retainLockChance) * 100
@property
def maxSpeed(self):
@@ -409,9 +470,9 @@ class Fit:
@validates("ID", "ownerID", "shipID")
def validator(self, key, val):
map = {
"ID" : lambda _val: isinstance(_val, int),
"ID": lambda _val: isinstance(_val, int),
"ownerID": lambda _val: isinstance(_val, int) or _val is None,
"shipID" : lambda _val: isinstance(_val, int) or _val is None
"shipID": lambda _val: isinstance(_val, int) or _val is None
}
if not map[key](val):
@@ -450,11 +511,13 @@ class Fit:
self.__weaponVolleyMap = {}
self.__remoteRepMap = {}
self.__minerYield = None
self.__droneYield = None
self.__minerWaste = None
self.__droneWaste = None
self.__effectiveSustainableTank = None
self.__sustainableTank = None
self.__droneDps = None
self.__droneVolley = None
self.__droneYield = None
self.__ehp = None
self.__calculated = False
self.__capStable = None
@@ -462,7 +525,7 @@ class Fit:
self.__capUsed = None
self.__capRecharge = None
self.__savedCapSimData.clear()
self.ecmProjectedStr = 1
self.__ecmProjectedList = []
# self.commandBonuses = {}
del self.__calculatedTargets[:]
@@ -525,6 +588,9 @@ class Fit:
if warfareBuffID not in self.commandBonuses or abs(self.commandBonuses[warfareBuffID][1]) < abs(value):
self.commandBonuses[warfareBuffID] = (runTime, value, module, effect)
def addProjectedEcm(self, strength):
self.__ecmProjectedList.append(strength)
def __runCommandBoosts(self, runTime="normal"):
pyfalog.debug("Applying gang boosts for {0}", repr(self))
for warfareBuffID in list(self.commandBonuses.keys()):
@@ -544,11 +610,15 @@ class Fit:
if warfareBuffID == 11: # Shield Burst: Active Shielding: Repair Duration/Capacitor
self.modules.filteredItemBoost(
lambda mod: mod.item.requiresSkill("Shield Operation") or mod.item.requiresSkill(
"Shield Emission Systems"), "capacitorNeed", value)
lambda mod: mod.item.requiresSkill("Shield Operation") or
mod.item.requiresSkill("Shield Emission Systems") or
mod.item.requiresSkill("Capital Shield Emission Systems"),
"capacitorNeed", value)
self.modules.filteredItemBoost(
lambda mod: mod.item.requiresSkill("Shield Operation") or mod.item.requiresSkill(
"Shield Emission Systems"), "duration", value)
lambda mod: mod.item.requiresSkill("Shield Operation") or
mod.item.requiresSkill("Shield Emission Systems") or
mod.item.requiresSkill("Capital Shield Emission Systems"),
"duration", value)
if warfareBuffID == 12: # Shield Burst: Shield Extension: Shield HP
self.ship.boostItemAttr("shieldCapacity", value, stackingPenalties=True)
@@ -558,12 +628,16 @@ class Fit:
self.ship.boostItemAttr("armor%sDamageResonance" % damageType, value, stackingPenalties=True)
if warfareBuffID == 14: # Armor Burst: Rapid Repair: Repair Duration/Capacitor
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems") or
mod.item.requiresSkill("Repair Systems"),
"capacitorNeed", value)
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems") or
mod.item.requiresSkill("Repair Systems"),
"duration", value)
self.modules.filteredItemBoost(
lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems") or
mod.item.requiresSkill("Repair Systems") or
mod.item.requiresSkill("Capital Remote Armor Repair Systems"),
"capacitorNeed", value)
self.modules.filteredItemBoost(
lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems") or
mod.item.requiresSkill("Repair Systems") or
mod.item.requiresSkill("Capital Remote Armor Repair Systems"),
"duration", value)
if warfareBuffID == 15: # Armor Burst: Armor Reinforcement: Armor HP
self.ship.boostItemAttr("armorHP", value, stackingPenalties=True)
@@ -637,8 +711,8 @@ class Fit:
"duration", value, stackingPenalties=True)
if warfareBuffID == 25: # Mining Burst: Mining Equipment Preservation: Crystal Volatility
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining"),
"crystalVolatilityChance", value, stackingPenalties=True)
self.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Mining"),
"crystalVolatilityChance", value, stackingPenalties=True)
if warfareBuffID == 26: # Information Burst: Sensor Optimization: Targeting Range
self.ship.boostItemAttr("maxTargetRange", value, stackingPenalties=True)
@@ -710,14 +784,14 @@ class Fit:
if warfareBuffID == 79: # AOE_Beacon_bioluminescence_cloud
self.ship.boostItemAttr("signatureRadius", value, stackingPenalties=True)
self.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"),
"signatureRadius", value, stackingPenalties=True)
if warfareBuffID == 80: # AOE_Beacon_caustic_cloud_local_repair
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Repair Systems"),
"armorDamageAmount", value, stackingPenalties=True)
if warfareBuffID == 80: # AOE_Beacon_caustic_cloud_inertia
self.ship.boostItemAttr("agility", value, stackingPenalties=True)
if warfareBuffID == 81: # AOE_Beacon_caustic_cloud_remote_repair
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"),
"armorDamageAmount", value, stackingPenalties=True)
if warfareBuffID == 81: # AOE_Beacon_caustic_cloud_velocity
self.ship.boostItemAttr("maxVelocity", value, stackingPenalties=True)
if warfareBuffID == 88: # AOE_Beacon_filament_cloud_shield_booster_shield_bonus
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Operation"),
@@ -732,7 +806,11 @@ class Fit:
if warfareBuffID == 90: # Weather_electric_storm_EM_resistance_penalty
for tankType in ("shield", "armor"):
self.ship.boostItemAttr("{}EmDamageResonance".format(tankType), value)
self.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"),
"{}EmDamageResonance".format(tankType), value)
self.ship.boostItemAttr("emDamageResonance", value) # for hull
self.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"),
"emDamageResonance", value) #for hull
if warfareBuffID == 92: # Weather_electric_storm_capacitor_recharge_bonus
self.ship.boostItemAttr("rechargeRate", value, stackingPenalties=True)
@@ -740,32 +818,54 @@ class Fit:
if warfareBuffID == 93: # Weather_xenon_gas_explosive_resistance_penalty
for tankType in ("shield", "armor"):
self.ship.boostItemAttr("{}ExplosiveDamageResonance".format(tankType), value)
self.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"),
"{}ExplosiveDamageResonance".format(tankType), value)
self.ship.boostItemAttr("explosiveDamageResonance", value) # for hull
self.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"),
"explosiveDamageResonance", value) # for hull
if warfareBuffID == 94: # Weather_xenon_gas_shield_hp_bonus
self.ship.boostItemAttr("shieldCapacity", value) # for hull
self.ship.boostItemAttr("shieldCapacity", value)
self.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"),
"shieldCapacity", value)
if warfareBuffID == 95: # Weather_infernal_thermal_resistance_penalty
for tankType in ("shield", "armor"):
self.ship.boostItemAttr("{}ThermalDamageResonance".format(tankType), value)
self.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"),
"{}ThermalDamageResonance".format(tankType), value)
self.ship.boostItemAttr("thermalDamageResonance", value) # for hull
self.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"),
"thermalDamageResonance", value) # for hull
if warfareBuffID == 96: # Weather_infernal_armor_hp_bonus
self.ship.boostItemAttr("armorHP", value) # for hull
self.ship.boostItemAttr("armorHP", value)
self.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"),
"armorHP", value)
if warfareBuffID == 97: # Weather_darkness_turret_range_penalty
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Gunnery"),
"maxRange", value, stackingPenalties=True)
self.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"),
"maxRange", value, stackingPenalties=True)
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Gunnery"),
"falloff", value, stackingPenalties=True)
self.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"),
"falloff", value, stackingPenalties=True)
if warfareBuffID == 98: # Weather_darkness_velocity_bonus
self.ship.boostItemAttr("maxVelocity", value)
self.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"),
"maxVelocity", value)
if warfareBuffID == 99: # Weather_caustic_toxin_kinetic_resistance_penalty
for tankType in ("shield", "armor"):
self.ship.boostItemAttr("{}KineticDamageResonance".format(tankType), value)
self.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"),
"{}KineticDamageResonance".format(tankType), value)
self.ship.boostItemAttr("kineticDamageResonance", value) # for hull
self.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"),
"kineticDamageResonance", value) # for hull
if warfareBuffID == 100: # Weather_caustic_toxin_scan_resolution_bonus
self.ship.boostItemAttr("scanResolution", value, stackingPenalties=True)
@@ -938,14 +1038,14 @@ class Fit:
for _ in range(projectionInfo.amount):
targetFit.register(item, origin=self)
item.calculateModifiedAttributes(
targetFit, runTime, forceProjected=True,
forcedProjRange=0)
targetFit, runTime, forceProjected=True,
forcedProjRange=0)
for mod in self.modules:
for _ in range(projectionInfo.amount):
targetFit.register(mod, origin=self)
mod.calculateModifiedAttributes(
targetFit, runTime, forceProjected=True,
forcedProjRange=projectionInfo.projectionRange)
targetFit, runTime, forceProjected=True,
forcedProjRange=projectionInfo.projectionRange)
def fill(self):
"""
@@ -961,7 +1061,8 @@ class Fit:
# Look for any dummies of that type to remove
posToRemove = {}
for slotType in (FittingSlot.LOW.value, FittingSlot.MED.value, FittingSlot.HIGH.value, FittingSlot.RIG.value, FittingSlot.SUBSYSTEM.value, FittingSlot.SERVICE.value):
for slotType in (
FittingSlot.LOW.value, FittingSlot.MED.value, FittingSlot.HIGH.value, FittingSlot.RIG.value, FittingSlot.SUBSYSTEM.value, FittingSlot.SERVICE.value):
amount = self.getSlotsFree(slotType, True)
if amount > 0:
for _ in range(int(amount)):
@@ -986,6 +1087,16 @@ class Fit:
if mod.isEmpty:
del self.modules[i]
def clearTail(self):
tailPositions = {}
for mod in reversed(self.modules):
if not mod.isEmpty:
break
tailPositions[self.modules.index(mod)] = mod.slot
for pos in sorted(tailPositions, reverse=True):
self.modules.remove(self.modules[pos])
return tailPositions
@property
def modCount(self):
x = 0
@@ -1028,22 +1139,23 @@ class Fit:
for mod in chain(self.modules, self.fighters):
if mod.slot is type and (not getattr(mod, "isEmpty", False) or countDummies):
if type in (FittingSlot.F_HEAVY, FittingSlot.F_SUPPORT, FittingSlot.F_LIGHT, FittingSlot.FS_HEAVY, FittingSlot.FS_LIGHT, FittingSlot.FS_SUPPORT) and not mod.active:
if type in (FittingSlot.F_HEAVY, FittingSlot.F_SUPPORT, FittingSlot.F_LIGHT, FittingSlot.FS_HEAVY, FittingSlot.FS_LIGHT,
FittingSlot.FS_SUPPORT) and not mod.active:
continue
amount += 1
return amount
slots = {
FittingSlot.LOW : "lowSlots",
FittingSlot.MED : "medSlots",
FittingSlot.HIGH : "hiSlots",
FittingSlot.RIG : "rigSlots",
FittingSlot.LOW: "lowSlots",
FittingSlot.MED: "medSlots",
FittingSlot.HIGH: "hiSlots",
FittingSlot.RIG: "rigSlots",
FittingSlot.SUBSYSTEM: "maxSubSystems",
FittingSlot.SERVICE : "serviceSlots",
FittingSlot.F_LIGHT : "fighterLightSlots",
FittingSlot.SERVICE: "serviceSlots",
FittingSlot.F_LIGHT: "fighterLightSlots",
FittingSlot.F_SUPPORT: "fighterSupportSlots",
FittingSlot.F_HEAVY : "fighterHeavySlots",
FittingSlot.F_HEAVY: "fighterHeavySlots",
FittingSlot.FS_LIGHT: "fighterStandupLightSlots",
FittingSlot.FS_SUPPORT: "fighterStandupSupportSlots",
FittingSlot.FS_HEAVY: "fighterStandupHeavySlots",
@@ -1095,7 +1207,7 @@ class Fit:
def droneBayUsed(self):
amount = 0
for d in self.drones:
amount += d.item.volume * d.amount
amount += d.item.attributes['volume'].value * d.amount
return amount
@@ -1103,7 +1215,7 @@ class Fit:
def fighterBayUsed(self):
amount = 0
for f in self.fighters:
amount += f.item.volume * f.amount
amount += f.item.attributes['volume'].value * f.amount
return amount
@@ -1325,8 +1437,8 @@ class Fit:
"""Return how much cap regen do we gain from having this module"""
currentRegen = self.calculateCapRecharge()
nomodRegen = self.calculateCapRecharge(
capacity=self.ship.getModifiedItemAttrExtended("capacitorCapacity", ignoreAfflictors=[mod]),
rechargeRate=self.ship.getModifiedItemAttrExtended("rechargeRate", ignoreAfflictors=[mod]) / 1000.0)
capacity=self.ship.getModifiedItemAttrExtended("capacitorCapacity", ignoreAfflictors=[mod]),
rechargeRate=self.ship.getModifiedItemAttrExtended("rechargeRate", ignoreAfflictors=[mod]) / 1000.0)
return currentRegen - nomodRegen
def getRemoteReps(self, spoolOptions=None):
@@ -1370,7 +1482,8 @@ class Fit:
"armorRepair": self.extraAttributes["armorRepair"],
"armorRepairPreSpool": self.extraAttributes["armorRepairPreSpool"],
"armorRepairFullSpool": self.extraAttributes["armorRepairFullSpool"],
"hullRepair": self.extraAttributes["hullRepair"]}
"hullRepair": self.extraAttributes["hullRepair"]
}
return reps
@property
@@ -1410,7 +1523,8 @@ class Fit:
"armorRepair": self.extraAttributes["armorRepair"],
"armorRepairPreSpool": self.extraAttributes["armorRepairPreSpool"],
"armorRepairFullSpool": self.extraAttributes["armorRepairFullSpool"],
"hullRepair": self.extraAttributes["hullRepair"]}
"hullRepair": self.extraAttributes["hullRepair"]
}
if not self.capStable or self.factorReload:
# Map a local repairer type to the attribute it uses
groupAttrMap = {
@@ -1418,14 +1532,16 @@ class Fit:
"Ancillary Shield Booster": "shieldBonus",
"Armor Repair Unit": "armorDamageAmount",
"Ancillary Armor Repairer": "armorDamageAmount",
"Hull Repair Unit": "structureDamageAmount"}
"Hull Repair Unit": "structureDamageAmount"
}
# Map local repairer type to tank type
groupStoreMap = {
"Shield Booster": "shieldRepair",
"Ancillary Shield Booster": "shieldRepair",
"Armor Repair Unit": "armorRepair",
"Ancillary Armor Repairer": "armorRepair",
"Hull Repair Unit": "hullRepair"}
"Hull Repair Unit": "hullRepair"
}
repairers = []
localAdjustment = {"shieldRepair": 0, "armorRepair": 0, "hullRepair": 0}
capUsed = self.capUsed
@@ -1477,7 +1593,7 @@ class Fit:
# Sort repairers by efficiency. We want to use the most efficient repairers first
repairers.sort(key=lambda _mod: _mod.getModifiedItemAttr(
groupAttrMap[_mod.item.group.name]) * (_mod.getModifiedItemAttr(
groupAttrMap[_mod.item.group.name]) * (_mod.getModifiedItemAttr(
"chargedArmorDamageMultiplier") or 1) / _mod.getModifiedItemAttr("capacitorNeed"), reverse=True)
# Loop through every module until we're above peak recharge
@@ -1529,24 +1645,27 @@ class Fit:
def calculateLockTime(self, radius):
scanRes = self.ship.getModifiedItemAttr("scanResolution")
if scanRes is not None and scanRes > 0:
# Yes, this function returns time in seconds, not miliseconds.
# 40,000 is indeed the correct constant here.
return min(40000 / scanRes / asinh(radius) ** 2, 30 * 60)
return calculateLockTime(srcScanRes=scanRes, tgtSigRadius=radius)
else:
return self.ship.getModifiedItemAttr("scanSpeed") / 1000.0
def calculateMiningStats(self):
def calculatemining(self):
minerYield = 0
minerWaste = 0
droneYield = 0
droneWaste = 0
for mod in self.modules:
minerYield += mod.miningStats
minerYield += mod.getMiningYPS()
minerWaste += mod.getMiningWPS()
for drone in self.drones:
droneYield += drone.miningStats
droneYield += drone.getMiningYPS()
droneWaste += drone.getMiningWPS()
self.__minerYield = minerYield
self.__minerWaste = minerWaste
self.__droneYield = droneYield
self.__droneWaste = droneWaste
def calculateWeaponDmgStats(self, spoolOptions):
weaponVolley = DmgTypes(0, 0, 0, 0)
@@ -1604,7 +1723,6 @@ class Fit:
secstatus = FitSystemSecurity.NULLSEC
return secstatus
def activeModulesIter(self):
for mod in self.modules:
if mod.state >= FittingModuleState.ACTIVE:
@@ -1626,6 +1744,22 @@ class Fit:
if ability.active:
yield fighter, ability
def getDampMultScanRes(self):
damps = []
for mod in self.activeModulesIter():
for effectName in ('remoteSensorDampFalloff', 'structureModuleEffectRemoteSensorDampener'):
if effectName in mod.item.effects:
damps.append((mod.getModifiedItemAttr('scanResolutionBonus'), 'default'))
if 'doomsdayAOEDamp' in mod.item.effects:
damps.append((mod.getModifiedItemAttr('scanResolutionBonus'), 'default'))
for drone in self.activeDronesIter():
if 'remoteSensorDampEntity' in drone.item.effects:
damps.extend(drone.amountActive * ((drone.getModifiedItemAttr('scanResolutionBonus'), 'default'),))
mults = {}
for strength, stackingGroup in damps:
mults.setdefault(stackingGroup, []).append((1 + strength / 100, None))
return calculateMultiplier(mults)
def __deepcopy__(self, memo=None):
fitCopy = Fit()
# Character and owner are not copied

View File

@@ -23,7 +23,6 @@ from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut
class Mode(ItemAttrShortcut, HandledItem):
def __init__(self, item, owner=None):
if item.group.name != "Ship Modifiers":
raise ValueError(
@@ -34,7 +33,6 @@ class Mode(ItemAttrShortcut, HandledItem):
self.__itemModifiedAttributes.original = self.item.attributes
self.__itemModifiedAttributes.overrides = self.item.overrides
@property
def item(self):
return self.__item

View File

@@ -27,7 +27,8 @@ from eos.const import FittingHardpoint, FittingModuleState, FittingSlot
from eos.effectHandlerHelpers import HandledCharge, HandledItem
from eos.modifiedAttributeDict import ChargeAttrShortcut, ItemAttrShortcut, ModifiedAttributeDict
from eos.saveddata.citadel import Citadel
from eos.saveddata.mutator import Mutator
from eos.saveddata.mutatedMixin import MutatedMixin, MutaError
from eos.saveddata.mutator import MutatorModule
from eos.utils.cycles import CycleInfo, CycleSequence
from eos.utils.default import DEFAULT
from eos.utils.float import floatUnerr
@@ -61,30 +62,20 @@ ProjectedSystem = {
}
class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut, MutatedMixin):
"""An instance of this class represents a module together with its charge and modified attributes"""
MINING_ATTRIBUTES = ("miningAmount",)
SYSTEM_GROUPS = ("Effect Beacon", "MassiveEnvironments", "Abyssal Hazards", "Non-Interactable Object")
SYSTEM_GROUPS = (
"Effect Beacon", "MassiveEnvironments", "Abyssal Hazards",
"Non-Interactable Object", "Destructible Effect Beacon")
def __init__(self, item, baseItem=None, mutaplasmid=None):
"""Initialize a module from the program"""
self.itemID = item.ID if item is not None else None
self.baseItemID = baseItem.ID if baseItem is not None else None
self.mutaplasmidID = mutaplasmid.ID if mutaplasmid is not None else None
if baseItem is not None:
# we're working with a mutated module, need to get abyssal module loaded with the base attributes
# Note: there may be a better way of doing this, such as a metho on this classe to convert(mutaplamid). This
# will require a bit more research though, considering there has never been a need to "swap" out the item of a Module
# before, and there may be assumptions taken with regards to the item never changing (pre-calculated / cached results, for example)
self.__item = eos.db.getItemWithBaseItemAttribute(self.itemID, self.baseItemID)
self.__baseItem = baseItem
self.__mutaplasmid = mutaplasmid
else:
self.__item = item
self.__baseItem = baseItem
self.__mutaplasmid = mutaplasmid
self._item = item
self._mutaInit(baseItem=baseItem, mutaplasmid=mutaplasmid)
if item is not None and self.isInvalid:
raise ValueError("Passed item is not a Module")
@@ -99,27 +90,22 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
@reconstructor
def init(self):
"""Initialize a module from the database and validate"""
self.__item = None
self.__baseItem = None
self._item = None
self.__charge = None
self.__mutaplasmid = None
# we need this early if module is invalid and returns early
self.__slot = self.dummySlot
if self.itemID:
self.__item = eos.db.getItem(self.itemID)
if self.__item is None:
self._item = eos.db.getItem(self.itemID)
if self._item is None:
pyfalog.error("Item (id: {0}) does not exist", self.itemID)
return
if self.baseItemID:
self.__item = eos.db.getItemWithBaseItemAttribute(self.itemID, self.baseItemID)
self.__baseItem = eos.db.getItem(self.baseItemID)
self.__mutaplasmid = eos.db.getMutaplasmid(self.mutaplasmidID)
if self.__baseItem is None:
pyfalog.error("Base Item (id: {0}) does not exist", self.itemID)
return
try:
self._mutaReconstruct()
except MutaError:
return
if self.isInvalid:
pyfalog.error("Item (id: {0}) is not a Module", self.itemID)
@@ -136,9 +122,12 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
if self.__charge and self.__charge.category.name != "Charge":
self.__charge = None
self.rahPatternOverride = None
self.__baseVolley = None
self.__baseRRAmount = None
self.__miningyield = None
self.__miningYield = None
self.__miningWaste = None
self.__reloadTime = None
self.__reloadForce = None
self.__chargeCycles = None
@@ -147,21 +136,13 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
self.__chargeModifiedAttributes = ModifiedAttributeDict(parent=self)
self.__slot = self.dummySlot # defaults to None
if self.__item:
self.__itemModifiedAttributes.original = self.__item.attributes
self.__itemModifiedAttributes.overrides = self.__item.overrides
self.__hardpoint = self.__calculateHardpoint(self.__item)
self.__slot = self.calculateSlot(self.__item)
# Instantiate / remove mutators if this is a mutated module
if self.__baseItem:
for x in self.mutaplasmid.attributes:
attr = self.item.attributes[x.name]
id = attr.ID
if id not in self.mutators: # create the mutator
Mutator(self, attr, attr.value)
# @todo: remove attributes that are no longer part of the mutaplasmid.
if self._item:
self.__itemModifiedAttributes.original = self._item.attributes
self.__itemModifiedAttributes.overrides = self._item.overrides
self.__hardpoint = self.__calculateHardpoint(self._item)
self.__slot = self.calculateSlot(self._item)
self._mutaLoadMutators(mutatorClass=MutatorModule)
self.__itemModifiedAttributes.mutators = self.mutators
if self.__charge:
@@ -196,15 +177,21 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
# todo: validate baseItem as well if it's set.
if self.isEmpty:
return False
return (
self.__item is None or (
self.__item.category.name not in ("Module", "Subsystem", "Structure Module") and
self.__item.group.name not in self.SYSTEM_GROUPS) or
(self.item.isAbyssal and not self.isMutated))
@property
def isMutated(self):
return self.baseItemID and self.mutaplasmidID
if self._item is None:
return True
if (
self._item.category.name not in ("Module", "Subsystem", "Structure Module")
and self._item.group.name not in self.SYSTEM_GROUPS
):
return True
if (
self._item.category.name == "Structure Module"
and self._item.group.name == "Quantum Cores"
):
return True
if self._mutaIsInvalid:
return True
return False
@property
def numCharges(self):
@@ -214,8 +201,8 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
if charge is None:
charges = 0
else:
chargeVolume = charge.volume
containerCapacity = self.item.capacity
chargeVolume = charge.attributes['volume'].value
containerCapacity = self.item.attributes['capacity'].value
if chargeVolume is None or containerCapacity is None:
charges = 0
else:
@@ -265,7 +252,9 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
@property
def isExclusiveSystemEffect(self):
return self.item.group.name in ("Effect Beacon", "Non-Interactable Object", "MassiveEnvironments")
# See issue #2258
# return self.item.group.name in ("Effect Beacon", "Non-Interactable Object", "MassiveEnvironments")
return False
@property
def isCapitalSize(self):
@@ -318,43 +307,75 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
"energyDestabilizationRange", "empFieldRange",
"ecmBurstRange", "warpScrambleRange", "cargoScanRange",
"shipScanRange", "surveyScanRange")
maxRange = None
for attr in attrs:
maxRange = self.getModifiedItemAttr(attr, None)
if maxRange is not None:
return maxRange
if self.charge is not None:
try:
chargeName = self.charge.group.name
except AttributeError:
pass
else:
if chargeName in ("Scanner Probe", "Survey Probe"):
return None
maxRange = self.getModifiedItemAttr(attr)
if maxRange:
break
if maxRange:
if 'burst projector' in self.item.name.lower():
maxRange -= self.owner.ship.getModifiedItemAttr("radius")
return maxRange
missileMaxRangeData = self.missileMaxRangeData
if missileMaxRangeData is None:
return None
lowerRange, higherRange, higherChance = missileMaxRangeData
maxRange = lowerRange * (1 - higherChance) + higherRange * higherChance
return maxRange
@property
def missileMaxRangeData(self):
if self.charge is None:
return None
try:
chargeName = self.charge.group.name
except AttributeError:
pass
else:
if chargeName in ("Scanner Probe", "Survey Probe"):
return None
def calculateRange(maxVelocity, mass, agility, flightTime):
# Source: http://www.eveonline.com/ingameboard.asp?a=topic&threadID=1307419&page=1#15
# D_m = V_m * (T_m + T_0*[exp(- T_m/T_0)-1])
maxVelocity = self.getModifiedChargeAttr("maxVelocity")
flightTime = self.getModifiedChargeAttr("explosionDelay") / 1000.0
mass = self.getModifiedChargeAttr("mass")
agility = self.getModifiedChargeAttr("agility")
if maxVelocity and (flightTime or mass or agility):
accelTime = min(flightTime, mass * agility / 1000000)
# Average distance done during acceleration
duringAcceleration = maxVelocity / 2 * accelTime
# Distance done after being at full speed
fullSpeed = maxVelocity * (flightTime - accelTime)
maxRange = duringAcceleration + fullSpeed
if 'fofMissileLaunching' in self.charge.effects:
rangeLimit = self.getModifiedChargeAttr("maxFOFTargetRange")
if rangeLimit:
maxRange = min(maxRange, rangeLimit)
return maxRange
accelTime = min(flightTime, mass * agility / 1000000)
# Average distance done during acceleration
duringAcceleration = maxVelocity / 2 * accelTime
# Distance done after being at full speed
fullSpeed = maxVelocity * (flightTime - accelTime)
maxRange = duringAcceleration + fullSpeed
return maxRange
maxVelocity = self.getModifiedChargeAttr("maxVelocity")
if not maxVelocity:
return None
shipRadius = self.owner.ship.getModifiedItemAttr("radius")
# Flight time has bonus based on ship radius, see https://github.com/pyfa-org/Pyfa/issues/2083
flightTime = floatUnerr(self.getModifiedChargeAttr("explosionDelay") / 1000 + shipRadius / maxVelocity)
mass = self.getModifiedChargeAttr("mass")
agility = self.getModifiedChargeAttr("agility")
lowerTime = math.floor(flightTime)
higherTime = math.ceil(flightTime)
lowerRange = calculateRange(maxVelocity, mass, agility, lowerTime)
higherRange = calculateRange(maxVelocity, mass, agility, higherTime)
# Fof range limit is supposedly calculated based on overview (surface-to-surface) range
if 'fofMissileLaunching' in self.charge.effects:
rangeLimit = self.getModifiedChargeAttr("maxFOFTargetRange")
if rangeLimit:
lowerRange = min(lowerRange, rangeLimit)
higherRange = min(higherRange, rangeLimit)
# Make range center-to-surface, as missiles spawn in the center of the ship
lowerRange = max(0, lowerRange - shipRadius)
higherRange = max(0, higherRange - shipRadius)
higherChance = flightTime - lowerTime
return lowerRange, higherRange, higherChance
@property
def falloff(self):
attrs = ("falloffEffectiveness", "falloff", "shipScanFalloff")
for attr in attrs:
falloff = self.getModifiedItemAttr(attr, None)
if falloff is not None:
falloff = self.getModifiedItemAttr(attr)
if falloff:
return falloff
@property
@@ -371,15 +392,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
@property
def item(self):
return self.__item if self.__item != 0 else None
@property
def baseItem(self):
return self.__baseItem
@property
def mutaplasmid(self):
return self.__mutaplasmid
return self._item if self._item != 0 else None
@property
def charge(self):
@@ -399,28 +412,39 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
self.__itemModifiedAttributes.clear()
@property
def miningStats(self):
if self.__miningyield is None:
if self.isEmpty:
self.__miningyield = 0
else:
if self.state >= FittingModuleState.ACTIVE:
volley = self.getModifiedItemAttr("specialtyMiningAmount") or self.getModifiedItemAttr(
"miningAmount") or 0
if volley:
cycleParams = self.getCycleParameters()
if cycleParams is None:
self.__miningyield = 0
else:
cycleTime = cycleParams.averageTime
self.__miningyield = volley / (cycleTime / 1000.0)
else:
self.__miningyield = 0
else:
self.__miningyield = 0
def getMiningYPS(self, ignoreState=False):
if self.isEmpty:
return 0
if not ignoreState and self.state < FittingModuleState.ACTIVE:
return 0
if self.__miningYield is None:
self.__miningYield, self.__miningWaste = self.__calculateMining()
return self.__miningYield
return self.__miningyield
def getMiningWPS(self, ignoreState=False):
if self.isEmpty:
return 0
if not ignoreState and self.state < FittingModuleState.ACTIVE:
return 0
if self.__miningWaste is None:
self.__miningYield, self.__miningWaste = self.__calculateMining()
return self.__miningWaste
def __calculateMining(self):
yield_ = self.getModifiedItemAttr("miningAmount")
if yield_:
cycleParams = self.getCycleParameters()
if cycleParams is None:
yps = 0
else:
cycleTime = cycleParams.averageTime
yps = yield_ / (cycleTime / 1000.0)
else:
yps = 0
wasteChance = self.getModifiedItemAttr("miningWasteProbability")
wasteMult = self.getModifiedItemAttr("miningWastedVolumeMultiplier")
wps = yps * max(0, min(1, wasteChance / 100)) * wasteMult
return yps, wps
def isDealingDamage(self, ignoreState=False):
volleyParams = self.getVolleyParameters(ignoreState=ignoreState)
@@ -429,6 +453,20 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
return True
return False
def canDealDamage(self, ignoreState=False):
if self.isEmpty:
return False
for effect in self.item.effects.values():
if effect.dealsDamage and (
ignoreState or
effect.isType('offline') or
(effect.isType('passive') and self.state >= FittingModuleState.ONLINE) or
(effect.isType('active') and self.state >= FittingModuleState.ACTIVE) or
(effect.isType('overheat') and self.state >= FittingModuleState.OVERHEATED)
):
return True
return False
def getVolleyParameters(self, spoolOptions=None, targetProfile=None, ignoreState=False):
if self.isEmpty or (self.state < FittingModuleState.ACTIVE and not ignoreState):
return {0: DmgTypes(0, 0, 0, 0)}
@@ -477,7 +515,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
return DmgTypes(0, 0, 0, 0)
return volleyParams[min(volleyParams)]
def getDps(self, spoolOptions=None, targetProfile=None, ignoreState=False):
def getDps(self, spoolOptions=None, targetProfile=None, ignoreState=False, getSpreadDPS=False):
dmgDuringCycle = DmgTypes(0, 0, 0, 0)
cycleParams = self.getCycleParameters()
if cycleParams is None:
@@ -494,7 +532,12 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
thermal=dmgDuringCycle.thermal * dpsFactor,
kinetic=dmgDuringCycle.kinetic * dpsFactor,
explosive=dmgDuringCycle.explosive * dpsFactor)
return dps
if not getSpreadDPS:
return dps
return {'em':dmgDuringCycle.em * dpsFactor,
'therm': dmgDuringCycle.thermal * dpsFactor,
'kin': dmgDuringCycle.kinetic * dpsFactor,
'exp': dmgDuringCycle.explosive * dpsFactor}
def isRemoteRepping(self, ignoreState=False):
repParams = self.getRepAmountParameters(ignoreState=ignoreState)
@@ -613,6 +656,8 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
"""
slot = self.slot
if slot is None:
return False
if fit.getSlotsFree(slot) <= (0 if self.owner != fit else -1):
return False
@@ -651,8 +696,8 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
return False
# Check max group fitted
max = self.getModifiedItemAttr("maxGroupFitted", None)
if max is not None:
max = self.getModifiedItemAttr("maxGroupFitted")
if max:
current = 0 # if self.owner != fit else -1 # Disabled, see #1278
for mod in fit.modules:
if (mod.item and mod.item.groupID == self.item.groupID and
@@ -664,7 +709,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
# Check this only if we're told to do so
if hardpointLimit:
if fit.getHardpointsFree(self.hardpoint) < 1:
if fit.getHardpointsFree(self.hardpoint) < (1 if self.owner != fit else 0):
return False
return True
@@ -680,6 +725,9 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
return False
elif state == FittingModuleState.OVERHEATED and not self.item.isType("overheat"):
return False
# Some destructible effect beacons contain active effects, hardcap those at online state
elif state > FittingModuleState.ONLINE and self.slot == FittingSlot.SYSTEM:
return False
else:
return True
@@ -702,7 +750,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
# Check if the local module is over it's max limit; if it's not, we're fine
maxGroupOnline = self.getModifiedItemAttr("maxGroupOnline", None)
maxGroupActive = self.getModifiedItemAttr("maxGroupActive", None)
if maxGroupOnline is None and maxGroupActive is None and projectedOnto is None:
if not maxGroupOnline and not maxGroupActive and projectedOnto is None:
return True
# Following is applicable only to local modules, we do not want to limit projected
@@ -718,11 +766,11 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
currOnline += 1
if mod.state >= FittingModuleState.ACTIVE:
currActive += 1
if maxGroupOnline is not None and currOnline > maxGroupOnline:
if maxGroupOnline and currOnline > maxGroupOnline:
if maxState is None or maxState > FittingModuleState.OFFLINE:
maxState = FittingModuleState.OFFLINE
break
if maxGroupActive is not None and currActive > maxGroupActive:
if maxGroupActive and currActive > maxGroupActive:
if maxState is None or maxState > FittingModuleState.ONLINE:
maxState = FittingModuleState.ONLINE
return True if maxState is None else maxState
@@ -746,8 +794,8 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
# Check sizes, if 'charge size > module volume' it won't fit
if charge is None:
return True
chargeVolume = charge.volume
moduleCapacity = self.item.capacity
chargeVolume = charge.attributes['volume'].value
moduleCapacity = self.item.attributes['capacity'].value
if chargeVolume is not None and moduleCapacity is not None and chargeVolume > moduleCapacity:
return False
@@ -760,7 +808,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
chargeGroup = charge.groupID
for i in range(5):
itemChargeGroup = self.getModifiedItemAttr('chargeGroup' + str(i), None)
if itemChargeGroup is None:
if not itemChargeGroup:
continue
if itemChargeGroup == chargeGroup:
return True
@@ -771,7 +819,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
validCharges = set()
for i in range(5):
itemChargeGroup = self.getModifiedItemAttr('chargeGroup' + str(i), None)
if itemChargeGroup is not None:
if itemChargeGroup:
g = eos.db.getGroup(int(itemChargeGroup), eager="items.attributes")
if g is None:
continue
@@ -833,7 +881,8 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
def clear(self):
self.__baseVolley = None
self.__baseRRAmount = None
self.__miningyield = None
self.__miningYield = None
self.__miningWaste = None
self.__reloadTime = None
self.__reloadForce = None
self.__chargeCycles = None
@@ -956,6 +1005,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
speed = max(
self.getModifiedItemAttr("speed", 0), # Most weapons
self.getModifiedItemAttr("duration", 0), # Most average modules
self.getModifiedItemAttr("durationHighisGood", 0), # Most average modules
self.getModifiedItemAttr("durationSensorDampeningBurstProjector", 0),
self.getModifiedItemAttr("durationTargetIlluminationBurstProjector", 0),
self.getModifiedItemAttr("durationECMJammerBurstProjector", 0),
@@ -1005,7 +1055,10 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
elif click == "ctrl":
state = FittingModuleState.OFFLINE
else:
state = transitionMap[currState]
try:
state = transitionMap[currState]
except KeyError:
state = min(transitionMap)
# If passive module tries to transition into online and fails,
# put it to passive instead
if not mod.isValidState(state) and currState == FittingModuleState.ONLINE:
@@ -1024,9 +1077,8 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
copy.spoolType = self.spoolType
copy.spoolAmount = self.spoolAmount
copy.projectionRange = self.projectionRange
for x in self.mutators.values():
Mutator(copy, x.attribute, x.value)
copy.rahPatternOverride = self.rahPatternOverride
self._mutaApplyMutators(mutatorClass=MutatorModule, targetInstance=copy)
return copy
@@ -1036,6 +1088,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
spoolType = self.spoolType
spoolAmount = self.spoolAmount
projectionRange = self.projectionRange
rahPatternOverride = self.rahPatternOverride
Module.__init__(self, item, self.baseItem, self.mutaplasmid)
self.state = state
@@ -1044,14 +1097,12 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
self.spoolType = spoolType
self.spoolAmount = spoolAmount
self.projectionRange = projectionRange
for x in self.mutators.values():
Mutator(self, x.attribute, x.value)
self.rahPatternOverride = rahPatternOverride
self._mutaApplyMutators(mutatorClass=MutatorModule)
def __repr__(self):
if self.item:
return "Module(ID={}, name={}) at {}".format(
self.item.ID, self.item.name, hex(id(self))
)
return "Module(ID={}, name={}) at {}".format(self.item.ID, self.item.name, hex(id(self)))
else:
return "EmptyModule() at {}".format(hex(id(self)))

View File

@@ -0,0 +1,105 @@
# ===============================================================================
# Copyright (C) 2010 Diego Duclos
#
# This file is part of eos.
#
# eos is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# eos is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with eos. If not, see <http://www.gnu.org/licenses/>.
# ===============================================================================
import eos.db
from logbook import Logger
pyfalog = Logger(__name__)
class MutaError(Exception):
pass
class MutatedMixin:
@property
def isMutated(self):
return bool(self.baseItemID and self.mutaplasmidID)
@property
def baseItem(self):
return self.__baseItem
@property
def mutaplasmid(self):
return self.__mutaplasmid
@property
def fullName(self):
if self.isMutated:
mutaShortName = self.mutaplasmid.shortName
mutaFullName = self.mutaplasmid.item.customName
# Short name can be unavailable for non-english language
if mutaShortName != mutaFullName:
return f'{self.mutaplasmid.shortName} {self.baseItem.customName}'
return self.item.customName
def _mutaInit(self, baseItem, mutaplasmid):
self.baseItemID = baseItem.ID if baseItem is not None else None
self.mutaplasmidID = mutaplasmid.ID if mutaplasmid is not None else None
if baseItem is not None:
# we're working with a mutated module, need to get abyssal module loaded with the base attributes
# Note: there may be a better way of doing this, such as a metho on this classe to convert(mutaplamid). This
# will require a bit more research though, considering there has never been a need to "swap" out the item of a Module
# before, and there may be assumptions taken with regards to the item never changing (pre-calculated / cached results, for example)
self._item = eos.db.getItemWithBaseItemAttribute(self._item.ID, self.baseItemID)
self.__baseItem = baseItem
self.__mutaplasmid = mutaplasmid
else:
self.__baseItem = None
self.__mutaplasmid = None
def _mutaReconstruct(self):
self.__baseItem = None
self.__mutaplasmid = None
if self.baseItemID:
self._item = eos.db.getItemWithBaseItemAttribute(self.itemID, self.baseItemID)
self.__baseItem = eos.db.getItem(self.baseItemID)
self.__mutaplasmid = eos.db.getMutaplasmid(self.mutaplasmidID)
if self.__baseItem is None:
pyfalog.error("Base Item (id: {0}) does not exist", self.itemID)
raise MutaError
def _mutaLoadMutators(self, mutatorClass):
# Instantiate / remove mutators if this is a mutated module
if self.__baseItem:
for x in self.mutaplasmid.attributes:
attr = self.item.attributes[x.name]
id = attr.ID
if id not in self.mutators: # create the mutator
mutatorClass(self, attr, attr.value)
# @todo: remove attributes that are no longer part of the mutaplasmid.
@property
def _mutaIsInvalid(self):
if self.item.isAbyssal and not self.isMutated:
return True
if self.isMutated and not self.__mutaplasmid:
return True
return False
def _mutaApplyMutators(self, mutatorClass, targetInstance=None):
if targetInstance is None:
targetInstance = self
for x in self.mutators.values():
mutatorClass(targetInstance, x.attribute, x.value)

View File

@@ -27,10 +27,10 @@ from eos.eqBase import EqBase
pyfalog = Logger(__name__)
class Mutator(EqBase):
""" Mutators are the object that represent an attribute override on the module level, in conjunction with
mutaplasmids. Each mutated module, when created, is instantiated with a list of these objects, dictated by the
mutaplasmid that is used on the base module.
class MutatorBase(EqBase):
""" Mutators are the object that represent an attribute override on the eos item level, in conjunction with
mutaplasmids. Each mutated item, when created, is instantiated with a list of these objects, dictated by the
mutaplasmid that is used on the base item.
A note on the different attributes on this object:
* attribute: points to the definition of the attribute from dgmattribs.
@@ -40,13 +40,13 @@ class Mutator(EqBase):
This could probably be cleaned up with smarter relationships, but whatever
"""
def __init__(self, module, attr, value):
# this needs to be above module assignment, as assigning the module will add it to the list and it via
def __init__(self, item, attr, value):
# this needs to be above item assignment, as assigning the item will add it to the list and it via
# relationship and needs this set 4correctly
self.attrID = attr.ID
self.module = module
self.moduleID = module.ID
self.item = item
self.itemID = item.ID
self.__attr = attr
self.build()
@@ -67,30 +67,31 @@ class Mutator(EqBase):
def build(self):
# try...except here to catch orphaned mutators. Pretty rare, only happens so far if hacking the database
# But put it here to remove the module link if it happens, until a better solution can be developed
# But put it here to remove the eos item link if it happens, until a better solution can be developed
try:
# dynamic attribute links to the Mutaplasmids attribute definition for this mutated definition
self.dynamicAttribute = next(a for a in self.module.mutaplasmid.attributes if a.attributeID == self.attrID)
self.dynamicAttribute = next(a for a in self.item.mutaplasmid.attributes if a.attributeID == self.attrID)
# base attribute links to the base ite's attribute for this mutated definition (contains original, base value)
self.baseAttribute = self.module.item.attributes[self.dynamicAttribute.name]
self.baseAttribute = self.item.baseItem.attributes[self.dynamicAttribute.name]
except (KeyboardInterrupt, SystemExit):
raise
except:
self.module = None
self.item = None
@validates("value")
def validator(self, key, val):
""" Validates values as properly falling within the range of the modules' Mutaplasmid """
""" Validates values as properly falling within the range of the items' Mutaplasmid """
if self.baseValue == 0:
return 0
mod = val / self.baseValue
if self.minMod <= mod <= self.maxMod:
# sweet, all good
returnVal = val
else:
# need to fudge the numbers a bit. Go with the value closest to base
if val >= 0:
returnVal = min(self.maxValue, max(self.minValue, val))
else:
returnVal = max(self.maxValue, min(self.minValue, val))
actualMin = min(self.minValue, self.maxValue)
actualMax = max(self.minValue, self.maxValue)
returnVal = min(actualMax, max(actualMin, val))
return returnVal
@property
@@ -98,7 +99,7 @@ class Mutator(EqBase):
# @todo: need to test what happens:
# 1) if an attribute is removed from the EVE database
# 2) if a mutaplasmid does not have the attribute anymore
# 3) if a mutaplasmid does not exist (in eve or on the module's item)
# 3) if a mutaplasmid does not exist (in eve or on the pyfa item's item)
# Can remove invalid ones in a SQLAlchemy collection class... eventually
return self.__attr is None
@@ -116,16 +117,33 @@ class Mutator(EqBase):
@property
def baseValue(self):
return self.baseAttribute.value
try:
return self.baseAttribute.value
except AttributeError:
return 0
@property
def minValue(self):
return self.minMod * self.baseAttribute.value
try:
return self.minMod * self.baseAttribute.value
except AttributeError:
return 0
@property
def maxValue(self):
return self.maxMod * self.baseAttribute.value
try:
return self.maxMod * self.baseAttribute.value
except AttributeError:
return 0
@property
def attribute(self):
return self.__attr
class MutatorModule(MutatorBase):
pass
class MutatorDrone(MutatorBase):
pass

View File

@@ -19,22 +19,241 @@
import math
import re
from collections import OrderedDict
from logbook import Logger
from sqlalchemy.orm import reconstructor
import eos.db
pyfalog = Logger(__name__)
def _t(x):
return x
def _c(x):
return '[' + x + ']'
BUILTINS = OrderedDict([
# 0 is taken by ideal target profile, composed manually in one of TargetProfile methods
(-1, (_t('Uniform (25%)'), 0.25, 0.25, 0.25, 0.25)),
(-2, (_t('Uniform (50%)'), 0.50, 0.50, 0.50, 0.50)),
(-3, (_t('Uniform (75%)'), 0.75, 0.75, 0.75, 0.75)),
(-4, (_t('Uniform (90%)'), 0.90, 0.90, 0.90, 0.90)),
(-5, (_c(_t('T1 Resist')) + _t('Shield'), 0.0, 0.20, 0.40, 0.50)),
(-6, (_c(_t('T1 Resist')) + _t('Armor'), 0.50, 0.45, 0.25, 0.10)),
(-7, (_c(_t('T1 Resist')) + _t('Hull'), 0.33, 0.33, 0.33, 0.33)),
(-8, (_c(_t('T1 Resist')) + _t('Shield (+T2 DCU)'), 0.125, 0.30, 0.475, 0.562)),
(-9, (_c(_t('T1 Resist')) + _t('Armor (+T2 DCU)'), 0.575, 0.532, 0.363, 0.235)),
(-10, (_c(_t('T1 Resist')) + _t('Hull (+T2 DCU)'), 0.598, 0.598, 0.598, 0.598)),
(-11, (_c(_t('T2 Resist')) + _t('Amarr (Shield)'), 0.0, 0.20, 0.70, 0.875)),
(-12, (_c(_t('T2 Resist')) + _t('Amarr (Armor)'), 0.50, 0.35, 0.625, 0.80)),
(-13, (_c(_t('T2 Resist')) + _t('Caldari (Shield)'), 0.20, 0.84, 0.76, 0.60)),
(-14, (_c(_t('T2 Resist')) + _t('Caldari (Armor)'), 0.50, 0.8625, 0.625, 0.10)),
(-15, (_c(_t('T2 Resist')) + _t('Gallente (Shield)'), 0.0, 0.60, 0.85, 0.50)),
(-16, (_c(_t('T2 Resist')) + _t('Gallente (Armor)'), 0.50, 0.675, 0.8375, 0.10)),
(-17, (_c(_t('T2 Resist')) + _t('Minmatar (Shield)'), 0.75, 0.60, 0.40, 0.50)),
(-18, (_c(_t('T2 Resist')) + _t('Minmatar (Armor)'), 0.90, 0.675, 0.25, 0.10)),
(-19, (_c(_t('NPC')) + _c(_t('Asteroid')) + _t('Angel Cartel'), 0.54, 0.42, 0.37, 0.32)),
(-20, (_c(_t('NPC')) + _c(_t('Asteroid')) + _t('Blood Raiders'), 0.34, 0.39, 0.45, 0.52)),
(-21, (_c(_t('NPC')) + _c(_t('Asteroid')) + _t('Guristas'), 0.55, 0.35, 0.3, 0.48)),
(-22, (_c(_t('NPC')) + _c(_t('Asteroid')) + _t('Rogue Drones'), 0.35, 0.38, 0.44, 0.49)),
(-23, (_c(_t('NPC')) + _c(_t('Asteroid')) + _t('Sanshas Nation'), 0.35, 0.4, 0.47, 0.53)),
(-24, (_c(_t('NPC')) + _c(_t('Asteroid')) + _t('Serpentis'), 0.49, 0.38, 0.29, 0.51)),
(-25, (_c(_t('NPC')) + _c(_t('Deadspace')) + _t('Angel Cartel'), 0.59, 0.48, 0.4, 0.32)),
(-26, (_c(_t('NPC')) + _c(_t('Deadspace')) + _t('Blood Raiders'), 0.31, 0.39, 0.47, 0.56)),
(-27, (_c(_t('NPC')) + _c(_t('Deadspace')) + _t('Guristas'), 0.57, 0.39, 0.31, 0.5)),
(-28, (_c(_t('NPC')) + _c(_t('Deadspace')) + _t('Rogue Drones'), 0.42, 0.42, 0.47, 0.49)),
(-29, (_c(_t('NPC')) + _c(_t('Deadspace')) + _t('Sanshas Nation'), 0.31, 0.39, 0.47, 0.56)),
(-30, (_c(_t('NPC')) + _c(_t('Deadspace')) + _t('Serpentis'), 0.49, 0.38, 0.29, 0.56)),
(-31, (_c(_t('NPC')) + _c(_t('Mission')) + _t('Amarr Empire'), 0.34, 0.38, 0.42, 0.46)),
(-32, (_c(_t('NPC')) + _c(_t('Mission')) + _t('Caldari State'), 0.51, 0.38, 0.3, 0.51)),
(-33, (_c(_t('NPC')) + _c(_t('Mission')) + _t('CONCORD'), 0.47, 0.46, 0.47, 0.47)),
(-34, (_c(_t('NPC')) + _c(_t('Mission')) + _t('Gallente Federation'), 0.51, 0.38, 0.31, 0.52)),
(-35, (_c(_t('NPC')) + _c(_t('Mission')) + _t('Khanid'), 0.51, 0.42, 0.36, 0.4)),
(-36, (_c(_t('NPC')) + _c(_t('Mission')) + _t('Minmatar Republic'), 0.51, 0.46, 0.41, 0.35)),
(-37, (_c(_t('NPC')) + _c(_t('Mission')) + _t('Mordus Legion'), 0.32, 0.48, 0.4, 0.62)),
(-38, (_c(_t('NPC')) + _c(_t('Other')) + _t('Sleeper'), 0.61, 0.61, 0.61, 0.61)),
(-39, (_c(_t('NPC')) + _c(_t('Other')) + _t('Sansha Incursion'), 0.65, 0.63, 0.64, 0.65)),
# Anomic Team, source: client data dump
(-40, (_c(_t('NPC')) + _c(_t('Burner')) + _c(_t('Team')) + _t('Enyo'), 0.575, 0.724, 0.862, 0.235, 1020, 37, 39)),
(-41, (_c(_t('NPC')) + _c(_t('Burner')) + _c(_t('Team')) + _t('Navitas'), 0.681, 0.586, 0.522, 0.49, 870, 30, 35)),
(-42, (_c(_t('NPC')) + _c(_t('Burner')) + _c(_t('Team')) + _t('Hawk'), 0.3, 0.86, 0.79, 0.65, 1122, 48, 39)),
(-43, (_c(_t('NPC')) + _c(_t('Burner')) + _c(_t('Team')) + _t('Bantam'), 0.344, 0.475, 0.606, 0.672, 1016, 45, 27)),
(-44, (_c(_t('NPC')) + _c(_t('Burner')) + _c(_t('Team')) + _t('Jaguar'), 0.781, 0.65, 0.475, 0.563, 1400, 42, 31)),
(-45, (_c(_t('NPC')) + _c(_t('Burner')) + _c(_t('Team')) + _t('Burst'), 0.344, 0.475, 0.606, 0.672, 1174, 39, 31)),
(-46, (_c(_t('NPC')) + _c(_t('Burner')) + _c(_t('Team')) + _t('Vengeance'), 0.66, 0.558, 0.745, 0.864, 1050, 37, 40)),
(-47, (_c(_t('NPC')) + _c(_t('Burner')) + _c(_t('Team')) + _t('Inquisitor'), 0.681, 0.586, 0.522, 0.49, 920, 29, 20.5)),
# Anomic Agent & Base, source: client data dump
(-48, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Cruor'), 0.795, 0.734, 0.693, 0.672, 900, 18, 20.5)),
(-49, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Dramiel'), 0.351, 0.481, 0.611, 0.676, 2100, 11, 25)),
(-50, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Daredevil'), 0.685, 0.59, 0.59, 0.433, 1200, 18, 25)),
(-51, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Succubus'), 0.351, 0.481, 0.611, 0.676, 4750, 30, 59)),
(-52, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Worm'), 0.475, 0.58, 0.685, 0.738, 360, 70, 39)),
(-53, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Ashimmu'), 0.8, 0.76, 0.68, 0.7, 500, 120, 137)),
(-54, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Sentinel'), 0.575, 0.448, 0.522, 0.66, 500, 50, 39)),
(-55, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Talos'), 0.681, 0.586, 0.586, 0.426, 150, 125, 266)),
(-56, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Dragonfly'), 0.35, 0.72, 0.70, 0.55, 1200, 15, 35)),
(-57, (_c(_t('NPC')) + _c(_t('Burner')) + _t('Mantis'), 0.60, 0.52, 0.71, 0.71, 900, 25, 35)),
# Source: ticket #2067
(-58, (_c(_t('NPC')) + _c(_t('Invasion')) + _t('Triglavian Entities'), 0.422, 0.367, 0.453, 0.411)),
(-59, (_c(_t('NPC')) + _c(_t('Invasion')) + _t('Amarr EDENCOM Entities'), 0.360, 0.310, 0.441, 0.602)),
(-60, (_c(_t('NPC')) + _c(_t('Invasion')) + _t('Caldari EDENCOM Entities'), 0.303, 0.610, 0.487, 0.401)),
(-61, (_c(_t('NPC')) + _c(_t('Invasion')) + _t('Gallente EDENCOM Entities'), 0.383, 0.414, 0.578, 0.513)),
(-62, (_c(_t('NPC')) + _c(_t('Invasion')) + _t('Minmatar EDENCOM Entities'), 0.620, 0.422, 0.355, 0.399)),
(-63, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Dark Matter All Tiers')) + _t('Drones'), 0.439, 0.522, 0.529, 0.435)),
(-64, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Dark Matter All Tiers')) + _t('Overmind'), 0.643, 0.593, 0.624, 0.639)),
(-65, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Dark Matter All Tiers')) + _t('Seeker'), 0.082, 0.082, 0.082, 0.082)),
(-66, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Dark Matter All Tiers')) + _t('Triglavian'), 0.494, 0.41, 0.464, 0.376)),
(-67, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Dark Matter All Tiers')) + _t('Drifter'), 0.415, 0.415, 0.415, 0.415)),
(-68, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Dark Matter All Tiers')) + _t('Sleeper'), 0.435, 0.435, 0.435, 0.435)),
(-69, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Dark Matter All Tiers')) + _t('All'), 0.508, 0.474, 0.495, 0.488)),
(-70, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T0/T1/T2')) + _t('Drones'), 0.323, 0.522, 0.529, 0.435)),
(-71, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T0/T1/T2')) + _t('Overmind'), 0.542, 0.593, 0.624, 0.639)),
(-72, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T0/T1/T2')) + _t('Seeker'), 0, 0.082, 0.082, 0.082)),
(-73, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T0/T1/T2')) + _t('Triglavian'), 0.356, 0.41, 0.464, 0.376)),
(-74, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T0/T1/T2')) + _t('Drifter'), 0.277, 0.415, 0.415, 0.415)),
(-75, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T0/T1/T2')) + _t('Sleeper'), 0.329, 0.435, 0.435, 0.435)),
(-76, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T0/T1/T2')) + _t('All'), 0.381, 0.474, 0.495, 0.488)),
(-77, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T3 (Some T5 Rooms)')) + _t('Drones'), 0.255, 0.522, 0.529, 0.435)),
(-78, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T3 (Some T5 Rooms)')) + _t('Overmind'), 0.48, 0.593, 0.624, 0.639)),
(-79, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T3 (Some T5 Rooms)')) + _t('Seeker'), 0, 0.082, 0.082, 0.0822)),
(-80, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T3 (Some T5 Rooms)')) + _t('Triglavian'), 0.268, 0.41, 0.464, 0.376)),
(-81, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T3 (Some T5 Rooms)')) + _t('Drifter'), 0.191, 0.415, 0.415, 0.415)),
(-82, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T3 (Some T5 Rooms)')) + _t('Sleeper'), 0.268, 0.435, 0.435, 0.435)),
(-83, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T3 (Some T5 Rooms)')) + _t('All'), 0.308, 0.474, 0.495, 0.488)),
(-84, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T4/T5/T6')) + _t('Drones'), 0.193, 0.522, 0.529, 0.435)),
(-85, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T4/T5/T6')) + _t('Overmind'), 0.423, 0.593, 0.624, 0.639)),
(-86, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T4/T5/T6')) + _t('Seeker'), 0, 0.082, 0.082, 0.082)),
(-87, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T4/T5/T6')) + _t('Triglavian'), 0.206, 0.41, 0.464, 0.376)),
(-88, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T4/T5/T6')) + _t('Drifter'), 0.111, 0.415, 0.415, 0.415)),
(-89, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T4/T5/T6')) + _t('Sleeper'), 0.215, 0.435, 0.435, 0.435)),
(-90, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T4/T5/T6')) + _t('All'), 0.247, 0.474, 0.495, 0.488)),
(-91, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T0/T1/T2')) + _t('Drones'), 0.461, 0.425, 0.541, 0.443)),
(-92, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T0/T1/T2')) + _t('Overmind'), 0.666, 0.489, 0.634, 0.646)),
(-93, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T0/T1/T2')) + _t('Seeker'), 0.084, 0, 0.084, 0.084)),
(-94, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T0/T1/T2')) + _t('Triglavian'), 0.537, 0.269, 0.489, 0.371)),
(-95, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T0/T1/T2')) + _t('Drifter'), 0.43, 0.289, 0.43, 0.43)),
(-96, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T0/T1/T2')) + _t('Sleeper'), 0.512, 0.402, 0.512, 0.512)),
(-97, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T0/T1/T2')) + _t('All'), 0.537, 0.352, 0.512, 0.495)),
(-98, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T3 (Some T5 Rooms)')) + _t('Drones'), 0.461, 0.36, 0.541, 0.443)),
(-99, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T3 (Some T5 Rooms)')) + _t('Overmind'), 0.666, 0.413, 0.634, 0.646)),
(-100, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T3 (Some T5 Rooms)')) + _t('Seeker'), 0.084, 0, 0.084, 0.084)),
(-101, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T3 (Some T5 Rooms)')) + _t('Triglavian'), 0.537, 0.166, 0.489, 0.371)),
(-102, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T3 (Some T5 Rooms)')) + _t('Drifter'), 0.43, 0.201, 0.43, 0.43)),
(-103, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T3 (Some T5 Rooms)')) + _t('Sleeper'), 0.512, 0.337, 0.512, 0.512)),
(-104, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T3 (Some T5 Rooms)')) + _t('All'), 0.537, 0.269, 0.512, 0.495)),
(-105, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T4/T5/T6')) + _t('Drones'), 0.461, 0.305, 0.541, 0.443)),
(-106, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T4/T5/T6')) + _t('Overmind'), 0.666, 0.345, 0.634, 0.646)),
(-107, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T4/T5/T6')) + _t('Seeker'), 0.084, 0, 0.084, 0.084)),
(-108, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T4/T5/T6')) + _t('Triglavian'), 0.537, 0.085, 0.489, 0.371)),
(-109, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T4/T5/T6')) + _t('Drifter'), 0.43, 0.117, 0.43, 0.43)),
(-110, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T4/T5/T6')) + _t('Sleeper'), 0.512, 0.276, 0.512, 0.512)),
(-111, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T4/T5/T6')) + _t('All'), 0.537, 0.201, 0.512, 0.495)),
(-112, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T0/T1/T2')) + _t('Drones'), 0.439, 0.522, 0.417, 0.435)),
(-113, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T0/T1/T2')) + _t('Overmind'), 0.643, 0.593, 0.511, 0.639)),
(-114, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T0/T1/T2')) + _t('Seeker'), 0.082, 0.082, 0, 0.082)),
(-115, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T0/T1/T2')) + _t('Triglavian'), 0.494, 0.41, 0.304, 0.376)),
(-116, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T0/T1/T2')) + _t('Drifter'), 0.415, 0.415, 0.277, 0.415)),
(-117, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T0/T1/T2')) + _t('Sleeper'), 0.435, 0.435, 0.329, 0.435)),
(-118, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T0/T1/T2')) + _t('All'), 0.508, 0.474, 0.359, 0.488)),
(-119, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T3 (Some T5 Rooms)')) + _t('Drones'), 0.439, 0.522, 0.351, 0.435)),
(-120, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T3 (Some T5 Rooms)')) + _t('Overmind'), 0.643, 0.593, 0.435, 0.639)),
(-121, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T3 (Some T5 Rooms)')) + _t('Seeker'), 0.082, 0.082, 0, 0.082)),
(-122, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T3 (Some T5 Rooms)')) + _t('Triglavian'), 0.494, 0.41, 0.198, 0.376)),
(-123, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T3 (Some T5 Rooms)')) + _t('Drifter'), 0.415, 0.415, 0.191, 0.415)),
(-124, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T3 (Some T5 Rooms)')) + _t('Sleeper'), 0.435, 0.435, 0.268, 0.435)),
(-125, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T3 (Some T5 Rooms)')) + _t('All'), 0.508, 0.474, 0.276, 0.488)),
(-126, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T4/T5/T6')) + _t('Drones'), 0.439, 0.522, 0.293, 0.435)),
(-127, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T4/T5/T6')) + _t('Overmind'), 0.643, 0.593, 0.362, 0.639)),
(-128, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T4/T5/T6')) + _t('Seeker'), 0.082, 0.082, 0, 0.082)),
(-129, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T4/T5/T6')) + _t('Triglavian'), 0.494, 0.41, 0.122, 0.376)),
(-130, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T4/T5/T6')) + _t('Drifter'), 0.415, 0.415, 0.111, 0.415)),
(-131, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T4/T5/T6')) + _t('Sleeper'), 0.435, 0.435, 0.215, 0.435)),
(-132, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T4/T5/T6')) + _t('All'), 0.508, 0.474, 0.208, 0.488)),
(-133, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T0/T1/T2')) + _t('Drones'), 0.449, 0.54, 0.549, 0.336)),
(-134, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T0/T1/T2')) + _t('Overmind'), 0.619, 0.574, 0.612, 0.522)),
(-135, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T0/T1/T2')) + _t('Seeker'), 0.085, 0.085, 0.085, 0)),
(-136, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T0/T1/T2')) + _t('Triglavian'), 0.477, 0.4, 0.461, 0.202)),
(-137, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T0/T1/T2')) + _t('Drifter'), 0.437, 0.437, 0.437, 0.295)),
(-138, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T0/T1/T2')) + _t('Sleeper'), 0.435, 0.435, 0.435, 0.329)),
(-139, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T0/T1/T2')) + _t('All'), 0.493, 0.468, 0.492, 0.35)),
(-140, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T3 (Some T5 Rooms)')) + _t('Drones'), 0.449, 0.54, 0.549, 0.264)),
(-141, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T3 (Some T5 Rooms)')) + _t('Overmind'), 0.619, 0.574, 0.612, 0.449)),
(-142, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T3 (Some T5 Rooms)')) + _t('Seeker'), 0.085, 0.085, 0.085, 0)),
(-143, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T3 (Some T5 Rooms)')) + _t('Triglavian'), 0.477, 0.4, 0.461, 0.081)),
(-144, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T3 (Some T5 Rooms)')) + _t('Drifter'), 0.437, 0.437, 0.437, 0.206)),
(-145, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T3 (Some T5 Rooms)')) + _t('Sleeper'), 0.435, 0.435, 0.435, 0.268)),
(-146, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T3 (Some T5 Rooms)')) + _t('All'), 0.493, 0.468, 0.492, 0.264)),
(-147, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T4/T5/T6')) + _t('Drones'), 0.449, 0.54, 0.549, 0.197)),
(-148, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T4/T5/T6')) + _t('Overmind'), 0.619, 0.574, 0.612, 0.379)),
(-149, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T4/T5/T6')) + _t('Seeker'), 0.085, 0.085, 0.085, 0)),
(-150, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T4/T5/T6')) + _t('Triglavian'), 0.477, 0.4, 0.461, 0.034)),
(-151, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T4/T5/T6')) + _t('Drifter'), 0.437, 0.437, 0.437, 0.121)),
(-152, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T4/T5/T6')) + _t('Sleeper'), 0.435, 0.435, 0.435, 0.215)),
(-153, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T4/T5/T6')) + _t('All'), 0.493, 0.468, 0.492, 0.196)),
# Source: ticket #2265
(-154, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Dark Matter All Tiers')) + _t('Concord'), 0.324, 0.318, 0.369, 0.372)),
(-155, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Dark Matter All Tiers')) + _t('Sansha'), 0.137, 0.331, 0.332, 0.322)),
(-156, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Dark Matter All Tiers')) + _t('Angel'), 0.582, 0.508, 0.457, 0.416)),
(-157, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T0/T1/T2')) + _t('Concord'), 0.121, 0.318, 0.369, 0.372)),
(-158, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T0/T1/T2')) + _t('Sansha'), 0.034, 0.331, 0.332, 0.322)),
(-159, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T0/T1/T2')) + _t('Angel'), 0.456, 0.508, 0.457, 0.416)),
(-160, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T3 (Some T5 Rooms)')) + _t('Concord'), 0.025, 0.318, 0.369, 0.372)),
(-161, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T3 (Some T5 Rooms)')) + _t('Sansha'), 0.018, 0.331, 0.332, 0.322)),
(-162, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T3 (Some T5 Rooms)')) + _t('Angel'), 0.373, 0.508, 0.457, 0.416)),
(-163, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T4/T5/T6')) + _t('Concord'), 0.008, 0.318, 0.369, 0.372)),
(-164, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T4/T5/T6')) + _t('Sansha'), 0.009, 0.331, 0.332, 0.322)),
(-165, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Electrical T4/T5/T6')) + _t('Angel'), 0.3, 0.508, 0.457, 0.416)),
(-166, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T0/T1/T2')) + _t('Concord'), 0.324, 0.107, 0.369, 0.372)),
(-167, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T0/T1/T2')) + _t('Sansha'), 0.148, 0.181, 0.329, 0.328)),
(-168, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T0/T1/T2')) + _t('Angel'), 0.587, 0.342, 0.439, 0.39)),
(-169, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T3 (Some T5 Rooms)')) + _t('Concord'), 0.324, 0.016, 0.369, 0.372)),
(-170, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T3 (Some T5 Rooms)')) + _t('Sansha'), 0.148, 0.14, 0.329, 0.328)),
(-171, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T3 (Some T5 Rooms)')) + _t('Angel'), 0.587, 0.241, 0.439, 0.39)),
(-172, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T4/T5/T6')) + _t('Concord'), 0.324, 0.004, 0.369, 0.372)),
(-173, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T4/T5/T6')) + _t('Sansha'), 0.148, 0.106, 0.329, 0.328)),
(-174, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Firestorm T4/T5/T6')) + _t('Angel'), 0.587, 0.172, 0.439, 0.39)),
(-175, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T0/T1/T2')) + _t('Concord'), 0.324, 0.318, 0.18, 0.372)),
(-176, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T0/T1/T2')) + _t('Sansha'), 0.137, 0.331, 0.166, 0.322)),
(-177, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T0/T1/T2')) + _t('Angel'), 0.582, 0.508, 0.295, 0.416)),
(-178, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T3 (Some T5 Rooms)')) + _t('Concord'), 0.324, 0.318, 0.089, 0.372)),
(-179, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T3 (Some T5 Rooms)')) + _t('Sansha'), 0.137, 0.331, 0.108, 0.322)),
(-180, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T3 (Some T5 Rooms)')) + _t('Angel'), 0.582, 0.508, 0.203, 0.416)),
(-181, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T4/T5/T6')) + _t('Concord'), 0.324, 0.318, 0.068, 0.372)),
(-182, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T4/T5/T6')) + _t('Sansha'), 0.137, 0.331, 0.073, 0.322)),
(-183, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Exotic T4/T5/T6')) + _t('Angel'), 0.582, 0.508, 0.14, 0.416)),
(-184, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T0/T1/T2')) + _t('Concord'), 0.324, 0.318, 0.369, 0.203)),
(-185, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T0/T1/T2')) + _t('Sansha'), 0.137, 0.355, 0.352, 0.16)),
(-186, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T0/T1/T2')) + _t('Angel'), 0.59, 0.528, 0.477, 0.286)),
(-187, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T3 (Some T5 Rooms)')) + _t('Concord'), 0.324, 0.318, 0.369, 0.112)),
(-188, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T3 (Some T5 Rooms)')) + _t('Sansha'), 0.137, 0.355, 0.352, 0.05)),
(-189, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T3 (Some T5 Rooms)')) + _t('Angel'), 0.59, 0.528, 0.477, 0.197)),
(-190, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T4/T5/T6')) + _t('Concord'), 0.324, 0.318, 0.369, 0.086)),
(-191, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T4/T5/T6')) + _t('Sansha'), 0.137, 0.355, 0.352, 0)),
(-192, (_c(_t('NPC')) + _c(_t('Abyssal')) + _c(_t('Gamma T4/T5/T6')) + _t('Angel'), 0.59, 0.528, 0.477, 0.126)),
(-193, (_c(_t('NPC')) + _c(_t('Invasion')) + _t('Drifter Entities'), 0.128, 0.375, 0.383, 0.383)),
(-194, (_c(_t('NPC')) + _c(_t('Invasion')) + _t('Rogue Drone Entities'), 0.104, 0.147, 0.147, 0.102)),
(-195, (_c(_t('NPC')) + _c(_t('Invasion')) + _t('Sleeper Entities'), 0.563, 0.563, 0.563, 0.563))])
class TargetProfile:
# also determined import/export order - VERY IMPORTANT
DAMAGE_TYPES = ("em", "thermal", "kinetic", "explosive")
DAMAGE_TYPES = ('em', 'thermal', 'kinetic', 'explosive')
_idealTarget = None
_builtins = None
def __init__(self, *args, **kwargs):
self.builtin = False
self.update(*args, **kwargs)
@reconstructor
def init(self):
self.builtin = False
def update(self, emAmount=0, thermalAmount=0, kineticAmount=0, explosiveAmount=0, maxVelocity=None, signatureRadius=None, radius=None):
self.emAmount = emAmount
self.thermalAmount = thermalAmount
@@ -44,21 +263,44 @@ class TargetProfile:
self._signatureRadius = signatureRadius
self._radius = radius
_idealTarget = None
@classmethod
def getBuiltinList(cls):
if cls._builtins is None:
cls.__generateBuiltins()
return list(cls._builtins.values())
@classmethod
def getBuiltinById(cls, id):
if cls._builtins is None:
cls.__generateBuiltins()
return cls._builtins.get(id)
@classmethod
def __generateBuiltins(cls):
cls._builtins = OrderedDict()
for id, data in BUILTINS.items():
rawName = data[0]
data = data[1:]
profile = TargetProfile(*data)
profile.ID = id
profile.rawName = rawName
profile.builtin = True
cls._builtins[id] = profile
@classmethod
def getIdeal(cls):
if cls._idealTarget is None:
cls._idealTarget = cls(
emAmount=0,
thermalAmount=0,
kineticAmount=0,
explosiveAmount=0,
maxVelocity=0,
signatureRadius=None,
radius=0)
cls._idealTarget.name = 'Ideal Target'
cls._idealTarget.ID = -1
emAmount=0,
thermalAmount=0,
kineticAmount=0,
explosiveAmount=0,
maxVelocity=0,
signatureRadius=None,
radius=0)
cls._idealTarget.rawName = _t('Ideal Target')
cls._idealTarget.ID = 0
cls._idealTarget.builtin = True
return cls._idealTarget
@property
@@ -100,7 +342,7 @@ class TargetProfile:
lookup = {}
current = eos.db.getTargetProfileList()
for pattern in current:
lookup[pattern.name] = pattern
lookup[pattern.rawName] = pattern
for line in lines:
try:
@@ -109,6 +351,8 @@ class TargetProfile:
line = line.split('#', 1)[0] # allows for comments
type, data = line.rsplit('=', 1)
type, data = type.strip(), [d.strip() for d in data.split(',')]
except (KeyboardInterrupt, SystemExit):
raise
except:
pyfalog.warning("Data isn't in correct format, continue to next line.")
continue
@@ -127,6 +371,8 @@ class TargetProfile:
try:
assert 0 <= val <= 100
fields["%sAmount" % cls.DAMAGE_TYPES[index]] = val / 100
except (KeyboardInterrupt, SystemExit):
raise
except:
pyfalog.warning("Caught unhandled exception in import patterns.")
continue
@@ -149,7 +395,7 @@ class TargetProfile:
eos.db.save(pattern)
else:
pattern = TargetProfile(**fields)
pattern.name = name.strip()
pattern.rawName = name.strip()
eos.db.save(pattern)
patterns.append(pattern)
@@ -166,7 +412,7 @@ class TargetProfile:
out += "# TargetProfile = [name],[EM %],[Thermal %],[Kinetic %],[Explosive %],[Max velocity m/s],[Signature radius m],[Radius m]\n\n"
for dp in patterns:
out += cls.EXPORT_FORMAT % (
dp.name,
dp.rawName,
dp.emAmount * 100,
dp.thermalAmount * 100,
dp.kineticAmount * 100,
@@ -178,9 +424,39 @@ class TargetProfile:
return out.strip()
@property
def name(self):
return self.rawName
@property
def fullName(self):
categories, tail = self.__parseRawName()
return '{}{}'.format(''.join('[{}]'.format(c) for c in categories), tail)
@property
def shortName(self):
return self.__parseRawName()[1]
@property
def hierarchy(self):
return self.__parseRawName()[0]
def __parseRawName(self):
hierarchy = []
remainingName = self.rawName.strip() if self.rawName else ''
while True:
start, end = remainingName.find('['), remainingName.find(']')
if start == -1 or end == -1:
return hierarchy, remainingName
splitter = remainingName.find('|')
if splitter != -1 and splitter == start - 1:
return hierarchy, remainingName[1:]
hierarchy.append(remainingName[start + 1:end])
remainingName = remainingName[end + 1:].strip()
def __deepcopy__(self, memo):
p = TargetProfile(
self.emAmount, self.thermalAmount, self.kineticAmount, self.explosiveAmount,
self._maxVelocity, self._signatureRadius, self._radius)
p.name = "%s copy" % self.name
self.emAmount, self.thermalAmount, self.kineticAmount, self.explosiveAmount,
self._maxVelocity, self._signatureRadius, self._radius)
p.rawName = "%s copy" % self.rawName
return p

View File

@@ -0,0 +1,39 @@
"""
Slightly modified version of function taken from here:
https://github.com/pyinstaller/pyinstaller/issues/1905#issuecomment-525221546
"""
import pkgutil
def iterNamespace(name, path):
"""Pyinstaller-compatible namespace iteration.
Yields the name of all modules found at a given Fully-qualified path.
To have it running with pyinstaller, it requires to ensure a hook inject the
"hidden" modules from your plugins folder inside the executable:
- if your plugins are under the ``myappname/pluginfolder`` module
- create a file ``specs/hook-<myappname.pluginfolder>.py``
- content of this file should be:
.. code-block:: python
from PyInstaller.utils.hooks import collect_submodules
hiddenimports = collect_submodules('<myappname.pluginfolder>')
"""
prefix = name + "."
for p in pkgutil.iter_modules(path, prefix):
yield p[1]
# special handling when the package is bundled with PyInstaller 3.5
# See https://github.com/pyinstaller/pyinstaller/issues/1905#issuecomment-445787510
toc = set()
for importer in pkgutil.iter_importers(name.partition(".")[0]):
if hasattr(importer, 'toc'):
toc |= importer.toc
for name in toc:
if name.startswith(prefix):
yield name

27
eos/utils/round.py Normal file
View File

@@ -0,0 +1,27 @@
import math
def roundToPrec(val, prec, nsValue=None):
"""
nsValue: custom value which should be used to determine normalization shift
"""
# We're not rounding integers anyway
# Also make sure that we do not ask to calculate logarithm of zero
if int(val) == val:
return int(val)
roundFactor = int(prec - math.floor(math.log10(abs(val if nsValue is None else nsValue))) - 1)
# But we don't want to round integers
if roundFactor < 0:
roundFactor = 0
# Do actual rounding
val = round(val, roundFactor)
# Make sure numbers with .0 part designating float don't get through
if int(val) == val:
val = int(val)
return val
def roundDec(val, prec):
if int(val) == val:
return int(val)
return round(val, prec)

View File

@@ -22,6 +22,10 @@ from eos.utils.float import floatUnerr
from utils.repr import makeReprStr
def _t(x):
return x
class DmgTypes:
"""Container for damage data stats."""
@@ -46,11 +50,11 @@ class DmgTypes:
# Round for comparison's sake because often damage profiles are
# generated from data which includes float errors
return (
floatUnerr(self.em) == floatUnerr(other.em) and
floatUnerr(self.thermal) == floatUnerr(other.thermal) and
floatUnerr(self.kinetic) == floatUnerr(other.kinetic) and
floatUnerr(self.explosive) == floatUnerr(other.explosive) and
floatUnerr(self.total) == floatUnerr(other.total))
floatUnerr(self.em) == floatUnerr(other.em) and
floatUnerr(self.thermal) == floatUnerr(other.thermal) and
floatUnerr(self.kinetic) == floatUnerr(other.kinetic) and
floatUnerr(self.explosive) == floatUnerr(other.explosive) and
floatUnerr(self.total) == floatUnerr(other.total))
def __bool__(self):
return any((
@@ -110,9 +114,19 @@ class DmgTypes:
return self
def __repr__(self):
spec = ['em', 'thermal', 'kinetic', 'explosive', 'total']
spec = DmgTypes.names()
spec.append('total')
return makeReprStr(self, spec)
@staticmethod
def names(short=None, postProcessor=None):
value = [_t('em'), _t('th'), _t('kin'), _t('exp')] if short else [_t('em'), _t('thermal'), _t('kinetic'), _t('explosive')]
if postProcessor:
value = [postProcessor(x) for x in value]
return value
class RRTypes:
"""Container for tank data stats."""
@@ -136,10 +150,10 @@ class RRTypes:
# Round for comparison's sake because often tanking numbers are
# generated from data which includes float errors
return (
floatUnerr(self.shield) == floatUnerr(other.shield) and
floatUnerr(self.armor) == floatUnerr(other.armor) and
floatUnerr(self.hull) == floatUnerr(other.hull) and
floatUnerr(self.capacitor) == floatUnerr(other.capacitor))
floatUnerr(self.shield) == floatUnerr(other.shield) and
floatUnerr(self.armor) == floatUnerr(other.armor) and
floatUnerr(self.hull) == floatUnerr(other.hull) and
floatUnerr(self.capacitor) == floatUnerr(other.capacitor))
def __bool__(self):
return any((self.shield, self.armor, self.hull, self.capacitor))
@@ -191,5 +205,17 @@ class RRTypes:
return self
def __repr__(self):
spec = ['shield', 'armor', 'hull', 'capacitor']
spec = RRTypes.names(False)
return makeReprStr(self, spec)
@staticmethod
def names(ehpOnly=True, postProcessor=None):
value = ['shield', 'armor', 'hull']
if not ehpOnly:
value.append('capacitor')
if postProcessor:
value = [postProcessor(x) for x in value]
return value

BIN
eve.db

Binary file not shown.

View File

@@ -18,40 +18,9 @@
# =============================================================================
import math
from service.settings import GraphSettings
# Just copy-paste penalization chain calculation code (with some modifications,
# as multipliers arrive in different form) in here to not make actual attribute
# calculations slower than they already are due to extra function calls
def calculateMultiplier(multipliers):
"""
multipliers: dictionary in format:
{stacking group name: [(mult, resist attr ID), (mult, resist attr ID)]}
"""
val = 1
for penalizedMultipliers in multipliers.values():
# A quick explanation of how this works:
# 1: Bonuses and penalties are calculated seperately, so we'll have to filter each of them
l1 = [v[0] for v in penalizedMultipliers if v[0] > 1]
l2 = [v[0] for v in penalizedMultipliers if v[0] < 1]
# 2: The most significant bonuses take the smallest penalty,
# This means we'll have to sort
abssort = lambda _val: -abs(_val - 1)
l1.sort(key=abssort)
l2.sort(key=abssort)
# 3: The first module doesn't get penalized at all
# Any module after the first takes penalties according to:
# 1 + (multiplier - 1) * math.exp(- math.pow(i, 2) / 7.1289)
for l in (l1, l2):
for i in range(len(l)):
bonus = l[i]
val *= 1 + (bonus - 1) * math.exp(- i ** 2 / 7.1289)
return val
def checkLockRange(src, distance):
if distance is None:
return True

View File

@@ -26,3 +26,5 @@ from . import fitCapacitor
from . import fitMobility
from . import fitWarpTime
from . import fitLockTime
# Hidden graphs, available via ctrl-alt-g
from . import fitEcmBurstScanresDamps

View File

@@ -26,11 +26,12 @@ VectorDef = namedtuple('VectorDef', ('lengthHandle', 'lengthUnit', 'angleHandle'
class YDef:
def __init__(self, handle, unit, label, selectorLabel=None):
def __init__(self, handle, unit, label, selectorLabel=None, hidden=False):
self.handle = handle
self.unit = unit
self.label = label
self._selectorLabel = selectorLabel
self.hidden = hidden
@property
def selectorLabel(self):
@@ -53,12 +54,13 @@ class YDef:
class XDef:
def __init__(self, handle, unit, label, mainInput, selectorLabel=None):
def __init__(self, handle, unit, label, mainInput, selectorLabel=None, hidden=False):
self.handle = handle
self.unit = unit
self.label = label
self.mainInput = mainInput
self._selectorLabel = selectorLabel
self.hidden = hidden
@property
def selectorLabel(self):

View File

@@ -29,6 +29,7 @@ from service.const import GraphCacheCleanupReason
class FitGraph(metaclass=ABCMeta):
# UI stuff
hidden = False
views = []
viewMap = {}

View File

@@ -18,31 +18,34 @@
# =============================================================================
from graphs.data.base import FitGraph, XDef, YDef, Input, InputCheckbox
import wx
from graphs.data.base import FitGraph, Input, InputCheckbox, XDef, YDef
from .getter import CapAmount2CapAmountGetter, CapAmount2CapRegenGetter, Time2CapAmountGetter, Time2CapRegenGetter
_t = wx.GetTranslation
class FitCapacitorGraph(FitGraph):
# UI stuff
internalName = 'capacitorGraph'
name = 'Capacitor'
name = _t('Capacitor')
xDefs = [
XDef(handle='time', unit='s', label='Time', mainInput=('time', 's')),
XDef(handle='capAmount', unit='GJ', label='Cap amount', mainInput=('capAmount', '%')),
XDef(handle='capAmount', unit='%', label='Cap amount', mainInput=('capAmount', '%'))]
XDef(handle='time', unit='s', label=_t('Time'), mainInput=('time', 's')),
XDef(handle='capAmount', unit='GJ', label=_t('Cap amount'), mainInput=('capAmount', '%')),
XDef(handle='capAmount', unit='%', label=_t('Cap amount'), mainInput=('capAmount', '%'))]
yDefs = [
YDef(handle='capAmount', unit='GJ', label='Cap amount'),
YDef(handle='capRegen', unit='GJ/s', label='Cap regen')]
YDef(handle='capAmount', unit='GJ', label=_t('Cap amount')),
YDef(handle='capRegen', unit='GJ/s', label=_t('Cap regen'))]
inputs = [
Input(handle='time', unit='s', label='Time', iconID=1392, defaultValue=120, defaultRange=(0, 300), conditions=[
Input(handle='time', unit='s', label=_t('Time'), iconID=1392, defaultValue=120, defaultRange=(0, 300), conditions=[
(('time', 's'), None)]),
Input(handle='capAmount', unit='%', label='Cap amount', iconID=1668, defaultValue=25, defaultRange=(0, 100), conditions=[
Input(handle='capAmount', unit='%', label=_t('Cap amount'), iconID=1668, defaultValue=25, defaultRange=(0, 100), conditions=[
(('capAmount', 'GJ'), None),
(('capAmount', '%'), None)]),
Input(handle='capAmountT0', unit='%', label='Starting cap amount', iconID=1668, defaultValue=100, defaultRange=(0, 100), conditions=[
Input(handle='capAmountT0', unit='%', label=_t('Starting cap amount'), iconID=1668, defaultValue=100, defaultRange=(0, 100), conditions=[
(('time', 's'), None)])]
checkboxes = [InputCheckbox(handle='useCapsim', label='Use capacitor simulator', defaultValue=True, conditions=[
checkboxes = [InputCheckbox(handle='useCapsim', label=_t('Use capacitor simulator'), defaultValue=True, conditions=[
(('time', 's'), ('capAmount', 'GJ'))])]
srcExtraCols = ('CapAmount', 'CapTime')

View File

@@ -50,7 +50,7 @@ class ProjectedDataCache(FitDataCache):
if 'doomsdayAOEWeb' in mod.item.effects:
webMods.append(ModProjData(
mod.getModifiedItemAttr('speedFactor'),
max(0, (mod.maxRange or 0) + mod.getModifiedItemAttr('doomsdayAOERange') - src.getRadius()),
max(0, (mod.maxRange or 0) + mod.getModifiedItemAttr('doomsdayAOERange')),
mod.falloff or 0,
'default',
getResistanceAttrID(modifyingItem=mod, effect=mod.item.effects['doomsdayAOEWeb'])))
@@ -65,7 +65,7 @@ class ProjectedDataCache(FitDataCache):
if 'doomsdayAOEPaint' in mod.item.effects:
tpMods.append(ModProjData(
mod.getModifiedItemAttr('signatureRadiusBonus'),
max(0, (mod.maxRange or 0) + mod.getModifiedItemAttr('doomsdayAOERange') - src.getRadius()),
max(0, (mod.maxRange or 0) + mod.getModifiedItemAttr('doomsdayAOERange')),
mod.falloff or 0,
'default',
getResistanceAttrID(modifyingItem=mod, effect=mod.item.effects['doomsdayAOEPaint'])))

View File

@@ -37,7 +37,14 @@ def getApplicationPerKey(src, tgt, atkSpeed, atkAngle, distance, tgtSpeed, tgtAn
for mod in src.item.activeModulesIter():
if not mod.isDealingDamage():
continue
if mod.hardpoint == FittingHardpoint.TURRET:
if "ChainLightning" in mod.item.effects:
if inLockRange:
applicationMap[mod] = getVortonMult(
mod=mod,
distance=distance,
tgtSpeed=tgtSpeed,
tgtSigRadius=tgtSigRadius)
elif mod.hardpoint == FittingHardpoint.TURRET:
if inLockRange:
applicationMap[mod] = getTurretMult(
mod=mod,
@@ -56,7 +63,6 @@ def getApplicationPerKey(src, tgt, atkSpeed, atkAngle, distance, tgtSpeed, tgtAn
if inLockRange or (mod.charge is not None and 'fofMissileLaunching' in mod.charge.effects):
applicationMap[mod] = getLauncherMult(
mod=mod,
src=src,
distance=distance,
tgtSpeed=tgtSpeed,
tgtSigRadius=tgtSigRadius)
@@ -151,19 +157,39 @@ def getTurretMult(mod, src, tgt, atkSpeed, atkAngle, distance, tgtSpeed, tgtAngl
return mult
def getLauncherMult(mod, src, distance, tgtSpeed, tgtSigRadius):
modRange = mod.maxRange
if modRange is None:
def getVortonMult(mod, distance, tgtSpeed, tgtSigRadius):
rangeFactor = calculateRangeFactor(
mod.getModifiedItemAttr('maxRange'),
0,
distance)
applicationFactor = _calcMissileFactor(
atkEr=mod.getModifiedItemAttr('aoeCloudSize'),
atkEv=mod.getModifiedItemAttr('aoeVelocity'),
atkDrf=mod.getModifiedItemAttr('aoeDamageReductionFactor'),
tgtSpeed=tgtSpeed,
tgtSigRadius=tgtSigRadius)
return rangeFactor * applicationFactor
def getLauncherMult(mod, distance, tgtSpeed, tgtSigRadius):
missileMaxRangeData = mod.missileMaxRangeData
if missileMaxRangeData is None:
return 0
if distance is not None and distance + src.getRadius() > modRange:
return 0
mult = _calcMissileFactor(
# The ranges already consider ship radius
lowerRange, higherRange, higherChance = missileMaxRangeData
if distance is None or distance <= lowerRange:
distanceFactor = 1
elif lowerRange < distance <= higherRange:
distanceFactor = higherChance
else:
distanceFactor = 0
applicationFactor = _calcMissileFactor(
atkEr=mod.getModifiedChargeAttr('aoeCloudSize'),
atkEv=mod.getModifiedChargeAttr('aoeVelocity'),
atkDrf=mod.getModifiedChargeAttr('aoeDamageReductionFactor'),
tgtSpeed=tgtSpeed,
tgtSigRadius=tgtSigRadius)
return mult
return distanceFactor * applicationFactor
def getSmartbombMult(mod, distance):

View File

@@ -18,21 +18,23 @@
# =============================================================================
from graphs.data.base import FitGraph, XDef, YDef, Input, VectorDef
import wx
from graphs.data.base import FitGraph, Input, VectorDef, XDef, YDef
from service.const import GraphCacheCleanupReason
from service.settings import GraphSettings
from .cache import ProjectedDataCache, TimeCache
from .getter import (
Distance2DpsGetter, Distance2VolleyGetter, Distance2InflictedDamageGetter,
Time2DpsGetter, Time2VolleyGetter, Time2InflictedDamageGetter,
TgtSpeed2DpsGetter, TgtSpeed2VolleyGetter, TgtSpeed2InflictedDamageGetter,
TgtSigRadius2DpsGetter, TgtSigRadius2VolleyGetter, TgtSigRadius2InflictedDamageGetter)
from .getter import (Distance2DpsGetter, Distance2InflictedDamageGetter, Distance2VolleyGetter, TgtSigRadius2DpsGetter, TgtSigRadius2InflictedDamageGetter,
TgtSigRadius2VolleyGetter, TgtSpeed2DpsGetter, TgtSpeed2InflictedDamageGetter, TgtSpeed2VolleyGetter, Time2DpsGetter,
Time2InflictedDamageGetter, Time2VolleyGetter)
_t = wx.GetTranslation
class FitDamageStatsGraph(FitGraph):
def __init__(self):
super().__init__()
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._timeCache = TimeCache()
self._projectedCache = ProjectedDataCache()
@@ -51,23 +53,26 @@ class FitDamageStatsGraph(FitGraph):
# UI stuff
internalName = 'dmgStatsGraph'
name = 'Damage Stats'
name = _t('Damage Stats')
xDefs = [
XDef(handle='distance', unit='km', label='Distance', mainInput=('distance', 'km')),
XDef(handle='time', unit='s', label='Time', mainInput=('time', 's')),
XDef(handle='tgtSpeed', unit='m/s', label='Target speed', mainInput=('tgtSpeed', '%')),
XDef(handle='tgtSpeed', unit='%', label='Target speed', mainInput=('tgtSpeed', '%')),
XDef(handle='tgtSigRad', unit='m', label='Target signature radius', mainInput=('tgtSigRad', '%')),
XDef(handle='tgtSigRad', unit='%', label='Target signature radius', mainInput=('tgtSigRad', '%'))]
XDef(handle='distance', unit='km', label=_t('Distance'), mainInput=('distance', 'km')),
XDef(handle='time', unit='s', label=_t('Time'), mainInput=('time', 's')),
XDef(handle='tgtSpeed', unit='m/s', label=_t('Target speed'), mainInput=('tgtSpeed', '%')),
XDef(handle='tgtSpeed', unit='%', label=_t('Target speed'), mainInput=('tgtSpeed', '%')),
XDef(handle='tgtSigRad', unit='m', label=_t('Target signature radius'), mainInput=('tgtSigRad', '%')),
XDef(handle='tgtSigRad', unit='%', label=_t('Target signature radius'), mainInput=('tgtSigRad', '%'))]
inputs = [
Input(handle='distance', unit='km', label='Distance', iconID=1391, defaultValue=None, defaultRange=(0, 100), mainTooltip='Distance between the attacker and the target, as seen in overview (surface-to-surface)', secondaryTooltip='Distance between the attacker and the target, as seen in overview (surface-to-surface)\nWhen set, places the target that far away from the attacker\nWhen not set, attacker\'s weapons always hit the target'),
Input(handle='time', unit='s', label='Time', iconID=1392, defaultValue=None, defaultRange=(0, 80), secondaryTooltip='When set, uses attacker\'s exact damage stats at a given time\nWhen not set, uses attacker\'s damage stats as shown in stats panel of main window'),
Input(handle='tgtSpeed', unit='%', label='Target speed', iconID=1389, defaultValue=100, defaultRange=(0, 100)),
Input(handle='tgtSigRad', unit='%', label='Target signature', iconID=1390, defaultValue=100, defaultRange=(100, 200), conditions=[
Input(handle='distance', unit='km', label=_t('Distance'), iconID=1391, defaultValue=None, defaultRange=(0, 100),
mainTooltip=_t('Distance between the attacker and the target, as seen in overview (surface-to-surface)'),
secondaryTooltip=_t('Distance between the attacker and the target, as seen in overview (surface-to-surface)\nWhen set, places the target that far away from the attacker\nWhen not set, attacker\'s weapons always hit the target')),
Input(handle='time', unit='s', label=_t('Time'), iconID=1392, defaultValue=None, defaultRange=(0, 80),
secondaryTooltip=_t('When set, uses attacker\'s exact damage stats at a given time\nWhen not set, uses attacker\'s damage stats as shown in stats panel of main window')),
Input(handle='tgtSpeed', unit='%', label=_t('Target speed'), iconID=1389, defaultValue=100, defaultRange=(0, 100)),
Input(handle='tgtSigRad', unit='%', label=_t('Target signature'), iconID=1390, defaultValue=100, defaultRange=(100, 200), conditions=[
(('tgtSigRad', 'm'), None),
(('tgtSigRad', '%'), None)])]
srcVectorDef = VectorDef(lengthHandle='atkSpeed', lengthUnit='%', angleHandle='atkAngle', angleUnit='degrees', label='Attacker')
tgtVectorDef = VectorDef(lengthHandle='tgtSpeed', lengthUnit='%', angleHandle='tgtAngle', angleUnit='degrees', label='Target')
srcVectorDef = VectorDef(lengthHandle='atkSpeed', lengthUnit='%', angleHandle='atkAngle', angleUnit='degrees', label=_t('Attacker'))
tgtVectorDef = VectorDef(lengthHandle='tgtSpeed', lengthUnit='%', angleHandle='tgtAngle', angleUnit='degrees', label=_t('Target'))
hasTargets = True
srcExtraCols = ('Dps', 'Volley', 'Speed', 'Radius')
@@ -75,9 +80,9 @@ class FitDamageStatsGraph(FitGraph):
def yDefs(self):
ignoreResists = GraphSettings.getInstance().get('ignoreResists')
return [
YDef(handle='dps', unit=None, label='DPS' if ignoreResists else 'Effective DPS'),
YDef(handle='volley', unit=None, label='Volley' if ignoreResists else 'Effective volley'),
YDef(handle='damage', unit=None, label='Damage inflicted' if ignoreResists else 'Effective damage inflicted')]
YDef(handle='dps', unit=None, label=_t('DPS') if ignoreResists else _t('Effective DPS')),
YDef(handle='volley', unit=None, label=_t('Volley') if ignoreResists else _t('Effective volley')),
YDef(handle='damage', unit=None, label=_t('Damage inflicted') if ignoreResists else _t('Effective damage inflicted'))]
@property
def tgtExtraCols(self):

View File

@@ -0,0 +1,24 @@
# =============================================================================
# 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/>.
# =============================================================================
from .graph import FitEcmBurstScanresDampsGraph
FitEcmBurstScanresDampsGraph.register()

View File

@@ -0,0 +1,117 @@
# =============================================================================
# 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/>.
# =============================================================================
from eos.calc import calculateLockTime
from graphs.data.base import SmoothPointGetter
ECM_BURST_DURATION = 30
DRONE_LOCK_TIME = 2
class TgtScanRes2TgtLockTimeGetter(SmoothPointGetter):
def _getCommonData(self, miscParams, src, tgt):
if miscParams['applyDamps']:
tgtScanResMult = src.item.getDampMultScanRes()
else:
tgtScanResMult = 1
return {
'tgtScanResMult': tgtScanResMult,
'sigRadius': src.item.ship.getModifiedItemAttr('signatureRadius')}
def _calculatePoint(self, x, miscParams, src, tgt, commonData):
scanRes = x
time = calculateLockTime(
srcScanRes=scanRes * commonData['tgtScanResMult'],
tgtSigRadius=commonData['sigRadius'])
return time
class TgtScanRes2TgtLockUptimeGetter(TgtScanRes2TgtLockTimeGetter):
def _calculatePoint(self, *args, **kwargs):
# Assuming you ECM burst every 30 seconds, find out how long you
# will be locked before you burst another time
lockTime = super()._calculatePoint(*args, **kwargs)
lockedTime = max(0, ECM_BURST_DURATION - lockTime)
return lockedTime
class SrcDmgBaseGetter(SmoothPointGetter):
def _getCommonData(self, miscParams, src, tgt):
if miscParams['applyDamps']:
tgtScanResMult = src.item.getDampMultScanRes()
else:
tgtScanResMult = 1
return {
'tgtScanResMult': tgtScanResMult,
'srcSigRadius': src.item.ship.getModifiedItemAttr('signatureRadius'),
'srcEhp': sum(src.item.ehp.values()),
'srcDpsWeapon': src.item.getWeaponDps().total,
'srcDpsDrone': src.item.getDroneDps().total if miscParams['applyDrones'] else 0}
@staticmethod
def _calculateInflictedDamage(srcSigRadius, srcWeaponDps, srcDroneDps, srcEhp, tgtScanRes, tgtDps, uptimeAdjustment, uptimeAmountLimit):
lockTime = calculateLockTime(srcScanRes=tgtScanRes, tgtSigRadius=srcSigRadius)
lockUptime = max(0, ECM_BURST_DURATION - lockTime - uptimeAdjustment)
lockDowntime = ECM_BURST_DURATION - lockUptime
inflictedDmg = 0
remainingEhp = srcEhp
for i in range(int(uptimeAmountLimit)):
timeAliveUnderFire = min(lockUptime, remainingEhp / tgtDps)
timeAlive = lockDowntime + timeAliveUnderFire
remainingEhp -= lockUptime * tgtDps
inflictedDmg += timeAlive * srcWeaponDps
inflictedDmg += max(0, timeAlive - DRONE_LOCK_TIME - 1) * srcDroneDps
if remainingEhp <= 0:
break
return inflictedDmg
class TgtScanRes2SrcDmgGetter(SrcDmgBaseGetter):
def _calculatePoint(self, x, miscParams, src, tgt, commonData):
damage = self._calculateInflictedDamage(
srcSigRadius=commonData['srcSigRadius'],
srcWeaponDps=commonData['srcDpsWeapon'],
srcDroneDps=commonData['srcDpsDrone'],
srcEhp=commonData['srcEhp'],
tgtScanRes=x * commonData['tgtScanResMult'],
tgtDps=miscParams['tgtDps'],
uptimeAdjustment=miscParams['uptimeAdj'],
uptimeAmountLimit=miscParams['uptimeAmtLimit'])
return damage
class TgtDps2SrcDmgGetter(SrcDmgBaseGetter):
def _calculatePoint(self, x, miscParams, src, tgt, commonData):
damage = self._calculateInflictedDamage(
srcSigRadius=commonData['srcSigRadius'],
srcWeaponDps=commonData['srcDpsWeapon'],
srcDroneDps=commonData['srcDpsDrone'],
srcEhp=commonData['srcEhp'],
tgtScanRes=miscParams['tgtScanRes'] * commonData['tgtScanResMult'],
tgtDps=x,
uptimeAdjustment=miscParams['uptimeAdj'],
uptimeAmountLimit=miscParams['uptimeAmtLimit'])
return damage

View File

@@ -0,0 +1,65 @@
# =============================================================================
# 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/>.
# =============================================================================
"""
Disclaimer by kadesh: this graph was made to analyze my ECM burst + damp frig
concept. I do not think it is useful for regular player, so it is disabled.
Enable by setting config.experimentalFeatures = True.
"""
import math
from graphs.data.base import FitGraph, XDef, YDef, Input, InputCheckbox
from .getter import (
TgtScanRes2TgtLockTimeGetter, TgtScanRes2TgtLockUptimeGetter,
TgtScanRes2SrcDmgGetter, TgtDps2SrcDmgGetter)
class FitEcmBurstScanresDampsGraph(FitGraph):
# UI stuff
hidden = True
internalName = 'ecmBurstScanresDamps'
name = 'ECM Burst + Scanres Damps'
xDefs = [
XDef(handle='tgtDps', unit=None, label='Enemy DPS', mainInput=('tgtDps', None)),
XDef(handle='tgtScanRes', unit='mm', label='Enemy scanres', mainInput=('tgtScanRes', 'mm'))]
yDefs = [
YDef(handle='srcDmg', unit=None, label='Damage inflicted'),
YDef(handle='tgtLockTime', unit='s', label='Lock time'),
YDef(handle='tgtLockUptime', unit='s', label='Lock uptime')]
inputs = [
Input(handle='tgtScanRes', unit='mm', label='Enemy scanres', iconID=74, defaultValue=700, defaultRange=(100, 1000)),
Input(handle='tgtDps', unit=None, label='Enemy DPS', iconID=1432, defaultValue=200, defaultRange=(100, 600)),
Input(handle='uptimeAdj', unit='s', label='Uptime adjustment', iconID=1392, defaultValue=1, defaultRange=(None, None), conditions=[(None, ('srcDmg', None))]),
Input(handle='uptimeAmtLimit', unit=None, label='Max amount of uptimes', iconID=1397, defaultValue=3, defaultRange=(None, None), conditions=[(None, ('srcDmg', None))])]
checkboxes = [
InputCheckbox(handle='applyDamps', label='Apply sensor dampeners', defaultValue=True),
InputCheckbox(handle='applyDrones', label='Use drones', defaultValue=True, conditions=[(None, ('srcDmg', None))])]
srcExtraCols = ('SigRadius', 'Damp ScanRes')
# Calculation stuff
_limiters = {'tgtScanRes': lambda src, tgt: (1, math.inf)}
_getters = {
('tgtScanRes', 'tgtLockTime'): TgtScanRes2TgtLockTimeGetter,
('tgtScanRes', 'tgtLockUptime'): TgtScanRes2TgtLockUptimeGetter,
('tgtScanRes', 'srcDmg'): TgtScanRes2SrcDmgGetter,
('tgtDps', 'srcDmg'): TgtDps2SrcDmgGetter}

View File

@@ -20,8 +20,8 @@
import math
from eos.calc import calculateRangeFactor
from graphs.calc import calculateMultiplier, checkLockRange, checkDroneControlRange
from eos.calc import calculateMultiplier, calculateRangeFactor
from graphs.calc import checkLockRange, checkDroneControlRange
from graphs.data.base import SmoothPointGetter
@@ -46,7 +46,7 @@ class Distance2NeutingStrGetter(SmoothPointGetter):
if 'doomsdayAOENeut' in mod.item.effects:
neuts.append((
mod.getModifiedItemAttr('energyNeutralizerAmount') / self.__getDuration(mod) * resonance,
max(0, (mod.maxRange or 0) + mod.getModifiedItemAttr('doomsdayAOERange') - src.getRadius()),
max(0, (mod.maxRange or 0) + mod.getModifiedItemAttr('doomsdayAOERange')),
mod.falloff or 0, False, False))
for drone in src.item.activeDronesIter():
if 'entityEnergyNeutralizerFalloff' in drone.item.effects:
@@ -93,7 +93,7 @@ class Distance2WebbingStrGetter(SmoothPointGetter):
if 'doomsdayAOEWeb' in mod.item.effects:
webs.append((
mod.getModifiedItemAttr('speedFactor') * resonance,
max(0, (mod.maxRange or 0) + mod.getModifiedItemAttr('doomsdayAOERange') - src.getRadius()),
max(0, (mod.maxRange or 0) + mod.getModifiedItemAttr('doomsdayAOERange')),
mod.falloff or 0, 'default', False, False))
for drone in src.item.activeDronesIter():
if 'remoteWebifierEntity' in drone.item.effects:
@@ -142,7 +142,7 @@ class Distance2EcmStrMaxGetter(SmoothPointGetter):
if 'doomsdayAOEECM' in mod.item.effects:
ecms.append((
max(mod.getModifiedItemAttr(a) for a in self.ECM_ATTRS_GENERAL) * resonance,
max(0, (mod.maxRange or 0) + mod.getModifiedItemAttr('doomsdayAOERange') - src.getRadius()),
max(0, (mod.maxRange or 0) + mod.getModifiedItemAttr('doomsdayAOERange')),
mod.falloff or 0, False, False))
for drone in src.item.activeDronesIter():
if 'entityECMFalloff' in drone.item.effects:
@@ -185,7 +185,7 @@ class Distance2DampStrLockRangeGetter(SmoothPointGetter):
if 'doomsdayAOEDamp' in mod.item.effects:
damps.append((
mod.getModifiedItemAttr('maxTargetRangeBonus') * resonance,
max(0, (mod.maxRange or 0) + mod.getModifiedItemAttr('doomsdayAOERange') - src.getRadius()),
max(0, (mod.maxRange or 0) + mod.getModifiedItemAttr('doomsdayAOERange')),
mod.falloff or 0, 'default', False, False))
for drone in src.item.activeDronesIter():
if 'remoteSensorDampEntity' in drone.item.effects:
@@ -226,7 +226,7 @@ class Distance2TdStrOptimalGetter(SmoothPointGetter):
if 'doomsdayAOETrack' in mod.item.effects:
tds.append((
mod.getModifiedItemAttr('maxRangeBonus') * resonance,
max(0, (mod.maxRange or 0) + mod.getModifiedItemAttr('doomsdayAOERange') - src.getRadius()),
max(0, (mod.maxRange or 0) + mod.getModifiedItemAttr('doomsdayAOERange')),
mod.falloff or 0, 'default', False, False))
for drone in src.item.activeDronesIter():
if 'npcEntityWeaponDisruptor' in drone.item.effects:
@@ -269,7 +269,7 @@ class Distance2GdStrRangeGetter(SmoothPointGetter):
gds.append((
mod.getModifiedItemAttr('missileVelocityBonus') * resonance,
mod.getModifiedItemAttr('explosionDelayBonus') * resonance,
max(0, (mod.maxRange or 0) + mod.getModifiedItemAttr('doomsdayAOERange') - src.getRadius()),
max(0, (mod.maxRange or 0) + mod.getModifiedItemAttr('doomsdayAOERange')),
mod.falloff or 0, 'default', False, False))
return {'gds': gds}
@@ -310,7 +310,7 @@ class Distance2TpStrGetter(SmoothPointGetter):
if 'doomsdayAOEPaint' in mod.item.effects:
tps.append((
mod.getModifiedItemAttr('signatureRadiusBonus') * resonance,
max(0, (mod.maxRange or 0) + mod.getModifiedItemAttr('doomsdayAOERange') - src.getRadius()),
max(0, (mod.maxRange or 0) + mod.getModifiedItemAttr('doomsdayAOERange')),
mod.falloff or 0, 'default', False, False))
for drone in src.item.activeDronesIter():
if 'remoteTargetPaintEntity' in drone.item.effects:

View File

@@ -18,30 +18,31 @@
# =============================================================================
import wx
from graphs.data.base import FitGraph, Input, XDef, YDef
from .getter import (
Distance2NeutingStrGetter, Distance2WebbingStrGetter, Distance2EcmStrMaxGetter,
Distance2DampStrLockRangeGetter, Distance2TdStrOptimalGetter, Distance2GdStrRangeGetter,
Distance2TpStrGetter)
from .getter import (Distance2DampStrLockRangeGetter, Distance2EcmStrMaxGetter, Distance2GdStrRangeGetter, Distance2NeutingStrGetter, Distance2TdStrOptimalGetter,
Distance2TpStrGetter, Distance2WebbingStrGetter)
_t = wx.GetTranslation
class FitEwarStatsGraph(FitGraph):
# UI stuff
internalName = 'ewarStatsGraph'
name = 'Electronic Warfare Stats'
xDefs = [XDef(handle='distance', unit='km', label='Distance', mainInput=('distance', 'km'))]
name = _t('Electronic Warfare Stats')
xDefs = [XDef(handle='distance', unit='km', label=_t('Distance'), mainInput=('distance', 'km'))]
yDefs = [
YDef(handle='neutStr', unit=None, label='Cap neutralized per second', selectorLabel='Neuts: cap per second'),
YDef(handle='webStr', unit='%', label='Speed reduction', selectorLabel='Webs: speed reduction'),
YDef(handle='ecmStrMax', unit=None, label='Combined ECM strength', selectorLabel='ECM: combined strength'),
YDef(handle='dampStrLockRange', unit='%', label='Lock range reduction', selectorLabel='Damps: lock range reduction'),
YDef(handle='tdStrOptimal', unit='%', label='Turret optimal range reduction', selectorLabel='TDs: turret optimal range reduction'),
YDef(handle='gdStrRange', unit='%', label='Missile flight range reduction', selectorLabel='GDs: missile flight range reduction'),
YDef(handle='tpStr', unit='%', label='Signature radius increase', selectorLabel='TPs: signature radius increase')]
YDef(handle='neutStr', unit=None, label=_t('Cap neutralized per second'), selectorLabel=_t('Neuts: cap per second')),
YDef(handle='webStr', unit='%', label=_t('Speed reduction'), selectorLabel=_t('Webs: speed reduction')),
YDef(handle='ecmStrMax', unit=None, label=_t('Combined ECM strength'), selectorLabel=_t('ECM: combined strength')),
YDef(handle='dampStrLockRange', unit='%', label=_t('Lock range reduction'), selectorLabel=_t('Damps: lock range reduction')),
YDef(handle='tdStrOptimal', unit='%', label=_t('Turret optimal range reduction'), selectorLabel=_t('TDs: turret optimal range reduction')),
YDef(handle='gdStrRange', unit='%', label=_t('Missile flight range reduction'), selectorLabel=_t('GDs: missile flight range reduction')),
YDef(handle='tpStr', unit='%', label=_t('Signature radius increase'), selectorLabel=_t('TPs: signature radius increase'))]
inputs = [
Input(handle='distance', unit='km', label='Distance', iconID=1391, defaultValue=None, defaultRange=(0, 100)),
Input(handle='resist', unit='%', label='Target resistance', iconID=1393, defaultValue=0, defaultRange=(0, 100))]
Input(handle='distance', unit='km', label=_t('Distance'), iconID=1391, defaultValue=None, defaultRange=(0, 100)),
Input(handle='resist', unit='%', label=_t('Target resistance'), iconID=1393, defaultValue=0, defaultRange=(0, 100))]
# Calculation stuff
_normalizers = {

View File

@@ -20,18 +20,21 @@
import math
import wx
from graphs.data.base import FitGraph, Input, XDef, YDef
from .getter import TgtSigRadius2LockTimeGetter
_t = wx.GetTranslation
class FitLockTimeGraph(FitGraph):
# UI stuff
internalName = 'lockTimeGraph'
name = 'Lock Time'
xDefs = [XDef(handle='tgtSigRad', unit='m', label='Target signature radius', mainInput=('tgtSigRad', 'm'))]
yDefs = [YDef(handle='time', unit='s', label='Lock time')]
inputs = [Input(handle='tgtSigRad', unit='m', label='Target signature', iconID=1390, defaultValue=None, defaultRange=(25, 500))]
name = _t('Lock Time')
xDefs = [XDef(handle='tgtSigRad', unit='m', label=_t('Target signature radius'), mainInput=('tgtSigRad', 'm'))]
yDefs = [YDef(handle='time', unit='s', label=_t('Lock time'))]
inputs = [Input(handle='tgtSigRad', unit='m', label=_t('Target signature'), iconID=1390, defaultValue=None, defaultRange=(25, 500))]
srcExtraCols = ('ScanResolution',)
# Calculation stuff

View File

@@ -23,24 +23,6 @@ import math
from graphs.data.base import SmoothPointGetter
class Time2SpeedGetter(SmoothPointGetter):
def _getCommonData(self, miscParams, src, tgt):
return {
'maxSpeed': src.getMaxVelocity(),
'mass': src.item.ship.getModifiedItemAttr('mass'),
'agility': src.item.ship.getModifiedItemAttr('agility')}
def _calculatePoint(self, x, miscParams, src, tgt, commonData):
time = x
maxSpeed = commonData['maxSpeed']
mass = commonData['mass']
agility = commonData['agility']
# https://wiki.eveuniversity.org/Acceleration#Mathematics_and_formulae
speed = maxSpeed * (1 - math.exp((-time * 1000000) / (agility * mass)))
return speed
class Time2DistanceGetter(SmoothPointGetter):
def _getCommonData(self, miscParams, src, tgt):
@@ -60,3 +42,62 @@ class Time2DistanceGetter(SmoothPointGetter):
distance_0 = maxSpeed * 0 + (maxSpeed * agility * mass * math.exp((-0 * 1000000) / (agility * mass)) / 1000000)
distance = distance_t - distance_0
return distance
class Time2SpeedGetter(SmoothPointGetter):
def _getCommonData(self, miscParams, src, tgt):
return {
'maxSpeed': src.getMaxVelocity(),
'mass': src.item.ship.getModifiedItemAttr('mass'),
'agility': src.item.ship.getModifiedItemAttr('agility')}
def _calculatePoint(self, x, miscParams, src, tgt, commonData):
time = x
maxSpeed = commonData['maxSpeed']
mass = commonData['mass']
agility = commonData['agility']
# https://wiki.eveuniversity.org/Acceleration#Mathematics_and_formulae
speed = maxSpeed * (1 - math.exp((-time * 1000000) / (agility * mass)))
return speed
class Time2MomentumGetter(Time2SpeedGetter):
def _calculatePoint(self, x, miscParams, src, tgt, commonData):
mass = commonData['mass']
speed = Time2SpeedGetter._calculatePoint(
self, x=x, miscParams=miscParams,
src=src, tgt=tgt, commonData=commonData)
momentum = speed * mass
return momentum
class Time2BumpSpeedGetter(Time2SpeedGetter):
def _calculatePoint(self, x, miscParams, src, tgt, commonData):
# S. Santorine, Ship Motion in EVE-Online, p3, Collisions & Bumping section
# https://docs.google.com/document/d/1rwVWjTvzVdPEFETf0vwm649AFb4bgRBaNLpRPaoB03o
# Internally, Santorine's formulas are using millions of kilograms, so we normalize to them here
bumperMass = commonData['mass'] / 10 ** 6
bumperSpeed = Time2SpeedGetter._calculatePoint(
self, x=x, miscParams=miscParams,
src=src, tgt=tgt, commonData=commonData)
tgtMass = miscParams['tgtMass'] / 10 ** 6
tgtSpeed = (2 * bumperSpeed * bumperMass) / (bumperMass + tgtMass)
return tgtSpeed
class Time2BumpDistanceGetter(Time2BumpSpeedGetter):
def _calculatePoint(self, x, miscParams, src, tgt, commonData):
# S. Santorine, Ship Motion in EVE-Online, p3, Collisions & Bumping section
# https://docs.google.com/document/d/1rwVWjTvzVdPEFETf0vwm649AFb4bgRBaNLpRPaoB03o
# Internally, Santorine's formulas are using millions of kilograms, so we normalize to them here
tgtMass = miscParams['tgtMass'] / 10 ** 6
tgtInertia = miscParams['tgtInertia']
tgtSpeed = Time2BumpSpeedGetter._calculatePoint(
self, x=x, miscParams=miscParams,
src=src, tgt=tgt, commonData=commonData)
tgtDistance = tgtSpeed * tgtMass * tgtInertia
return tgtDistance

Some files were not shown because too many files have changed in this diff Show More