Compare commits

...

254 Commits

Author SHA1 Message Date
blitzmann
6dbb61dc12 Merge branch 'master' of https://github.com/pyfa-org/Pyfa 2018-05-19 14:53:07 -04:00
blitzmann
f1d3b68308 Bump version 2018-05-19 14:52:58 -04:00
blitzmann
cec61fc225 Some QOL tweaks to the character editor SSO section 2018-05-18 23:41:37 -04:00
blitzmann
caf31517ca Fix attempt for #1560 2018-05-18 01:18:46 -04:00
blitzmann
22edebbf9b More stuff for #1586 2018-05-18 01:18:31 -04:00
blitzmann
806f17f6ea Test fix for #1586 2018-05-17 00:50:17 -04:00
Ryan Holmes
77a5896997 Merge pull request #1584 from minlexx/fix_browse_esi_fittings
Skip fits for unknown ships in "Browse EVE Fittings"
2018-05-15 17:49:41 -04:00
Alexey Min
cc754342c8 Skip fits for unknown ships in "Browsing EVE Fittings" 2018-05-16 00:36:37 +03:00
blitzmann
eeb700c75d Get back into tox's good graces 2018-05-15 01:41:32 -04:00
blitzmann
9bb83d4574 Merge branch 'master' of https://github.com/pyfa-org/pyfa 2018-05-14 22:08:07 -04:00
blitzmann
8023b2ea29 Bump version 2018-05-14 21:59:25 -04:00
blitzmann
3e1244a27a Fix for not being able to drag fit to tab area (#1569) 2018-05-14 19:26:23 -04:00
blitzmann
51e610830f Fix (not really) an issue with a dead character editor still receiving events 2018-05-14 18:27:53 -04:00
blitzmann
a2877f6b5f Merge branch 'master' of https://github.com/pyfa-org/Pyfa 2018-05-14 01:03:06 -04:00
Ryan Holmes
acf6b3dffd Merge pull request #1549 from flashspys/patch-1
Add Drone Damage Amplifier to defaults
2018-05-14 00:39:44 -04:00
blitzmann
b6a1c4b308 Add support for using own client details (messy code, but it works!) 2018-05-14 00:37:26 -04:00
blitzmann
e29ab817af Add the client details back to the preferences page 2018-05-13 13:27:49 -04:00
blitzmann
86576581cd Break out the phobos dump from the data compilation (dump has to work in py2, whereas compilation requires py3 due to hooking directly in with EOS classes). protip: PyCharm configurations are awesome 2018-05-13 01:17:59 -04:00
blitzmann
50bd46015b fix the osx icon 2018-05-12 23:10:27 -04:00
blitzmann
aa2ffaf1ea Update installer script and create new dist script for windows. todo: find out hwo to invoke PyInstaller from python 2018-05-12 22:49:18 -04:00
blitzmann
5cc6b6c69c Move over all esi stuff to it's own class, which the esi service extends from. Also fix an issue in the EVE fittings browser where deleting a fit didn't actually remove it from the list of fits (due to that list being populated by the return data of ESI, which can be cached). More clean up. 2018-05-12 13:57:14 -04:00
blitzmann
0365f71c00 Move over ESI functionality to be completely separate from esipy 2018-05-12 12:25:34 -04:00
blitzmann
4d666907c9 Start breaking out esipy, first up: getting for login 2018-05-11 23:24:30 -04:00
Felix Wehnert
0466678176 Add Drone Damage Amplifier to defaults 2018-05-09 10:20:11 +02:00
blitzmann
c1e239b9b3 Add a message box informing user of failed ESI initialization 2018-05-08 22:00:35 -04:00
blitzmann
26aaeabd7f Bump version 2018-05-08 21:33:20 -04:00
blitzmann
0e0bc9dfd2 Remove some references to old api stuff (#1547) 2018-05-08 21:06:32 -04:00
blitzmann
8990cbfd6a update readme 2018-05-08 00:01:55 -04:00
blitzmann
8f34c03289 implement a stop gap measure for #1384 2018-05-07 23:20:11 -04:00
blitzmann
c1322a3566 Run through pyfa.io for update checks (allows us to log version usage and provide more flexibility going forward). Falls back to simple github request 2018-05-07 22:55:50 -04:00
blitzmann
5101e2851a replace eve-central with evemarketer 2018-05-07 22:28:40 -04:00
blitzmann
7b7f67ad2e bump version 2018-05-07 22:24:18 -04:00
blitzmann
dbdc566ae4 Only raise exception for status code > 400 (was previously excluding 201 and the like) 2018-05-07 22:09:31 -04:00
blitzmann
f4fd991907 Remove some event handling from the ESI windows to prevent errors from happening (#1501) 2018-05-07 22:00:41 -04:00
blitzmann
42ad74158b Remove the ASCII text from console 2018-05-07 21:55:47 -04:00
blitzmann
c99afa79e1 Merge branch 'test-3' 2018-05-07 21:54:51 -04:00
blitzmann
789c3b869a Add new Exception type 2018-05-06 13:04:02 -04:00
blitzmann
7d8d87662b Merge remote-tracking branch 'origin/py3' into test-3 2018-05-06 12:58:04 -04:00
blitzmann
645a5ced14 check response for a 200 2018-05-06 12:57:48 -04:00
Ryan Holmes
5ed98e8fed Update requirements.txt 2018-05-04 23:30:43 -04:00
Ryan Holmes
f12370389c Merge pull request #1538 from fsufitch/issue/1533
Improve user experience customizing jargon.yaml
2018-05-02 22:36:44 -04:00
blitzmann
fc35d7bb26 Add mutaplasmids to group listing for sql inclusion 2018-05-02 22:32:49 -04:00
Filip Sufitchi
95c1f7bde0 Improve user experience customizing jargon.yaml 2018-05-02 11:02:46 -04:00
blitzmann
d52c249921 Remove client path hashing for secret file (was interfering with OS X build, because it unpacks pyfa into a different, random directory each time it runs) 2018-04-29 12:49:03 -04:00
blitzmann
0b9a50cd8d fix mac spec 2018-04-29 11:56:47 -04:00
blitzmann
25bbfec318 add a few things to requirements.txt 2018-04-29 11:31:33 -04:00
blitzmann
1192c26b8f Add the jargon yaml files to the build process 2018-04-29 11:30:41 -04:00
blitzmann
4a3201ffd4 Merge branch 'py3' of https://github.com/pyfa-org/Pyfa into test-3 2018-04-28 20:35:26 -04:00
blitzmann
406a22100d Merge branch 'esi' into test-3 2018-04-28 20:35:14 -04:00
Ryan Holmes
2547cf70c2 Merge pull request #1515 from Neugeniko/Issue#1511
Fix new sustained reps code for logistic drones.
2018-04-28 20:08:07 -04:00
Ryan Holmes
e83fa4d40b Merge pull request #1513 from fsufitch/issue/1369
Issue/1369
2018-04-07 09:10:44 -04:00
Ryan Holmes
c552cb5e40 Merge pull request #1510 from fsufitch/issue/1509
Use list control length instead of sys.maxsize for platform compatibility
2018-04-07 08:27:40 -04:00
Neugeniko
363904411d Projected Logistic Drones don't have a cap use or rawCycleTime.
Only call the logic for the appropriate module groups.
2018-04-04 16:55:05 +10:00
Filip Sufitchi
49bc9f50bc Add a comprehensive default jargon file 2018-04-02 17:35:58 -04:00
Filip Sufitchi
30501feb99 Raise search limit back up to 3 2018-04-02 17:25:45 -04:00
Filip Sufitchi
efc07e1553 Move minimum search length check to the service, to cover jargon 2018-04-02 17:23:30 -04:00
Filip Sufitchi
9bbeec523e Remove some debug try/except stuff 2018-04-02 16:44:14 -04:00
Filip Sufitchi
943ee517f4 Add jargon service
Jargon service translates search strings for the market
using community-sourced abbreviations. It also creates the
jargon.yaml file in the user's save directory, for
future customization (either manual, or via the UI) and
provides the code hooks to load and update it. The
JargonLoader is capable of hot-loading new jargon.yaml
configurations without restarting Pyfa.
2018-04-02 16:40:39 -04:00
Filip Sufitchi
18bb3bf246 Use list control length instead of sys.maxsize for platform compatibility 2018-03-31 16:28:59 -04:00
blitzmann
117d51caab change back to the py3-dev database 2018-03-25 21:42:51 -04:00
blitzmann
a3e411f225 Merge tag 'v2.0.0b5' into esi
# Conflicts:
#	eos/saveddata/character.py
2018-03-25 19:21:32 -04:00
blitzmann
083af3ebc7 Suppress pre-release notification only if current build is not a pre-release 2018-03-25 13:25:09 -04:00
blitzmann
3fd4d106d9 Merge branch 'master' into test-3
# Conflicts:
#	config.py
#	eos/effects/warpdisruptsphere.py
#	service/update.py
2018-03-25 13:16:07 -04:00
Ryan Holmes
7c376c93a2 Merge pull request #1500 from pyfa-org/release/1.36
Release/1.36
2018-03-25 13:12:46 -04:00
blitzmann
4ef7b645a8 Update effects, icons, db, version bump 2018-03-25 13:11:29 -04:00
blitzmann
cccc7ff2d0 Merge branch 'master' into release/1.36
# Conflicts:
#	service/update.py
2018-03-25 13:03:52 -04:00
blitzmann
b7c45f4c6e Fix the update check logic; only go through the 5 latests updates, and if the check fails (specifically due to our new beta tagging for v2) then simply continue to the next one instead of failing outright 2018-03-25 03:33:02 -04:00
blitzmann
3964658d9a Enable cargohold for structures (#1414) 2018-03-25 03:18:34 -04:00
blitzmann
03212be54a Merge branch 'master' into test-3
# Conflicts:
#	eos/capSim.py
#	eos/effects/structurewarpscrambleblockmwdwithnpceffect.py
#	eos/effects/warpdisruptsphere.py
2018-03-25 03:03:59 -04:00
blitzmann
46098f2127 Merge branch 'master' into release/1.36 2018-03-25 03:00:28 -04:00
Ryan Holmes
2005d0b0b9 Merge pull request #1494 from Neugeniko/Issue#1439-2
Respect 'Factor in Reload' preference for sustainable tank (tidied up).
2018-03-25 02:59:48 -04:00
blitzmann
3293380515 styling fix 2018-03-24 18:18:16 -04:00
Neugeniko
68e5b22fe2 Implement effect for Standup Warp Disruption Burst Projector.
Make active so we can see its effect on capacitor.

(cherry picked from commit 03c2088e6b)
2018-03-24 17:59:49 -04:00
Neugeniko
0f7dd7cc0c Correct the structure guidance enhancers to apply bonuses to relevant charges.
(cherry picked from commit 5c20ee7ade)
2018-03-24 17:59:34 -04:00
Neugeniko
0713251685 Redefine scrambler operation.
If we are projecting effects, modify warpScrambleStatus all the time, and turn off modules requiring 'High Speed Maneuvering' (MWDs) or "Micro Jump Drive Operation" (MJD etc) if a scrambling script is loaded.

(cherry picked from commit 04c62aabea)
2018-03-24 17:59:23 -04:00
Neugeniko
3fda62e320 Redefne structure mounted disruptor operation
If we are projecting effects, modify warpScrambleStatus all the time, and turn off modules requiring 'High Speed Maneuvering' (MWDs) or "Micro Jump Drive Operation" (MJD etc) if a scrambling script is loaded.

(cherry picked from commit 0994158abd)
2018-03-24 17:59:08 -04:00
Neugeniko
5f1c2d6676 Redefine Warp Disruption Field Generators operation
If we are applying effects onto our self, disallowAssistance all the time, and go into low mass/high sig etc mode when no script is loaded.

If we are projecting effects, modify warpScrambleStatus all the time, and turn off modules requiring 'High Speed Maneuvering' (MWDs) or "Micro Jump Drive Operation" (MJD etc) if a scrambling script is loaded.

(cherry picked from commit 77a66a66ea)
2018-03-24 17:58:52 -04:00
blitzmann
c0ba559ca6 Merge branch 'master' into release/1.36 2018-03-24 17:52:22 -04:00
Ryan Holmes
164720db3d Merge pull request #1495 from Neugeniko/Issue#1482-2
Fix cap use for non repeatable modules.
2018-03-22 00:58:16 -04:00
blitzmann
bd90ec4bf0 Don't fail an api update when a skill has been removed (#1473) 2018-03-21 22:12:21 -04:00
blitzmann
d7e24dfa8f Merge branch 'Issue#1486' 2018-03-21 21:51:13 -04:00
Ebag333
2f1ad21392 Calculate DPS for beam type doomsdays. 2018-03-21 21:49:04 -04:00
Ebag333
687d539534 Enable misc info for doomsdays. 2018-03-21 21:48:46 -04:00
Ryan Holmes
bd6793bc19 Merge pull request #1498 from Neugeniko/Issue#1484
Create basic effects files for various modules.
2018-03-21 21:18:08 -04:00
Ryan Holmes
6dd6452c11 Merge pull request #1491 from Neugeniko/Capsimreload
CapSim changes to make reloadtime a variable.
2018-03-21 21:03:40 -04:00
Ryan Holmes
e59a1cd27b Merge pull request #1489 from Neugeniko/Issue#1488
Bomb Launcher Problems Issue#1488
2018-03-21 20:55:05 -04:00
blitzmann
24d14d5e19 Fix attribute that was changed on some items 2018-03-21 20:48:05 -04:00
blitzmann
e0584367c5 fix some update logic for the new beta releases 2018-03-21 20:32:59 -04:00
blitzmann
a2f48e8944 update effects and a few tox fixes 2018-03-21 20:18:00 -04:00
blitzmann
95ba18f8a9 Update database 2018-03-20 23:03:02 -04:00
blitzmann
122d8ef367 Add Monitor effects 2018-03-20 23:01:05 -04:00
blitzmann
281d2ed1f4 Add Praxis effect 2018-03-20 22:55:12 -04:00
blitzmann
48924774f9 Add drake navy issue effects 2018-03-20 22:53:34 -04:00
blitzmann
ee00914a2b Renamed attribute 2018-03-20 22:49:40 -04:00
Ryan Holmes
48d90c1eca Add login method to login request 2018-03-18 00:54:59 -04:00
Ryan Holmes
415b3db809 Merge branch 'esi-noserver' into esi 2018-03-18 00:47:07 -04:00
Ryan Holmes
53451dfaf6 Implement a setting for manual vs server login method 2018-03-18 00:46:53 -04:00
Ryan Holmes
49181dce2b Develop dialog to input SSO Info from pyfa.io, and handle the login. Still need to hook this up to a settings option 2018-03-17 21:08:11 -04:00
Ryan Holmes
199763bcca Separate the server login handler from the sso info handler 2018-03-17 20:31:38 -04:00
Ryan Holmes
033647da61 Move to a central login() method 2018-03-17 20:23:14 -04:00
Ryan Holmes
00e8e9d84a Send pyfa version to server when logging in 2018-03-17 20:18:28 -04:00
Neugeniko
96b701687b pep 8 2018-03-17 13:53:03 +11:00
Neugeniko
e1e90cc23e pep 8 2018-03-17 13:43:19 +11:00
Neugeniko
f41ecae8c8 Add a minimal effects files for mjfg, lance dd's,
reaper dd's, bosonic dd's and gtfo dd's.
2018-03-17 13:23:58 +11:00
Neugeniko
65ec8cf4ee Take into consideration reactivation time for non repeatable modules
like the assault DC and MJD when calculating their 'actual'
cycle time.
2018-03-17 11:44:29 +11:00
Neugeniko
4fc93f7089 Respect 'Factor in Reload' preference for sustainable tank (tidied up). 2018-03-17 09:39:50 +11:00
Ryan Holmes
3ec01a20c2 Bunch of renaming to bring variables up to speed 2018-03-16 18:15:48 -04:00
Ryan Holmes
8a10f0a766 sCrest > sEsi 2018-03-16 18:06:33 -04:00
Ryan Holmes
0db125177f Remove some more cruft 2018-03-16 18:05:49 -04:00
Ryan Holmes
a3f532f62f Remove some cruft, and show EVE fitting when selecting it, rather than double clicking 2018-03-16 18:01:47 -04:00
Neugeniko
234f36c8c0 Zero reload times when reloads are off so we can group modules
with normally different reload times.
2018-03-16 15:57:14 +11:00
Ryan Holmes
2c3957b2db Disable CREST settings in preferences 2018-03-15 23:07:34 -04:00
Ryan Holmes
79deca41c1 Fix skill fetching 2018-03-15 23:05:28 -04:00
Ryan Holmes
570df7f645 Fix a bug with the character selection, and automatically assign a character after logging into eve via character editor 2018-03-15 22:13:56 -04:00
Ryan Holmes
8153b80d05 Work around for sso character not being delete from characters after sso deleted, some other minor tweaks 2018-03-15 19:58:17 -04:00
Ryan Holmes
b74654a5b3 Fix relationships for sso / characters 2018-03-15 02:58:58 -04:00
Ryan Holmes
4c6f68b07e Move sso character stuff to character file (for now while I try to figure out how to properly delete these) 2018-03-14 18:46:49 -04:00
Ryan Holmes
9839efc2dc Fix sso character relationship to not delete sso character (whoops) 2018-03-14 18:40:38 -04:00
Ryan Holmes
9d379d966c Implement working character associations with SSO character 2018-03-14 17:53:48 -04:00
Neugeniko
61086989dc Need to wait speed + reactivation delay between void bomb. 2018-03-14 16:04:15 +11:00
Neugeniko
023ea43611 Divide by numShots not need as we do that below. 2018-03-14 15:39:20 +11:00
Neugeniko
044e032ab3 Allow variable reload time in CapSim. 2018-03-14 13:40:49 +11:00
Neugeniko
da7b95041d Allow variable reload time in CapSim. 2018-03-14 13:40:31 +11:00
blitzmann
f52f39984f Add api view back to character editor (non functioning) 2018-03-12 21:13:18 -04:00
blitzmann
1513b07071 set cargo amoutninput to select all instead of inserstion end 2018-03-11 19:40:38 -04:00
blitzmann
be19b7414a Make a relation between characters and sso chars 2018-03-11 19:39:58 -04:00
blitzmann
75f9a0252a Start working on character to SSO character mapping 2018-03-11 17:38:51 -04:00
blitzmann
5169c35d5c Implement SSO Character delete, Remove some unused character logout code (still need to hook this up with character delete functionality) 2018-03-11 16:50:12 -04:00
blitzmann
bbdf1ee6cc More tweaks / error handling for esi 2018-03-11 15:31:41 -04:00
blitzmann
6c6e8a9972 Do some error handling 2018-03-11 14:47:57 -04:00
Neugeniko
97742b08c8 Attribute disallowRepeatingAction has been renamed to
disallowRepeatingActivation in the database. Make appropriate
modification to instances of this attribute name in code.

(cherry picked from commit ff607e4b03)
2018-03-11 12:19:43 -04:00
Ryan Holmes
8ef4c61fc3 Merge pull request #1481 from Neugeniko/Issue#1480
Attribute disallowRepeatingAction has been renamed.
2018-03-11 12:19:12 -04:00
Neugeniko
ff607e4b03 Attribute disallowRepeatingAction has been renamed to
disallowRepeatingActivation in the database. Make appropriate
modification to instances of this attribute name in code.
2018-03-11 21:38:46 +11:00
blitzmann
30c1ab125c Increase esipy requirement 2018-03-10 17:58:47 -05:00
blitzmann
8276746dad Do custom EsiPY prefix, change sso endpoint to look at pyfa.io instead of localhost 2018-03-10 15:46:23 -05:00
blitzmann
5560ef4d34 Merge branch 'test-3' into esi
# Conflicts:
#	eos/saveddata/character.py
#	service/character.py
#	service/eveapi.py
#	service/pycrest/eve.py
2018-03-10 15:32:21 -05:00
blitzmann
fac31f7254 Merge branch 'py3-requests-proxy' of git://github.com/minlexx/Pyfa into minlexx-py3-requests-proxy 2018-03-10 12:10:54 -05:00
blitzmann
1efcc8d3ef style 2018-03-10 12:09:51 -05:00
Neugeniko
03c2088e6b Implement effect for Standup Warp Disruption Burst Projector.
Make active so we can see its effect on capacitor.
2018-03-10 10:08:22 +11:00
Neugeniko
5c20ee7ade Correct the structure guidance enhancers to apply bonuses to relevant charges. 2018-03-10 00:50:58 +11:00
Neugeniko
04c62aabea Redefine scrambler operation.
If we are projecting effects, modify warpScrambleStatus all the time, and turn off modules requiring 'High Speed Maneuvering' (MWDs) or "Micro Jump Drive Operation" (MJD etc) if a scrambling script is loaded.
2018-03-07 15:29:31 +11:00
Neugeniko
0994158abd Redefne structure mounted disruptor operation
If we are projecting effects, modify warpScrambleStatus all the time, and turn off modules requiring 'High Speed Maneuvering' (MWDs) or "Micro Jump Drive Operation" (MJD etc) if a scrambling script is loaded.
2018-03-07 15:27:53 +11:00
Neugeniko
77a66a66ea Redefine Warp Disruption Field Generators operation
If we are applying effects onto our self, disallowAssistance all the time, and go into low mass/high sig etc mode when no script is loaded.

If we are projecting effects, modify warpScrambleStatus all the time, and turn off modules requiring 'High Speed Maneuvering' (MWDs) or "Micro Jump Drive Operation" (MJD etc) if a scrambling script is loaded.
2018-03-07 15:25:57 +11:00
Alexey Minnekhanov
0b615b578a Bring back proxy settings in network preferences view 2018-03-05 18:02:34 +03:00
Alexey Minnekhanov
53f07db5fe Network: apply proxy settings for requests library call 2018-03-05 16:24:00 +03:00
Alexey Minnekhanov
4d4815d6af Network: change User-Agent header from "urllib" to "requests" with version 2018-03-05 16:16:06 +03:00
blitzmann
b296e0709c Merge branch 'master' into test-3 2018-03-05 00:07:28 -05:00
blitzmann
373e0a390b Don't multiple HP of structure, instead assign it. Fixes #1460 2018-03-04 19:42:28 -05:00
blitzmann
222037ff40 Remove proxy support for the time being 2018-03-04 18:10:40 -05:00
blitzmann
4fac10ccb7 Fix update checker, migrate to requests package for all network stuff. The eveapi package is probably not going to work with caching and whatnot, but pyfa doesn't use it and XML API is EOL anyway 2018-03-04 18:08:34 -05:00
blitzmann
7956ca0409 Merge remote-tracking branch 'origin/master' into test-3
# Conflicts:
#	config.py
2018-03-04 16:47:08 -05:00
Ryan Holmes
5b27d0559f Merge pull request #1464 from pyfa-org/release/v1.35.2
Bump version
2018-03-04 16:44:28 -05:00
blitzmann
85157fdf85 Bump version 2018-03-04 16:44:00 -05:00
blitzmann
caf8e7f2d6 Test something on mac 2018-03-03 17:40:40 -05:00
blitzmann
124bb027ab Use bs4.UnicodeDammit to handle different encodings for file imports (fits only for now, can look into expanding for other file import types) 2018-03-03 14:21:55 -05:00
blitzmann
d5ca14ca52 Remove encoding checks for eft import (#1459) 2018-03-03 00:50:56 -05:00
blitzmann
99982aa547 Merge branch 'master' into test-3 2018-03-02 00:17:54 -05:00
blitzmann
3f4493c0ea Another fix for #1403 2018-03-02 00:17:33 -05:00
blitzmann
cdb604b29f Merge remote-tracking branch 'origin/py3' into test-3 2018-03-01 23:42:29 -05:00
blitzmann
d3b8cebc8a Merge branch 'master' into test-3 2018-03-01 23:42:22 -05:00
Ryan Holmes
7d245660bc Merge pull request #1455 from pyfa-org/issue/1451
Don't fail when trying to project a module that can't be projected.
2018-03-01 23:41:50 -05:00
blitzmann
b1e40427a3 Tox 2018-03-01 23:41:39 -05:00
blitzmann
73925df24b Don't fail when trying to project a module that can't be projected. 2018-03-01 23:24:00 -05:00
Ryan Holmes
04fbd3b548 Merge pull request #1454 from Neugeniko/py3
Resolves issue #1453 with xmlparser.ParseFile(file) wanting bytes.
2018-03-01 20:59:08 -05:00
Neugeniko
d9dd94d6c3 Update character.py
Resolves issue #1453 with xmlparser.ParseFile(file) wanting bytes.
2018-03-02 12:00:11 +11:00
blitzmann
963353a1dc Merge branch 'master' into test-3 2018-03-01 00:40:40 -05:00
Ryan Holmes
3411dcfd91 Merge pull request #1446 from pyfa-org/issue/1445-master
Issue/1445 master
2018-03-01 00:30:24 -05:00
blitzmann
ae6434affb Fix issue with importing EVE API XML 2018-03-01 00:30:08 -05:00
blitzmann
f773e0a935 fix misplaced parentheses
(cherry picked from commit 2a60216a92d39bb200c26bce8573484f54a80dc5)
2018-03-01 00:26:52 -05:00
blitzmann
7ab3ad9e08 Don't delete and recreate skills when updating char sheet. Instead, get each individual skill and change level.
(cherry picked from commit 55c79c1bafaa9a1b159b73a72c6480f7e8d6cb6b)
2018-03-01 00:26:47 -05:00
blitzmann
3f3a82ca6c Merge branch 'py3' of https://github.com/pyfa-org/Pyfa into test-3 2018-02-28 22:02:22 -05:00
blitzmann
e902cc5780 Add event.skip() to fit rename textCtrl focus loss 2018-02-28 22:00:48 -05:00
Ryan Holmes
fcb6952119 Merge pull request #1444 from Neugeniko/py3
Ensure graph frame unbinds its event handlers.
2018-02-27 21:09:10 -05:00
Neugeniko
3eecd57979 Update graphFrame.py 2018-02-28 03:17:37 +11:00
blitzmann
6e73b9fefd Tox 2018-02-27 00:52:15 -05:00
blitzmann
f0b0285f77 Merge branch 'development' of https://github.com/pyfa-org/Pyfa into development 2018-02-27 00:46:10 -05:00
blitzmann
e706a015b3 Merge branch 'issue/1397' into development 2018-02-27 00:45:53 -05:00
blitzmann
a804f9a1ad Fix working on missing skills context menu (#1412)
(cherry picked from commit 96c13c344a)
2018-02-27 00:45:46 -05:00
blitzmann
9e1b7dbb87 Add ability to drag a module from fitting window to projected pane 2018-02-27 00:44:42 -05:00
blitzmann
551ffe9ed3 Quick fix for #1430 2018-02-27 00:23:29 -05:00
blitzmann
cbe1ce5bcd Merge branch 'master' into test-3 2018-02-27 00:16:45 -05:00
Ryan Holmes
917afd5067 Merge pull request #1441 from pyfa-org/issue/1440
Convert sec status to float on character import (#1440)
2018-02-27 00:15:59 -05:00
blitzmann
c3fb9231a4 Convert sec status to float on character import (#1440) 2018-02-27 00:15:20 -05:00
blitzmann
2ed2f8e262 Fix issue with losing wrapper for FittingView when closing 2018-02-25 12:04:57 -05:00
blitzmann
390d90ac47 Merge branch 'issue/1434' into test-3 (#1434)
# Conflicts:
#	gui/builtinViews/fittingView.py
2018-02-25 01:57:36 -05:00
blitzmann
4c877e7a5a re-enable the tab preview 2018-02-25 01:45:43 -05:00
blitzmann
972fe433f7 remove vestigial file 2018-02-25 00:11:13 -05:00
blitzmann
ecaf6f96a9 guage: respect animation setting (#1434) 2018-02-25 00:10:17 -05:00
Ryan Holmes
8541e2a869 Merge pull request #1438 from pyfa-org/issue/1433
DNA export should respect Export Charges setting
2018-02-24 23:50:21 -05:00
blitzmann
48a2963472 Merge branch 'issue/1433' into test-3 2018-02-24 23:40:26 -05:00
blitzmann
f245a02372 DNA export should respect Export Charges setting 2018-02-24 23:37:53 -05:00
blitzmann
1c16343b46 Merge branch 'master' into test-3 2018-02-24 22:24:16 -05:00
Ryan Holmes
d0f66f2d16 Merge pull request #1437 from pyfa-org/issue/1432
Always open imports in a new tab (#1432)
2018-02-24 22:23:13 -05:00
blitzmann
df0c6ed269 Merge branch 'bug/amount-modal' into test-3 2018-02-24 22:21:02 -05:00
Ryan Holmes
9a4e26a5be Merge pull request #1436 from pyfa-org/bug/amount-modal
Handle getting value for Change Amount differently for projected fits (#1435)
2018-02-24 22:20:51 -05:00
blitzmann
2b87f91279 tox fix 2018-02-24 22:20:24 -05:00
blitzmann
9b48b61b9b Merge branch 'bug/amount-modal' into test-3 2018-02-24 22:17:22 -05:00
blitzmann
57e67b3699 Handle getting value for Change Amount differently for projected fits 2018-02-24 22:17:04 -05:00
blitzmann
15e60c3d24 Always open imports in a new tab (#1432) 2018-02-24 21:11:53 -05:00
blitzmann
ebf07db6c6 Partial fix for #1430 2018-02-24 19:01:54 -05:00
blitzmann
1559767201 Ensure that the event is propagated when the graph window handles FitChanged (#1430) 2018-02-24 18:21:32 -05:00
blitzmann
6e5e52df37 See #1429, version file not currently applied 2018-02-24 17:13:50 -05:00
blitzmann
e9289c102b Fix issue in notebook tab preview (this feature hasn't been enabled for a while, but we may want to turn it back on eventually. As such, ensure that we don't run into any problems here) 2018-02-24 13:53:56 -05:00
blitzmann
81d61d7e29 lol context management not available in 4.0.0b2, and can't update to >4.0 until #1421 is addressed 2018-02-24 13:49:17 -05:00
blitzmann
9ab4ec2d4f Convert AutoBufferedPaintDC > BufferedPaintDC (and use spiffy new context management). See #1418 2018-02-24 13:45:36 -05:00
blitzmann
565332dfcd Set wx requirement to v4b2, see #1421 2018-02-24 13:28:45 -05:00
blitzmann
f94fbd740a Merge branch 'bug/amount-modal' into test-3
# Conflicts:
#	gui/builtinContextMenus/amount.py
2018-02-24 02:55:40 -05:00
Ryan Holmes
a2719ec2f7 Merge pull request #1427 from pyfa-org/bug/amount-modal
Redesign amount modal
2018-02-24 02:54:38 -05:00
blitzmann
1da12cb18e tox fix 2018-02-24 02:54:23 -05:00
blitzmann
e0cddcd061 Redesign amount modal, basing style off wx.TextEntryDialog (but not a wx.TextEntryDialog, because there are possibilities to do a few different things in this modal) 2018-02-24 02:47:13 -05:00
blitzmann
78f632a4f6 Merge remote-tracking branch 'origin/master' into test-3 2018-02-24 02:03:29 -05:00
Ryan Holmes
52754535a0 Merge pull request #1426 from pyfa-org/issue/1416
Issue/1416
2018-02-24 02:02:57 -05:00
blitzmann
a9db667c9c Do the same treatment to Target Resists as Damage Patterns 2018-02-24 02:01:23 -05:00
blitzmann
f442632fbc Fix importing damage profiles which included overwriting existing ones and then performing a delete on one of them. #1416 2018-02-24 01:49:09 -05:00
blitzmann
362086cc83 Fix type in database default for damage types 2018-02-24 01:47:05 -05:00
blitzmann
73d59569ff Merge remote-tracking branch 'origin/master' into test-3
# Conflicts:
#	gui/characterEditor.py
2018-02-23 23:28:33 -05:00
Ryan Holmes
cb4fadf84c Merge pull request #1425 from pyfa-org/issue/1419
Migrate the Save Character As dialog tot he same dialog / validator a…
2018-02-23 23:26:44 -05:00
blitzmann
e85d144928 Migrate the Save Character As dialog tot he same dialog / validator as the Edit Character Name (#1419) 2018-02-23 23:25:11 -05:00
blitzmann
07099f4057 Fix working on missing skills context menu (#1412)
(cherry picked from commit 96c13c344a)
2018-02-23 01:17:18 -05:00
blitzmann
96c13c344a Fix working on missing skills context menu (#1412) 2018-02-23 01:15:57 -05:00
blitzmann
8f42822d9e Add roman package as a requirement, and remove the roman module from pyfa 2018-02-23 01:04:31 -05:00
blitzmann
c5ba79cfbb Merge branch 'master' into test-3
# Conflicts:
#	.gitignore
2018-02-23 01:02:51 -05:00
Ryan Holmes
f69f76856a Merge pull request #1391 from BlueShanks/acceptRomanNumerals
Character Editor: accept skill plans in Roman numerals too
2018-02-23 01:01:57 -05:00
Ryan Holmes
3a23d820cc Merge branch 'master' into acceptRomanNumerals 2018-02-23 01:01:07 -05:00
blitzmann
c917d22db5 tox fixes for the roman library to prevent travis from yelling at us 2018-02-23 01:00:43 -05:00
blitzmann
c3f8b102fa Add the romans library to pyfa itself to avoid having to modify the binaries 2018-02-23 00:57:26 -05:00
blitzmann
5e566db47d Fix attribute override export / import (#1420) 2018-02-23 00:36:52 -05:00
blitzmann
5b2c5907ed Skills needed export (#1412): Need to encode the string before we gzip it 2018-02-23 00:23:45 -05:00
blitzmann
dd78a41171 Fix HTML export (more encoding stuff). Also fix old bug in which unicode fit names don't display correctly in the webbrowser. #1411 2018-02-23 00:13:14 -05:00
blitzmann
201fb4e241 Remove all encoding bits from fit importing. Might not work 100% of the time, but as 2.0 gets introduced to the community, we'll get reports of possible problems. #1410 2018-02-23 00:00:53 -05:00
blitzmann
8abd25fe40 Use a scolled window in preference pages 2018-02-19 23:32:44 -05:00
blitzmann
d59c897921 Fix image list in preferences dialog 2018-02-19 01:20:57 -05:00
Ryan Holmes
cb89d13d9f Merge pull request #1409 from pyfa-org/bug/1403
Remove some defunct items from market service
2018-02-19 00:16:42 -05:00
blitzmann
a1aa78adc0 Remove some defunct items from market service 2018-02-18 23:28:17 -05:00
blitzmann
4bbbd33917 Return 0 instead of -1 when there is no charge on a weapon. It was changed from None to -1 during the py3 conversion (as None can no longer be compared against non-None types). -1 was throwing the capSim off by quite a bit. See #1405 2018-02-18 22:55:31 -05:00
blitzmann
42ccc53166 bit of clean up for reload setting 2018-02-18 20:35:46 -05:00
blitzmann
35ad21da38 Add pattern for commit hashes to update dialog 2018-02-17 16:27:12 -05:00
BlueShanks
bd0fcbef3a Fixed style issues which were throwing errors
The code was failing an automatic test because of style issues, which are hopefully fixed now
2018-02-17 18:26:00 +10:00
BlueShanks
c5c673e360 moved a line back to where it was
results in a net smaller diff
2018-02-16 00:46:51 +10:00
BlueShanks
5cd21da7b1 Character Editor: accept skill plans in Roman numerals too 2018-02-16 00:19:56 +10:00
blitzmann
fe1c4cc4d4 Make a callback for token refresh, not sure how I'm gonna handle this one yet 2018-02-12 21:43:23 -05:00
blitzmann
ed649dd4c7 Merge branch 'test-3' into esi 2018-02-11 23:23:30 -05:00
blitzmann
33eccaa374 Merge branch 'test-3' into esi
# Conflicts:
#	requirements.txt
2018-02-11 23:17:50 -05:00
blitzmann
7b0f672f04 Get encrypted refresh tokens working 2018-02-09 18:25:53 -05:00
blitzmann
33bf5234d0 Goodbye eveapi! You have served us well all these years!
Start stripping XML API stuff and implement ESI skill fetching.
2018-02-09 00:42:59 -05:00
blitzmann
cb392e7e5f Rename a few things 2018-02-08 01:52:38 -05:00
blitzmann
dfba033190 Implement posting fit to EVE server 2018-02-08 01:50:52 -05:00
blitzmann
5fbe623ae6 get fit deletion working. Need to be aware that we are still using cached fit listing... 2018-02-08 01:38:24 -05:00
blitzmann
e025bff99b Get ESI fitting import working completely. Use a file cache for EsiApp to prevent long startup times (possibly cache for about a week or so, and start in background if it needs to be gotten again?) 2018-02-08 01:24:40 -05:00
blitzmann
e77dddc15b More work on getting fittings form a character via ESI. It's starting to be come a pain working with this client with having to background it until it initializes. Thinking about rolling my own, considering we only need a few calls and not a whole package. 2018-02-07 02:07:42 -05:00
blitzmann
eea8019593 Get fitting browser to show up with proper characters (still not functional). Start testing ways to store the esipy app on the service (it can take a few seconds to initialize due to network calls) 2018-02-07 01:21:22 -05:00
blitzmann
c7360c8cc3 Get logging into EVE working and SSO characters saving, along with client hashes being stored alongside characters. 2018-02-07 00:44:37 -05:00
blitzmann
2376148380 Start ripping CREST stuff out, add new SSOCharacter stuff in. Long process ahead. 2018-02-02 02:05:49 -05:00
blitzmann
9e8166c13d Add ESI service skeleton, and my customer ESI Proxy class 2018-01-11 20:54:33 -05:00
153 changed files with 2560 additions and 3223 deletions

2
.gitignore vendored
View File

@@ -120,3 +120,5 @@ eos.iml
gitversion
.version
/.version
*.swp

View File

@@ -15,12 +15,8 @@ The latest version along with release notes can always be found on the project's
Windows and OS X users are supplied self-contained builds of pyfa on the [latest releases](https://github.com/pyfa-org/Pyfa/releases/latest) page. An `.exe` installer is also available for Windows builds. Linux users can run pyfa using their distribution's Python interpreter. There is no official self-contained package for Linux, however, there are a number of third-party packages available through distribution-specific repositories.
#### OS X
There are two different distributives for OS X: `-mac` and `-mac-deprecated`.
* `-mac`: based on wxPython 3.0.2.0 and has updated libraries. This is the recommended build.
* `-mac-deprecated`: utilizes older binaries running on wxPython 2.8; because of this, some features are not available (currently CREST support and Attribute Overrides). Additionally, as development happens primarily on wxPython 3.0, a few GUI bugs may pop up as `-mac-deprecated` is not actively tested. However, due to some general issues with wxPython 3.0, especially on some newer OS X versions, `-mac-deprecated` is still offered for those that need it.
There is also a [Homebrew](http://brew.sh) option for installing pyfa on OS X. Please note this is maintained by a third-party and is not tested by pyfa developers. Simply fire up in terminal:
Apart from the official release, there is also a [Homebrew](http://brew.sh) option for installing pyfa on OS X. Please note this is maintained by a third-party and is not tested by pyfa developers. Simply fire up in terminal:
```
$ brew install Caskroom/cask/pyfa
```

View File

@@ -3,6 +3,9 @@ import sys
from logbook import CRITICAL, DEBUG, ERROR, FingersCrossedHandler, INFO, Logger, NestedSetup, NullHandler, \
StreamHandler, TimedRotatingFileHandler, WARNING
import hashlib
from cryptography.fernet import Fernet
pyfalog = Logger(__name__)
@@ -20,12 +23,15 @@ debug = False
saveInRoot = False
# Version data
version = "2.0.0b3"
tag = "git"
expansionName = "YC120.2"
expansionVersion = "1.2"
version = "2.0.2"
tag = "Stable"
expansionName = "YC120.3"
expansionVersion = "1.8"
evemonMinVersion = "4081"
minItemSearchLength = 3
pyfaPath = None
savePath = None
saveDB = None
@@ -33,6 +39,10 @@ gameDB = None
logPath = None
loggingLevel = None
logging_setup = None
cipher = None
clientHash = None
ESI_CACHE = 'esi_cache'
LOGLEVEL_MAP = {
"critical": CRITICAL,
@@ -43,6 +53,10 @@ LOGLEVEL_MAP = {
}
def getClientSecret():
return clientHash
def isFrozen():
if hasattr(sys, 'frozen'):
return True
@@ -85,6 +99,8 @@ def defPaths(customSavePath=None):
global gameDB
global saveInRoot
global logPath
global cipher
global clientHash
pyfalog.debug("Configuring Pyfa")
@@ -109,12 +125,22 @@ def defPaths(customSavePath=None):
__createDirs(savePath)
secret_file = os.path.join(savePath, ".secret")
if not os.path.exists(secret_file):
with open(secret_file, "wb") as _file:
_file.write(Fernet.generate_key())
with open(secret_file, 'rb') as fp:
key = fp.read()
clientHash = hashlib.sha3_256(key).hexdigest()
cipher = Fernet(key)
# if isFrozen():
# os.environ["REQUESTS_CA_BUNDLE"] = os.path.join(pyfaPath, "cacert.pem")
# os.environ["SSL_CERT_FILE"] = os.path.join(pyfaPath, "cacert.pem")
# The database where we store all the fits etc
saveDB = os.path.join(savePath, "saveddata-py3-dev.db")
saveDB = os.path.join(savePath, "saveddata.db")
# The database where the static EVE data from the datadump is kept.
# This is not the standard sqlite datadump but a modified version created by eos

View File

@@ -19,6 +19,7 @@ added_files = [
('../../imgs/icons/*.png', 'imgs/icons'),
('../../imgs/renders/*.png', 'imgs/renders'),
('../../dist_assets/win/pyfa.ico', '.'),
('../../service/jargon/*.yaml', 'service/jargon'),
(requests.certs.where(), '.'), # is this needed anymore?
('../../eve.db', '.'),
('../../README.md', '.'),
@@ -29,6 +30,8 @@ added_files = [
import_these = []
icon = os.path.join(os.getcwd(), "dist_assets", "mac", "pyfa.icns")
# 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):
@@ -64,10 +67,10 @@ exe = EXE(pyz,
upx=True,
runtime_tmpdir=None,
console=False ,
icon='dist_assets/mac/pyfa.icns',
icon=icon,
)
app = BUNDLE(exe,
name='pyfa.app',
icon=None,
icon=icon,
bundle_identifier=None)

46
dist_assets/win/dist.py Normal file
View File

@@ -0,0 +1,46 @@
# helper script to zip up pyinstaller distribution and create installer file
import os.path
from subprocess import call
import zipfile
def zipdir(path, zip):
for root, dirs, files in os.walk(path):
for file in files:
zip.write(os.path.join(root, file))
config = {}
exec(compile(open("config.py").read(), "config.py", 'exec'), config)
iscc = "C:\Program Files (x86)\Inno Setup 5\ISCC.exe" # inno script location via wine
print("Creating archive")
source = os.path.join(os.getcwd(), "dist", "pyfa")
fileName = "pyfa-{}-{}-{}-win".format(
config['version'],
config['expansionName'].lower(),
config['expansionVersion']
)
archive = zipfile.ZipFile(os.path.join(os.getcwd(), "dist", fileName + ".zip"), 'w', compression=zipfile.ZIP_DEFLATED)
zipdir(source, archive)
archive.close()
print("Compiling EXE")
expansion = "%s %s" % (config['expansionName'], config['expansionVersion']),
call([
iscc,
os.path.join(os.getcwd(), "dist_assets", "win", "pyfa-setup.iss"),
"/dMyAppVersion=%s" % (config['version']),
"/dMyAppExpansion=%s" % (expansion),
"/dMyAppDir=%s" % source,
"/dMyOutputDir=%s" % os.path.join(os.getcwd(), "dist"),
"/dMyOutputFile=%s" % fileName]) # stdout=devnull, stderr=devnull
print("Done")

View File

@@ -19,7 +19,8 @@
#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 VersionFlag 16
#define MajorVersionFlag 2
#define MinorVersionFlag 0
#ifndef MyOutputFile
#define MyOutputFile LowerCase(StringChange(MyAppName+'-'+MyAppVersion+'-'+MyAppExpansion+'-win-wx3', " ", "-"))
@@ -138,15 +139,19 @@ var
V: Integer;
iResultCode: Integer;
sUnInstallString: string;
iOldVersion: Cardinal;
iOldVersionMajor: Cardinal;
iOldVersionMinor: Cardinal;
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
begin
RegQueryDWordValue(HKEY_LOCAL_MACHINE,
'Software\Microsoft\Windows\CurrentVersion\Uninstall\{3DA39096-C08D-49CD-90E0-1D177F32C8AA}_is1',
'MinorVersion', iOldVersion);
if iOldVersion < {#VersionFlag} then // If old version with old structure is installed.
'MajorVersion', iOldVersionMajor);
RegQueryDWordValue(HKEY_LOCAL_MACHINE,
'Software\Microsoft\Windows\CurrentVersion\Uninstall\{3DA39096-C08D-49CD-90E0-1D177F32C8AA}_is1',
'MinorVersion', iOldVersionMinor);
if (iOldVersionMajor < {#MajorVersionFlag}) or ((iOldVersionMajor = {#MajorVersionFlag}) and (iOldVersionMinor < {#MinorVersionFlag})) then // If old version with old structure is installed.
begin
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

View File

@@ -18,6 +18,7 @@ added_files = [
('../../imgs/gui/*.gif', 'imgs/gui'),
('../../imgs/icons/*.png', 'imgs/icons'),
('../../imgs/renders/*.png', 'imgs/renders'),
('../../service/jargon/*.yaml', 'service/jargon'),
('../../dist_assets/win/pyfa.ico', '.'),
(requests.certs.where(), '.'), # is this needed anymore?
('../../eve.db', '.'),
@@ -78,4 +79,5 @@ coll = COLLECT(
upx=True,
name='pyfa',
icon='dist_assets/win/pyfa.ico',
)
)

View File

@@ -0,0 +1,45 @@
# UTF-8
#
# For more details about fixed file info 'ffi' see:
# http://msdn.microsoft.com/en-us/library/ms646997.aspx
VSVersionInfo(
ffi=FixedFileInfo(
# filevers and prodvers should be always a tuple with four items: (1, 2, 3, 4)
# Set not needed items to zero 0.
filevers=(1, 15, 1, 0),
prodvers=(1, 15, 1, 0),
# Contains a bitmask that specifies the valid bits 'flags'r
mask=0x3f,
# Contains a bitmask that specifies the Boolean attributes of the file.
flags=0x0,
# The operating system for which this file was designed.
# 0x4 - NT and there is no need to change it.
OS=0x40004,
# The general type of file.
# 0x1 - the file is an application.
fileType=0x1,
# The function of the file.
# 0x0 - the function is not defined for this fileType
subtype=0x0,
# Creation date and time stamp.
date=(0, 0)
),
kids=[
StringFileInfo(
[
StringTable(
u'040904E4',
[StringStruct(u'LegalCopyright', u''),
StringStruct(u'InternalName', u'pyfa.exe'),
StringStruct(u'FileVersion', u'1.15.1.0'),
StringStruct(u'CompanyName', u''),
StringStruct(u'OriginalFilename', u'pyfa.exe'),
StringStruct(u'ProductVersion', u'1.15.1.0'),
StringStruct(u'FileDescription', u'Python fitting assistant'),
StringStruct(u'LegalTrademarks', u''),
StringStruct(u'Comments', u''),
StringStruct(u'ProductName', u'pyfa')])
]),
VarFileInfo([VarStruct(u'Translation', [1033, 1252])])
]
)

View File

@@ -72,7 +72,7 @@ class CapSimulator(object):
disable_period = False
# Loop over modules, clearing clipSize if applicable, and group modules based on attributes
for (duration, capNeed, clipSize, disableStagger) in self.modules:
for (duration, capNeed, clipSize, disableStagger, reloadTime) in self.modules:
if self.scale:
duration, capNeed = self.scale_activation(duration, capNeed)
@@ -80,24 +80,25 @@ class CapSimulator(object):
# a cap booster module.
if not self.reload and capNeed > 0:
clipSize = 0
reloadTime = 0
# Group modules based on their properties
if (duration, capNeed, clipSize, disableStagger) in mods:
mods[(duration, capNeed, clipSize, disableStagger)] += 1
if (duration, capNeed, clipSize, disableStagger, reloadTime) in mods:
mods[(duration, capNeed, clipSize, disableStagger, reloadTime)] += 1
else:
mods[(duration, capNeed, clipSize, disableStagger)] = 1
mods[(duration, capNeed, clipSize, disableStagger, reloadTime)] = 1
# Loop over grouped modules, configure staggering and push to the simulation state
for (duration, capNeed, clipSize, disableStagger), amount in mods.items():
for (duration, capNeed, clipSize, disableStagger, reloadTime), amount in mods.items():
if self.stagger and not disableStagger:
if clipSize == 0:
duration = int(duration / amount)
else:
stagger_amount = (duration * clipSize + 10000) / (amount * clipSize)
stagger_amount = (duration * clipSize + reloadTime) / (amount * clipSize)
for i in range(1, amount):
heapq.heappush(self.state,
[i * stagger_amount, duration,
capNeed, 0, clipSize])
capNeed, 0, clipSize, reloadTime])
else:
capNeed *= amount
@@ -107,7 +108,7 @@ class CapSimulator(object):
if clipSize:
disable_period = True
heapq.heappush(self.state, [0, duration, capNeed, 0, clipSize])
heapq.heappush(self.state, [0, duration, capNeed, 0, clipSize, reloadTime])
if disable_period:
self.period = self.t_max
@@ -144,7 +145,7 @@ class CapSimulator(object):
while 1:
activation = pop(state)
t_now, duration, capNeed, shot, clipSize = activation
t_now, duration, capNeed, shot, clipSize, reloadTime = activation
if t_now >= t_max:
break
@@ -180,7 +181,7 @@ class CapSimulator(object):
if clipSize:
if shot % clipSize == 0:
shot = 0
t_now += 10000 # include reload time
t_now += reloadTime # include reload time
activation[0] = t_now
activation[3] = shot

View File

@@ -76,7 +76,7 @@ sd_lock = threading.RLock()
# 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, \
from eos.db.saveddata import booster, cargo, character, damagePattern, databaseRepair, drone, fighter, fit, implant, implantSet, loadDefaultDatabaseValues, \
miscData, module, override, price, queries, skill, targetResists, user
# Import queries

View File

@@ -12,7 +12,6 @@ __all__ = [
"miscData",
"targetResists",
"override",
"crest",
"implantSet",
"loadDefaultDatabaseValues"
]

View File

@@ -17,24 +17,21 @@
# along with eos. If not, see <http://www.gnu.org/licenses/>.
# ===============================================================================
from sqlalchemy import Table, Column, Integer, ForeignKey, String, DateTime, Float
from sqlalchemy import Table, Column, Integer, ForeignKey, String, DateTime, Float, UniqueConstraint
from sqlalchemy.orm import relation, mapper
import datetime
from eos.db import saveddata_meta
from eos.db.saveddata.implant import charImplants_table
from eos.effectHandlerHelpers import HandledImplantBoosterList
from eos.effectHandlerHelpers import HandledImplantBoosterList, HandledSsoCharacterList
from eos.saveddata.implant import Implant
from eos.saveddata.user import User
from eos.saveddata.character import Character, Skill
from eos.saveddata.ssocharacter import SsoCharacter
characters_table = Table("characters", saveddata_meta,
Column("ID", Integer, primary_key=True),
Column("name", String, nullable=False),
Column("apiID", Integer),
Column("apiKey", String),
Column("defaultChar", Integer),
Column("chars", String, nullable=True),
Column("defaultLevel", Integer, nullable=True),
Column("alphaCloneID", Integer, nullable=True),
Column("ownerID", ForeignKey("users.ID"), nullable=True),
@@ -42,6 +39,28 @@ characters_table = Table("characters", saveddata_meta,
Column("created", DateTime, nullable=True, default=datetime.datetime.now),
Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now))
sso_table = Table("ssoCharacter", saveddata_meta,
Column("ID", Integer, primary_key=True),
Column("client", String, nullable=False),
Column("characterID", Integer, nullable=False),
Column("characterName", String, nullable=False),
Column("refreshToken", String, nullable=False),
Column("accessToken", String, nullable=False),
Column("accessTokenExpires", DateTime, nullable=False),
Column("created", DateTime, nullable=True, default=datetime.datetime.now),
Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now),
UniqueConstraint('client', 'characterID', name='uix_client_characterID'),
UniqueConstraint('client', 'characterName', name='uix_client_characterName')
)
sso_character_map_table = Table("ssoCharacterMap", saveddata_meta,
Column("characterID", ForeignKey("characters.ID"), primary_key=True),
Column("ssoCharacterID", ForeignKey("ssoCharacter.ID"), primary_key=True),
)
mapper(SsoCharacter, sso_table)
mapper(Character, characters_table,
properties={
"_Character__alphaCloneID": characters_table.c.alphaCloneID,
@@ -63,5 +82,10 @@ mapper(Character, characters_table,
primaryjoin=charImplants_table.c.charID == characters_table.c.ID,
secondaryjoin=charImplants_table.c.implantID == Implant.ID,
secondary=charImplants_table),
"_Character__ssoCharacters" : relation(
SsoCharacter,
collection_class=HandledSsoCharacterList,
backref='characters',
secondary=sso_character_map_table)
}
)

View File

@@ -1,34 +0,0 @@
# ===============================================================================
# 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 Table, Column, Integer, String, DateTime
from sqlalchemy.orm import mapper
import datetime
from eos.db import saveddata_meta
from eos.saveddata.crestchar import CrestChar
crest_table = Table("crest", saveddata_meta,
Column("ID", Integer, primary_key=True),
Column("name", String, nullable=False, unique=True),
Column("refresh_token", String, nullable=False),
# These records aren't updated. Instead, they are dropped and created, hence we don't have a modified field
Column("created", DateTime, nullable=True, default=datetime.datetime.now))
mapper(CrestChar, crest_table)

View File

@@ -68,7 +68,7 @@ class DefaultDatabaseValues(object):
["[Hybrid Charges]Uranium", "0", "38.4", "57.6", "0"],
["[Missiles]Mjolnir", "100", "0", "0", "0"], ["[Missiles]Inferno", "0", "100", "0", "0"],
["[Missiles]Scourge", "0", "0", "100", "0"], ["[Missiles]Nova", "0", "0", "0", "100"],
["[Missiles][Structure) Standup Missile", "100", "100", "100", "100"],
["[Missiles][Structure] Standup Missile", "100", "100", "100", "100"],
["[Projectile Ammo][T2] Tremor", "0", "0", "24", "40"],
["[Projectile Ammo][T2] Quake", "0", "0", "40", "72"],
["[Projectile Ammo][T2] Hail", "0", "0", "26.4", "96.8"],

View File

@@ -27,7 +27,7 @@ from eos.db.saveddata.fit import projectedFits_table
from eos.db.util import processEager, processWhere
from eos.saveddata.price import Price
from eos.saveddata.user import User
from eos.saveddata.crestchar import CrestChar
from eos.saveddata.ssocharacter import SsoCharacter
from eos.saveddata.damagePattern import DamagePattern
from eos.saveddata.targetResists import TargetResists
from eos.saveddata.character import Character
@@ -467,29 +467,28 @@ def getProjectedFits(fitID):
raise TypeError("Need integer as argument")
def getCrestCharacters(eager=None):
def getSsoCharacters(clientHash, eager=None):
eager = processEager(eager)
with sd_lock:
characters = saveddata_session.query(CrestChar).options(*eager).all()
characters = saveddata_session.query(SsoCharacter).filter(SsoCharacter.client == clientHash).options(*eager).all()
return characters
@cachedQuery(CrestChar, 1, "lookfor")
def getCrestCharacter(lookfor, eager=None):
@cachedQuery(SsoCharacter, 1, "lookfor", "clientHash")
def getSsoCharacter(lookfor, clientHash, eager=None):
filter = SsoCharacter.client == clientHash
if isinstance(lookfor, int):
if eager is None:
with sd_lock:
character = saveddata_session.query(CrestChar).get(lookfor)
else:
eager = processEager(eager)
with sd_lock:
character = saveddata_session.query(CrestChar).options(*eager).filter(CrestChar.ID == lookfor).first()
filter = and_(filter, SsoCharacter.ID == lookfor)
elif isinstance(lookfor, str):
eager = processEager(eager)
with sd_lock:
character = saveddata_session.query(CrestChar).options(*eager).filter(CrestChar.name == lookfor).first()
filter = and_(filter, SsoCharacter.characterName == lookfor)
else:
raise TypeError("Need integer or string as argument")
eager = processEager(eager)
with sd_lock:
character = saveddata_session.query(SsoCharacter).options(*eager).filter(filter).first()
return character
@@ -544,7 +543,7 @@ def commit():
try:
saveddata_session.commit()
saveddata_session.flush()
except Exception:
except Exception as ex:
saveddata_session.rollback()
exc_info = sys.exc_info()
raise exc_info[0](exc_info[1]).with_traceback(exc_info[2])

View File

@@ -205,6 +205,16 @@ class HandledImplantBoosterList(HandledList):
HandledList.append(self, thing)
class HandledSsoCharacterList(list):
def append(self, character):
old = next((x for x in self if x.client == character.client), None)
if old is not None:
pyfalog.warning("Removing SSO Character with same hash: {}".format(repr(old)))
list.remove(self, old)
list.append(self, character)
class HandledProjectedModList(HandledList):
def append(self, proj):
if proj.isInvalid:

View File

@@ -1,7 +1,7 @@
# ammoInfluenceCapNeed
#
# Used by:
# Items from category: Charge (478 of 925)
# Items from category: Charge (478 of 928)
type = "passive"

View File

@@ -1,7 +1,7 @@
# ammoInfluenceRange
#
# Used by:
# Items from category: Charge (572 of 925)
# Items from category: Charge (572 of 928)
type = "passive"

View File

@@ -3,6 +3,7 @@
# Used by:
# Charges from group: Festival Charges (23 of 23)
# Charges from group: Interdiction Probe (2 of 2)
# Charges from group: Structure Festival Charges (3 of 3)
type = "passive"

View File

@@ -1,7 +1,7 @@
# boosterArmorHpPenalty
#
# Used by:
# Implants from group: Booster (12 of 54)
# Implants from group: Booster (12 of 62)
type = "boosterSideEffect"
# User-friendly name for the side effect

View File

@@ -1,7 +1,7 @@
# boosterMaxVelocityPenalty
#
# Used by:
# Implants from group: Booster (12 of 54)
# Implants from group: Booster (12 of 62)
type = "boosterSideEffect"
# User-friendly name for the side effect

View File

@@ -1,7 +1,7 @@
# boosterShieldCapacityPenalty
#
# Used by:
# Implants from group: Booster (12 of 54)
# Implants from group: Booster (12 of 62)
type = "boosterSideEffect"
# User-friendly name for the side effect

View File

@@ -0,0 +1,9 @@
# doomsdayAOEBubble
#
# Used by:
# Module: Warp Disruption Burst Projector
type = "projected", "active"
def handler(fit, module, context):
return

View File

@@ -0,0 +1,9 @@
# doomsdayBeamDOT
#
# Used by:
# Modules named like: Lance (4 of 4)
type = "active"
def handler(fit, src, context):
pass

View File

@@ -0,0 +1,9 @@
# doomsdayConeDOT
#
# Used by:
# Module: Bosonic Field Generator
type = "active"
def handler(fit, src, context):
pass

View File

@@ -0,0 +1,9 @@
# doomsdayHOG
#
# Used by:
# Module: Gravitational Transportation Field Oscillator
type = "active"
def handler(fit, src, context):
pass

View File

@@ -0,0 +1,9 @@
# doomsdaySlash
#
# Used by:
# Modules named like: Reaper (4 of 4)
type = "active"
def handler(fit, src, context):
pass

View File

@@ -0,0 +1,20 @@
# eliteBonusFlagCruiserAllResistances1
#
# Used by:
# Ship: Monitor
type = "passive"
def handler(fit, src, context):
fit.ship.boostItemAttr("explosiveDamageResonance", src.getModifiedItemAttr("eliteBonusFlagCruisers1"), skill="Flag Cruisers")
fit.ship.boostItemAttr("shieldKineticDamageResonance", src.getModifiedItemAttr("eliteBonusFlagCruisers1"), skill="Flag Cruisers")
fit.ship.boostItemAttr("shieldExplosiveDamageResonance", src.getModifiedItemAttr("eliteBonusFlagCruisers1"), skill="Flag Cruisers")
fit.ship.boostItemAttr("armorThermalDamageResonance", src.getModifiedItemAttr("eliteBonusFlagCruisers1"), skill="Flag Cruisers")
fit.ship.boostItemAttr("thermalDamageResonance", src.getModifiedItemAttr("eliteBonusFlagCruisers1"), skill="Flag Cruisers")
fit.ship.boostItemAttr("shieldEmDamageResonance", src.getModifiedItemAttr("eliteBonusFlagCruisers1"), skill="Flag Cruisers")
fit.ship.boostItemAttr("armorExplosiveDamageResonance", src.getModifiedItemAttr("eliteBonusFlagCruisers1"), skill="Flag Cruisers")
fit.ship.boostItemAttr("armorEmDamageResonance", src.getModifiedItemAttr("eliteBonusFlagCruisers1"), skill="Flag Cruisers")
fit.ship.boostItemAttr("shieldThermalDamageResonance", src.getModifiedItemAttr("eliteBonusFlagCruisers1"), skill="Flag Cruisers")
fit.ship.boostItemAttr("kineticDamageResonance", src.getModifiedItemAttr("eliteBonusFlagCruisers1"), skill="Flag Cruisers")
fit.ship.boostItemAttr("armorKineticDamageResonance", src.getModifiedItemAttr("eliteBonusFlagCruisers1"), skill="Flag Cruisers")
fit.ship.boostItemAttr("emDamageResonance", src.getModifiedItemAttr("eliteBonusFlagCruisers1"), skill="Flag Cruisers")

View File

@@ -9,4 +9,4 @@ type = "passive"
def handler(fit, container, context):
level = container.level if "skill" in context else 1
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("High Speed Maneuvering"),
"capacitorNeed", container.getModifiedItemAttr("capacitorNeedMultiplier") * level)
"capacitorNeed", container.getModifiedItemAttr("capNeedBonus") * level)

View File

@@ -0,0 +1,9 @@
# microJumpPortalDrive
#
# Used by:
# Module: Micro Jump Field Generator
type = "active"
def handler(fit, src, context):
pass

View File

@@ -1,7 +1,7 @@
# missileDMGBonus
#
# Used by:
# Modules from group: Ballistic Control system (18 of 18)
# Modules from group: Ballistic Control system (20 of 20)
type = "passive"

View File

@@ -1,7 +1,7 @@
# missileLauncherSpeedMultiplier
#
# Used by:
# Modules from group: Ballistic Control system (18 of 18)
# Modules from group: Ballistic Control system (20 of 20)
type = "passive"

View File

@@ -2,6 +2,7 @@
#
# Used by:
# Modules from group: Drone Damage Modules (11 of 11)
# Modules named like: C3 'Hivaa Saitsuo' Ballistic Control System (2 of 2)
type = "passive"

View File

@@ -2,7 +2,7 @@
#
# Used by:
# Modules from group: Missile Launcher Torpedo (22 of 22)
# Items from market group: Ship Equipment > Turrets & Bays (429 of 861)
# Items from market group: Ship Equipment > Turrets & Bays (429 of 863)
# Module: Interdiction Sphere Launcher I
type = "overheat"

View File

@@ -1,7 +1,7 @@
# overloadSelfDamageBonus
#
# Used by:
# Modules from group: Energy Weapon (101 of 213)
# Modules from group: Energy Weapon (101 of 214)
# Modules from group: Hybrid Weapon (105 of 221)
# Modules from group: Projectile Weapon (99 of 165)
type = "overheat"

View File

@@ -0,0 +1,17 @@
# roleBonusFlagCruiserModuleFittingReduction
#
# Used by:
# Ship: Monitor
type = "passive"
def handler(fit, src, context):
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name in ("Propulsion Module", "Micro Jump Drive"),
"power", src.getModifiedItemAttr("flagCruiserFittingBonusPropMods"))
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name in ("Propulsion Module", "Micro Jump Drive"),
"cpu", src.getModifiedItemAttr("flagCruiserFittingBonusPropMods"))
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name in ("Target Painter", "Scan Probe Launcher"),
"cpu", src.getModifiedItemAttr("flagCruiserFittingBonusPainterProbes"))
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name in ("Target Painter", "Scan Probe Launcher"),
"power", src.getModifiedItemAttr("flagCruiserFittingBonusPainterProbes"))

View File

@@ -0,0 +1,12 @@
# roleBonusFlagCruiserTargetPainterModifications
#
# Used by:
# Ship: Monitor
type = "passive"
def handler(fit, src, context):
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Target Painter", "signatureRadiusBonus",
src.getModifiedItemAttr("targetPainterStrengthModifierFlagCruisers"))
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Target Painter", "maxRange",
src.getModifiedItemAttr("targetPainterRangeModifierFlagCruisers"))

View File

@@ -4,4 +4,4 @@ runTime = "early"
def handler(fit, src, context):
fit.ship.multiplyItemAttr("structureFullPowerStateHitpointMultiplier", src.getModifiedItemAttr("serviceModuleFullPowerStateHitpointMultiplier"))
fit.ship.forceItemAttr("structureFullPowerStateHitpointMultiplier", src.getModifiedItemAttr("serviceModuleFullPowerStateHitpointMultiplier"))

View File

@@ -0,0 +1,9 @@
# shipAgilityBonusGC1
#
# Used by:
# Ship: Monitor
type = "passive"
def handler(fit, src, context):
fit.ship.boostItemAttr("agility", src.getModifiedItemAttr("shipBonusGC"), skill="Gallente Cruiser")

View File

@@ -0,0 +1,9 @@
# shipArmorHitPointsAC1
#
# Used by:
# Ship: Monitor
type = "passive"
def handler(fit, src, context):
fit.ship.boostItemAttr("armorHP", src.getModifiedItemAttr("shipBonusAC"), skill="Amarr Cruiser")

View File

@@ -2,6 +2,7 @@
#
# Used by:
# Ship: Gnosis
# Ship: Praxis
# Ship: Sunesis
# Ship: Taipan
# Ship: Velator

View File

@@ -3,6 +3,7 @@
# Used by:
# Variations of ship: Procurer (2 of 2)
# Ship: Gnosis
# Ship: Praxis
# Ship: Sunesis
# Ship: Taipan
# Ship: Velator

View File

@@ -0,0 +1,10 @@
# shipBonusHeavyAssaultMissileEMDamageCBC2
#
# Used by:
# Ship: Drake Navy Issue
type = "passive"
def handler(fit, src, context):
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Heavy Assault Missiles"), "emDamage",
src.getModifiedItemAttr("shipBonusCBC2"), skill="Caldari Battlecruiser")

View File

@@ -0,0 +1,10 @@
# shipBonusHeavyAssaultMissileExplosiveDamageCBC2
#
# Used by:
# Ship: Drake Navy Issue
type = "passive"
def handler(fit, src, context):
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Heavy Assault Missiles"), "explosiveDamage",
src.getModifiedItemAttr("shipBonusCBC2"), skill="Caldari Battlecruiser")

View File

@@ -0,0 +1,10 @@
# shipBonusHeavyAssaultMissileKineticDamageCBC2
#
# Used by:
# Ship: Drake Navy Issue
type = "passive"
def handler(fit, src, context):
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Heavy Assault Missiles"),
"kineticDamage", src.getModifiedItemAttr("shipBonusCBC2"), skill="Caldari Battlecruiser")

View File

@@ -0,0 +1,10 @@
# shipBonusHeavyAssaultMissileThermalDamageCBC2
#
# Used by:
# Ship: Drake Navy Issue
type = "passive"
def handler(fit, src, context):
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Heavy Assault Missiles"),
"thermalDamage", src.getModifiedItemAttr("shipBonusCBC2"), skill="Caldari Battlecruiser")

View File

@@ -0,0 +1,10 @@
# shipBonusHeavyMissileEMDamageCBC2
#
# Used by:
# Ship: Drake Navy Issue
type = "passive"
def handler(fit, src, context):
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Heavy Missiles"),
"emDamage", src.getModifiedItemAttr("shipBonusCBC2"), skill="Caldari Battlecruiser")

View File

@@ -0,0 +1,10 @@
# shipBonusHeavyMissileExplosiveDamageCBC2
#
# Used by:
# Ship: Drake Navy Issue
type = "passive"
def handler(fit, src, context):
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Heavy Missiles"), "explosiveDamage",
src.getModifiedItemAttr("shipBonusCBC2"), skill="Caldari Battlecruiser")

View File

@@ -0,0 +1,10 @@
# shipBonusHeavyMissileKineticDamageCBC2
#
# Used by:
# Ship: Drake Navy Issue
type = "passive"
def handler(fit, src, context):
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Heavy Missiles"), "kineticDamage",
src.getModifiedItemAttr("shipBonusCBC2"), skill="Caldari Battlecruiser")

View File

@@ -0,0 +1,10 @@
# shipBonusHeavyMissileThermalDamageCBC2
#
# Used by:
# Ship: Drake Navy Issue
type = "passive"
def handler(fit, src, context):
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Heavy Missiles"), "thermalDamage",
src.getModifiedItemAttr("shipBonusCBC2"), skill="Caldari Battlecruiser")

View File

@@ -2,6 +2,7 @@
#
# Used by:
# Ship: Gnosis
# Ship: Praxis
# Ship: Taipan
# Ship: Velator
type = "passive"

View File

@@ -9,4 +9,4 @@ type = "passive"
def handler(fit, ship, context):
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Missile Launcher Operation"),
"explosionDelay", ship.getModifiedItemAttr("shipBonusPirateFaction2"))
"explosionDelay", ship.getModifiedItemAttr("shipBonusRole8"))

View File

@@ -7,6 +7,6 @@ type = "passive"
def handler(fit, ship, context):
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"),
"maxRange", ship.getModifiedItemAttr("shipBonusPirateFaction2"))
"maxRange", ship.getModifiedItemAttr("shipBonusRole8"))
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"),
"falloffEffectiveness", ship.getModifiedItemAttr("shipBonusPirateFaction2"))
"falloffEffectiveness", ship.getModifiedItemAttr("shipBonusRole8"))

View File

@@ -0,0 +1,38 @@
# shipLargeWeaponsDamageBonus
#
# Used by:
# Ship: Praxis
type = "passive"
def handler(fit, src, context):
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Large Hybrid Turret"), "damageMultiplier",
src.getModifiedItemAttr("shipBonusRole7"))
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Large Projectile Turret"), "damageMultiplier",
src.getModifiedItemAttr("shipBonusRole7"))
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Large Energy Turret"), "damageMultiplier",
src.getModifiedItemAttr("shipBonusRole7"))
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Heavy Missiles"), "thermalDamage",
src.getModifiedItemAttr("shipBonusRole7"))
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Heavy Missiles"), "emDamage",
src.getModifiedItemAttr("shipBonusRole7"))
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Heavy Missiles"), "kineticDamage",
src.getModifiedItemAttr("shipBonusRole7"))
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Heavy Missiles"), "explosiveDamage",
src.getModifiedItemAttr("shipBonusRole7"))
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Torpedoes"), "emDamage",
src.getModifiedItemAttr("shipBonusRole7"))
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Torpedoes"), "kineticDamage",
src.getModifiedItemAttr("shipBonusRole7"))
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Torpedoes"), "explosiveDamage",
src.getModifiedItemAttr("shipBonusRole7"))
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Torpedoes"), "thermalDamage",
src.getModifiedItemAttr("shipBonusRole7"))
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Cruise Missiles"), "thermalDamage",
src.getModifiedItemAttr("shipBonusRole7"))
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Cruise Missiles"), "explosiveDamage",
src.getModifiedItemAttr("shipBonusRole7"))
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Cruise Missiles"), "kineticDamage",
src.getModifiedItemAttr("shipBonusRole7"))
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Cruise Missiles"), "emDamage",
src.getModifiedItemAttr("shipBonusRole7"))

View File

@@ -4,10 +4,11 @@
# Ships named like: Stratios (2 of 2)
# Ship: Astero
# Ship: Gnosis
# Ship: Praxis
# Ship: Sunesis
type = "passive"
def handler(fit, container, context):
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Astrometrics"),
"baseSensorStrength", container.getModifiedItemAttr("shipBonusPirateFaction2"))
"baseSensorStrength", container.getModifiedItemAttr("shipBonusRole8"))

View File

@@ -1,7 +1,8 @@
# shipShieldEmResistance1CBC2
#
# Used by:
# Variations of ship: Drake (3 of 3)
# Ship: Drake
# Ship: Nighthawk
# Ship: Vulture
type = "passive"

View File

@@ -1,7 +1,8 @@
# shipShieldExplosiveResistance1CBC2
#
# Used by:
# Variations of ship: Drake (3 of 3)
# Ship: Drake
# Ship: Nighthawk
# Ship: Vulture
type = "passive"

View File

@@ -0,0 +1,9 @@
# shipShieldHitpointsCC1
#
# Used by:
# Ship: Monitor
type = "passive"
def handler(fit, src, context):
fit.ship.boostItemAttr("shieldCapacity", src.getModifiedItemAttr("shipBonusCC"), skill="Caldari Cruiser")

View File

@@ -1,7 +1,8 @@
# shipShieldKineticResistance1CBC2
#
# Used by:
# Variations of ship: Drake (3 of 3)
# Ship: Drake
# Ship: Nighthawk
# Ship: Vulture
type = "passive"

View File

@@ -1,7 +1,8 @@
# shipShieldThermalResistance1CBC2
#
# Used by:
# Variations of ship: Drake (3 of 3)
# Ship: Drake
# Ship: Nighthawk
# Ship: Vulture
type = "passive"

View File

@@ -0,0 +1,9 @@
# shipSignatureRadiusMC1
#
# Used by:
# Ship: Monitor
type = "passive"
def handler(fit, src, context):
fit.ship.boostItemAttr("signatureRadius", src.getModifiedItemAttr("shipBonusMC"), skill="Minmatar Cruiser")

View File

@@ -1,14 +1,15 @@
# Not used by any item
type = "active"
type = "passive"
def handler(fit, container, context):
missileGroups = ("Structure Anti-Capital Missile", "Structure Anti-Subcapital Missile")
for srcAttr, tgtAttr in (
("aoeCloudSizeBonus", "aoeCloudSize"),
("aoeVelocityBonus", "aoeVelocity"),
("missileVelocityBonus", "maxVelocity"),
("explosionDelayBonus", "explosionDelay"),
):
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Missile Launcher Operation"),
fit.modules.filteredChargeBoost(lambda mod: mod.charge.group.name in missileGroups,
tgtAttr, container.getModifiedItemAttr(srcAttr),
stackingPenalties=True)

View File

@@ -7,12 +7,11 @@ type = "projected", "active"
def handler(fit, module, context):
if "projected" not in context:
return
fit.ship.increaseItemAttr("warpScrambleStatus", module.getModifiedItemAttr("warpScrambleStrength"))
# this is such a dirty hack
for mod in fit.modules:
if not mod.isEmpty and mod.item.requiresSkill("High Speed Maneuvering") and mod.state > State.ONLINE:
mod.state = State.ONLINE
if "projected" in context:
fit.ship.increaseItemAttr("warpScrambleStatus", module.getModifiedItemAttr("warpScrambleStrength"))
if module.charge is not None and module.charge.ID == 47336:
for mod in fit.modules:
if not mod.isEmpty and mod.item.requiresSkill("High Speed Maneuvering") and mod.state > State.ONLINE:
mod.state = State.ONLINE
if not mod.isEmpty and mod.item.requiresSkill("Micro Jump Drive Operation") and mod.state > State.ONLINE:
mod.state = State.ONLINE

View File

@@ -2,7 +2,7 @@
#
# Used by:
# Drones from group: Combat Drone (74 of 74)
# Modules from group: Energy Weapon (212 of 213)
# Modules from group: Energy Weapon (212 of 214)
type = 'active'

View File

@@ -15,12 +15,13 @@ def handler(fit, src, context):
if src.item.group.name == 'Missile Launcher Bomb':
# Bomb Launcher Cooldown Timer
moduleReactivationDelay = src.getModifiedItemAttr("moduleReactivationDelay")
speed = src.getModifiedItemAttr("speed")
# Void and Focused Void Bombs
neutAmount = src.getModifiedChargeAttr("energyNeutralizerAmount")
if moduleReactivationDelay and neutAmount:
fit.addDrain(src, moduleReactivationDelay, neutAmount, 0)
if moduleReactivationDelay and neutAmount and speed:
fit.addDrain(src, speed + moduleReactivationDelay, neutAmount, 0)
# Lockbreaker Bombs
ecmStrengthBonus = src.getModifiedChargeAttr("scan{0}StrengthBonus".format(fit.scanType))

View File

@@ -1,3 +1,4 @@
# warpDisruptSphere
#
# Used by:
@@ -9,18 +10,22 @@ runTime = "early"
def handler(fit, module, context):
fit.ship.boostItemAttr("mass", module.getModifiedItemAttr("massBonusPercentage"))
fit.ship.boostItemAttr("signatureRadius", module.getModifiedItemAttr("signatureRadiusBonus"))
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Propulsion Module",
"speedBoostFactor", module.getModifiedItemAttr("speedBoostFactorBonus"))
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Propulsion Module",
"speedFactor", module.getModifiedItemAttr("speedFactorBonus"))
fit.ship.forceItemAttr("disallowAssistance", 1)
if "projected" in context:
fit.ship.increaseItemAttr("warpScrambleStatus", module.getModifiedItemAttr("warpScrambleStrength"))
if module.charge is not None and module.charge.ID == 45010:
for mod in fit.modules:
if not mod.isEmpty and mod.item.requiresSkill("High Speed Maneuvering") and mod.state > State.ONLINE:
mod.state = State.ONLINE
if not mod.isEmpty and mod.item.requiresSkill("Micro Jump Drive Operation") and mod.state > State.ONLINE:
mod.state = State.ONLINE
else:
if module.charge is None:
fit.ship.boostItemAttr("mass", module.getModifiedItemAttr("massBonusPercentage"))
fit.ship.boostItemAttr("signatureRadius", module.getModifiedItemAttr("signatureRadiusBonus"))
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Propulsion Module",
"speedBoostFactor", module.getModifiedItemAttr("speedBoostFactorBonus"))
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Propulsion Module",
"speedFactor", module.getModifiedItemAttr("speedFactorBonus"))
fit.ship.forceItemAttr("disallowAssistance", 1)

View File

@@ -16,5 +16,10 @@ def handler(fit, module, context):
# this is such a dirty hack
for mod in fit.modules:
if not mod.isEmpty and mod.item.requiresSkill("High Speed Maneuvering") and mod.state > State.ONLINE:
if not mod.isEmpty and mod.state > State.ONLINE and (
mod.item.requiresSkill("Micro Jump Drive Operation") or
mod.item.requiresSkill("High Speed Maneuvering")
):
mod.state = State.ONLINE
if not mod.isEmpty and mod.item.requiresSkill("Micro Jump Drive Operation") and mod.state > State.ONLINE:
mod.state = State.ONLINE

View File

@@ -81,7 +81,7 @@ class FitDpsGraph(Graph):
total += dps * self.calculateTurretMultiplier(mod, data)
elif mod.hardpoint == Hardpoint.MISSILE:
if mod.state >= State.ACTIVE and mod.maxRange >= distance:
if mod.state >= State.ACTIVE and mod.maxRange is not None and mod.maxRange >= distance:
total += dps * self.calculateMissileMultiplier(mod, data)
if distance <= fit.extraAttributes["droneControlRange"]:

View File

@@ -52,7 +52,6 @@ class Character(object):
self.addSkill(Skill(self, item.ID, self.defaultLevel))
self.__implants = HandledImplantBoosterList()
self.apiKey = None
@reconstructor
def init(self):
@@ -119,12 +118,9 @@ class Character(object):
return all0
def apiUpdateCharSheet(self, skills, secStatus=0):
def clearSkills(self):
del self.__skills[:]
self.__skillIdMap.clear()
for skillRow in skills:
self.addSkill(Skill(self, skillRow["typeID"], skillRow["level"]))
self.secStatus = secStatus
@property
def ro(self):
@@ -166,6 +162,17 @@ class Character(object):
def name(self, name):
self.savedName = name
def setSsoCharacter(self, character, clientHash):
if character is not None:
self.__ssoCharacters.append(character)
else:
for x in self.__ssoCharacters:
if x.client == clientHash:
self.__ssoCharacters.remove(x)
def getSsoCharacter(self, clientHash):
return next((x for x in self.__ssoCharacters if x.client == clientHash), None)
@property
def alphaCloneID(self):
return self.__alphaCloneID
@@ -265,20 +272,17 @@ class Character(object):
def __deepcopy__(self, memo):
copy = Character("%s copy" % self.name, initSkills=False)
copy.apiKey = self.apiKey
copy.apiID = self.apiID
for skill in self.skills:
copy.addSkill(Skill(copy, skill.itemID, skill.level, False, skill.learned))
return copy
@validates("ID", "name", "apiKey", "ownerID")
@validates("ID", "name", "ownerID")
def validator(self, key, val):
map = {
"ID" : lambda _val: isinstance(_val, int),
"name" : lambda _val: True,
"apiKey" : lambda _val: _val is None or (isinstance(_val, str) and len(_val) > 0),
"ownerID": lambda _val: isinstance(_val, int) or _val is None
}

View File

@@ -18,12 +18,16 @@
# ===============================================================================
import re
import eos.db
class DamagePattern(object):
DAMAGE_TYPES = ("em", "thermal", "kinetic", "explosive")
def __init__(self, emAmount=25, thermalAmount=25, kineticAmount=25, explosiveAmount=25):
def __init__(self, *args, **kwargs):
self.update(*args, **kwargs)
def update(self, emAmount=25, thermalAmount=25, kineticAmount=25, explosiveAmount=25):
self.emAmount = emAmount
self.thermalAmount = thermalAmount
self.kineticAmount = kineticAmount
@@ -74,6 +78,14 @@ class DamagePattern(object):
lines = re.split('[\n\r]+', text)
patterns = []
numPatterns = 0
# When we import damage profiles, we create new ones and update old ones. To do this, get a list of current
# patterns to allow lookup
lookup = {}
current = eos.db.getDamagePatternList()
for pattern in current:
lookup[pattern.name] = pattern
for line in lines:
try:
if line.strip()[0] == "#": # comments
@@ -99,10 +111,18 @@ class DamagePattern(object):
continue
if len(fields) == 4: # Avoid possible blank lines
pattern = DamagePattern(**fields)
pattern.name = name.strip()
if name.strip() in lookup:
pattern = lookup[name.strip()]
pattern.update(**fields)
eos.db.save(pattern)
else:
pattern = DamagePattern(**fields)
pattern.name = name.strip()
eos.db.save(pattern)
patterns.append(pattern)
eos.db.commit()
return patterns, numPatterns
EXPORT_FORMAT = "DamageProfile = %s,%d,%d,%d,%d\n"

View File

@@ -1084,7 +1084,7 @@ class Fit(object):
def calculateSustainableTank(self, effective=True):
if self.__sustainableTank is None:
if self.capStable:
if self.capStable and not self.factorReload:
sustainable = {
"armorRepair" : self.extraAttributes["armorRepair"],
"shieldRepair": self.extraAttributes["shieldRepair"],
@@ -1142,16 +1142,38 @@ class Fit(object):
usesCap = False
except AttributeError:
usesCap = False
# Modules which do not use cap are not penalized based on cap use
if usesCap:
cycleTime = mod.getModifiedItemAttr("duration")
# Normal Repairers
if usesCap and not mod.charge:
cycleTime = mod.rawCycleTime
amount = mod.getModifiedItemAttr(groupAttrMap[mod.item.group.name])
sustainable[attr] -= amount / (cycleTime / 1000.0)
repairers.append(mod)
# Ancillary Armor reps etc
elif usesCap and mod.charge:
cycleTime = mod.rawCycleTime
amount = mod.getModifiedItemAttr(groupAttrMap[mod.item.group.name])
if mod.charge.name == "Nanite Repair Paste":
multiplier = mod.getModifiedItemAttr("chargedArmorDamageMultiplier") or 1
else:
multiplier = 1
sustainable[attr] -= amount * multiplier / (cycleTime / 1000.0)
repairers.append(mod)
# Ancillary Shield boosters etc
elif not usesCap and mod.item.group.name in ("Ancillary Shield Booster", "Ancillary Remote Shield Booster"):
cycleTime = mod.rawCycleTime
amount = mod.getModifiedItemAttr(groupAttrMap[mod.item.group.name])
if self.factorReload and mod.charge:
reloadtime = mod.reloadTime
else:
reloadtime = 0.0
offdutycycle = reloadtime / ((max(mod.numShots, 1) * cycleTime) + reloadtime)
sustainable[attr] -= amount * offdutycycle / (cycleTime / 1000.0)
# 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("capacitorNeed"), reverse=True)
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
# Most efficient first, as we sorted earlier.
@@ -1160,15 +1182,35 @@ class Fit(object):
for mod in repairers:
if capUsed > totalPeakRecharge:
break
cycleTime = mod.cycleTime
if self.factorReload and mod.charge:
reloadtime = mod.reloadTime
else:
reloadtime = 0.0
cycleTime = mod.rawCycleTime
capPerSec = mod.capUse
if capPerSec is not None and cycleTime is not None:
# Check how much this repper can work
sustainability = min(1, (totalPeakRecharge - capUsed) / capPerSec)
# Add the sustainable amount
amount = mod.getModifiedItemAttr(groupAttrMap[mod.item.group.name])
sustainable[groupStoreMap[mod.item.group.name]] += sustainability * (amount / (cycleTime / 1000.0))
# Add the sustainable amount
if not mod.charge:
sustainable[groupStoreMap[mod.item.group.name]] += sustainability * amount / (
cycleTime / 1000.0)
else:
if mod.charge.name == "Nanite Repair Paste":
multiplier = mod.getModifiedItemAttr("chargedArmorDamageMultiplier") or 1
else:
multiplier = 1
ondutycycle = (max(mod.numShots, 1) * cycleTime) / (
(max(mod.numShots, 1) * cycleTime) + reloadtime)
sustainable[groupStoreMap[
mod.item.group.name]] += sustainability * amount * ondutycycle * multiplier / (
cycleTime / 1000.0)
capUsed += capPerSec
sustainable["passiveShield"] = self.calculateShieldRecharge()
@@ -1186,7 +1228,7 @@ class Fit(object):
rechargeRate = self.ship.getModifiedItemAttr("shieldRechargeRate") / 1000.0
return 10 / rechargeRate * sqrt(percent) * (1 - sqrt(percent)) * capacity
def addDrain(self, src, cycleTime, capNeed, clipSize=0):
def addDrain(self, src, cycleTime, capNeed, clipSize=0, reloadTime=0):
""" Used for both cap drains and cap fills (fills have negative capNeed) """
energyNeutralizerSignatureResolution = src.getModifiedItemAttr("energyNeutralizerSignatureResolution")
@@ -1196,7 +1238,7 @@ class Fit(object):
if energyNeutralizerSignatureResolution:
capNeed = capNeed * min(1, signatureRadius / energyNeutralizerSignatureResolution)
self.__extraDrains.append((cycleTime, capNeed, clipSize))
self.__extraDrains.append((cycleTime, capNeed, clipSize, reloadTime))
def removeDrain(self, i):
del self.__extraDrains[i]
@@ -1214,6 +1256,7 @@ class Fit(object):
cycleTime = mod.rawCycleTime or 0
reactivationTime = mod.getModifiedItemAttr("moduleReactivationDelay") or 0
fullCycleTime = cycleTime + reactivationTime
reloadTime = mod.reloadTime
if fullCycleTime > 0:
capNeed = mod.capUse
if capNeed > 0:
@@ -1225,11 +1268,11 @@ class Fit(object):
disableStagger = mod.hardpoint == Hardpoint.TURRET
drains.append((int(fullCycleTime), mod.getModifiedItemAttr("capacitorNeed") or 0,
mod.numShots or 0, disableStagger))
mod.numShots or 0, disableStagger, reloadTime))
for fullCycleTime, capNeed, clipSize in self.iterDrains():
for fullCycleTime, capNeed, clipSize, reloadTime in self.iterDrains():
# Stagger incoming effects for cap simulation
drains.append((int(fullCycleTime), capNeed, clipSize, False))
drains.append((int(fullCycleTime), capNeed, clipSize, False, reloadTime))
if capNeed > 0:
capUsed += capNeed / (fullCycleTime / 1000.0)
else:

View File

@@ -182,7 +182,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
@property
def numShots(self):
if self.charge is None:
return -1
return 0
if self.__chargeCycles is None and self.charge:
numCharges = self.numCharges
# Usual ammo like projectiles and missiles
@@ -337,8 +337,15 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
volley *= self.getModifiedItemAttr("damageMultiplier") or 1
if volley:
cycleTime = self.cycleTime
# Some weapons repeat multiple times in one cycle (think doomsdays)
# Get the number of times it fires off
weaponDoT = max(
self.getModifiedItemAttr("doomsdayDamageDuration", 1) / self.getModifiedItemAttr("doomsdayDamageCycleTime", 1),
1
)
self.__volley = volley
self.__dps = volley / (cycleTime / 1000.0)
self.__dps = (volley * weaponDoT) / (cycleTime / 1000.0)
return self.__dps, self.__volley
@@ -710,7 +717,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
# Module can only fire one shot at a time, think bomb launchers or defender launchers
if self.disallowRepeatingAction:
if numShots > 1:
if numShots > 0:
"""
The actual mechanics behind this is complex. Behavior will be (for 3 ammo):
fire, reactivation delay, fire, reactivation delay, fire, max(reactivation delay, reload)
@@ -720,12 +727,13 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
Currently would apply to bomb launchers and defender missiles
"""
effective_reload_time = ((self.reactivationDelay * (numShots - 1)) + max(raw_reload_time, self.reactivationDelay, 0)) / numShots
effective_reload_time = ((self.reactivationDelay * (numShots - 1)) + max(raw_reload_time, self.reactivationDelay, 0))
else:
"""
Applies to MJD/MJFG
"""
effective_reload_time = max(raw_reload_time, self.reactivationDelay, 0)
speed = speed + effective_reload_time
else:
"""
Currently no other modules would have a reactivation delay, so for sanities sake don't try and account for it.
@@ -752,7 +760,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
@property
def disallowRepeatingAction(self):
return self.getModifiedItemAttr("disallowRepeatingAction", 0)
return self.getModifiedItemAttr("disallowRepeatingActivation", 0)
@property
def reactivationDelay(self):

View File

@@ -18,17 +18,31 @@
# ===============================================================================
from sqlalchemy.orm import reconstructor
import datetime
import time
# from tomorrow import threads
class CrestChar(object):
def __init__(self, id, name, refresh_token=None):
self.ID = id
self.name = name
self.refresh_token = refresh_token
class SsoCharacter(object):
def __init__(self, charID, name, client, accessToken=None, refreshToken=None):
self.characterID = charID
self.characterName = name
self.client = client
self.accessToken = accessToken
self.refreshToken = refreshToken
self.accessTokenExpires = None
@reconstructor
def init(self):
pass
def is_token_expired(self):
if self.accessTokenExpires is None:
return True
return datetime.datetime.now() >= self.accessTokenExpires
def __repr__(self):
return "SsoCharacter(ID={}, name={}, client={}) at {}".format(
self.ID, self.characterName, self.client, hex(id(self))
)

View File

@@ -19,6 +19,7 @@
import re
from logbook import Logger
import eos.db
pyfalog = Logger(__name__)
@@ -27,7 +28,10 @@ class TargetResists(object):
# also determined import/export order - VERY IMPORTANT
DAMAGE_TYPES = ("em", "thermal", "kinetic", "explosive")
def __init__(self, emAmount=0, thermalAmount=0, kineticAmount=0, explosiveAmount=0):
def __init__(self, *args, **kwargs):
self.update(*args, **kwargs)
def update(self, emAmount=0, thermalAmount=0, kineticAmount=0, explosiveAmount=0):
self.emAmount = emAmount
self.thermalAmount = thermalAmount
self.kineticAmount = kineticAmount
@@ -38,6 +42,14 @@ class TargetResists(object):
lines = re.split('[\n\r]+', text)
patterns = []
numPatterns = 0
# When we import damage profiles, we create new ones and update old ones. To do this, get a list of current
# patterns to allow lookup
lookup = {}
current = eos.db.getTargetResistsList()
for pattern in current:
lookup[pattern.name] = pattern
for line in lines:
try:
if line.strip()[0] == "#": # comments
@@ -66,10 +78,18 @@ class TargetResists(object):
continue
if len(fields) == 4: # Avoid possible blank lines
pattern = TargetResists(**fields)
pattern.name = name.strip()
if name.strip() in lookup:
pattern = lookup[name.strip()]
pattern.update(**fields)
eos.db.save(pattern)
else:
pattern = TargetResists(**fields)
pattern.name = name.strip()
eos.db.save(pattern)
patterns.append(pattern)
eos.db.commit()
return patterns, numPatterns
EXPORT_FORMAT = "TargetResists = %s,%.1f,%.1f,%.1f,%.1f\n"

BIN
eve.db

Binary file not shown.

View File

@@ -135,7 +135,7 @@ class CargoView(d.Display):
sFit = Fit.getInstance()
fit = sFit.getFit(event.fitID)
self.Parent.Parent.DisablePage(self, not fit or fit.isStructure)
# self.Parent.Parent.DisablePage(self, not fit or fit.isStructure)
# Clear list and get out if current fitId is None
if event.fitID is None and self.lastFitId is not None:

View File

@@ -99,15 +99,22 @@ class ProjectedView(d.Display):
data[0] is hard-coded str of originating source
data[1] is typeID or index of data we want to manipulate
"""
sFit = Fit.getInstance()
fit = sFit.getFit(self.mainFrame.getActiveFit())
if data[0] == "projected":
# if source is coming from projected, we are trying to combine drones.
self.mergeDrones(x, y, int(data[1]))
elif data[0] == "fitting":
dstRow, _ = self.HitTest((x, y))
# Gather module information to get position
module = fit.modules[int(data[1])]
sFit.project(fit.ID, module.item.ID)
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fit.ID))
elif data[0] == "market":
sFit = Fit.getInstance()
fitID = self.mainFrame.getActiveFit()
sFit.project(fitID, int(data[1]))
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit()))
sFit.project(fit.ID, int(data[1]))
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fit.ID))
def kbEvent(self, event):
keycode = event.GetKeyCode()

View File

@@ -26,57 +26,71 @@ class ChangeAmount(ContextMenu):
return u"Change {0} Quantity".format(itmContext)
def activate(self, fullContext, selection, i):
srcContext = fullContext[0]
dlg = AmountChanger(self.mainFrame, selection[0], srcContext)
dlg.ShowModal()
thing = selection[0]
mainFrame = gui.mainFrame.MainFrame.getInstance()
fitID = mainFrame.getActiveFit()
if isinstance(thing, es_Fit):
value = thing.getProjectionInfo(fitID).amount
else:
value = thing.amount
dlg = AmountChanger(self.mainFrame, value)
if dlg.ShowModal() == wx.ID_OK:
if dlg.input.GetLineText(0).strip() == '':
return
sFit = Fit.getInstance()
cleanInput = re.sub(r'[^0-9.]', '', dlg.input.GetLineText(0).strip())
if isinstance(thing, es_Cargo):
sFit.addCargo(fitID, thing.item.ID, int(float(cleanInput)), replace=True)
elif isinstance(thing, es_Fit):
sFit.changeAmount(fitID, thing, int(float(cleanInput)))
elif isinstance(thing, es_Fighter):
sFit.changeActiveFighters(fitID, thing, int(float(cleanInput)))
wx.PostEvent(mainFrame, GE.FitChanged(fitID=fitID))
ChangeAmount.register()
class AmountChanger(wx.Dialog):
def __init__(self, parent, thing, context):
wx.Dialog.__init__(self, parent, title="Select Amount", size=wx.Size(220, 60))
self.thing = thing
self.context = context
def __init__(self, parent, value):
wx.Dialog.__init__(self, parent, title="Change Amount")
self.SetMinSize((346, 156))
bSizer1 = wx.BoxSizer(wx.HORIZONTAL)
bSizer1 = wx.BoxSizer(wx.VERTICAL)
bSizer2 = wx.BoxSizer(wx.VERTICAL)
text = wx.StaticText(self, wx.ID_ANY, "New Amount:")
bSizer2.Add(text, 0)
bSizer1.Add(bSizer2, 0, wx.ALL, 10)
self.input = wx.TextCtrl(self, wx.ID_ANY, style=wx.TE_PROCESS_ENTER)
self.input.SetValue(str(value))
self.input.SelectAll()
bSizer1.Add(self.input, 1, wx.ALL, 5)
bSizer1.Add(self.input, 0, wx.LEFT | wx.RIGHT | wx.EXPAND, 15)
bSizer3 = wx.BoxSizer(wx.VERTICAL)
bSizer3.Add(wx.StaticLine(self, wx.ID_ANY), 0, wx.BOTTOM | wx.EXPAND, 15)
bSizer3.Add(self.CreateStdDialogButtonSizer(wx.OK | wx.CANCEL), 0, wx.EXPAND)
bSizer1.Add(bSizer3, 0, wx.ALL | wx.EXPAND, 10)
self.input.SetFocus()
self.input.Bind(wx.EVT_CHAR, self.onChar)
self.input.Bind(wx.EVT_TEXT_ENTER, self.change)
self.button = wx.Button(self, wx.ID_OK, "Done")
bSizer1.Add(self.button, 0, wx.ALL, 5)
self.input.Bind(wx.EVT_TEXT_ENTER, self.processEnter)
self.SetSizer(bSizer1)
self.Layout()
self.Centre(wx.BOTH)
self.button.Bind(wx.EVT_BUTTON, self.change)
self.CenterOnParent()
self.Fit()
def change(self, event):
if self.input.GetLineText(0).strip() == '':
event.Skip()
self.Close()
return
sFit = Fit.getInstance()
cleanInput = re.sub(r'[^0-9.]', '', self.input.GetLineText(0).strip())
mainFrame = gui.mainFrame.MainFrame.getInstance()
fitID = mainFrame.getActiveFit()
if isinstance(self.thing, es_Cargo):
sFit.addCargo(fitID, self.thing.item.ID, int(float(cleanInput)), replace=True)
elif isinstance(self.thing, es_Fit):
sFit.changeAmount(fitID, self.thing, int(float(cleanInput)))
elif isinstance(self.thing, es_Fighter):
sFit.changeActiveFighters(fitID, self.thing, int(float(cleanInput)))
wx.PostEvent(mainFrame, GE.FitChanged(fitID=fitID))
event.Skip()
self.Close()
def processEnter(self, evt):
self.EndModal(wx.ID_OK)
# checks to make sure it's valid number
@staticmethod

View File

@@ -31,9 +31,7 @@ class FactorReload(ContextMenu):
def getBitmap(self, context, selection):
sFit = Fit.getInstance()
fitID = self.mainFrame.getActiveFit()
fit = sFit.getFit(fitID)
if fit.factorReload:
if sFit.serviceFittingOptions["useGlobalForceReload"]:
return BitmapLoader.getBitmap("state_active_small", "gui")
else:
return None

View File

@@ -1,4 +1,3 @@
import sys
import csv
import config
@@ -203,7 +202,7 @@ class ItemParams(wx.Panel):
else:
attrIcon = self.imageList.Add(BitmapLoader.getBitmap("7_15", "icons"))
index = self.paramList.InsertItem(sys.maxsize, attrName, attrIcon)
index = self.paramList.InsertItem(self.paramList.GetItemCount(), attrName, attrIcon)
idNameMap[idCount] = attrName
self.paramList.SetItemData(index, idCount)
idCount += 1

View File

@@ -1,5 +1,3 @@
import sys
# noinspection PyPackageRequirements
import wx
@@ -147,7 +145,7 @@ class ItemCompare(wx.Panel):
self.paramList.SetColumnWidth(len(self.attrs) + 1, 60)
for item in self.items:
i = self.paramList.InsertItem(sys.maxsize, item.name)
i = self.paramList.InsertItem(self.paramList.GetItemCount(), item.name)
for x, attr in enumerate(self.attrs.keys()):
if attr in item.attributes:
info = self.attrs[attr]

View File

@@ -1,4 +1,3 @@
import sys
import os
import subprocess
import config
@@ -51,7 +50,7 @@ class ItemEffects(wx.Panel):
names.sort()
for name in names:
index = self.effectList.InsertItem(sys.maxsize, name)
index = self.effectList.InsertItem(self.effectList.GetItemCount(), name)
if effects[name].isImplemented:
if effects[name].activeByDefault:

View File

@@ -1,5 +1,3 @@
import sys
# noinspection PyPackageRequirements
import wx
@@ -79,7 +77,7 @@ class ItemProperties(wx.Panel):
attrName = name.title()
value = getattr(self.item, name)
index = self.paramList.InsertItem(sys.maxsize, attrName)
index = self.paramList.InsertItem(self.paramList.GetItemCount(), attrName)
# index = self.paramList.InsertImageStringItem(sys.maxint, attrName)
idNameMap[idCount] = attrName
self.paramList.SetItemData(index, idCount)

View File

@@ -1,5 +1,6 @@
import wx
import config
import gui.builtinMarketBrowser.pfSearchBox as SBox
from gui.contextMenu import ContextMenu
from gui.display import Display
@@ -170,10 +171,6 @@ class ItemView(Display):
if len(realsearch) == 0:
self.selectionMade()
return
# Show nothing if query is too short
elif len(realsearch) < 3:
self.clearSearch()
return
self.marketBrowser.searchMode = True
self.sMkt.searchItems(search, self.populateSearch)

View File

@@ -6,7 +6,6 @@ __all__ = [
"pyfaDatabasePreferences",
"pyfaLoggingPreferences",
"pyfaEnginePreferences",
"pyfaStatViewPreferences",
"pyfaCrestPreferences"
]
"pyfaEsiPreferences",
"pyfaStatViewPreferences"]

View File

@@ -1,150 +0,0 @@
# noinspection PyPackageRequirements
import wx
from gui.preferenceView import PreferenceView
from gui.bitmap_loader import BitmapLoader
import gui.mainFrame
from service.settings import CRESTSettings
# noinspection PyPackageRequirements
from wx.lib.intctrl import IntCtrl
from service.crest import Crest
class PFCrestPref(PreferenceView):
title = "CREST"
def populatePanel(self, panel):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = CRESTSettings.getInstance()
self.dirtySettings = False
dlgWidth = panel.GetParent().GetParent().ClientSize.width
mainSizer = wx.BoxSizer(wx.VERTICAL)
self.stTitle = wx.StaticText(panel, wx.ID_ANY, self.title, wx.DefaultPosition, wx.DefaultSize, 0)
self.stTitle.Wrap(-1)
self.stTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString))
mainSizer.Add(self.stTitle, 0, wx.ALL, 5)
self.m_staticline1 = wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL)
mainSizer.Add(self.m_staticline1, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5)
self.stInfo = wx.StaticText(panel, wx.ID_ANY,
"Please see the pyfa wiki on GitHub for information regarding these options.",
wx.DefaultPosition, wx.DefaultSize, 0)
self.stInfo.Wrap(dlgWidth - 50)
mainSizer.Add(self.stInfo, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5)
rbSizer = wx.BoxSizer(wx.HORIZONTAL)
self.rbMode = wx.RadioBox(panel, -1, "Mode", wx.DefaultPosition, wx.DefaultSize,
['Implicit', 'User-supplied details'], 1, wx.RA_SPECIFY_COLS)
self.rbServer = wx.RadioBox(panel, -1, "Server", wx.DefaultPosition, wx.DefaultSize,
['Tranquility', 'Singularity'], 1, wx.RA_SPECIFY_COLS)
self.rbMode.SetSelection(self.settings.get('mode'))
self.rbServer.SetSelection(self.settings.get('server'))
rbSizer.Add(self.rbMode, 1, wx.TOP | wx.RIGHT, 5)
rbSizer.Add(self.rbServer, 1, wx.ALL, 5)
self.rbMode.Bind(wx.EVT_RADIOBOX, self.OnModeChange)
self.rbServer.Bind(wx.EVT_RADIOBOX, self.OnServerChange)
mainSizer.Add(rbSizer, 1, wx.ALL | wx.EXPAND, 0)
timeoutSizer = wx.BoxSizer(wx.HORIZONTAL)
self.stTimout = wx.StaticText(panel, wx.ID_ANY, "Timeout (seconds):", wx.DefaultPosition, wx.DefaultSize, 0)
self.stTimout.Wrap(-1)
timeoutSizer.Add(self.stTimout, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
self.intTimeout = IntCtrl(panel, max=300000, limited=True, value=self.settings.get('timeout'))
timeoutSizer.Add(self.intTimeout, 0, wx.ALL, 5)
self.intTimeout.Bind(wx.lib.intctrl.EVT_INT, self.OnTimeoutChange)
mainSizer.Add(timeoutSizer, 0, wx.ALL | wx.EXPAND, 0)
detailsTitle = wx.StaticText(panel, wx.ID_ANY, "CREST client details", wx.DefaultPosition, wx.DefaultSize, 0)
detailsTitle.Wrap(-1)
detailsTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString))
mainSizer.Add(detailsTitle, 0, wx.ALL, 5)
mainSizer.Add(wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0,
wx.EXPAND, 5)
fgAddrSizer = wx.FlexGridSizer(2, 2, 0, 0)
fgAddrSizer.AddGrowableCol(1)
fgAddrSizer.SetFlexibleDirection(wx.BOTH)
fgAddrSizer.SetNonFlexibleGrowMode(wx.FLEX_GROWMODE_SPECIFIED)
self.stSetID = wx.StaticText(panel, wx.ID_ANY, "Client ID:", wx.DefaultPosition, wx.DefaultSize, 0)
self.stSetID.Wrap(-1)
fgAddrSizer.Add(self.stSetID, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
self.inputClientID = wx.TextCtrl(panel, wx.ID_ANY, self.settings.get('clientID'), wx.DefaultPosition,
wx.DefaultSize, 0)
fgAddrSizer.Add(self.inputClientID, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5)
self.stSetSecret = wx.StaticText(panel, wx.ID_ANY, "Client Secret:", wx.DefaultPosition, wx.DefaultSize, 0)
self.stSetSecret.Wrap(-1)
fgAddrSizer.Add(self.stSetSecret, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
self.inputClientSecret = wx.TextCtrl(panel, wx.ID_ANY, self.settings.get('clientSecret'), wx.DefaultPosition,
wx.DefaultSize, 0)
fgAddrSizer.Add(self.inputClientSecret, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5)
self.btnApply = wx.Button(panel, wx.ID_ANY, "Save Client Settings", wx.DefaultPosition, wx.DefaultSize, 0)
self.btnApply.Bind(wx.EVT_BUTTON, self.OnBtnApply)
mainSizer.Add(fgAddrSizer, 0, wx.EXPAND, 5)
mainSizer.Add(self.btnApply, 0, wx.ALIGN_RIGHT, 5)
self.ToggleProxySettings(self.settings.get('mode'))
panel.SetSizer(mainSizer)
panel.Layout()
def OnTimeoutChange(self, event):
self.settings.set('timeout', event.GetEventObject().GetValue())
def OnModeChange(self, event):
self.settings.set('mode', event.GetInt())
self.ToggleProxySettings(self.settings.get('mode'))
Crest.restartService()
def OnServerChange(self, event):
self.settings.set('server', event.GetInt())
Crest.restartService()
def OnBtnApply(self, event):
self.settings.set('clientID', self.inputClientID.GetValue().strip())
self.settings.set('clientSecret', self.inputClientSecret.GetValue().strip())
sCrest = Crest.getInstance()
sCrest.delAllCharacters()
def ToggleProxySettings(self, mode):
if mode:
self.stSetID.Enable()
self.inputClientID.Enable()
self.stSetSecret.Enable()
self.inputClientSecret.Enable()
else:
self.stSetID.Disable()
self.inputClientID.Disable()
self.stSetSecret.Disable()
self.inputClientSecret.Disable()
def getImage(self):
return BitmapLoader.getBitmap("eve", "gui")
PFCrestPref.register()

View File

@@ -0,0 +1,197 @@
# noinspection PyPackageRequirements
import wx
from gui.preferenceView import PreferenceView
from gui.bitmap_loader import BitmapLoader
import gui.mainFrame
from service.settings import EsiSettings
# noinspection PyPackageRequirements
from wx.lib.intctrl import IntCtrl
from service.esi import Esi
class PFEsiPref(PreferenceView):
title = "EVE SSO"
def populatePanel(self, panel):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = EsiSettings.getInstance()
self.dirtySettings = False
dlgWidth = panel.GetParent().GetParent().ClientSize.width
mainSizer = wx.BoxSizer(wx.VERTICAL)
self.stTitle = wx.StaticText(panel, wx.ID_ANY, self.title, wx.DefaultPosition, wx.DefaultSize, 0)
self.stTitle.Wrap(-1)
self.stTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString))
mainSizer.Add(self.stTitle, 0, wx.ALL, 5)
self.m_staticline1 = wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL)
mainSizer.Add(self.m_staticline1, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5)
self.stInfo = wx.StaticText(panel, wx.ID_ANY,
"Please see the pyfa wiki on GitHub for information regarding these options.",
wx.DefaultPosition, wx.DefaultSize, 0)
self.stInfo.Wrap(dlgWidth - 50)
mainSizer.Add(self.stInfo, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5)
rbSizer = wx.BoxSizer(wx.HORIZONTAL)
self.rbMode = wx.RadioBox(panel, -1, "Login Authentication Method", wx.DefaultPosition, wx.DefaultSize,
['Local Server', 'Manual'], 1, wx.RA_SPECIFY_COLS)
self.rbMode.SetItemToolTip(0, "This options starts a local webserver that the web application will call back to"
" with information about the character login.")
self.rbMode.SetItemToolTip(1, "This option prompts users to copy and paste information from the web application "
"to allow for character login. Use this if having issues with the local server.")
self.rbSsoMode = wx.RadioBox(panel, -1, "SSO Mode", wx.DefaultPosition, wx.DefaultSize,
['pyfa.io', 'Custom application'], 1, wx.RA_SPECIFY_COLS)
self.rbSsoMode.SetItemToolTip(0, "This options routes SSO Logins through pyfa.io, allowing you to easily login "
"without any configuration. When in doubt, use this option.")
self.rbSsoMode.SetItemToolTip(1, "This option goes through EVE SSO directly, but requires more configuration. Use "
"this is pyfa.io is blocked for some reason, or if you do not wish to route data throguh pyfa.io.")
self.rbMode.SetSelection(self.settings.get('loginMode'))
self.rbSsoMode.SetSelection(self.settings.get('ssoMode'))
rbSizer.Add(self.rbSsoMode, 1, wx.ALL, 5)
rbSizer.Add(self.rbMode, 1, wx.TOP | wx.RIGHT, 5)
self.rbMode.Bind(wx.EVT_RADIOBOX, self.OnModeChange)
self.rbSsoMode.Bind(wx.EVT_RADIOBOX, self.OnSSOChange)
mainSizer.Add(rbSizer, 1, wx.ALL | wx.EXPAND, 0)
detailsTitle = wx.StaticText(panel, wx.ID_ANY, "Custom Application", wx.DefaultPosition, wx.DefaultSize, 0)
detailsTitle.Wrap(-1)
detailsTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString))
mainSizer.Add(detailsTitle, 0, wx.ALL, 5)
mainSizer.Add(wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0,
wx.EXPAND, 5)
fgAddrSizer = wx.FlexGridSizer(2, 2, 0, 0)
fgAddrSizer.AddGrowableCol(1)
fgAddrSizer.SetFlexibleDirection(wx.BOTH)
fgAddrSizer.SetNonFlexibleGrowMode(wx.FLEX_GROWMODE_SPECIFIED)
self.stSetID = wx.StaticText(panel, wx.ID_ANY, u"Client ID:", wx.DefaultPosition, wx.DefaultSize, 0)
self.stSetID.Wrap(-1)
fgAddrSizer.Add(self.stSetID, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
self.inputClientID = wx.TextCtrl(panel, wx.ID_ANY, self.settings.get('clientID'), wx.DefaultPosition,
wx.DefaultSize, 0)
fgAddrSizer.Add(self.inputClientID, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5)
self.stSetSecret = wx.StaticText(panel, wx.ID_ANY, u"Client Secret:", wx.DefaultPosition, wx.DefaultSize, 0)
self.stSetSecret.Wrap(-1)
fgAddrSizer.Add(self.stSetSecret, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
self.inputClientSecret = wx.TextCtrl(panel, wx.ID_ANY, self.settings.get('clientSecret'), wx.DefaultPosition,
wx.DefaultSize, 0)
fgAddrSizer.Add(self.inputClientSecret, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5)
self.inputClientID.Bind(wx.EVT_TEXT, self.OnClientDetailChange)
self.inputClientSecret.Bind(wx.EVT_TEXT, self.OnClientDetailChange)
mainSizer.Add(fgAddrSizer, 0, wx.EXPAND, 5)
# self.stTimout = wx.StaticText(panel, wx.ID_ANY, "Timeout (seconds):", wx.DefaultPosition, wx.DefaultSize, 0)
# self.stTimout.Wrap(-1)
#
# timeoutSizer.Add(self.stTimout, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
# self.intTimeout = IntCtrl(panel, max=300000, limited=True, value=self.settings.get('timeout'))
# timeoutSizer.Add(self.intTimeout, 0, wx.ALL, 5)
# self.intTimeout.Bind(wx.lib.intctrl.EVT_INT, self.OnTimeoutChange)
#
# mainSizer.Add(timeoutSizer, 0, wx.ALL | wx.EXPAND, 0)
# detailsTitle = wx.StaticText(panel, wx.ID_ANY, "CREST client details", wx.DefaultPosition, wx.DefaultSize, 0)
# detailsTitle.Wrap(-1)
# detailsTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString))
#
# mainSizer.Add(detailsTitle, 0, wx.ALL, 5)
# mainSizer.Add(wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0,
# wx.EXPAND, 5)
# fgAddrSizer = wx.FlexGridSizer(2, 2, 0, 0)
# fgAddrSizer.AddGrowableCol(1)
# fgAddrSizer.SetFlexibleDirection(wx.BOTH)
# fgAddrSizer.SetNonFlexibleGrowMode(wx.FLEX_GROWMODE_SPECIFIED)
#
# self.stSetID = wx.StaticText(panel, wx.ID_ANY, "Client ID:", wx.DefaultPosition, wx.DefaultSize, 0)
# self.stSetID.Wrap(-1)
# fgAddrSizer.Add(self.stSetID, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
#
# self.inputClientID = wx.TextCtrl(panel, wx.ID_ANY, self.settings.get('clientID'), wx.DefaultPosition,
# wx.DefaultSize, 0)
#
# fgAddrSizer.Add(self.inputClientID, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5)
#
# self.stSetSecret = wx.StaticText(panel, wx.ID_ANY, "Client Secret:", wx.DefaultPosition, wx.DefaultSize, 0)
# self.stSetSecret.Wrap(-1)
#
# fgAddrSizer.Add(self.stSetSecret, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
#
# self.inputClientSecret = wx.TextCtrl(panel, wx.ID_ANY, self.settings.get('clientSecret'), wx.DefaultPosition,
# wx.DefaultSize, 0)
#
# fgAddrSizer.Add(self.inputClientSecret, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5)
#
# self.btnApply = wx.Button(panel, wx.ID_ANY, "Save Client Settings", wx.DefaultPosition, wx.DefaultSize, 0)
# self.btnApply.Bind(wx.EVT_BUTTON, self.OnBtnApply)
#
# mainSizer.Add(fgAddrSizer, 0, wx.EXPAND, 5)
# mainSizer.Add(self.btnApply, 0, wx.ALIGN_RIGHT, 5)
# self.ToggleProxySettings(self.settings.get('loginMode'))
self.ToggleSSOMode(self.settings.get('ssoMode'))
panel.SetSizer(mainSizer)
panel.Layout()
def OnTimeoutChange(self, event):
self.settings.set('timeout', event.GetEventObject().GetValue())
def OnModeChange(self, event):
self.settings.set('loginMode', event.GetInt())
def OnSSOChange(self, event):
self.settings.set('ssoMode', event.GetInt())
self.ToggleSSOMode(event.GetInt())
def ToggleSSOMode(self, mode):
if mode:
self.stSetID.Enable()
self.inputClientID.Enable()
self.stSetSecret.Enable()
self.inputClientSecret.Enable()
self.rbMode.Disable()
else:
self.stSetID.Disable()
self.inputClientID.Disable()
self.stSetSecret.Disable()
self.inputClientSecret.Disable()
self.rbMode.Enable()
def OnClientDetailChange(self, evt):
self.settings.set('clientID', self.inputClientID.GetValue().strip())
self.settings.set('clientSecret', self.inputClientSecret.GetValue().strip())
# sEsi = Esi.getInstance()
# sEsi.delAllCharacters()
#
def getImage(self):
return BitmapLoader.getBitmap("eve", "gui")
PFEsiPref.register()

View File

@@ -42,6 +42,15 @@ class PFGeneralPref(PreferenceView):
self.inputLogPath.SetBackgroundColour((200, 200, 200))
mainSizer.Add(self.inputLogPath, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5)
import requests
self.certPath = wx.StaticText(panel, wx.ID_ANY, "Cert Path:", wx.DefaultPosition, wx.DefaultSize, 0)
self.certPath .Wrap(-1)
mainSizer.Add(self.certPath, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
self.certPathCtrl = wx.TextCtrl(panel, wx.ID_ANY, requests.certs.where(), wx.DefaultPosition, wx.DefaultSize, 0)
self.certPathCtrl.SetEditable(False)
self.certPathCtrl.SetBackgroundColour((200, 200, 200))
mainSizer.Add(self.certPathCtrl, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5)
# Debug Logging
self.cbdebugLogging = wx.CheckBox(panel, wx.ID_ANY, "Debug Logging Enabled", wx.DefaultPosition, wx.DefaultSize, 0)
mainSizer.Add(self.cbdebugLogging, 0, wx.ALL | wx.EXPAND, 5)

View File

@@ -121,11 +121,11 @@ class PFNetworkPref(PreferenceView):
self.stPSetLogin = wx.StaticText(panel, wx.ID_ANY, "Username:", wx.DefaultPosition, wx.DefaultSize, 0)
self.stPSetLogin.Wrap(-1)
self.editProxySettingsLogin = wx.TextCtrl(panel, wx.ID_ANY, self.nAuth[0], wx.DefaultPosition, wx.DefaultSize,
0)
0)
self.stPSetPassword = wx.StaticText(panel, wx.ID_ANY, "Password:", wx.DefaultPosition, wx.DefaultSize, 0)
self.stPSetPassword.Wrap(-1)
self.editProxySettingsPassword = wx.TextCtrl(panel, wx.ID_ANY, self.nAuth[1], wx.DefaultPosition,
wx.DefaultSize, wx.TE_PASSWORD)
wx.DefaultSize, wx.TE_PASSWORD)
pAuthSizer = wx.BoxSizer(wx.HORIZONTAL)
pAuthSizer.Add(self.stPSetLogin, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
pAuthSizer.Add(self.editProxySettingsLogin, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
@@ -134,7 +134,7 @@ class PFNetworkPref(PreferenceView):
mainSizer.Add(pAuthSizer, 0, wx.EXPAND, 5)
self.stPSAutoDetected = wx.StaticText(panel, wx.ID_ANY, "Auto-detected: ", wx.DefaultPosition, wx.DefaultSize,
0)
0)
self.stPSAutoDetected.Wrap(-1)
mainSizer.Add(self.stPSAutoDetected, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)

View File

@@ -289,6 +289,7 @@ class FitItem(SFItem.SFBrowserItem):
def editLostFocus(self, event):
self.RestoreEditButton()
self.Refresh()
event.Skip()
def editCheckEsc(self, event):
if event.GetKeyCode() == wx.WXK_ESCAPE:

View File

@@ -51,9 +51,13 @@ class PFBitmapFrame(wx.Frame):
pass
def OnWindowPaint(self, event):
# todo: evaluate wx.DragImage, might make this class obsolete, however might also lose our customizations
# (like the sexy fade-in animation)
rect = self.GetRect()
canvas = wx.Bitmap(rect.width, rect.height)
mdc = wx.AutoBufferedPaintDC(self)
# todo: convert to context manager after updating to wxPython >v4.0.1 (4.0.1 has a bug, see #1421)
# See #1418 for discussion
mdc = wx.BufferedPaintDC(self)
mdc.SelectObject(canvas)
mdc.DrawBitmap(self.bitmap, 0, 0)
mdc.SetPen(wx.Pen("#000000", width=1))

View File

@@ -129,7 +129,7 @@ class FirepowerViewFull(StatsView):
# Remove effective label
hsizer = self.headerPanel.GetSizer()
hsizer.Hide(self.stEff)
#self.stEff.Destroy()
# self.stEff.Destroy()
# Get the new view
view = StatsView.getView("miningyieldViewFull")(self.parent)

View File

@@ -76,6 +76,31 @@ class Miscellanea(ViewColumn):
stuff.getModifiedItemAttr("boosterDuration")
text = "{0} min".format(formatAmount(stuff.getModifiedItemAttr("boosterDuration") / 1000 / 60, 3, 0, 3))
return text, "Booster Duration"
elif itemGroup in ("Super Weapon", "Structure Doomsday Weapon"):
doomsday_duration = stuff.getModifiedItemAttr("doomsdayDamageDuration", 1)
doomsday_dottime = stuff.getModifiedItemAttr("doomsdayDamageCycleTime", 1)
func = stuff.getModifiedItemAttr
volley = sum(
map(
lambda attr: (func("%sDamage" % attr) or 0),
("em", "thermal", "kinetic", "explosive")
)
)
volley *= stuff.getModifiedItemAttr("damageMultiplier") or 1
if volley <= 0:
text = ""
tooltip = ""
elif max(doomsday_duration / doomsday_dottime, 1) > 1:
text = "{0} dmg over {1} s".format(formatAmount(volley * (doomsday_duration / doomsday_dottime), 3, 0, 3), doomsday_duration / 1000)
tooltip = "Raw damage done over time"
else:
text = "{0} dmg".format(formatAmount(volley * (doomsday_duration / doomsday_dottime), 3, 0, 3))
tooltip = "Raw damage done"
return text, tooltip
pass
elif itemGroup in ("Energy Weapon", "Hybrid Weapon", "Projectile Weapon", "Combat Drone", "Fighter Drone"):
trackingSpeed = stuff.getModifiedItemAttr("trackingSpeed")
if not trackingSpeed:

View File

@@ -69,11 +69,12 @@ class FitSpawner(gui.multiSwitch.TabSpawner):
pyfalog.critical(e)
if count < 0:
startup = getattr(event, "startup", False) # see OpenFitsThread in gui.mainFrame
from_import = getattr(event, "from_import", False) # always open imported into a new tab
sFit = Fit.getInstance()
openFitInNew = sFit.serviceFittingOptions["openFitInNew"]
mstate = wx.GetMouseState()
if (not openFitInNew and mstate.CmdDown()) or startup or (openFitInNew and not mstate.CmdDown()):
if from_import or (not openFitInNew and mstate.CmdDown()) or startup or (openFitInNew and not mstate.CmdDown()):
self.multiSwitch.AddPage()
view = self.multiSwitch.GetSelectedPage()
@@ -87,7 +88,7 @@ class FitSpawner(gui.multiSwitch.TabSpawner):
def handleDrag(self, type, fitID):
if type == "fit":
for page in self.multiSwitch.pages:
for page in self.multiSwitch._pages:
if isinstance(page, FittingView) and page.activeFitID == fitID:
index = self.multiSwitch.GetPageIndex(page)
self.multiSwitch.SetSelection(index)
@@ -221,12 +222,15 @@ class FittingView(d.Display):
wx.PostEvent(self.mainFrame, FitSelected(fitID=fitID))
def Destroy(self):
# @todo: when wxPython 4.0.2 is release, https://github.com/pyfa-org/Pyfa/issues/1586#issuecomment-390074915
# Make sure to remove the shitty checks that I have to put in place for these handlers to ignore when self is None
print("+++++ Destroy " + repr(self))
print(self.parent.Unbind(EVT_NOTEBOOK_PAGE_CHANGED))
print(self.mainFrame.Unbind(GE.FIT_CHANGED))
print(self.mainFrame.Unbind(EVT_FIT_RENAMED))
print(self.mainFrame.Unbind(EVT_FIT_REMOVED))
print(self.mainFrame.Unbind(ITEM_SELECTED))
# print(self.parent.Unbind(EVT_NOTEBOOK_PAGE_CHANGED))
# print(self.mainFrame.Unbind(GE.FIT_CHANGED, handler=self.fitChanged))
# print(self.mainFrame.Unbind(EVT_FIT_RENAMED, handler=self.fitRenamed ))
# print(self.mainFrame.Unbind(EVT_FIT_REMOVED, handler=self.fitRemoved))
# print(self.mainFrame.Unbind(ITEM_SELECTED, handler=self.appendItem))
d.Display.Destroy(self)
@@ -290,6 +294,9 @@ class FittingView(d.Display):
"""
print('_+_+_+_+_+_ Fit Removed: {} {} activeFitID: {}, eventFitID: {}'.format(repr(self), str(bool(self)), self.activeFitID, event.fitID))
pyfalog.debug("FittingView::fitRemoved")
if not self:
event.Skip()
return
if event.fitID == self.getActiveFit():
pyfalog.debug(" Deleted fit is currently active")
self.parent.DeletePage(self.parent.GetPageIndex(self))
@@ -297,8 +304,12 @@ class FittingView(d.Display):
try:
# Sometimes there is no active page after deletion, hence the try block
sFit = Fit.getInstance()
sFit.refreshFit(self.getActiveFit())
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.activeFitID))
# stopgap for #1384
fit = sFit.getFit(self.getActiveFit())
if fit:
sFit.refreshFit(self.getActiveFit())
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.activeFitID))
except RuntimeError:
pyfalog.warning("Caught dead object")
pass
@@ -306,6 +317,9 @@ class FittingView(d.Display):
event.Skip()
def fitRenamed(self, event):
if not self:
event.Skip()
return
fitID = event.fitID
if fitID == self.getActiveFit():
self.updateTab()
@@ -342,6 +356,9 @@ class FittingView(d.Display):
self.parent.SetPageTextIcon(pageIndex, text, bitmap)
def appendItem(self, event):
if not self:
event.Skip()
return
if self.parent.IsActive(self):
itemID = event.itemID
fitID = self.activeFitID
@@ -523,7 +540,9 @@ class FittingView(d.Display):
def fitChanged(self, event):
print('====== Fit Changed: {} {} activeFitID: {}, eventFitID: {}'.format(repr(self), str(bool(self)), self.activeFitID, event.fitID))
if not self:
event.Skip()
return
try:
if self.activeFitID is not None and self.activeFitID == event.fitID:
self.generateMods()
@@ -693,28 +712,26 @@ class FittingView(d.Display):
# pyfalog.critical(e)
def OnShow(self, event):
pass
# if event.Show():
# try:
# self.MakeSnapshot()
# except Exception as e:
# pyfalog.critical("Failed to make snapshot")
# pyfalog.critical(e)
# event.Skip()
if self and not self.IsShown():
try:
self.MakeSnapshot()
except Exception as e:
pyfalog.critical("Failed to make snapshot")
pyfalog.critical(e)
event.Skip()
def Snapshot(self):
return self.FVsnapshot
# noinspection PyPropertyAccess
def MakeSnapshot(self, maxColumns=1337):
if self.FVsnapshot:
del self.FVsnapshot
tbmp = wx.Bitmap(16, 16)
tdc = wx.MemoryDC()
tdc.SelectObject(tbmp)
font = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
tdc.SetFont(font)
columnsWidths = []
@@ -727,6 +744,7 @@ class FittingView(d.Display):
except Exception as e:
pyfalog.critical("Failed to get fit")
pyfalog.critical(e)
return
if fit is None:
return

View File

@@ -30,9 +30,10 @@ from gui.bitmap_loader import BitmapLoader
from gui.contextMenu import ContextMenu
import gui.globalEvents as GE
from gui.builtinViews.implantEditor import BaseImplantEditorView
from gui.builtinViews.entityEditor import EntityEditor, BaseValidator
from gui.builtinViews.entityEditor import EntityEditor, BaseValidator, TextEntryValidatedDialog
from service.fit import Fit
from service.character import Character
from service.esi import Esi
from service.network import AuthenticationError, TimeoutError
from service.market import Market
from logbook import Logger
@@ -42,9 +43,22 @@ from wx.lib.agw.floatspin import FloatSpin
from gui.utils.clipboard import toClipboard, fromClipboard
import roman
import re
import webbrowser
pyfalog = Logger(__name__)
def arabicOrRomanToInt(s):
m = re.match(r'\d+$', s)
if m:
i = int(s)
else:
i = roman.fromRoman(s)
return i
class CharacterTextValidor(BaseValidator):
def __init__(self):
BaseValidator.__init__(self)
@@ -53,14 +67,14 @@ class CharacterTextValidor(BaseValidator):
return CharacterTextValidor()
def Validate(self, win):
entityEditor = win.parent
textCtrl = self.GetWindow()
text = textCtrl.GetValue().strip()
sChar = Character.getInstance()
try:
if len(text) == 0:
raise ValueError("You must supply a name for the Character!")
elif text in [x.name for x in entityEditor.choices]:
elif text in [x.name for x in sChar.getCharacterList()]:
raise ValueError("Character name already in use, please choose another.")
return True
@@ -162,7 +176,7 @@ class CharacterEditor(wx.Frame):
self.viewsNBContainer.AddPage(self.sview, "Skills")
self.viewsNBContainer.AddPage(self.iview, "Implants")
self.viewsNBContainer.AddPage(self.aview, "API")
self.viewsNBContainer.AddPage(self.aview, "EVE SSO")
mainSizer.Add(self.viewsNBContainer, 1, wx.EXPAND | wx.ALL, 5)
@@ -230,8 +244,8 @@ class CharacterEditor(wx.Frame):
def saveCharAs(self, event):
char = self.entityEditor.getActiveEntity()
dlg = SaveCharacterAs(self, char.ID)
dlg.ShowModal()
self.SaveCharacterAs(self, char.ID)
wx.PostEvent(self, GE.CharListUpdated())
def revertChar(self, event):
sChr = Character.getInstance()
@@ -273,6 +287,22 @@ class CharacterEditor(wx.Frame):
wx.Frame.Destroy(self)
@staticmethod
def SaveCharacterAs(parent, charID):
sChar = Character.getInstance()
name = sChar.getCharName(charID)
dlg = TextEntryValidatedDialog(parent, CharacterTextValidor,
"Enter a name for your new Character:",
"Save Character As...")
dlg.SetValue("{} Copy".format(name))
dlg.txtctrl.SetInsertionPointEnd()
dlg.CenterOnParent()
if dlg.ShowModal() == wx.ID_OK:
sChar = Character.getInstance()
return sChar.saveCharacterAs(charID, dlg.txtctrl.GetValue().strip())
class SkillTreeView(wx.Panel):
def __init__(self, parent):
@@ -414,7 +444,8 @@ class SkillTreeView(wx.Panel):
lines = text.splitlines()
for l in lines:
skill, level = l.strip()[:-1].strip(), int(l.strip()[-1])
s = l.strip()
skill, level = s.rsplit(None, 1)[0], arabicOrRomanToInt(s.rsplit(None, 1)[1])
skill = char.getSkill(skill)
if skill:
skill.setLevel(level, ignoreRestrict=True)
@@ -693,118 +724,132 @@ class APIView(wx.Panel):
self.charEditor = self.Parent.Parent # first parent is Notebook, second is Character Editor
self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW))
self.apiUrlCreatePredefined = "https://community.eveonline.com/support/api-key/CreatePredefined?accessMask=8"
self.apiUrlKeyList = "https://community.eveonline.com/support/api-key/"
pmainSizer = wx.BoxSizer(wx.VERTICAL)
hintSizer = wx.BoxSizer(wx.HORIZONTAL)
hintSizer.AddStretchSpacer()
self.stDisabledTip = wx.StaticText(self, wx.ID_ANY,
"You cannot add API Details for All 0 and All 5 characters.\n"
"You cannot link All 0 or All 5 characters to an EVE character.\n"
"Please select another character or make a new one.", style=wx.ALIGN_CENTER)
self.stDisabledTip.Wrap(-1)
hintSizer.Add(self.stDisabledTip, 0, wx.TOP | wx.BOTTOM, 10)
self.stDisabledTip.Hide()
hintSizer.AddStretchSpacer()
pmainSizer.Add(hintSizer, 0, wx.EXPAND, 5)
fgSizerInput = wx.FlexGridSizer(3, 2, 0, 0)
fgSizerInput = wx.FlexGridSizer(1, 3, 0, 0)
fgSizerInput.AddGrowableCol(1)
fgSizerInput.SetFlexibleDirection(wx.BOTH)
fgSizerInput.SetNonFlexibleGrowMode(wx.FLEX_GROWMODE_SPECIFIED)
self.m_staticIDText = wx.StaticText(self, wx.ID_ANY, "keyID:", wx.DefaultPosition, wx.DefaultSize, 0)
self.m_staticIDText.Wrap(-1)
fgSizerInput.Add(self.m_staticIDText, 0, wx.ALL | wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL, 5)
self.inputID = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0)
fgSizerInput.Add(self.inputID, 1, wx.ALL | wx.EXPAND, 5)
self.m_staticKeyText = wx.StaticText(self, wx.ID_ANY, "vCode:", wx.DefaultPosition, wx.DefaultSize, 0)
self.m_staticKeyText.Wrap(-1)
fgSizerInput.Add(self.m_staticKeyText, 0, wx.ALL | wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL, 5)
self.inputKey = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0)
fgSizerInput.Add(self.inputKey, 0, wx.ALL | wx.EXPAND, 5)
self.m_staticCharText = wx.StaticText(self, wx.ID_ANY, "Character:", wx.DefaultPosition, wx.DefaultSize, 0)
self.m_staticCharText.Wrap(-1)
fgSizerInput.Add(self.m_staticCharText, 0, wx.ALL | wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL, 5)
fgSizerInput.Add(self.m_staticCharText, 0, wx.ALL | wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL, 10)
self.charChoice = wx.Choice(self, wx.ID_ANY, style=0)
self.charChoice.Append("No Selection", 0)
fgSizerInput.Add(self.charChoice, 1, wx.ALL | wx.EXPAND, 5)
fgSizerInput.Add(self.charChoice, 1, wx.TOP | wx.BOTTOM | wx.EXPAND, 10)
self.charChoice.Enable(False)
self.fetchButton = wx.Button(self, wx.ID_ANY, "Get Skills", wx.DefaultPosition, wx.DefaultSize, 0)
self.fetchButton.Bind(wx.EVT_BUTTON, self.fetchSkills)
fgSizerInput.Add(self.fetchButton, 0, wx.ALL | wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL, 10)
pmainSizer.Add(fgSizerInput, 0, wx.EXPAND, 5)
btnSizer = wx.BoxSizer(wx.HORIZONTAL)
btnSizer.AddStretchSpacer()
self.btnFetchCharList = wx.Button(self, wx.ID_ANY, "Get Characters")
btnSizer.Add(self.btnFetchCharList, 0, wx.ALL, 2)
self.btnFetchCharList.Bind(wx.EVT_BUTTON, self.fetchCharList)
self.btnFetchSkills = wx.Button(self, wx.ID_ANY, "Fetch Skills")
btnSizer.Add(self.btnFetchSkills, 0, wx.ALL, 2)
self.btnFetchSkills.Bind(wx.EVT_BUTTON, self.fetchSkills)
self.btnFetchSkills.Enable(False)
btnSizer.AddStretchSpacer()
pmainSizer.Add(btnSizer, 0, wx.EXPAND, 5)
self.stStatus = wx.StaticText(self, wx.ID_ANY, wx.EmptyString)
pmainSizer.Add(self.stStatus, 0, wx.ALL, 5)
pmainSizer.AddStretchSpacer()
self.stAPITip = wx.StaticText(self, wx.ID_ANY,
"You can create a pre-defined key here (only CharacterSheet is required):",
wx.DefaultPosition, wx.DefaultSize, 0)
self.stAPITip.Wrap(-1)
pmainSizer.Add(self.stAPITip, 0, wx.ALL, 2)
self.m_staticline1 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL)
pmainSizer.Add(self.m_staticline1, 0, wx.EXPAND | wx.ALL, 10)
self.hlEveAPI = wx.lib.agw.hyperlink.HyperLinkCtrl(self, wx.ID_ANY, label=self.apiUrlCreatePredefined)
pmainSizer.Add(self.hlEveAPI, 0, wx.ALL, 2)
self.noCharactersTip = wx.StaticText(self, wx.ID_ANY, "Don't see your EVE character in the list?", style=wx.ALIGN_CENTER)
self.stAPITip2 = wx.StaticText(self, wx.ID_ANY, "Or, you can choose an existing key from:", wx.DefaultPosition,
wx.DefaultSize, 0)
self.stAPITip2.Wrap(-1)
pmainSizer.Add(self.stAPITip2, 0, wx.ALL, 2)
self.noCharactersTip.Wrap(-1)
pmainSizer.Add(self.noCharactersTip, 0, wx.CENTER | wx.TOP | wx.BOTTOM, 0)
self.hlEveAPI2 = wx.lib.agw.hyperlink.HyperLinkCtrl(self, wx.ID_ANY, label=self.apiUrlKeyList)
pmainSizer.Add(self.hlEveAPI2, 0, wx.ALL, 2)
self.addButton = wx.Button(self, wx.ID_ANY, "Log In with EVE SSO", wx.DefaultPosition, wx.DefaultSize, 0)
self.addButton.Bind(wx.EVT_BUTTON, self.addCharacter)
pmainSizer.Add(self.addButton, 0, wx.ALL | wx.ALIGN_CENTER, 10)
self.charEditor.mainFrame.Bind(GE.EVT_SSO_LOGOUT, self.ssoListChanged)
self.charEditor.mainFrame.Bind(GE.EVT_SSO_LOGIN, self.ssoListChanged)
self.charEditor.entityEditor.Bind(wx.EVT_CHOICE, self.charChanged)
self.charChoice.Bind(wx.EVT_CHOICE, self.ssoCharChanged)
self.SetSizer(pmainSizer)
self.Layout()
self.charChanged(None)
self.ssoListChanged(None)
def ssoCharChanged(self, event):
sChar = Character.getInstance()
activeChar = self.charEditor.entityEditor.getActiveEntity()
ssoChar = self.getActiveCharacter()
sChar.setSsoCharacter(activeChar.ID, ssoChar)
self.fetchButton.Enable(ssoChar is not None)
event.Skip()
def fetchSkills(self, evt):
sChar = Character.getInstance()
char = self.charEditor.entityEditor.getActiveEntity()
sChar.apiFetch(char.ID, self.__fetchCallback)
def addCharacter(self, event):
sEsi = Esi.getInstance()
sEsi.login()
def getActiveCharacter(self):
selection = self.charChoice.GetCurrentSelection()
return self.charChoice.GetClientData(selection) if selection is not -1 else None
def ssoListChanged(self, event):
if not self: # todo: fix event not unbinding properly
return
self.charChanged(event)
def charChanged(self, event):
sChar = Character.getInstance()
sEsi = Esi.getInstance()
activeChar = self.charEditor.entityEditor.getActiveEntity()
ID, key, char, chars = sChar.getApiDetails(activeChar.ID)
self.inputID.SetValue(str(ID))
self.inputKey.SetValue(key)
if event and event.EventType == GE.EVT_SSO_LOGIN.typeId and hasattr(event, 'character'):
# Automatically assign the character that was just logged into
sChar.setSsoCharacter(activeChar.ID, event.character.ID)
sso = sChar.getSsoCharacter(activeChar.ID)
self.fetchButton.Enable(sso is not None)
ssoChars = sEsi.getSsoCharacters()
self.charChoice.Clear()
if chars:
for charName in chars:
self.charChoice.Append(charName)
self.charChoice.SetStringSelection(char)
self.charChoice.Enable(True)
self.btnFetchSkills.Enable(True)
else:
self.charChoice.Append("No characters...", 0)
self.charChoice.SetSelection(0)
self.charChoice.Enable(False)
self.btnFetchSkills.Enable(False)
noneID = self.charChoice.Append("None", None)
for char in ssoChars:
currId = self.charChoice.Append(char.characterName, char.ID)
if sso is not None and char.ID == sso.ID:
self.charChoice.SetSelection(currId)
if sso is None:
self.charChoice.SetSelection(noneID)
#
# if chars:
# for charName in chars:
# self.charChoice.Append(charName)
# self.charChoice.SetStringSelection(char)
# else:
# self.charChoice.Append("No characters...", 0)
# self.charChoice.SetSelection(0)
#
if activeChar.name in ("All 0", "All 5"):
self.Enable(False)
self.stDisabledTip.Show()
@@ -817,85 +862,18 @@ class APIView(wx.Panel):
if event is not None:
event.Skip()
def fetchCharList(self, event):
self.stStatus.SetLabel("")
if self.inputID.GetLineText(0) == "" or self.inputKey.GetLineText(0) == "":
self.stStatus.SetLabel("Invalid keyID or vCode!")
return
sChar = Character.getInstance()
try:
activeChar = self.charEditor.entityEditor.getActiveEntity()
list = sChar.apiCharList(activeChar.ID, self.inputID.GetLineText(0), self.inputKey.GetLineText(0))
except AuthenticationError as e:
msg = "Authentication failure. Please check keyID and vCode combination."
pyfalog.info(msg)
self.stStatus.SetLabel(msg)
except TimeoutError as e:
msg = "Request timed out. Please check network connectivity and/or proxy settings."
pyfalog.info(msg)
self.stStatus.SetLabel(msg)
except Exception as e:
pyfalog.error(e)
self.stStatus.SetLabel("Error:\n%s" % e)
else:
self.charChoice.Clear()
for charName in list:
self.charChoice.Append(charName)
self.btnFetchSkills.Enable(True)
self.charChoice.Enable(True)
self.Layout()
self.charChoice.SetSelection(0)
def fetchSkills(self, event):
charName = self.charChoice.GetString(self.charChoice.GetSelection())
if charName:
sChar = Character.getInstance()
activeChar = self.charEditor.entityEditor.getActiveEntity()
sChar.apiFetch(activeChar.ID, charName, self.__fetchCallback)
self.stStatus.SetLabel("Getting skills for {}".format(charName))
def __fetchCallback(self, e=None):
charName = self.charChoice.GetString(self.charChoice.GetSelection())
if e is None:
self.stStatus.SetLabel("Successfully fetched {}\'s skills from EVE API.".format(charName))
else:
if e:
exc_type, exc_obj, exc_trace = e
pyfalog.error("Unable to retrieve {0}\'s skills. Error message:\n{1}".format(charName, exc_obj))
self.stStatus.SetLabel("Unable to retrieve {}\'s skills. Error message:\n{}".format(charName, exc_obj))
pyfalog.warn("Error fetching skill information for character")
pyfalog.warn(exc_obj)
class SaveCharacterAs(wx.Dialog):
def __init__(self, parent, charID):
wx.Dialog.__init__(self, parent, title="Save Character As...", size=wx.Size(300, 60))
self.charID = charID
self.parent = parent
sChar = Character.getInstance()
name = sChar.getCharName(charID)
bSizer1 = wx.BoxSizer(wx.HORIZONTAL)
self.input = wx.TextCtrl(self, wx.ID_ANY, name, style=wx.TE_PROCESS_ENTER)
bSizer1.Add(self.input, 1, wx.ALL, 5)
self.input.Bind(wx.EVT_TEXT_ENTER, self.change)
self.button = wx.Button(self, wx.ID_OK, "Save")
bSizer1.Add(self.button, 0, wx.ALL, 5)
self.SetSizer(bSizer1)
self.Layout()
self.Centre(wx.BOTH)
self.button.Bind(wx.EVT_BUTTON, self.change)
def change(self, event):
sChar = Character.getInstance()
sChar.saveCharacterAs(self.charID, self.input.GetLineText(0))
wx.PostEvent(self.parent, GE.CharListUpdated())
event.Skip()
self.Close()
wx.MessageBox(
"Error fetching skill information",
"Error", wx.ICON_ERROR | wx.STAY_ON_TOP)
else:
wx.MessageBox(
"Successfully fetched skills", "Success", wx.ICON_INFORMATION | wx.STAY_ON_TOP)
class SecStatusDialog(wx.Dialog):

View File

@@ -79,6 +79,7 @@ class CharacterSelection(wx.Panel):
self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged)
self.SetMinSize(wx.Size(25, -1))
self.toggleRefreshButton()
self.charChoice.Enable(False)
@@ -97,7 +98,7 @@ class CharacterSelection(wx.Panel):
grantItem = menu.Append(wx.ID_ANY, "Grant Missing Skills")
self.Bind(wx.EVT_MENU, self.grantMissingSkills, grantItem)
exportItem = menu.Append(wx.ID_ANY, "Export Missing Skills")
exportItem = menu.Append(wx.ID_ANY, "Copy Missing Skills")
self.Bind(wx.EVT_MENU, self.exportSkills, exportItem)
self.PopupMenu(menu, pos)
@@ -151,9 +152,7 @@ class CharacterSelection(wx.Panel):
def refreshApi(self, event):
self.btnRefresh.Enable(False)
sChar = Character.getInstance()
ID, key, charName, chars = sChar.getApiDetails(self.getActiveCharacter())
if charName:
sChar.apiFetch(self.getActiveCharacter(), charName, self.refreshAPICallback)
sChar.apiFetch(self.getActiveCharacter(), self.refreshAPICallback)
def refreshAPICallback(self, e=None):
self.btnRefresh.Enable(True)
@@ -161,33 +160,39 @@ class CharacterSelection(wx.Panel):
self.refreshCharacterList()
else:
exc_type, exc_obj, exc_trace = e
pyfalog.warn("Error fetching API information for character")
pyfalog.warn("Error fetching skill information for character")
pyfalog.warn(exc_obj)
wx.MessageBox(
"Error fetching API information, please check your API details in the character editor and try again later",
"Error fetching skill information",
"Error", wx.ICON_ERROR | wx.STAY_ON_TOP)
def charChanged(self, event):
fitID = self.mainFrame.getActiveFit()
charID = self.getActiveCharacter()
sChar = Character.getInstance()
if charID == -1:
# revert to previous character
self.charChoice.SetSelection(self.charCache)
self.mainFrame.showCharacterEditor(event)
return
if sChar.getCharName(charID) not in ("All 0", "All 5") and sChar.apiEnabled(charID):
self.btnRefresh.Enable(True)
else:
self.btnRefresh.Enable(False)
self.toggleRefreshButton()
sFit = Fit.getInstance()
sFit.changeChar(fitID, charID)
self.charCache = self.charChoice.GetCurrentSelection()
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
def toggleRefreshButton(self):
charID = self.getActiveCharacter()
sChar = Character.getInstance()
char = sChar.getCharacter(charID)
if sChar.getCharName(charID) not in ("All 0", "All 5") and sChar.getSsoCharacter(char.ID) is not None:
self.btnRefresh.Enable(True)
else:
self.btnRefresh.Enable(False)
def selectChar(self, charID):
choice = self.charChoice
numItems = len(choice.GetItems())
@@ -244,6 +249,8 @@ class CharacterSelection(wx.Panel):
if not fit.calculated:
self.charChanged(None)
self.toggleRefreshButton()
event.Skip()
def exportSkills(self, evt):

View File

View File

@@ -1,4 +1,4 @@
#===============================================================================
# ===============================================================================
#
# ToDo: Bug - when selecting close on a tab, sometimes the tab to the right is
# selected, most likely due to determination of mouse position
@@ -11,7 +11,7 @@
# tab index?). This will also help with finding close buttons.
# ToDo: Fix page preview code (PFNotebookPagePreview)
#
#= ==============================================================================
# ===============================================================================
import wx
import wx.lib.newevent
@@ -19,7 +19,7 @@ import wx.lib.newevent
from gui.bitmap_loader import BitmapLoader
from gui.utils import draw
from gui.utils import color as color_utils
from service.fit import Fit
_PageChanging, EVT_NOTEBOOK_PAGE_CHANGING = wx.lib.newevent.NewEvent()
_PageChanged, EVT_NOTEBOOK_PAGE_CHANGED = wx.lib.newevent.NewEvent()
@@ -413,7 +413,7 @@ class _TabRenderer:
mdc.SelectObject(ebmp)
mdc.SetFont(self.font)
textSizeX, textSizeY = mdc.GetTextExtent(self.text)
totalSize = self.left_width + self.right_width + textSizeX + self.close_btn_width / 2 + 16 + self.padding* 2
totalSize = self.left_width + self.right_width + textSizeX + self.close_btn_width / 2 + 16 + self.padding * 2
mdc.SelectObject(wx.NullBitmap)
return totalSize, self.tab_height
@@ -1057,6 +1057,10 @@ class _TabsContainer(wx.Panel):
Checks to see if we have a tab preview and sets up the timer for it
to display
"""
sFit = Fit.getInstance()
if not sFit.serviceFittingOptions["showTooltip"] or False:
return
if self.preview_timer:
if self.preview_timer.IsRunning():
if self.preview_wnd:
@@ -1286,6 +1290,7 @@ class _TabsContainer(wx.Panel):
if not self.preview_tab.GetSelected():
page = self.Parent.GetPage(self.GetTabIndex(self.preview_tab))
if page.Snapshot():
self.preview_wnd = PFNotebookPagePreview(
self,
(mposx + 3, mposy + 3),
@@ -1373,7 +1378,7 @@ class PFNotebookPagePreview(wx.Frame):
def OnWindowPaint(self, event):
rect = self.GetRect()
canvas = wx.Bitmap(rect.width, rect.height)
mdc = wx.AudoBufferedPaintDC(self)
mdc = wx.BufferedPaintDC(self)
mdc.SelectObject(canvas)
color = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW)
mdc.SetBackground(wx.Brush(color))
@@ -1473,4 +1478,3 @@ if __name__ == "__main__":
top = Frame("Test Chrome Tabs")
top.Show()
app.MainLoop()

View File

@@ -27,7 +27,7 @@ class CopySelectDialog(wx.Dialog):
copyFormatEftImps = 1
copyFormatXml = 2
copyFormatDna = 3
copyFormatCrest = 4
copyFormatEsi = 4
copyFormatMultiBuy = 5
def __init__(self, parent):
@@ -40,7 +40,7 @@ class CopySelectDialog(wx.Dialog):
CopySelectDialog.copyFormatEftImps: "EFT text format",
CopySelectDialog.copyFormatXml: "EVE native XML format",
CopySelectDialog.copyFormatDna: "A one-line text format",
CopySelectDialog.copyFormatCrest: "A JSON format used for EVE CREST",
CopySelectDialog.copyFormatEsi: "A JSON format used for EVE CREST",
CopySelectDialog.copyFormatMultiBuy: "MultiBuy text format"}
selector = wx.RadioBox(self, wx.ID_ANY, label="Copy to the clipboard using:", choices=copyFormats,
style=wx.RA_SPECIFY_ROWS)

View File

@@ -17,7 +17,6 @@
# along with pyfa. If not, see <http://www.gnu.org/licenses/>.
# =============================================================================
import sys
# noinspection PyPackageRequirements
import wx
import gui.mainFrame
@@ -182,13 +181,13 @@ class Display(wx.ListCtrl):
if listItemCount < stuffItemCount:
for i in range(stuffItemCount - listItemCount):
self.InsertItem(sys.maxsize, "")
self.InsertItem(self.GetItemCount(), "")
if listItemCount > stuffItemCount:
if listItemCount - stuffItemCount > 20 > stuffItemCount:
self.DeleteAllItems()
for i in range(stuffItemCount):
self.InsertItem(sys.maxsize, "")
self.InsertItem(self.GetItemCount(), "")
else:
for i in range(listItemCount - stuffItemCount):
self.DeleteItem(self.getLastItem())

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