Compare commits
269 Commits
v2.9.1
...
v2.9.4dev2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c85c735f9a | ||
|
|
c65b582497 | ||
|
|
7f2ac83e17 | ||
|
|
5ef2a40d1e | ||
|
|
5b52da737a | ||
|
|
51e8713cd6 | ||
|
|
c9b60f2c65 | ||
|
|
d777999af4 | ||
|
|
74444d56c4 | ||
|
|
a433c9638a | ||
|
|
672141cffc | ||
|
|
ac132cbb92 | ||
|
|
d9535b08b1 | ||
|
|
d93544b3bc | ||
|
|
2320c3cb57 | ||
|
|
bd5710c676 | ||
|
|
49f1412d91 | ||
|
|
54eea7d702 | ||
|
|
e26bcb2e5e | ||
|
|
7305c0a017 | ||
|
|
7d37b9e0e0 | ||
|
|
87f28db730 | ||
|
|
56d9a8b626 | ||
|
|
cb8f76c582 | ||
|
|
af0b7b92c7 | ||
|
|
9418b7a709 | ||
|
|
47c34f2186 | ||
|
|
2ca418c287 | ||
|
|
775e69305c | ||
|
|
0f1cbb4234 | ||
|
|
306710a314 | ||
|
|
776a4ee977 | ||
|
|
9dccfd756a | ||
|
|
15281ee6ce | ||
|
|
9a0dd6c521 | ||
|
|
a570f291ae | ||
|
|
cde7fdcaba | ||
|
|
e4780bc8ba | ||
|
|
4d35e5aee1 | ||
|
|
f7b705b9e2 | ||
|
|
48f44cdb0c | ||
|
|
013a2264c0 | ||
|
|
8222686dda | ||
|
|
7f2121e98d | ||
|
|
4b6c881dca | ||
|
|
5f9bf4a861 | ||
|
|
154db5df0b | ||
|
|
321b939d3a | ||
|
|
95a1d669f5 | ||
|
|
9e3c9bd056 | ||
|
|
bb9b3780ae | ||
|
|
4c976d9f35 | ||
|
|
52a1314803 | ||
|
|
a5475eb244 | ||
|
|
ba0a5db72f | ||
|
|
2bac4a954f | ||
|
|
e9f3453b04 | ||
|
|
c950592b5b | ||
|
|
1cd42669a0 | ||
|
|
2b24f14122 | ||
|
|
4932b685e1 | ||
|
|
cfffa1d99d | ||
|
|
44a7e53b9e | ||
|
|
b35bdd4e33 | ||
|
|
7f52f6fe44 | ||
|
|
34e49da0c1 | ||
|
|
5132698974 | ||
|
|
832cebcaaf | ||
|
|
4eaccd1eed | ||
|
|
5245f289a5 | ||
|
|
672aed44f2 | ||
|
|
8c890cf9a5 | ||
|
|
8f9a95db93 | ||
|
|
5a056e6d47 | ||
|
|
b121085271 | ||
|
|
b3ef55cb7f | ||
|
|
bbc8fd0f97 | ||
|
|
b11a576922 | ||
|
|
7375258b9f | ||
|
|
c447cf06d7 | ||
|
|
a7dcf1ace6 | ||
|
|
bd3d81e2f8 | ||
|
|
732b7a5ab9 | ||
|
|
3c16600c53 | ||
|
|
d0921ba9ec | ||
|
|
8ec840740b | ||
|
|
580ff1c435 | ||
|
|
d68296bfd7 | ||
|
|
c520b5e4f5 | ||
|
|
2da85888be | ||
|
|
bdd4a8cfb7 | ||
|
|
dbef3b8c84 | ||
|
|
63a6d746e8 | ||
|
|
7b8d9f8dbe | ||
|
|
7aa73e4b2f | ||
|
|
33883c562a | ||
|
|
36c17dde8d | ||
|
|
5a9fd188f0 | ||
|
|
4b65662c9f | ||
|
|
cd3d1a9557 | ||
|
|
9f63b0b412 | ||
|
|
9249ef24b3 | ||
|
|
63a599ca85 | ||
|
|
fa2bceaff9 | ||
|
|
e9dffeadf6 | ||
|
|
161c4629cf | ||
|
|
951ffcd35a | ||
|
|
ba21ebe058 | ||
|
|
f8c2604fb2 | ||
|
|
966763aaa4 | ||
|
|
4eb8973c31 | ||
|
|
72fe52e560 | ||
|
|
e346239174 | ||
|
|
dd27a26fa9 | ||
|
|
162b115c91 | ||
|
|
99f4ed6b33 | ||
|
|
53252241e1 | ||
|
|
698328e335 | ||
|
|
dece788f66 | ||
|
|
958fbac582 | ||
|
|
99d72956e8 | ||
|
|
eb601e991a | ||
|
|
cb7f0052c4 | ||
|
|
8b75b5f184 | ||
|
|
bf5935e033 | ||
|
|
67e80deed9 | ||
|
|
e39f9ffecf | ||
|
|
e865c9a399 | ||
|
|
a919510d07 | ||
|
|
bd86d3289f | ||
|
|
c8ff644b63 | ||
|
|
6703a08976 | ||
|
|
3d70d9e37c | ||
|
|
ef62d5cf97 | ||
|
|
caf5f33c80 | ||
|
|
29c29469c6 | ||
|
|
7b564f1f53 | ||
|
|
f280955ac3 | ||
|
|
e09fce8411 | ||
|
|
5777103d21 | ||
|
|
8671b20790 | ||
|
|
dc30b3ed1d | ||
|
|
07a9f77287 | ||
|
|
9d58ceb14d | ||
|
|
71c421702c | ||
|
|
989f12453d | ||
|
|
b7d6892d9f | ||
|
|
cfb351a751 | ||
|
|
dde1e7990d | ||
|
|
6e4ec54ac6 | ||
|
|
81da217a09 | ||
|
|
2d1613d8bc | ||
|
|
ccc2e6ece3 | ||
|
|
1206e95cfb | ||
|
|
921ccd3be9 | ||
|
|
178e3a3d56 | ||
|
|
5d255547e4 | ||
|
|
bd148b8792 | ||
|
|
b88ebdcfc0 | ||
|
|
3a26815d18 | ||
|
|
b70fcd9659 | ||
|
|
71aa557770 | ||
|
|
d35bf6514f | ||
|
|
abe015bee3 | ||
|
|
929520091f | ||
|
|
d4847112a9 | ||
|
|
4e2c3a3fcc | ||
|
|
91e6d89022 | ||
|
|
b9a71c08b7 | ||
|
|
070dd62e6d | ||
|
|
b404abca41 | ||
|
|
99f00b25a1 | ||
|
|
45936b5b98 | ||
|
|
b1aac9f56d | ||
|
|
13f370ceb9 | ||
|
|
b5a4f97cb5 | ||
|
|
0679a0af0f | ||
|
|
53fe3242b9 | ||
|
|
6615bed1cd | ||
|
|
ad0c7a7a9d | ||
|
|
87ba6a9af0 | ||
|
|
5c44df7f21 | ||
|
|
24bc675319 | ||
|
|
be2e0b5de4 | ||
|
|
e4481e8fb4 | ||
|
|
19b1eb161b | ||
|
|
30ed1ac81d | ||
|
|
b4288e17e5 | ||
|
|
c03d000c45 | ||
|
|
881ec8b5b4 | ||
|
|
71d5b28b75 | ||
|
|
a15fdc3b23 | ||
|
|
55cd33e653 | ||
|
|
af0b2b9f1b | ||
|
|
983641d1d5 | ||
|
|
4ab21e92bf | ||
|
|
413f00a475 | ||
|
|
bde2043294 | ||
|
|
d45857f1fc | ||
|
|
8a19bf78ce | ||
|
|
d523722988 | ||
|
|
031cb6fcfb | ||
|
|
72fc560241 | ||
|
|
1d7be66eb1 | ||
|
|
5d32a31dc3 | ||
|
|
4821bd1c72 | ||
|
|
6694caafa0 | ||
|
|
7b71c16cec | ||
|
|
df6e7b5772 | ||
|
|
7abc14eb7f | ||
|
|
4d21fa517a | ||
|
|
f2a82c31c4 | ||
|
|
337973965a | ||
|
|
f0b3aafd54 | ||
|
|
f6b97859aa | ||
|
|
1064a90a1c | ||
|
|
4c736de598 | ||
|
|
8d0ad26159 | ||
|
|
3efa07d821 | ||
|
|
44240c1d37 | ||
|
|
9eaeb60af7 | ||
|
|
cecb8f69a3 | ||
|
|
d402735c8b | ||
|
|
327ad78eb8 | ||
|
|
edc1ef0e38 | ||
|
|
c6bfd0bc05 | ||
|
|
3badab0353 | ||
|
|
8ca5b34c14 | ||
|
|
417ffd396c | ||
|
|
ccb0732f7d | ||
|
|
a994f55011 | ||
|
|
c2d309430e | ||
|
|
c3e1ec2760 | ||
|
|
8c40489049 | ||
|
|
051800bc16 | ||
|
|
d1a3e5c0e8 | ||
|
|
48d795676f | ||
|
|
3fec9ba173 | ||
|
|
264208b42e | ||
|
|
62e8da6ff2 | ||
|
|
e3f21cf700 | ||
|
|
fbc34224bc | ||
|
|
93cd3b97fa | ||
|
|
fda83bcb49 | ||
|
|
85b046a640 | ||
|
|
1177575f77 | ||
|
|
fc4a10efe3 | ||
|
|
f541b4329e | ||
|
|
7ba1a4c78f | ||
|
|
0675ed9a73 | ||
|
|
68a13a6bb8 | ||
|
|
61ef7c3487 | ||
|
|
a5bb16c460 | ||
|
|
e694ced86c | ||
|
|
babc2d1e42 | ||
|
|
8e717b19d9 | ||
|
|
dbca0f9dea | ||
|
|
7380244cd9 | ||
|
|
5b7c777d6b | ||
|
|
2fb9d3479f | ||
|
|
21f095250d | ||
|
|
270376e09c | ||
|
|
1ed71c6580 | ||
|
|
febc98045c | ||
|
|
72ecc62732 | ||
|
|
9e1681d3f9 | ||
|
|
0471ffa924 | ||
|
|
4269a00428 | ||
|
|
440b2caa8d |
@@ -24,7 +24,7 @@ $ brew install Caskroom/cask/pyfa
|
||||
### Linux Distro-specific Packages
|
||||
The following is a list of pyfa packages available for certain distributions. Please note that these packages are maintained by third-parties and are not evaluated by the pyfa developers.
|
||||
|
||||
* Debian/Ubuntu/derivitives: https://github.com/AdamMajer/Pyfa/releases
|
||||
* Debian/Ubuntu/derivatives: https://github.com/AdamMajer/Pyfa/releases
|
||||
* Arch: https://aur.archlinux.org/packages/pyfa/
|
||||
* openSUSE: https://build.opensuse.org/package/show/home:rmk2/pyfa
|
||||
* FreeBSD: http://www.freshports.org/games/pyfa/ (see [#484](https://github.com/pyfa-org/Pyfa/issues/484) for instructions)
|
||||
@@ -53,6 +53,10 @@ pyfa is licensed under the GNU GPL v3.0, see LICENSE
|
||||
* [TweetFleet Slack](https://www.fuzzwork.co.uk/tweetfleet-slack-invites/): @blitzmann
|
||||
* [Gitter chat](https://gitter.im/pyfa-org/Pyfa): @ blitzmann
|
||||
* Email: sable.blitzmann@gmail.com
|
||||
* Kadesh / DarkPhoenix
|
||||
* GitHub: @DarkFenX
|
||||
* EVE: Kadesh Priestess
|
||||
* Email: phoenix@mail.ru
|
||||
|
||||
## CCP Copyright Notice
|
||||
EVE Online, the EVE logo, EVE and all associated logos and designs are the intellectual property of CCP hf. All artwork, screenshots, characters, vehicles, storylines, world facts or other recognizable features of the intellectual property relating to these trademarks are likewise the intellectual property of CCP hf. EVE Online and the EVE logo are the registered trademarks of CCP hf. All rights are reserved worldwide. All other trademarks are the property of their respective owners. CCP hf. has granted permission to pyfa to use EVE Online and all associated logos and designs for promotional and information purposes on its website but does not endorse, and is not in any way affiliated with, pyfa. CCP is in no way responsible for the content on or functioning of this program, nor can it be liable for any damage arising from the use of this program.
|
||||
|
||||
@@ -29,7 +29,8 @@ added_files = [
|
||||
|
||||
|
||||
import_these = [
|
||||
'numpy.core._dtype_ctypes' # https://github.com/pyinstaller/pyinstaller/issues/3982
|
||||
'numpy.core._dtype_ctypes', # https://github.com/pyinstaller/pyinstaller/issues/3982
|
||||
'sqlalchemy.ext.baked' # windows build doesn't launch without if when using sqlalchemy 1.3.x
|
||||
]
|
||||
|
||||
icon = os.path.join(os.getcwd(), "dist_assets", "mac", "pyfa.icns")
|
||||
@@ -86,4 +87,4 @@ app = BUNDLE(
|
||||
'CFBundleDisplayName': 'pyfa',
|
||||
'CFBundleIdentifier': 'org.pyfaorg.pyfa',
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
@@ -17,7 +17,7 @@ added_files = [
|
||||
('../../imgs/gui/*.gif', 'imgs/gui'),
|
||||
('../../imgs/icons/*.png', 'imgs/icons'),
|
||||
('../../imgs/renders/*.png', 'imgs/renders'),
|
||||
('../../service/jargon/*.yaml', 'service/jargon'),
|
||||
('../../service/jargon/*.yaml', 'service/jargon'),
|
||||
('../../dist_assets/win/pyfa.ico', '.'),
|
||||
('../../dist_assets/win/pyfa.exe.manifest', '.'),
|
||||
('../../dist_assets/win/Microsoft.VC90.CRT.manifest', '.'),
|
||||
@@ -29,7 +29,8 @@ added_files = [
|
||||
]
|
||||
|
||||
import_these = [
|
||||
'numpy.core._dtype_ctypes' # https://github.com/pyinstaller/pyinstaller/issues/3982
|
||||
'numpy.core._dtype_ctypes', # https://github.com/pyinstaller/pyinstaller/issues/3982
|
||||
'sqlalchemy.ext.baked' # windows build doesn't launch without if when using sqlalchemy 1.3.x
|
||||
]
|
||||
|
||||
# Walk directories that do dynamic importing
|
||||
|
||||
@@ -93,3 +93,11 @@ class SpoolType(IntEnum):
|
||||
SCALE = 0 # [0..1]
|
||||
TIME = 1 # Expressed via time in seconds since spool up started
|
||||
CYCLES = 2 # Expressed in amount of cycles since spool up started
|
||||
|
||||
|
||||
@unique
|
||||
class FitSystemSecurity(IntEnum):
|
||||
HISEC = 0
|
||||
LOWSEC = 1
|
||||
NULLSEC = 2
|
||||
WSPACE = 3
|
||||
|
||||
15
eos/db/migrations/upgrade31.py
Normal file
15
eos/db/migrations/upgrade31.py
Normal file
@@ -0,0 +1,15 @@
|
||||
"""
|
||||
Migration 31
|
||||
|
||||
- added fit system security column
|
||||
"""
|
||||
|
||||
|
||||
import sqlalchemy
|
||||
|
||||
|
||||
def upgrade(saveddata_engine):
|
||||
try:
|
||||
saveddata_engine.execute("SELECT systemSecurity FROM fits LIMIT 1")
|
||||
except sqlalchemy.exc.DatabaseError:
|
||||
saveddata_engine.execute("ALTER TABLE fits ADD COLUMN systemSecurity INT")
|
||||
@@ -44,6 +44,7 @@ from eos.saveddata.module import Module
|
||||
from eos.saveddata.targetResists import TargetResists
|
||||
from eos.saveddata.user import User
|
||||
|
||||
|
||||
fits_table = Table("fits", saveddata_meta,
|
||||
Column("ID", Integer, primary_key=True),
|
||||
Column("ownerID", ForeignKey("users.ID"), nullable=True, index=True),
|
||||
@@ -59,7 +60,8 @@ fits_table = Table("fits", saveddata_meta,
|
||||
Column("notes", String, nullable=True),
|
||||
Column("ignoreRestrictions", Boolean, default=0),
|
||||
Column("created", DateTime, nullable=True, default=datetime.datetime.now),
|
||||
Column("modified", DateTime, nullable=True, default=datetime.datetime.now, onupdate=datetime.datetime.now)
|
||||
Column("modified", DateTime, nullable=True, default=datetime.datetime.now, onupdate=datetime.datetime.now),
|
||||
Column("systemSecurity", Integer, nullable=True)
|
||||
)
|
||||
|
||||
projectedFits_table = Table("projectedFits", saveddata_meta,
|
||||
|
||||
@@ -17,10 +17,8 @@
|
||||
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
||||
# ===============================================================================
|
||||
|
||||
from logbook import Logger
|
||||
|
||||
from eos.exception import HandledListActionError
|
||||
from utils.deprecated import deprecated
|
||||
from logbook import Logger
|
||||
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
@@ -137,27 +135,23 @@ class HandledModuleList(HandledList):
|
||||
self.__toModule(emptyPosition, mod)
|
||||
if mod.isInvalid:
|
||||
self.__toDummy(mod.position)
|
||||
raise HandledListActionError(mod)
|
||||
return
|
||||
|
||||
self.appendIgnoreEmpty(mod)
|
||||
else:
|
||||
self.appendIgnoreEmpty(mod)
|
||||
|
||||
def appendIgnoreEmpty(self, mod):
|
||||
mod.position = len(self)
|
||||
HandledList.append(self, mod)
|
||||
if mod.isInvalid:
|
||||
self.remove(mod)
|
||||
raise HandledListActionError(mod)
|
||||
|
||||
def replace(self, idx, mod):
|
||||
try:
|
||||
oldMod = self[idx]
|
||||
except IndexError:
|
||||
raise HandledListActionError(mod)
|
||||
return
|
||||
self.__toModule(idx, mod)
|
||||
if mod.isInvalid:
|
||||
self.__toModule(idx, oldMod)
|
||||
raise HandledListActionError(mod)
|
||||
|
||||
def replaceRackPosition(self, rackPosition, mod):
|
||||
listPositions = []
|
||||
@@ -182,7 +176,6 @@ class HandledModuleList(HandledList):
|
||||
self.__toDummy(modListPosition)
|
||||
else:
|
||||
self.__toModule(modListPosition, oldMod)
|
||||
raise HandledListActionError(mod)
|
||||
|
||||
def insert(self, idx, mod):
|
||||
mod.position = idx
|
||||
@@ -193,8 +186,6 @@ class HandledModuleList(HandledList):
|
||||
HandledList.insert(self, idx, mod)
|
||||
if mod.isInvalid:
|
||||
self.remove(mod)
|
||||
raise HandledListActionError(mod)
|
||||
|
||||
|
||||
def remove(self, mod):
|
||||
HandledList.remove(self, mod)
|
||||
@@ -236,13 +227,11 @@ class HandledDroneCargoList(HandledList):
|
||||
HandledList.append(self, thing)
|
||||
if thing.isInvalid:
|
||||
self.remove(thing)
|
||||
raise HandledListActionError(thing)
|
||||
|
||||
def insert(self, idx, thing):
|
||||
HandledList.insert(self, idx, thing)
|
||||
if thing.isInvalid:
|
||||
self.remove(thing)
|
||||
raise HandledListActionError(thing)
|
||||
|
||||
|
||||
class HandledImplantList(HandledList):
|
||||
@@ -251,23 +240,23 @@ class HandledImplantList(HandledList):
|
||||
if implant.isInvalid:
|
||||
HandledList.append(self, implant)
|
||||
self.remove(implant)
|
||||
raise HandledListActionError(implant)
|
||||
return
|
||||
if self.__slotCheck(implant):
|
||||
HandledList.append(self, implant)
|
||||
self.remove(implant)
|
||||
raise HandledListActionError(implant)
|
||||
return
|
||||
HandledList.append(self, implant)
|
||||
|
||||
def insert(self, idx, implant):
|
||||
if implant.isInvalid:
|
||||
HandledList.insert(self, idx, implant)
|
||||
self.remove(implant)
|
||||
raise HandledListActionError(implant)
|
||||
return
|
||||
if self.__slotCheck(implant):
|
||||
HandledList.insert(self, idx, implant)
|
||||
self.remove(implant)
|
||||
raise HandledListActionError(implant)
|
||||
HandledList.append(self, implant)
|
||||
return
|
||||
HandledList.insert(self, idx, implant)
|
||||
|
||||
def makeRoom(self, implant):
|
||||
# if needed, remove booster that was occupying slot
|
||||
@@ -292,23 +281,23 @@ class HandledBoosterList(HandledList):
|
||||
if booster.isInvalid:
|
||||
HandledList.append(self, booster)
|
||||
self.remove(booster)
|
||||
raise HandledListActionError(booster)
|
||||
return
|
||||
if self.__slotCheck(booster):
|
||||
HandledList.append(self, booster)
|
||||
self.remove(booster)
|
||||
raise HandledListActionError(booster)
|
||||
return
|
||||
HandledList.append(self, booster)
|
||||
|
||||
def insert(self, idx, booster):
|
||||
if booster.isInvalid:
|
||||
HandledList.insert(self, idx, booster)
|
||||
self.remove(booster)
|
||||
raise HandledListActionError(booster)
|
||||
return
|
||||
if self.__slotCheck(booster):
|
||||
HandledList.insert(self, idx, booster)
|
||||
self.remove(booster)
|
||||
raise HandledListActionError(booster)
|
||||
HandledList.append(self, booster)
|
||||
return
|
||||
HandledList.insert(self, idx, booster)
|
||||
|
||||
def makeRoom(self, booster):
|
||||
# if needed, remove booster that was occupying slot
|
||||
@@ -346,16 +335,12 @@ class HandledProjectedModList(HandledList):
|
||||
# rows and relationships in database are removed as well
|
||||
HandledList.append(self, proj)
|
||||
self.remove(proj)
|
||||
raise HandledListActionError(proj)
|
||||
|
||||
return
|
||||
proj.projected = True
|
||||
|
||||
HandledList.append(self, proj)
|
||||
|
||||
# Remove non-projectable modules
|
||||
if not proj.item.isType("projected") and not proj.isExclusiveSystemEffect:
|
||||
self.remove(proj)
|
||||
raise HandledListActionError(proj)
|
||||
|
||||
def insert(self, idx, proj):
|
||||
if proj.isInvalid:
|
||||
@@ -363,16 +348,12 @@ class HandledProjectedModList(HandledList):
|
||||
# rows and relationships in database are removed as well
|
||||
HandledList.insert(self, idx, proj)
|
||||
self.remove(proj)
|
||||
raise HandledListActionError(proj)
|
||||
|
||||
return
|
||||
proj.projected = True
|
||||
|
||||
HandledList.insert(self, idx, proj)
|
||||
|
||||
# Remove non-projectable modules
|
||||
if not proj.item.isType("projected") and not proj.isExclusiveSystemEffect:
|
||||
self.remove(proj)
|
||||
raise HandledListActionError(proj)
|
||||
|
||||
@property
|
||||
def currentSystemEffect(self):
|
||||
@@ -399,24 +380,18 @@ class HandledProjectedDroneList(HandledDroneCargoList):
|
||||
def append(self, proj):
|
||||
proj.projected = True
|
||||
HandledList.append(self, proj)
|
||||
|
||||
# Remove invalid or non-projectable drones
|
||||
if proj.isInvalid or not proj.item.isType("projected"):
|
||||
self.remove(proj)
|
||||
proj.projected = False
|
||||
raise HandledListActionError(proj)
|
||||
return True
|
||||
|
||||
def insert(self, idx, proj):
|
||||
proj.projected = True
|
||||
HandledList.insert(self, idx, proj)
|
||||
|
||||
# Remove invalid or non-projectable drones
|
||||
if proj.isInvalid or not proj.item.isType("projected"):
|
||||
self.remove(proj)
|
||||
proj.projected = False
|
||||
raise HandledListActionError(proj)
|
||||
return True
|
||||
|
||||
|
||||
class HandledItem(object):
|
||||
|
||||
460
eos/effects.py
460
eos/effects.py
@@ -19,7 +19,7 @@
|
||||
|
||||
|
||||
import eos.config
|
||||
from eos.const import FittingModuleState
|
||||
from eos.const import FittingModuleState, FitSystemSecurity
|
||||
from eos.utils.spoolSupport import SpoolType, SpoolOptions, calculateSpoolup, resolveSpoolOptions
|
||||
|
||||
|
||||
@@ -6739,7 +6739,7 @@ class Effect2302(BaseEffect):
|
||||
damageControl
|
||||
|
||||
Used by:
|
||||
Modules from group: Damage Control (22 of 27)
|
||||
Modules from group: Damage Control (24 of 29)
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
@@ -9050,7 +9050,7 @@ class Effect3001(BaseEffect):
|
||||
|
||||
Used by:
|
||||
Modules from group: Missile Launcher Torpedo (22 of 22)
|
||||
Items from market group: Ship Equipment > Turrets & Bays (429 of 883)
|
||||
Items from market group: Ship Equipment > Turrets & Bays (429 of 888)
|
||||
Module: Interdiction Sphere Launcher I
|
||||
"""
|
||||
|
||||
@@ -9113,7 +9113,7 @@ class Effect3025(BaseEffect):
|
||||
Used by:
|
||||
Modules from group: Energy Weapon (101 of 214)
|
||||
Modules from group: Hybrid Weapon (105 of 221)
|
||||
Modules from group: Precursor Weapon (15 of 15)
|
||||
Modules from group: Precursor Weapon (18 of 18)
|
||||
Modules from group: Projectile Weapon (99 of 165)
|
||||
"""
|
||||
|
||||
@@ -10027,16 +10027,16 @@ class Effect3380(BaseEffect):
|
||||
|
||||
@staticmethod
|
||||
def handler(fit, module, context):
|
||||
|
||||
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 > FittingModuleState.ONLINE:
|
||||
mod.state = FittingModuleState.ONLINE
|
||||
if not mod.isEmpty and mod.item.requiresSkill('Micro Jump Drive Operation') and mod.state > FittingModuleState.ONLINE:
|
||||
mod.state = FittingModuleState.ONLINE
|
||||
if module.charge is not None:
|
||||
if module.charge.ID in (29003, 45010):
|
||||
fit.ship.increaseItemAttr('warpScrambleStatus', module.getModifiedItemAttr('warpScrambleStrength'))
|
||||
if module.charge.ID == 45010:
|
||||
fit.modules.filteredItemIncrease(
|
||||
lambda mod: mod.item.requiresSkill('High Speed Maneuvering') or mod.item.requiresSkill('Micro Jump Drive Operation'),
|
||||
'activationBlocked', 1)
|
||||
else:
|
||||
fit.ship.forceItemAttr('disallowAssistance', 1)
|
||||
if module.charge is None:
|
||||
fit.ship.boostItemAttr('mass', module.getModifiedItemAttr('massBonusPercentage'))
|
||||
fit.ship.boostItemAttr('signatureRadius', module.getModifiedItemAttr('signatureRadiusBonus'))
|
||||
@@ -10045,8 +10045,6 @@ class Effect3380(BaseEffect):
|
||||
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == 'Propulsion Module',
|
||||
'speedFactor', module.getModifiedItemAttr('speedFactorBonus'))
|
||||
|
||||
fit.ship.forceItemAttr('disallowAssistance', 1)
|
||||
|
||||
|
||||
class Effect3392(BaseEffect):
|
||||
"""
|
||||
@@ -15234,96 +15232,57 @@ class Effect4575(BaseEffect):
|
||||
|
||||
# Remote Shield Repper Bonuses
|
||||
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill('Capital Shield Emission Systems'),
|
||||
'duration',
|
||||
src.getModifiedItemAttr('industrialCoreRemoteLogisticsDurationBonus'),
|
||||
)
|
||||
'duration', src.getModifiedItemAttr('industrialCoreRemoteLogisticsDurationBonus'))
|
||||
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill('Capital Shield Emission Systems'),
|
||||
'maxRange',
|
||||
src.getModifiedItemAttr('industrialCoreRemoteLogisticsRangeBonus'),
|
||||
stackingPenalties=True
|
||||
)
|
||||
'maxRange', src.getModifiedItemAttr('industrialCoreRemoteLogisticsRangeBonus'),
|
||||
stackingPenalties=True)
|
||||
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill('Capital Shield Emission Systems'),
|
||||
'capacitorNeed',
|
||||
src.getModifiedItemAttr('industrialCoreRemoteLogisticsDurationBonus')
|
||||
)
|
||||
'capacitorNeed', src.getModifiedItemAttr('industrialCoreRemoteLogisticsDurationBonus'))
|
||||
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill('Capital Shield Emission Systems'),
|
||||
'falloffEffectiveness',
|
||||
src.getModifiedItemAttr('industrialCoreRemoteLogisticsRangeBonus'),
|
||||
stackingPenalties=True
|
||||
)
|
||||
'falloffEffectiveness', src.getModifiedItemAttr('industrialCoreRemoteLogisticsRangeBonus'),
|
||||
stackingPenalties=True)
|
||||
|
||||
# Local Shield Repper Bonuses
|
||||
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill('Capital Shield Operation'),
|
||||
'duration',
|
||||
src.getModifiedItemAttr('industrialCoreLocalLogisticsDurationBonus'),
|
||||
)
|
||||
'duration', src.getModifiedItemAttr('industrialCoreLocalLogisticsDurationBonus'))
|
||||
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill('Capital Shield Operation'),
|
||||
'shieldBonus',
|
||||
src.getModifiedItemAttr('industrialCoreLocalLogisticsAmountBonus'),
|
||||
stackingPenalties=True
|
||||
)
|
||||
'shieldBonus', src.getModifiedItemAttr('industrialCoreLocalLogisticsAmountBonus'),
|
||||
stackingPenalties=True)
|
||||
|
||||
# Mining Burst Bonuses
|
||||
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill('Mining Foreman'),
|
||||
'warfareBuff1Value',
|
||||
src.getModifiedItemAttr('industrialCoreBonusMiningBurstStrength'),
|
||||
)
|
||||
|
||||
'warfareBuff1Value', src.getModifiedItemAttr('industrialCoreBonusMiningBurstStrength'))
|
||||
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill('Mining Foreman'),
|
||||
'warfareBuff2Value',
|
||||
src.getModifiedItemAttr('industrialCoreBonusMiningBurstStrength'),
|
||||
)
|
||||
|
||||
'warfareBuff2Value', src.getModifiedItemAttr('industrialCoreBonusMiningBurstStrength'))
|
||||
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill('Mining Foreman'),
|
||||
'warfareBuff3Value',
|
||||
src.getModifiedItemAttr('industrialCoreBonusMiningBurstStrength'),
|
||||
)
|
||||
|
||||
'warfareBuff3Value', src.getModifiedItemAttr('industrialCoreBonusMiningBurstStrength'))
|
||||
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill('Mining Foreman'),
|
||||
'warfareBuff4Value',
|
||||
src.getModifiedItemAttr('industrialCoreBonusMiningBurstStrength'),
|
||||
)
|
||||
'warfareBuff4Value', src.getModifiedItemAttr('industrialCoreBonusMiningBurstStrength'))
|
||||
|
||||
# Command Burst Range Bonus
|
||||
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill('Leadership'),
|
||||
'maxRange',
|
||||
src.getModifiedItemAttr('industrialCoreBonusCommandBurstRange'),
|
||||
stackingPenalties=True
|
||||
)
|
||||
'maxRange', src.getModifiedItemAttr('industrialCoreBonusCommandBurstRange'),
|
||||
stackingPenalties=True)
|
||||
|
||||
# Drone Bonuses
|
||||
fit.drones.filteredItemBoost(lambda drone: drone.item.requiresSkill('Ice Harvesting Drone Operation'),
|
||||
'duration',
|
||||
src.getModifiedItemAttr('industrialCoreBonusDroneIceHarvesting'),
|
||||
)
|
||||
'duration', src.getModifiedItemAttr('industrialCoreBonusDroneIceHarvesting'))
|
||||
fit.drones.filteredItemBoost(lambda drone: drone.item.requiresSkill('Mining Drone Operation'),
|
||||
'miningAmount',
|
||||
src.getModifiedItemAttr('industrialCoreBonusDroneMining'),
|
||||
)
|
||||
'miningAmount', src.getModifiedItemAttr('industrialCoreBonusDroneMining'))
|
||||
fit.drones.filteredItemBoost(lambda drone: drone.item.requiresSkill('Drones'),
|
||||
'maxVelocity',
|
||||
src.getModifiedItemAttr('industrialCoreBonusDroneVelocity'),
|
||||
)
|
||||
'maxVelocity', src.getModifiedItemAttr('industrialCoreBonusDroneVelocity'),
|
||||
stackingPenalties=True)
|
||||
fit.drones.filteredItemBoost(lambda drone: drone.item.requiresSkill('Drones'),
|
||||
'damageMultiplier', src.getModifiedItemAttr('industrialCoreBonusDroneDamageHP'),
|
||||
stackingPenalties=True)
|
||||
fit.drones.filteredItemBoost(lambda drone: drone.item.requiresSkill('Drones'),
|
||||
'shieldCapacity', src.getModifiedItemAttr('industrialCoreBonusDroneDamageHP'))
|
||||
fit.drones.filteredItemBoost(lambda drone: drone.item.requiresSkill('Drones'),
|
||||
'armorHP', src.getModifiedItemAttr('industrialCoreBonusDroneDamageHP'))
|
||||
fit.drones.filteredItemBoost(lambda drone: drone.item.requiresSkill('Drones'),
|
||||
'hp', src.getModifiedItemAttr('industrialCoreBonusDroneDamageHP'))
|
||||
|
||||
fit.drones.filteredItemBoost(lambda drone: drone.item.requiresSkill('Drones'),
|
||||
'damageMultiplier',
|
||||
src.getModifiedItemAttr('industrialCoreBonusDroneDamageHP'),
|
||||
stackingPenalties=True
|
||||
)
|
||||
fit.drones.filteredItemBoost(lambda drone: drone.item.requiresSkill('Drones'),
|
||||
'shieldCapacity',
|
||||
src.getModifiedItemAttr('industrialCoreBonusDroneDamageHP'),
|
||||
)
|
||||
fit.drones.filteredItemBoost(lambda drone: drone.item.requiresSkill('Drones'),
|
||||
'armorHP',
|
||||
src.getModifiedItemAttr('industrialCoreBonusDroneDamageHP'),
|
||||
)
|
||||
fit.drones.filteredItemBoost(lambda drone: drone.item.requiresSkill('Drones'),
|
||||
'hp',
|
||||
src.getModifiedItemAttr('industrialCoreBonusDroneDamageHP'),
|
||||
)
|
||||
|
||||
# Todo: remote impedance (no reps, etc)
|
||||
# Remote impedance (no reps, etc)
|
||||
fit.ship.increaseItemAttr('warpScrambleStatus', src.getModifiedItemAttr('siegeModeWarpStatus'))
|
||||
fit.ship.boostItemAttr('remoteRepairImpedance', src.getModifiedItemAttr('remoteRepairImpedanceBonus'))
|
||||
fit.ship.increaseItemAttr('disallowTethering', src.getModifiedItemAttr('disallowTethering'))
|
||||
@@ -16287,9 +16246,9 @@ class Effect4902(BaseEffect):
|
||||
MWDSignatureRadiusRoleBonus
|
||||
|
||||
Used by:
|
||||
Ships from group: Assault Frigate (8 of 12)
|
||||
Ships from group: Command Destroyer (4 of 4)
|
||||
Ships from group: Heavy Assault Cruiser (8 of 11)
|
||||
Ships from group: Assault Frigate (9 of 13)
|
||||
Ships from group: Command Destroyer (5 of 5)
|
||||
Ships from group: Heavy Assault Cruiser (9 of 12)
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
@@ -23394,16 +23353,9 @@ class Effect5934(BaseEffect):
|
||||
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.state > FittingModuleState.ONLINE and (
|
||||
mod.item.requiresSkill('Micro Jump Drive Operation') or
|
||||
mod.item.requiresSkill('High Speed Maneuvering')
|
||||
):
|
||||
mod.state = FittingModuleState.ONLINE
|
||||
if not mod.isEmpty and mod.item.requiresSkill('Micro Jump Drive Operation') and mod.state > FittingModuleState.ONLINE:
|
||||
mod.state = FittingModuleState.ONLINE
|
||||
fit.modules.filteredItemIncrease(
|
||||
lambda mod: mod.item.requiresSkill('High Speed Maneuvering') or mod.item.requiresSkill('Micro Jump Drive Operation'),
|
||||
'activationBlocked', module.getModifiedItemAttr('activationBlockedStrenght'))
|
||||
|
||||
|
||||
class Effect5938(BaseEffect):
|
||||
@@ -25239,7 +25191,7 @@ class Effect6214(BaseEffect):
|
||||
roleBonusCDLinksPGReduction
|
||||
|
||||
Used by:
|
||||
Ships from group: Command Destroyer (4 of 4)
|
||||
Ships from group: Command Destroyer (5 of 5)
|
||||
Ship: Porpoise
|
||||
"""
|
||||
|
||||
@@ -25292,12 +25244,9 @@ class Effect6222(BaseEffect):
|
||||
def handler(fit, module, context):
|
||||
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 > FittingModuleState.ONLINE:
|
||||
mod.state = FittingModuleState.ONLINE
|
||||
if not mod.isEmpty and mod.item.requiresSkill('Micro Jump Drive Operation') and mod.state > FittingModuleState.ONLINE:
|
||||
mod.state = FittingModuleState.ONLINE
|
||||
fit.modules.filteredItemIncrease(
|
||||
lambda mod: mod.item.requiresSkill('High Speed Maneuvering') or mod.item.requiresSkill('Micro Jump Drive Operation'),
|
||||
'activationBlocked', module.getModifiedItemAttr('activationBlockedStrenght'))
|
||||
|
||||
|
||||
class Effect6230(BaseEffect):
|
||||
@@ -25824,8 +25773,7 @@ class Effect6315(BaseEffect):
|
||||
eliteBonusCommandDestroyerSkirmish1
|
||||
|
||||
Used by:
|
||||
Ship: Bifrost
|
||||
Ship: Magus
|
||||
Ships from group: Command Destroyer (3 of 5)
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
@@ -25874,7 +25822,7 @@ class Effect6317(BaseEffect):
|
||||
eliteBonusCommandDestroyerMJFGspool2
|
||||
|
||||
Used by:
|
||||
Ships from group: Command Destroyer (4 of 4)
|
||||
Ships from group: Command Destroyer (5 of 5)
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
@@ -26147,8 +26095,7 @@ class Effect6334(BaseEffect):
|
||||
eliteBonusCommandDestroyerInfo1
|
||||
|
||||
Used by:
|
||||
Ship: Pontifex
|
||||
Ship: Stork
|
||||
Ships from group: Command Destroyer (3 of 5)
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
@@ -28908,7 +28855,7 @@ class Effect6581(BaseEffect):
|
||||
|
||||
@staticmethod
|
||||
def handler(fit, src, context):
|
||||
# Remote effect bonuses (duration / amount / range / fallout)
|
||||
# Remote effect bonuses (duration / amount / range / falloff)
|
||||
for skill, amtAttr, stack in (
|
||||
('Capital Remote Armor Repair Systems', 'armorDamageAmount', True),
|
||||
('Capital Shield Emission Systems', 'shieldBonus', True),
|
||||
@@ -28924,14 +28871,15 @@ class Effect6581(BaseEffect):
|
||||
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill(skill), 'falloffEffectiveness',
|
||||
src.getModifiedItemAttr('siegeRemoteLogisticsRangeBonus'), stackingPenalties=True)
|
||||
|
||||
# Local armor/shield rep effects (duration / amoutn)
|
||||
# Local armor/shield rep effects (duration / amount)
|
||||
for skill, amtAttr in (
|
||||
('Capital Shield Operation', 'shieldBonus'),
|
||||
('Capital Repair Systems', 'armorDamageAmount')):
|
||||
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill(skill), 'duration',
|
||||
src.getModifiedItemAttr('siegeLocalLogisticsDurationBonus'))
|
||||
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill(skill), amtAttr,
|
||||
src.getModifiedItemAttr('siegeLocalLogisticsAmountBonus'))
|
||||
src.getModifiedItemAttr('siegeLocalLogisticsAmountBonus'),
|
||||
stackingPenalties=True)
|
||||
|
||||
# Speed bonus
|
||||
fit.ship.boostItemAttr('maxVelocity', src.getModifiedItemAttr('speedFactor'), stackingPenalties=True)
|
||||
@@ -30521,7 +30469,14 @@ class Effect6672(BaseEffect):
|
||||
|
||||
@staticmethod
|
||||
def handler(fit, module, context):
|
||||
secModifier = module.getModifiedItemAttr('securityModifier')
|
||||
secMap = {
|
||||
FitSystemSecurity.HISEC: 'hiSecModifier',
|
||||
FitSystemSecurity.LOWSEC: 'lowSecModifier',
|
||||
FitSystemSecurity.NULLSEC: 'nullSecModifier',
|
||||
FitSystemSecurity.WSPACE: 'nullSecModifier'}
|
||||
fitSec = fit.getSystemSecurity()
|
||||
attrName = secMap[fitSec]
|
||||
secModifier = module.getModifiedItemAttr(attrName)
|
||||
module.multiplyItemAttr('structureRigDoomsdayDamageLossTargetBonus', secModifier)
|
||||
module.multiplyItemAttr('structureRigScanResBonus', secModifier)
|
||||
module.multiplyItemAttr('structureRigPDRangeBonus', secModifier)
|
||||
@@ -32257,7 +32212,7 @@ class Effect6845(BaseEffect):
|
||||
shipBonusCommandDestroyerRole1DefenderBonus
|
||||
|
||||
Used by:
|
||||
Ships from group: Command Destroyer (4 of 4)
|
||||
Ships from group: Command Destroyer (4 of 5)
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
@@ -33806,10 +33761,10 @@ class Effect6994(BaseEffect):
|
||||
|
||||
class Effect6995(BaseEffect):
|
||||
"""
|
||||
targetABCAttack
|
||||
targetDisintegratorAttack
|
||||
|
||||
Used by:
|
||||
Modules from group: Precursor Weapon (15 of 15)
|
||||
Modules from group: Precursor Weapon (18 of 18)
|
||||
"""
|
||||
|
||||
type = 'active'
|
||||
@@ -33973,6 +33928,7 @@ class Effect7012(BaseEffect):
|
||||
|
||||
Used by:
|
||||
Variations of module: Assault Damage Control I (5 of 5)
|
||||
Module: Abyssal Assault Damage Control
|
||||
"""
|
||||
|
||||
runTime = 'early'
|
||||
@@ -34149,6 +34105,7 @@ class Effect7026(BaseEffect):
|
||||
@staticmethod
|
||||
def handler(fit, src, context, *args, **kwargs):
|
||||
src.boostItemAttr('maxRange', src.getModifiedChargeAttr('warpScrambleRangeBonus'))
|
||||
src.forceItemAttr('activationBlockedStrenght', src.getModifiedChargeAttr('activationBlockedStrenght'))
|
||||
|
||||
|
||||
class Effect7027(BaseEffect):
|
||||
@@ -34856,7 +34813,7 @@ class Effect7077(BaseEffect):
|
||||
disintegratorWeaponDamageMultiply
|
||||
|
||||
Used by:
|
||||
Modules from group: Entropic Radiation Sink (4 of 4)
|
||||
Modules from group: Entropic Radiation Sink (6 of 6)
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
@@ -34873,7 +34830,7 @@ class Effect7078(BaseEffect):
|
||||
disintegratorWeaponSpeedMultiply
|
||||
|
||||
Used by:
|
||||
Modules from group: Entropic Radiation Sink (4 of 4)
|
||||
Modules from group: Entropic Radiation Sink (6 of 6)
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
@@ -34922,8 +34879,8 @@ class Effect7085(BaseEffect):
|
||||
shipbonusPCTDamagePC1
|
||||
|
||||
Used by:
|
||||
Variations of ship: Vedmak (2 of 2)
|
||||
Ship: Tiamat
|
||||
Ship: Vedmak
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
@@ -34939,8 +34896,8 @@ class Effect7086(BaseEffect):
|
||||
shipbonusPCTTrackingPC2
|
||||
|
||||
Used by:
|
||||
Variations of ship: Vedmak (2 of 2)
|
||||
Ship: Tiamat
|
||||
Ship: Vedmak
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
@@ -34956,7 +34913,6 @@ class Effect7087(BaseEffect):
|
||||
shipbonusPCTOptimalPF2
|
||||
|
||||
Used by:
|
||||
Ship: Damavik
|
||||
Ship: Hydra
|
||||
"""
|
||||
|
||||
@@ -34973,7 +34929,7 @@ class Effect7088(BaseEffect):
|
||||
shipbonusPCTDamagePF1
|
||||
|
||||
Used by:
|
||||
Ship: Damavik
|
||||
Variations of ship: Damavik (2 of 2)
|
||||
Ship: Hydra
|
||||
"""
|
||||
|
||||
@@ -35005,13 +34961,13 @@ class Effect7092(BaseEffect):
|
||||
shipBonusRemoteRepCapNeedRoleBonus2
|
||||
|
||||
Used by:
|
||||
Ship: Damavik
|
||||
Variations of ship: Damavik (2 of 2)
|
||||
Variations of ship: Kikimora (2 of 2)
|
||||
Variations of ship: Vedmak (2 of 2)
|
||||
Ship: Drekavac
|
||||
Ship: Hydra
|
||||
Ship: Kikimora
|
||||
Ship: Leshak
|
||||
Ship: Tiamat
|
||||
Ship: Vedmak
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
@@ -35027,14 +34983,14 @@ class Effect7093(BaseEffect):
|
||||
shipBonusSmartbombCapNeedRoleBonus2
|
||||
|
||||
Used by:
|
||||
Variations of ship: Damavik (2 of 2)
|
||||
Variations of ship: Kikimora (2 of 2)
|
||||
Variations of ship: Rodiva (2 of 2)
|
||||
Ship: Damavik
|
||||
Variations of ship: Vedmak (2 of 2)
|
||||
Ship: Drekavac
|
||||
Ship: Hydra
|
||||
Ship: Kikimora
|
||||
Ship: Leshak
|
||||
Ship: Tiamat
|
||||
Ship: Vedmak
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
@@ -35050,13 +35006,13 @@ class Effect7094(BaseEffect):
|
||||
shipBonusRemoteRepMaxRangeRoleBonus1
|
||||
|
||||
Used by:
|
||||
Ship: Damavik
|
||||
Variations of ship: Damavik (2 of 2)
|
||||
Variations of ship: Kikimora (2 of 2)
|
||||
Variations of ship: Vedmak (2 of 2)
|
||||
Ship: Drekavac
|
||||
Ship: Hydra
|
||||
Ship: Kikimora
|
||||
Ship: Leshak
|
||||
Ship: Tiamat
|
||||
Ship: Vedmak
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
@@ -35088,7 +35044,7 @@ class Effect7111(BaseEffect):
|
||||
systemSmallPrecursorTurretDamage
|
||||
|
||||
Used by:
|
||||
Celestials named like: Wolf Rayet Effect Beacon Class (5 of 6)
|
||||
Celestials named like: Wolf Rayet Effect Beacon Class (6 of 6)
|
||||
"""
|
||||
|
||||
runTime = 'early'
|
||||
@@ -35106,13 +35062,13 @@ class Effect7112(BaseEffect):
|
||||
shipBonusNeutCapNeedRoleBonus2
|
||||
|
||||
Used by:
|
||||
Ship: Damavik
|
||||
Variations of ship: Damavik (2 of 2)
|
||||
Variations of ship: Kikimora (2 of 2)
|
||||
Variations of ship: Vedmak (2 of 2)
|
||||
Ship: Drekavac
|
||||
Ship: Hydra
|
||||
Ship: Kikimora
|
||||
Ship: Leshak
|
||||
Ship: Tiamat
|
||||
Ship: Vedmak
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
@@ -35218,7 +35174,7 @@ class Effect7154(BaseEffect):
|
||||
shipBonusPD1DisintegratorDamage
|
||||
|
||||
Used by:
|
||||
Ship: Kikimora
|
||||
Variations of ship: Kikimora (2 of 2)
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
@@ -35268,7 +35224,7 @@ class Effect7157(BaseEffect):
|
||||
shipBonusPD2DisintegratorMaxRange
|
||||
|
||||
Used by:
|
||||
Ship: Kikimora
|
||||
Variations of ship: Kikimora (2 of 2)
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
@@ -35574,8 +35530,9 @@ class Effect7183(BaseEffect):
|
||||
|
||||
@staticmethod
|
||||
def handler(fit, src, context):
|
||||
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == 'Warp Scrambler', 'maxRange',
|
||||
src.getModifiedItemAttr('warpScrambleRangeBonus'), stackingPenalties=False)
|
||||
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == 'Warp Scrambler',
|
||||
'maxRange', src.getModifiedItemAttr('warpScrambleRangeBonus'),
|
||||
stackingPenalties=False)
|
||||
|
||||
|
||||
class Effect7184(BaseEffect):
|
||||
@@ -35624,3 +35581,232 @@ class Effect7186(BaseEffect):
|
||||
def handler(fit, ship, context):
|
||||
fit.drones.filteredItemBoost(lambda drone: drone.item.requiresSkill('Medium Drone Operation'),
|
||||
'armorHP', ship.getModifiedItemAttr('shipBonusRole8'))
|
||||
|
||||
|
||||
class Effect7193(BaseEffect):
|
||||
"""
|
||||
systemMiningCycleTimeBonus
|
||||
|
||||
Used by:
|
||||
Celestials named like: Invasion Effects (3 of 3)
|
||||
"""
|
||||
|
||||
runTime = 'early'
|
||||
type = ('projected', 'passive')
|
||||
|
||||
@staticmethod
|
||||
def handler(fit, beacon, context):
|
||||
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill('Mining'),
|
||||
'duration', beacon.getModifiedItemAttr('miningDurationMultiplier'))
|
||||
|
||||
|
||||
class Effect7202(BaseEffect):
|
||||
"""
|
||||
systemDroneSpeedBonusPercent
|
||||
|
||||
Used by:
|
||||
Celestials named like: Invasion Effects (3 of 3)
|
||||
"""
|
||||
|
||||
runTime = 'early'
|
||||
type = ('projected', 'passive')
|
||||
|
||||
@staticmethod
|
||||
def handler(fit, beacon, context):
|
||||
fit.drones.filteredItemBoost(lambda drone: drone.item.requiresSkill('Drones'),
|
||||
'maxVelocity', beacon.getModifiedItemAttr('droneMaxVelocityBonus'),
|
||||
stackingPenalties=True)
|
||||
|
||||
|
||||
class Effect7203(BaseEffect):
|
||||
"""
|
||||
systemDroneDamageBonusPercent
|
||||
|
||||
Used by:
|
||||
Celestials named like: Invasion Effects (3 of 3)
|
||||
"""
|
||||
|
||||
runTime = 'early'
|
||||
type = ('projected', 'passive')
|
||||
|
||||
@staticmethod
|
||||
def handler(fit, beacon, context):
|
||||
fit.drones.filteredItemBoost(lambda drone: drone.item.requiresSkill('Drones'),
|
||||
'damageMultiplier', beacon.getModifiedItemAttr('droneDamageBonus'),
|
||||
stackingPenalties=True)
|
||||
|
||||
|
||||
class Effect7204(BaseEffect):
|
||||
"""
|
||||
shipArmorEMResistancePF2
|
||||
|
||||
Used by:
|
||||
Variations of ship: Damavik (2 of 2)
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
|
||||
@staticmethod
|
||||
def handler(fit, ship, context):
|
||||
fit.ship.boostItemAttr('armorEmDamageResonance', ship.getModifiedItemAttr('shipBonusPF2'), skill='Precursor Frigate')
|
||||
|
||||
|
||||
class Effect7205(BaseEffect):
|
||||
"""
|
||||
shipArmorKinResistancePF2
|
||||
|
||||
Used by:
|
||||
Variations of ship: Damavik (2 of 2)
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
|
||||
@staticmethod
|
||||
def handler(fit, ship, context):
|
||||
fit.ship.boostItemAttr('armorKineticDamageResonance', ship.getModifiedItemAttr('shipBonusPF2'), skill='Precursor Frigate')
|
||||
|
||||
|
||||
class Effect7206(BaseEffect):
|
||||
"""
|
||||
shipArmorThermResistancePF2
|
||||
|
||||
Used by:
|
||||
Variations of ship: Damavik (2 of 2)
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
|
||||
@staticmethod
|
||||
def handler(fit, ship, context):
|
||||
fit.ship.boostItemAttr('armorThermalDamageResonance', ship.getModifiedItemAttr('shipBonusPF2'), skill='Precursor Frigate')
|
||||
|
||||
|
||||
class Effect7207(BaseEffect):
|
||||
"""
|
||||
shipArmorExpResistancePF2
|
||||
|
||||
Used by:
|
||||
Variations of ship: Damavik (2 of 2)
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
|
||||
@staticmethod
|
||||
def handler(fit, ship, context):
|
||||
fit.ship.boostItemAttr('armorExplosiveDamageResonance', ship.getModifiedItemAttr('shipBonusPF2'), skill='Precursor Frigate')
|
||||
|
||||
|
||||
class Effect7209(BaseEffect):
|
||||
"""
|
||||
shipPCTOptimalBonusEliteGunship2
|
||||
|
||||
Used by:
|
||||
Ship: Nergal
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
|
||||
@staticmethod
|
||||
def handler(fit, ship, context):
|
||||
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill('Small Precursor Weapon'),
|
||||
'maxRange', ship.getModifiedItemAttr('eliteBonusGunship2'),
|
||||
skill='Assault Frigates')
|
||||
|
||||
|
||||
class Effect7210(BaseEffect):
|
||||
"""
|
||||
shipBonusCommandDestroyerRole2DefenderBonus
|
||||
|
||||
Used by:
|
||||
Ship: Draugur
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
|
||||
@staticmethod
|
||||
def handler(fit, ship, context):
|
||||
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill('Defender Missiles'),
|
||||
'moduleReactivationDelay', ship.getModifiedItemAttr('shipBonusRole2'))
|
||||
|
||||
|
||||
class Effect7211(BaseEffect):
|
||||
"""
|
||||
shipDmgMultiMaxEliteHeavyGunship1
|
||||
|
||||
Used by:
|
||||
Ship: Ikitursa
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
|
||||
@staticmethod
|
||||
def handler(fit, ship, context):
|
||||
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill('Medium Precursor Weapon'),
|
||||
'damageMultiplierBonusMax', ship.getModifiedItemAttr('eliteBonusHeavyGunship1'),
|
||||
skill='Heavy Assault Cruisers')
|
||||
|
||||
|
||||
class Effect7216(BaseEffect):
|
||||
"""
|
||||
shipDmgMultiMaxEliteGunship1
|
||||
|
||||
Used by:
|
||||
Ship: Nergal
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
|
||||
@staticmethod
|
||||
def handler(fit, ship, context):
|
||||
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill('Small Precursor Weapon'),
|
||||
'damageMultiplierBonusMax', ship.getModifiedItemAttr('eliteBonusGunship1'),
|
||||
skill='Assault Frigates')
|
||||
|
||||
|
||||
class Effect7223(BaseEffect):
|
||||
"""
|
||||
systemAgilityBonusPercentItem
|
||||
|
||||
Used by:
|
||||
Celestials named like: Invasion Effects (3 of 3)
|
||||
"""
|
||||
|
||||
runTime = 'early'
|
||||
type = ('projected', 'passive')
|
||||
|
||||
@staticmethod
|
||||
def handler(fit, beacon, context):
|
||||
fit.ship.boostItemAttr('agility', beacon.getModifiedItemAttr('agilityBonus'), stackingPenalties=True)
|
||||
|
||||
|
||||
class Effect7227(BaseEffect):
|
||||
"""
|
||||
systemHullHPBonusPercentItem
|
||||
|
||||
Used by:
|
||||
Celestials named like: Invasion Effects (3 of 3)
|
||||
"""
|
||||
|
||||
runTime = 'early'
|
||||
type = ('projected', 'passive')
|
||||
|
||||
@staticmethod
|
||||
def handler(fit, beacon, context):
|
||||
fit.ship.boostItemAttr('hp', beacon.getModifiedItemAttr('hullHpBonus'))
|
||||
|
||||
|
||||
class Effect7228(BaseEffect):
|
||||
"""
|
||||
shipMediumPrecursorWeaponOptimalEliteHeavyGunship2
|
||||
|
||||
Used by:
|
||||
Ship: Ikitursa
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
|
||||
@staticmethod
|
||||
def handler(fit, ship, context):
|
||||
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill('Medium Precursor Weapon'),
|
||||
'maxRange', ship.getModifiedItemAttr('eliteBonusHeavyGunship2'),
|
||||
skill='Heavy Assault Cruisers')
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
class HandledListActionError(Exception):
|
||||
...
|
||||
@@ -250,6 +250,17 @@ class Item(EqBase):
|
||||
|
||||
return self.__attributes
|
||||
|
||||
@property
|
||||
def attribsWithOverrides(self):
|
||||
overrides = self.overrides
|
||||
attribs = {}
|
||||
for aname, attr in self.attributes.items():
|
||||
if aname in overrides:
|
||||
attribs[aname] = overrides[aname]
|
||||
else:
|
||||
attribs[aname] = attr
|
||||
return attribs
|
||||
|
||||
def getAttribute(self, key, default=None):
|
||||
if key in self.attributes:
|
||||
return self.attributes[key].value
|
||||
@@ -479,7 +490,7 @@ class Item(EqBase):
|
||||
|
||||
@property
|
||||
def isModule(self):
|
||||
return self.category.name == 'Module'
|
||||
return self.category.name in ('Module', 'Structure Module')
|
||||
|
||||
@property
|
||||
def isSubsystem(self):
|
||||
|
||||
@@ -20,7 +20,8 @@
|
||||
import itertools
|
||||
|
||||
|
||||
class Graph(object):
|
||||
class Graph:
|
||||
|
||||
def __init__(self, fit, function, data=None):
|
||||
self.fit = fit
|
||||
self.data = {}
|
||||
@@ -50,11 +51,11 @@ class Graph(object):
|
||||
point = {}
|
||||
for i in range(len(pointValues)):
|
||||
point[pointNames[i]] = pointValues[i]
|
||||
|
||||
yield point, self.function(point)
|
||||
|
||||
|
||||
class Data(object):
|
||||
class Data:
|
||||
|
||||
def __init__(self, name, dataString, step=None):
|
||||
self.name = name
|
||||
self.step = step
|
||||
@@ -83,7 +84,8 @@ class Data(object):
|
||||
return len(self.data) == 1 and self.data[0].isConstant()
|
||||
|
||||
|
||||
class Constant(object):
|
||||
class Constant:
|
||||
|
||||
def __init__(self, const):
|
||||
if isinstance(const, str):
|
||||
self.value = None if const == "" else float(const)
|
||||
@@ -98,7 +100,8 @@ class Constant(object):
|
||||
return True
|
||||
|
||||
|
||||
class Range(object):
|
||||
class Range:
|
||||
|
||||
def __init__(self, string, step):
|
||||
start, end = string.split("-")
|
||||
self.start = float(start)
|
||||
@@ -108,8 +111,8 @@ class Range(object):
|
||||
def __iter__(self):
|
||||
current = start = self.start
|
||||
end = self.end
|
||||
step = self.step or (end - start) / 50.0
|
||||
i = 1
|
||||
step = self.step or (end - start) / 200
|
||||
i = 0
|
||||
while current < end:
|
||||
current = start + i * step
|
||||
i += 1
|
||||
|
||||
24
eos/graph/fitCapAmountTime.py
Normal file
24
eos/graph/fitCapAmountTime.py
Normal file
@@ -0,0 +1,24 @@
|
||||
import math
|
||||
from logbook import Logger
|
||||
|
||||
from eos.graph import Graph
|
||||
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
|
||||
class FitCapAmountTimeGraph(Graph):
|
||||
|
||||
defaults = {"time": 0}
|
||||
|
||||
def __init__(self, fit, data=None):
|
||||
Graph.__init__(self, fit, self.calcAmount, data if data is not None else self.defaults)
|
||||
self.fit = fit
|
||||
|
||||
def calcAmount(self, data):
|
||||
time = data["time"]
|
||||
maxCap = self.fit.ship.getModifiedItemAttr('capacitorCapacity')
|
||||
regenTime = self.fit.ship.getModifiedItemAttr('rechargeRate') / 1000
|
||||
# https://wiki.eveuniversity.org/Capacitor#Capacitor_recharge_rate
|
||||
cap = maxCap * (1 + math.exp(5 * -time / regenTime) * -1) ** 2
|
||||
return cap
|
||||
25
eos/graph/fitCapRegenAmount.py
Normal file
25
eos/graph/fitCapRegenAmount.py
Normal file
@@ -0,0 +1,25 @@
|
||||
import math
|
||||
from logbook import Logger
|
||||
|
||||
from eos.graph import Graph
|
||||
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
|
||||
class FitCapRegenAmountGraph(Graph):
|
||||
|
||||
defaults = {"percentage": '0-100'}
|
||||
|
||||
def __init__(self, fit, data=None):
|
||||
Graph.__init__(self, fit, self.calcRegen, data if data is not None else self.defaults)
|
||||
self.fit = fit
|
||||
|
||||
def calcRegen(self, data):
|
||||
perc = data['percentage']
|
||||
maxCap = self.fit.ship.getModifiedItemAttr('capacitorCapacity')
|
||||
regenTime = self.fit.ship.getModifiedItemAttr('rechargeRate') / 1000
|
||||
currentCap = maxCap * perc / 100
|
||||
# https://wiki.eveuniversity.org/Capacitor#Capacitor_recharge_rate
|
||||
regen = 10 * maxCap / regenTime * (math.sqrt(currentCap / maxCap) - currentCap / maxCap)
|
||||
return regen
|
||||
26
eos/graph/fitDistanceTime.py
Normal file
26
eos/graph/fitDistanceTime.py
Normal file
@@ -0,0 +1,26 @@
|
||||
import math
|
||||
from logbook import Logger
|
||||
|
||||
from eos.graph import Graph
|
||||
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
|
||||
class FitDistanceTimeGraph(Graph):
|
||||
|
||||
defaults = {"time": 0}
|
||||
|
||||
def __init__(self, fit, data=None):
|
||||
Graph.__init__(self, fit, self.calcDistance, data if data is not None else self.defaults)
|
||||
self.fit = fit
|
||||
|
||||
def calcDistance(self, data):
|
||||
time = data["time"]
|
||||
maxSpeed = self.fit.ship.getModifiedItemAttr('maxVelocity')
|
||||
mass = self.fit.ship.getModifiedItemAttr('mass')
|
||||
agility = self.fit.ship.getModifiedItemAttr('agility')
|
||||
# Definite integral of:
|
||||
# https://wiki.eveuniversity.org/Acceleration#Mathematics_and_formulae
|
||||
distance = maxSpeed * time + (maxSpeed * agility * mass * math.exp((-time * 1000000) / (agility * mass)) / 1000000)
|
||||
return distance
|
||||
113
eos/graph/fitDmgTime.py
Normal file
113
eos/graph/fitDmgTime.py
Normal file
@@ -0,0 +1,113 @@
|
||||
# ===============================================================================
|
||||
# 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 logbook import Logger
|
||||
|
||||
from eos.graph import Graph
|
||||
from eos.utils.spoolSupport import SpoolType, SpoolOptions
|
||||
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
|
||||
class FitDmgTimeGraph(Graph):
|
||||
|
||||
defaults = {"time": 0}
|
||||
|
||||
def __init__(self, fit, data=None):
|
||||
Graph.__init__(self, fit, self.calcDmg, data if data is not None else self.defaults)
|
||||
self.fit = fit
|
||||
self.__cache = {}
|
||||
|
||||
def calcDmg(self, data):
|
||||
time = data["time"] * 1000
|
||||
closestTime = max((t for t in self.__cache if t <= time), default=None)
|
||||
if closestTime is None:
|
||||
return 0
|
||||
return self.__cache[closestTime]
|
||||
|
||||
def recalc(self):
|
||||
|
||||
def addDmg(addedTime, addedDmg):
|
||||
if addedDmg == 0:
|
||||
return
|
||||
if addedTime not in self.__cache:
|
||||
prevTime = max((t for t in self.__cache if t < addedTime), default=None)
|
||||
if prevTime is None:
|
||||
self.__cache[addedTime] = 0
|
||||
else:
|
||||
self.__cache[addedTime] = self.__cache[prevTime]
|
||||
for time in (t for t in self.__cache if t >= addedTime):
|
||||
self.__cache[time] += addedDmg
|
||||
|
||||
self.__cache.clear()
|
||||
fit = self.fit
|
||||
# We'll handle calculations in milliseconds
|
||||
maxTime = self.data["time"].data[0].end * 1000
|
||||
for mod in fit.modules:
|
||||
cycleParams = mod.getCycleParameters(reloadOverride=True)
|
||||
if cycleParams is None:
|
||||
continue
|
||||
currentTime = 0
|
||||
nonstopCycles = 0
|
||||
for cycleTime, inactiveTime in cycleParams.iterCycles():
|
||||
volleyParams = mod.getVolleyParameters(spoolOptions=SpoolOptions(SpoolType.CYCLES, nonstopCycles, True))
|
||||
for volleyTime, volley in volleyParams.items():
|
||||
if currentTime + volleyTime <= maxTime and volleyTime <= cycleTime:
|
||||
addDmg(currentTime + volleyTime, volley.total)
|
||||
currentTime += cycleTime
|
||||
currentTime += inactiveTime
|
||||
if inactiveTime == 0:
|
||||
nonstopCycles += 1
|
||||
else:
|
||||
nonstopCycles = 0
|
||||
if currentTime > maxTime:
|
||||
break
|
||||
for drone in fit.drones:
|
||||
cycleParams = drone.getCycleParameters(reloadOverride=True)
|
||||
if cycleParams is None:
|
||||
continue
|
||||
currentTime = 0
|
||||
volleyParams = drone.getVolleyParameters()
|
||||
for cycleTime, inactiveTime in cycleParams.iterCycles():
|
||||
for volleyTime, volley in volleyParams.items():
|
||||
if currentTime + volleyTime <= maxTime and volleyTime <= cycleTime:
|
||||
addDmg(currentTime + volleyTime, volley.total)
|
||||
currentTime += cycleTime
|
||||
currentTime += inactiveTime
|
||||
if currentTime > maxTime:
|
||||
break
|
||||
for fighter in fit.fighters:
|
||||
cycleParams = fighter.getCycleParametersPerEffectOptimizedDps(reloadOverride=True)
|
||||
if cycleParams is None:
|
||||
continue
|
||||
volleyParams = fighter.getVolleyParametersPerEffect()
|
||||
for effectID, abilityCycleParams in cycleParams.items():
|
||||
if effectID not in volleyParams:
|
||||
continue
|
||||
currentTime = 0
|
||||
abilityVolleyParams = volleyParams[effectID]
|
||||
for cycleTime, inactiveTime in abilityCycleParams.iterCycles():
|
||||
for volleyTime, volley in abilityVolleyParams.items():
|
||||
if currentTime + volleyTime <= maxTime and volleyTime <= cycleTime:
|
||||
addDmg(currentTime + volleyTime, volley.total)
|
||||
currentTime += cycleTime
|
||||
currentTime += inactiveTime
|
||||
if currentTime > maxTime:
|
||||
break
|
||||
@@ -17,16 +17,19 @@
|
||||
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
||||
# ===============================================================================
|
||||
|
||||
from math import log, sin, radians, exp
|
||||
from math import exp, log, radians, sin, inf
|
||||
|
||||
from eos.graph import Graph
|
||||
from eos.const import FittingModuleState, FittingHardpoint
|
||||
from logbook import Logger
|
||||
|
||||
from eos.const import FittingHardpoint, FittingModuleState
|
||||
from eos.graph import Graph
|
||||
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
|
||||
class FitDpsGraph(Graph):
|
||||
class FitDpsRangeGraph(Graph):
|
||||
|
||||
defaults = {
|
||||
"angle" : 0,
|
||||
"distance" : 0,
|
||||
@@ -169,8 +172,7 @@ class FitDpsGraph(Graph):
|
||||
|
||||
return min(sigRadiusFactor, velocityFactor, 1)
|
||||
|
||||
@staticmethod
|
||||
def calculateTurretChanceToHit(mod, data):
|
||||
def calculateTurretChanceToHit(self, mod, data):
|
||||
distance = data["distance"] * 1000
|
||||
tracking = mod.getModifiedItemAttr("trackingSpeed")
|
||||
turretOptimal = mod.maxRange
|
||||
@@ -179,7 +181,17 @@ class FitDpsGraph(Graph):
|
||||
targetSigRad = data["signatureRadius"]
|
||||
targetSigRad = turretSigRes if targetSigRad is None else targetSigRad
|
||||
transversal = sin(radians(data["angle"])) * data["velocity"]
|
||||
trackingEq = (((transversal / (distance * tracking)) *
|
||||
|
||||
# Angular velocity is calculated using range from ship center to target center.
|
||||
# We do not know target radius but we know attacker radius
|
||||
angDistance = distance + self.fit.ship.getModifiedItemAttr('radius', 0)
|
||||
if angDistance == 0 and transversal == 0:
|
||||
angularVelocity = 0
|
||||
elif angDistance == 0 and transversal != 0:
|
||||
angularVelocity = inf
|
||||
else:
|
||||
angularVelocity = transversal / angDistance
|
||||
trackingEq = (((angularVelocity / tracking) *
|
||||
(turretSigRes / targetSigRad)) ** 2)
|
||||
rangeEq = ((max(0, distance - turretOptimal)) / turretFalloff) ** 2
|
||||
|
||||
112
eos/graph/fitDpsTime.py
Normal file
112
eos/graph/fitDpsTime.py
Normal file
@@ -0,0 +1,112 @@
|
||||
# ===============================================================================
|
||||
# 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 logbook import Logger
|
||||
|
||||
from eos.graph import Graph
|
||||
from eos.utils.spoolSupport import SpoolType, SpoolOptions
|
||||
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
|
||||
class FitDpsTimeGraph(Graph):
|
||||
|
||||
defaults = {"time": 0}
|
||||
|
||||
def __init__(self, fit, data=None):
|
||||
Graph.__init__(self, fit, self.calcDps, data if data is not None else self.defaults)
|
||||
self.fit = fit
|
||||
self.__cache = []
|
||||
|
||||
def calcDps(self, data):
|
||||
time = data["time"] * 1000
|
||||
entries = (e for e in self.__cache if e[0] <= time < e[1])
|
||||
dps = sum(e[2] for e in entries)
|
||||
return dps
|
||||
|
||||
def recalc(self):
|
||||
|
||||
def addDmg(addedTimeStart, addedTimeFinish, addedDmg):
|
||||
if addedDmg == 0:
|
||||
return
|
||||
addedDps = 1000 * addedDmg / (addedTimeFinish - addedTimeStart)
|
||||
self.__cache.append((addedTimeStart, addedTimeFinish, addedDps))
|
||||
|
||||
self.__cache = []
|
||||
fit = self.fit
|
||||
# We'll handle calculations in milliseconds
|
||||
maxTime = self.data["time"].data[0].end * 1000
|
||||
for mod in fit.modules:
|
||||
cycleParams = mod.getCycleParameters(reloadOverride=True)
|
||||
if cycleParams is None:
|
||||
continue
|
||||
currentTime = 0
|
||||
nonstopCycles = 0
|
||||
for cycleTime, inactiveTime in cycleParams.iterCycles():
|
||||
cycleDamage = 0
|
||||
volleyParams = mod.getVolleyParameters(spoolOptions=SpoolOptions(SpoolType.CYCLES, nonstopCycles, True))
|
||||
for volleyTime, volley in volleyParams.items():
|
||||
if currentTime + volleyTime <= maxTime and volleyTime <= cycleTime:
|
||||
cycleDamage += volley.total
|
||||
addDmg(currentTime, currentTime + cycleTime, cycleDamage)
|
||||
currentTime += cycleTime
|
||||
currentTime += inactiveTime
|
||||
if inactiveTime > 0:
|
||||
nonstopCycles = 0
|
||||
else:
|
||||
nonstopCycles += 1
|
||||
if currentTime > maxTime:
|
||||
break
|
||||
for drone in fit.drones:
|
||||
cycleParams = drone.getCycleParameters(reloadOverride=True)
|
||||
if cycleParams is None:
|
||||
continue
|
||||
currentTime = 0
|
||||
for cycleTime, inactiveTime in cycleParams.iterCycles():
|
||||
cycleDamage = 0
|
||||
volleyParams = drone.getVolleyParameters()
|
||||
for volleyTime, volley in volleyParams.items():
|
||||
if currentTime + volleyTime <= maxTime and volleyTime <= cycleTime:
|
||||
cycleDamage += volley.total
|
||||
addDmg(currentTime, currentTime + cycleTime, cycleDamage)
|
||||
currentTime += cycleTime
|
||||
currentTime += inactiveTime
|
||||
if currentTime > maxTime:
|
||||
break
|
||||
for fighter in fit.fighters:
|
||||
cycleParams = fighter.getCycleParametersPerEffectOptimizedDps(reloadOverride=True)
|
||||
if cycleParams is None:
|
||||
continue
|
||||
volleyParams = fighter.getVolleyParametersPerEffect()
|
||||
for effectID, abilityCycleParams in cycleParams.items():
|
||||
if effectID not in volleyParams:
|
||||
continue
|
||||
abilityVolleyParams = volleyParams[effectID]
|
||||
currentTime = 0
|
||||
for cycleTime, inactiveTime in abilityCycleParams.iterCycles():
|
||||
cycleDamage = 0
|
||||
for volleyTime, volley in abilityVolleyParams.items():
|
||||
if currentTime + volleyTime <= maxTime and volleyTime <= cycleTime:
|
||||
cycleDamage += volley.total
|
||||
addDmg(currentTime, currentTime + cycleTime, cycleDamage)
|
||||
currentTime += cycleTime
|
||||
currentTime += inactiveTime
|
||||
if currentTime > maxTime:
|
||||
break
|
||||
29
eos/graph/fitShieldAmountTime.py
Normal file
29
eos/graph/fitShieldAmountTime.py
Normal file
@@ -0,0 +1,29 @@
|
||||
import math
|
||||
from logbook import Logger
|
||||
|
||||
from eos.graph import Graph
|
||||
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
|
||||
class FitShieldAmountTimeGraph(Graph):
|
||||
|
||||
defaults = {"time": 0}
|
||||
|
||||
def __init__(self, fit, data=None):
|
||||
Graph.__init__(self, fit, self.calcAmount, data if data is not None else self.defaults)
|
||||
self.fit = fit
|
||||
import gui.mainFrame
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
|
||||
def calcAmount(self, data):
|
||||
time = data["time"]
|
||||
maxShield = self.fit.ship.getModifiedItemAttr('shieldCapacity')
|
||||
regenTime = self.fit.ship.getModifiedItemAttr('shieldRechargeRate') / 1000
|
||||
# https://wiki.eveuniversity.org/Capacitor#Capacitor_recharge_rate (shield is similar to cap)
|
||||
shield = maxShield * (1 + math.exp(5 * -time / regenTime) * -1) ** 2
|
||||
useEhp = self.mainFrame.statsPane.nameViewMap["resistancesViewFull"].showEffective
|
||||
if self.fit.damagePattern is not None and useEhp:
|
||||
shield = self.fit.damagePattern.effectivify(self.fit, shield, 'shield')
|
||||
return shield
|
||||
49
eos/graph/fitShieldRegenAmount.py
Normal file
49
eos/graph/fitShieldRegenAmount.py
Normal file
@@ -0,0 +1,49 @@
|
||||
# ===============================================================================
|
||||
# Copyright (C) 2010 Diego Duclos
|
||||
#
|
||||
# This file is part of eos.
|
||||
#
|
||||
# eos is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# eos is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
||||
# ===============================================================================
|
||||
|
||||
import math
|
||||
from logbook import Logger
|
||||
|
||||
from eos.graph import Graph
|
||||
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
|
||||
class FitShieldRegenAmountGraph(Graph):
|
||||
|
||||
defaults = {"percentage": '0-100'}
|
||||
|
||||
def __init__(self, fit, data=None):
|
||||
Graph.__init__(self, fit, self.calcRegen, data if data is not None else self.defaults)
|
||||
self.fit = fit
|
||||
import gui.mainFrame
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
|
||||
def calcRegen(self, data):
|
||||
perc = data["percentage"]
|
||||
maxShield = self.fit.ship.getModifiedItemAttr('shieldCapacity')
|
||||
regenTime = self.fit.ship.getModifiedItemAttr('shieldRechargeRate') / 1000
|
||||
currentShield = maxShield * perc / 100
|
||||
# https://wiki.eveuniversity.org/Capacitor#Capacitor_recharge_rate (shield is similar to cap)
|
||||
regen = 10 * maxShield / regenTime * (math.sqrt(currentShield / maxShield) - currentShield / maxShield)
|
||||
useEhp = self.mainFrame.statsPane.nameViewMap["resistancesViewFull"].showEffective
|
||||
if self.fit.damagePattern is not None and useEhp:
|
||||
regen = self.fit.damagePattern.effectivify(self.fit, regen, 'shield')
|
||||
return regen
|
||||
25
eos/graph/fitSpeedTime.py
Normal file
25
eos/graph/fitSpeedTime.py
Normal file
@@ -0,0 +1,25 @@
|
||||
import math
|
||||
from logbook import Logger
|
||||
|
||||
from eos.graph import Graph
|
||||
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
|
||||
class FitSpeedTimeGraph(Graph):
|
||||
|
||||
defaults = {"time": 0}
|
||||
|
||||
def __init__(self, fit, data=None):
|
||||
Graph.__init__(self, fit, self.calcSpeed, data if data is not None else self.defaults)
|
||||
self.fit = fit
|
||||
|
||||
def calcSpeed(self, data):
|
||||
time = data["time"]
|
||||
maxSpeed = self.fit.ship.getModifiedItemAttr('maxVelocity')
|
||||
mass = self.fit.ship.getModifiedItemAttr('mass')
|
||||
agility = self.fit.ship.getModifiedItemAttr('agility')
|
||||
# https://wiki.eveuniversity.org/Acceleration#Mathematics_and_formulae
|
||||
speed = maxSpeed * (1 - math.exp((-time * 1000000) / (agility * mass)))
|
||||
return speed
|
||||
61
eos/graph/fitWarpTimeDistance.py
Normal file
61
eos/graph/fitWarpTimeDistance.py
Normal file
@@ -0,0 +1,61 @@
|
||||
import math
|
||||
from logbook import Logger
|
||||
|
||||
from eos.graph import Graph
|
||||
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
|
||||
AU_METERS = 149597870700
|
||||
|
||||
|
||||
class FitWarpTimeDistanceGraph(Graph):
|
||||
|
||||
defaults = {"distance": 0}
|
||||
|
||||
def __init__(self, fit, data=None):
|
||||
Graph.__init__(self, fit, self.calcTime, data if data is not None else self.defaults)
|
||||
self.fit = fit
|
||||
|
||||
def calcTime(self, data):
|
||||
distance = data["distance"]
|
||||
if distance == 0:
|
||||
return 0
|
||||
maxWarpDistance = self.fit.maxWarpDistance
|
||||
if distance > maxWarpDistance:
|
||||
return None
|
||||
maxSubwarpSpeed = self.fit.ship.getModifiedItemAttr('maxVelocity')
|
||||
maxWarpSpeed = self.fit.warpSpeed
|
||||
time = calculate_time_in_warp(maxWarpSpeed, maxSubwarpSpeed, distance * AU_METERS)
|
||||
return time
|
||||
|
||||
|
||||
# Taken from https://wiki.eveuniversity.org/Warp_time_calculation#Implementation
|
||||
# with minor modifications
|
||||
# Warp speed in AU/s, subwarp speed in m/s, distance in m
|
||||
def calculate_time_in_warp(max_warp_speed, max_subwarp_speed, warp_dist):
|
||||
|
||||
k_accel = max_warp_speed
|
||||
k_decel = min(max_warp_speed / 3, 2)
|
||||
|
||||
warp_dropout_speed = max_subwarp_speed / 2
|
||||
max_ms_warp_speed = max_warp_speed * AU_METERS
|
||||
|
||||
accel_dist = AU_METERS
|
||||
decel_dist = max_ms_warp_speed / k_decel
|
||||
|
||||
minimum_dist = accel_dist + decel_dist
|
||||
|
||||
cruise_time = 0
|
||||
|
||||
if minimum_dist > warp_dist:
|
||||
max_ms_warp_speed = warp_dist * k_accel * k_decel / (k_accel + k_decel)
|
||||
else:
|
||||
cruise_time = (warp_dist - minimum_dist) / max_ms_warp_speed
|
||||
|
||||
accel_time = math.log(max_ms_warp_speed / k_accel) / k_accel
|
||||
decel_time = math.log(max_ms_warp_speed / warp_dropout_speed) / k_decel
|
||||
|
||||
total_time = cruise_time + accel_time + decel_time
|
||||
return total_time
|
||||
@@ -218,13 +218,13 @@ class ModifiedAttributeDict(collections.MutableMapping):
|
||||
if attrInfo is None:
|
||||
cappingId = cappingAttrKeyCache[key] = None
|
||||
else:
|
||||
# see GH issue #620
|
||||
cappingId = cappingAttrKeyCache[key] = attrInfo.maxAttributeID
|
||||
cappingId = attrInfo.maxAttributeID
|
||||
if cappingId is None:
|
||||
cappingKey = None
|
||||
else:
|
||||
cappingAttrInfo = getAttributeInfo(cappingId)
|
||||
cappingKey = None if cappingAttrInfo is None else cappingAttrInfo.name
|
||||
cappingAttrKeyCache[key] = cappingKey
|
||||
|
||||
if cappingKey:
|
||||
cappingValue = self.original.get(cappingKey, self.__calculateValue(cappingKey))
|
||||
@@ -238,7 +238,7 @@ class ModifiedAttributeDict(collections.MutableMapping):
|
||||
if force is not None:
|
||||
if cappingValue is not None:
|
||||
force = min(force, cappingValue)
|
||||
if key in (50, 30, 48, 11):
|
||||
if key in ("cpu", "power", "cpuOutput", "powerOutput"):
|
||||
force = round(force, 2)
|
||||
return force
|
||||
# Grab our values if they're there, otherwise we'll take default values
|
||||
@@ -259,11 +259,7 @@ class ModifiedAttributeDict(collections.MutableMapping):
|
||||
dv = attrInfo.defaultValue
|
||||
default = defaultValuesCache[key] = dv if dv is not None else 0.0
|
||||
|
||||
val = self.__intermediary.get(key,
|
||||
self.__preAssigns.get(key,
|
||||
self.getOriginal(key, default)
|
||||
)
|
||||
)
|
||||
val = self.__intermediary.get(key, self.__preAssigns.get(key, self.getOriginal(key, default)))
|
||||
|
||||
# We'll do stuff in the following order:
|
||||
# preIncrease > multiplier > stacking penalized multipliers > postIncrease
|
||||
@@ -293,7 +289,7 @@ class ModifiedAttributeDict(collections.MutableMapping):
|
||||
# Cap value if we have cap defined
|
||||
if cappingValue is not None:
|
||||
val = min(val, cappingValue)
|
||||
if key in (50, 30, 48, 11):
|
||||
if key in ("cpu", "power", "cpuOutput", "powerOutput"):
|
||||
val = round(val, 2)
|
||||
return val
|
||||
|
||||
|
||||
@@ -17,13 +17,14 @@
|
||||
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
||||
# ===============================================================================
|
||||
|
||||
import math
|
||||
from logbook import Logger
|
||||
|
||||
from sqlalchemy.orm import validates, reconstructor
|
||||
from sqlalchemy.orm import reconstructor, validates
|
||||
|
||||
import eos.db
|
||||
from eos.effectHandlerHelpers import HandledItem, HandledCharge
|
||||
from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut, ChargeAttrShortcut
|
||||
from eos.effectHandlerHelpers import HandledCharge, HandledItem
|
||||
from eos.modifiedAttributeDict import ChargeAttrShortcut, ItemAttrShortcut, ModifiedAttributeDict
|
||||
from eos.utils.cycles import CycleInfo
|
||||
from eos.utils.stats import DmgTypes
|
||||
|
||||
|
||||
@@ -104,7 +105,16 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
|
||||
@property
|
||||
def cycleTime(self):
|
||||
return max(self.getModifiedItemAttr("duration", 0), 0)
|
||||
if self.hasAmmo:
|
||||
cycleTime = self.getModifiedItemAttr("missileLaunchDuration", 0)
|
||||
else:
|
||||
for attr in ("speed", "duration"):
|
||||
cycleTime = self.getModifiedItemAttr(attr, None)
|
||||
if cycleTime is not None:
|
||||
break
|
||||
if cycleTime is None:
|
||||
return 0
|
||||
return max(cycleTime, 0)
|
||||
|
||||
@property
|
||||
def dealsDamage(self):
|
||||
@@ -121,9 +131,9 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
def hasAmmo(self):
|
||||
return self.charge is not None
|
||||
|
||||
def getVolley(self, targetResists=None):
|
||||
def getVolleyParameters(self, targetResists=None):
|
||||
if not self.dealsDamage or self.amountActive <= 0:
|
||||
return DmgTypes(0, 0, 0, 0)
|
||||
return {0: DmgTypes(0, 0, 0, 0)}
|
||||
if self.__baseVolley is None:
|
||||
dmgGetter = self.getModifiedChargeAttr if self.hasAmmo else self.getModifiedItemAttr
|
||||
dmgMult = self.amountActive * (self.getModifiedItemAttr("damageMultiplier", 1))
|
||||
@@ -137,15 +147,19 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
thermal=self.__baseVolley.thermal * (1 - getattr(targetResists, "thermalAmount", 0)),
|
||||
kinetic=self.__baseVolley.kinetic * (1 - getattr(targetResists, "kineticAmount", 0)),
|
||||
explosive=self.__baseVolley.explosive * (1 - getattr(targetResists, "explosiveAmount", 0)))
|
||||
return volley
|
||||
return {0: volley}
|
||||
|
||||
def getVolley(self, targetResists=None):
|
||||
return self.getVolleyParameters(targetResists=targetResists)[0]
|
||||
|
||||
def getDps(self, targetResists=None):
|
||||
volley = self.getVolley(targetResists=targetResists)
|
||||
if not volley:
|
||||
return DmgTypes(0, 0, 0, 0)
|
||||
cycleAttr = "missileLaunchDuration" if self.hasAmmo else "speed"
|
||||
cycleTime = self.getModifiedItemAttr(cycleAttr)
|
||||
dpsFactor = 1 / (cycleTime / 1000)
|
||||
cycleParams = self.getCycleParameters()
|
||||
if cycleParams is None:
|
||||
return DmgTypes(0, 0, 0, 0)
|
||||
dpsFactor = 1 / (cycleParams.averageTime / 1000)
|
||||
dps = DmgTypes(
|
||||
em=volley.em * dpsFactor,
|
||||
thermal=volley.thermal * dpsFactor,
|
||||
@@ -153,6 +167,12 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
explosive=volley.explosive * dpsFactor)
|
||||
return dps
|
||||
|
||||
def getCycleParameters(self, reloadOverride=None):
|
||||
cycleTime = self.cycleTime
|
||||
if cycleTime == 0:
|
||||
return None
|
||||
return CycleInfo(self.cycleTime, 0, math.inf)
|
||||
|
||||
def getRemoteReps(self, ignoreState=False):
|
||||
if self.amountActive <= 0 and not ignoreState:
|
||||
return (None, 0)
|
||||
@@ -174,7 +194,12 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
rrAmount = 0
|
||||
if rrAmount:
|
||||
droneAmount = self.amount if ignoreState else self.amountActive
|
||||
rrAmount *= droneAmount / (self.cycleTime / 1000)
|
||||
cycleParams = self.getCycleParameters()
|
||||
if cycleParams is None:
|
||||
rrType = None
|
||||
rrAmount = 0
|
||||
else:
|
||||
rrAmount *= droneAmount / (cycleParams.averageTime / 1000)
|
||||
self.__baseRemoteReps = (rrType, rrAmount)
|
||||
return self.__baseRemoteReps
|
||||
|
||||
@@ -182,12 +207,14 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
def miningStats(self):
|
||||
if self.__miningyield is None:
|
||||
if self.mines is True and self.amountActive > 0:
|
||||
attr = "duration"
|
||||
getter = self.getModifiedItemAttr
|
||||
|
||||
cycleTime = self.getModifiedItemAttr(attr)
|
||||
volley = sum([getter(d) for d in self.MINING_ATTRIBUTES]) * self.amountActive
|
||||
self.__miningyield = volley / (cycleTime / 1000.0)
|
||||
cycleParams = self.getCycleParameters()
|
||||
if cycleParams is None:
|
||||
self.__miningyield = 0
|
||||
else:
|
||||
cycleTime = cycleParams.averageTime
|
||||
volley = sum([getter(d) for d in self.MINING_ATTRIBUTES]) * self.amountActive
|
||||
self.__miningyield = volley / (cycleTime / 1000.0)
|
||||
else:
|
||||
self.__miningyield = 0
|
||||
|
||||
|
||||
@@ -17,16 +17,19 @@
|
||||
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
||||
# ===============================================================================
|
||||
|
||||
import math
|
||||
from logbook import Logger
|
||||
|
||||
from sqlalchemy.orm import validates, reconstructor
|
||||
from sqlalchemy.orm import reconstructor, validates
|
||||
|
||||
import eos.db
|
||||
from eos.effectHandlerHelpers import HandledItem, HandledCharge
|
||||
from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut, ChargeAttrShortcut
|
||||
from eos.saveddata.fighterAbility import FighterAbility
|
||||
from eos.utils.stats import DmgTypes
|
||||
from eos.const import FittingSlot
|
||||
from eos.effectHandlerHelpers import HandledCharge, HandledItem
|
||||
from eos.modifiedAttributeDict import ChargeAttrShortcut, ItemAttrShortcut, ModifiedAttributeDict
|
||||
from eos.saveddata.fighterAbility import FighterAbility
|
||||
from eos.utils.cycles import CycleInfo, CycleSequence
|
||||
from eos.utils.stats import DmgTypes
|
||||
from eos.utils.float import floatUnerr
|
||||
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
@@ -172,28 +175,37 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
def hasAmmo(self):
|
||||
return self.charge is not None
|
||||
|
||||
def getVolley(self, targetResists=None):
|
||||
def getVolleyParametersPerEffect(self, targetResists=None):
|
||||
if not self.active or self.amountActive <= 0:
|
||||
return DmgTypes(0, 0, 0, 0)
|
||||
return {}
|
||||
if self.__baseVolley is None:
|
||||
em = 0
|
||||
therm = 0
|
||||
kin = 0
|
||||
exp = 0
|
||||
self.__baseVolley = {}
|
||||
for ability in self.abilities:
|
||||
# Not passing resists here as we want to calculate and store base volley
|
||||
abilityVolley = ability.getVolley()
|
||||
em += abilityVolley.em
|
||||
therm += abilityVolley.thermal
|
||||
kin += abilityVolley.kinetic
|
||||
exp += abilityVolley.explosive
|
||||
self.__baseVolley = DmgTypes(em, therm, kin, exp)
|
||||
volley = DmgTypes(
|
||||
em=self.__baseVolley.em * (1 - getattr(targetResists, "emAmount", 0)),
|
||||
thermal=self.__baseVolley.thermal * (1 - getattr(targetResists, "thermalAmount", 0)),
|
||||
kinetic=self.__baseVolley.kinetic * (1 - getattr(targetResists, "kineticAmount", 0)),
|
||||
explosive=self.__baseVolley.explosive * (1 - getattr(targetResists, "explosiveAmount", 0)))
|
||||
return volley
|
||||
self.__baseVolley[ability.effectID] = {0: ability.getVolley()}
|
||||
adjustedVolley = {}
|
||||
for effectID, effectData in self.__baseVolley.items():
|
||||
adjustedVolley[effectID] = {}
|
||||
for volleyTime, volleyValue in effectData.items():
|
||||
adjustedVolley[effectID][volleyTime] = DmgTypes(
|
||||
em=volleyValue.em * (1 - getattr(targetResists, "emAmount", 0)),
|
||||
thermal=volleyValue.thermal * (1 - getattr(targetResists, "thermalAmount", 0)),
|
||||
kinetic=volleyValue.kinetic * (1 - getattr(targetResists, "kineticAmount", 0)),
|
||||
explosive=volleyValue.explosive * (1 - getattr(targetResists, "explosiveAmount", 0)))
|
||||
return adjustedVolley
|
||||
|
||||
def getVolley(self, targetResists=None):
|
||||
volleyParams = self.getVolleyParametersPerEffect(targetResists=targetResists)
|
||||
em = 0
|
||||
therm = 0
|
||||
kin = 0
|
||||
exp = 0
|
||||
for volleyData in volleyParams.values():
|
||||
em += volleyData[0].em
|
||||
therm += volleyData[0].thermal
|
||||
kin += volleyData[0].kinetic
|
||||
exp += volleyData[0].explosive
|
||||
return DmgTypes(em, therm, kin, exp)
|
||||
|
||||
def getDps(self, targetResists=None):
|
||||
em = 0
|
||||
@@ -207,44 +219,81 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
explosive += dps.explosive
|
||||
return DmgTypes(em=em, thermal=thermal, kinetic=kinetic, explosive=explosive)
|
||||
|
||||
def getUptime(self):
|
||||
if not self.owner.factorReload:
|
||||
return 1
|
||||
activeTimes = []
|
||||
reloadTimes = []
|
||||
for ability in self.abilities:
|
||||
if ability.numShots > 0:
|
||||
activeTimes.append(ability.numShots * ability.cycleTime)
|
||||
reloadTimes.append(ability.reloadTime)
|
||||
if not activeTimes:
|
||||
return 1
|
||||
shortestActive = sorted(activeTimes)[0]
|
||||
longestReload = sorted(reloadTimes, reverse=True)[0]
|
||||
uptime = shortestActive / (shortestActive + longestReload)
|
||||
return uptime
|
||||
|
||||
def getDpsPerEffect(self, targetResists=None):
|
||||
if not self.active or self.amountActive <= 0:
|
||||
return {}
|
||||
uptime = self.getUptime()
|
||||
if uptime == 1:
|
||||
return {a.effectID: a.getDps(targetResists=targetResists) for a in self.abilities}
|
||||
# Decide if it's better to keep steady dps up and never reload or reload from time to time
|
||||
dpsMapSteady = {}
|
||||
dpsMapPeakAdjusted = {}
|
||||
cycleParams = self.getCycleParametersPerEffectOptimizedDps(targetResists=targetResists)
|
||||
dpsMap = {}
|
||||
for ability in self.abilities:
|
||||
abilityDps = ability.getDps(targetResists=targetResists)
|
||||
dpsMapPeakAdjusted[ability.effectID] = DmgTypes(
|
||||
em=abilityDps.em * uptime,
|
||||
thermal=abilityDps.thermal * uptime,
|
||||
kinetic=abilityDps.kinetic * uptime,
|
||||
explosive=abilityDps.explosive * uptime)
|
||||
# Infinite use - add to steady dps
|
||||
if ability.numShots == 0:
|
||||
dpsMapSteady[ability.effectID] = abilityDps
|
||||
totalSteady = sum(i.total for i in dpsMapSteady.values())
|
||||
totalPeakAdjusted = sum(i.total for i in dpsMapPeakAdjusted.values())
|
||||
return dpsMapSteady if totalSteady >= totalPeakAdjusted else dpsMapPeakAdjusted
|
||||
if ability.effectID in cycleParams:
|
||||
cycleTime = cycleParams[ability.effectID].averageTime
|
||||
dpsMap[ability.effectID] = ability.getDps(targetResists=targetResists, cycleTimeOverride=cycleTime)
|
||||
return dpsMap
|
||||
|
||||
def getCycleParametersPerEffectOptimizedDps(self, targetResists=None, reloadOverride=None):
|
||||
cycleParamsInfinite = self.getCycleParametersPerEffectInfinite()
|
||||
cycleParamsReload = self.getCycleParametersPerEffect(reloadOverride=reloadOverride)
|
||||
dpsMapOnlyInfinite = {}
|
||||
dpsMapAllWithReloads = {}
|
||||
# Decide if it's better to keep steady dps up and never reload or reload from time to time
|
||||
for ability in self.abilities:
|
||||
if ability.effectID in cycleParamsInfinite:
|
||||
cycleTime = cycleParamsInfinite[ability.effectID].averageTime
|
||||
dpsMapOnlyInfinite[ability.effectID] = ability.getDps(targetResists=targetResists, cycleTimeOverride=cycleTime)
|
||||
if ability.effectID in cycleParamsReload:
|
||||
cycleTime = cycleParamsReload[ability.effectID].averageTime
|
||||
dpsMapAllWithReloads[ability.effectID] = ability.getDps(targetResists=targetResists, cycleTimeOverride=cycleTime)
|
||||
totalOnlyInfinite = sum(i.total for i in dpsMapOnlyInfinite.values())
|
||||
totalAllWithReloads = sum(i.total for i in dpsMapAllWithReloads.values())
|
||||
return cycleParamsInfinite if totalOnlyInfinite >= totalAllWithReloads else cycleParamsReload
|
||||
|
||||
def getCycleParametersPerEffectInfinite(self):
|
||||
return {a.effectID: CycleInfo(a.cycleTime, 0, math.inf) for a in self.abilities if a.numShots == 0 and a.cycleTime > 0}
|
||||
|
||||
def getCycleParametersPerEffect(self, reloadOverride=None):
|
||||
factorReload = reloadOverride if reloadOverride is not None else self.owner.factorReload
|
||||
# Assume it can cycle infinitely
|
||||
if not factorReload:
|
||||
return {a.effectID: CycleInfo(a.cycleTime, 0, math.inf) for a in self.abilities if a.cycleTime > 0}
|
||||
limitedAbilities = [a for a in self.abilities if a.numShots > 0 and a.cycleTime > 0]
|
||||
if len(limitedAbilities) == 0:
|
||||
return {a.effectID: CycleInfo(a.cycleTime, 0, math.inf) for a in self.abilities if a.cycleTime > 0}
|
||||
validAbilities = [a for a in self.abilities if a.cycleTime > 0]
|
||||
if len(validAbilities) == 0:
|
||||
return {}
|
||||
mostLimitedAbility = min(limitedAbilities, key=lambda a: a.cycleTime * a.numShots)
|
||||
durationToRefuel = mostLimitedAbility.cycleTime * mostLimitedAbility.numShots
|
||||
# find out how many shots various abilities will do until reload, and how much time
|
||||
# "extra" cycle will last (None for no extra cycle)
|
||||
cyclesUntilRefuel = {mostLimitedAbility.effectID: (mostLimitedAbility.numShots, None)}
|
||||
for ability in (a for a in validAbilities if a is not mostLimitedAbility):
|
||||
fullCycles = int(floatUnerr(durationToRefuel / ability.cycleTime))
|
||||
extraShotTime = floatUnerr(durationToRefuel - (fullCycles * ability.cycleTime))
|
||||
if extraShotTime == 0:
|
||||
extraShotTime = None
|
||||
cyclesUntilRefuel[ability.effectID] = (fullCycles, extraShotTime)
|
||||
refuelTimes = {}
|
||||
for ability in validAbilities:
|
||||
spentShots, extraShotTime = cyclesUntilRefuel[ability.effectID]
|
||||
if extraShotTime is not None:
|
||||
spentShots += 1
|
||||
refuelTimes[ability.effectID] = ability.getReloadTime(spentShots)
|
||||
refuelTime = max(refuelTimes.values())
|
||||
cycleParams = {}
|
||||
for ability in validAbilities:
|
||||
regularShots, extraShotTime = cyclesUntilRefuel[ability.effectID]
|
||||
sequence = []
|
||||
if extraShotTime is not None:
|
||||
if regularShots > 0:
|
||||
sequence.append(CycleInfo(ability.cycleTime, 0, regularShots))
|
||||
sequence.append(CycleInfo(extraShotTime, refuelTime, 1))
|
||||
else:
|
||||
regularShotsNonReload = regularShots - 1
|
||||
if regularShotsNonReload > 0:
|
||||
sequence.append(CycleInfo(ability.cycleTime, 0, regularShotsNonReload))
|
||||
sequence.append(CycleInfo(ability.cycleTime, refuelTime, 1))
|
||||
cycleParams[ability.effectID] = CycleSequence(sequence, math.inf)
|
||||
return cycleParams
|
||||
|
||||
@property
|
||||
def maxRange(self):
|
||||
|
||||
@@ -95,8 +95,15 @@ class FighterAbility(object):
|
||||
|
||||
@property
|
||||
def reloadTime(self):
|
||||
return self.getReloadTime()
|
||||
|
||||
def getReloadTime(self, spentShots=None):
|
||||
if spentShots is not None:
|
||||
spentShots = max(self.numShots, spentShots)
|
||||
else:
|
||||
spentShots = self.numShots
|
||||
rearm_time = (self.REARM_TIME_MAPPING[self.fighter.getModifiedItemAttr("fighterSquadronRole")] or 0 if self.hasCharges else 0)
|
||||
return self.fighter.getModifiedItemAttr("fighterRefuelingTime") + rearm_time * self.numShots
|
||||
return self.fighter.getModifiedItemAttr("fighterRefuelingTime") + rearm_time * spentShots
|
||||
|
||||
@property
|
||||
def numShots(self):
|
||||
@@ -128,11 +135,12 @@ class FighterAbility(object):
|
||||
explosive=exp * dmgMult * (1 - getattr(targetResists, "explosiveAmount", 0)))
|
||||
return volley
|
||||
|
||||
def getDps(self, targetResists=None):
|
||||
def getDps(self, targetResists=None, cycleTimeOverride=None):
|
||||
volley = self.getVolley(targetResists=targetResists)
|
||||
if not volley:
|
||||
return DmgTypes(0, 0, 0, 0)
|
||||
dpsFactor = 1 / (self.cycleTime / 1000)
|
||||
cycleTime = cycleTimeOverride if cycleTimeOverride is not None else self.cycleTime
|
||||
dpsFactor = 1 / (cycleTime / 1000)
|
||||
dps = DmgTypes(
|
||||
em=volley.em * dpsFactor,
|
||||
thermal=volley.thermal * dpsFactor,
|
||||
|
||||
@@ -17,26 +17,27 @@
|
||||
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
||||
# ===============================================================================
|
||||
|
||||
import datetime
|
||||
import time
|
||||
from copy import deepcopy
|
||||
from itertools import chain
|
||||
from math import sqrt, log, asinh
|
||||
import datetime
|
||||
|
||||
from sqlalchemy.orm import validates, reconstructor
|
||||
from logbook import Logger
|
||||
from math import asinh, log, sqrt
|
||||
from sqlalchemy.orm import reconstructor, validates
|
||||
|
||||
import eos.db
|
||||
from eos import capSim
|
||||
from eos.effectHandlerHelpers import HandledModuleList, HandledDroneCargoList, HandledImplantList, HandledBoosterList, HandledProjectedDroneList, HandledProjectedModList
|
||||
from eos.const import ImplantLocation, CalcType, FittingSlot
|
||||
from eos.saveddata.ship import Ship
|
||||
from eos.saveddata.drone import Drone
|
||||
from eos.const import CalcType, FitSystemSecurity, FittingHardpoint, FittingModuleState, FittingSlot, ImplantLocation
|
||||
from eos.effectHandlerHelpers import (
|
||||
HandledBoosterList, HandledDroneCargoList, HandledImplantList,
|
||||
HandledModuleList, HandledProjectedDroneList, HandledProjectedModList)
|
||||
from eos.saveddata.character import Character
|
||||
from eos.saveddata.citadel import Citadel
|
||||
from eos.const import FittingModuleState, FittingHardpoint
|
||||
from eos.saveddata.module import Module
|
||||
from eos.saveddata.ship import Ship
|
||||
from eos.utils.stats import DmgTypes
|
||||
from logbook import Logger
|
||||
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
@@ -924,8 +925,10 @@ class Fit(object):
|
||||
recalc. Figure out a way to keep track of any changes to slot layout and call this automatically
|
||||
"""
|
||||
if self.ship is None:
|
||||
return
|
||||
return {}
|
||||
|
||||
# Look for any dummies of that type to remove
|
||||
posToRemove = {}
|
||||
for slotType in (FittingSlot.LOW.value, FittingSlot.MED.value, FittingSlot.HIGH.value, FittingSlot.RIG.value, FittingSlot.SUBSYSTEM.value, FittingSlot.SERVICE.value):
|
||||
amount = self.getSlotsFree(slotType, True)
|
||||
if amount > 0:
|
||||
@@ -933,16 +936,17 @@ class Fit(object):
|
||||
self.modules.append(Module.buildEmpty(slotType))
|
||||
|
||||
if amount < 0:
|
||||
# Look for any dummies of that type to remove
|
||||
toRemove = []
|
||||
for mod in self.modules:
|
||||
if mod.isEmpty and mod.slot == slotType:
|
||||
toRemove.append(mod)
|
||||
pos = self.modules.index(mod)
|
||||
posToRemove[pos] = slotType
|
||||
amount += 1
|
||||
if amount == 0:
|
||||
break
|
||||
for mod in toRemove:
|
||||
self.modules.remove(mod)
|
||||
for pos in sorted(posToRemove, reverse=True):
|
||||
mod = self.modules[pos]
|
||||
self.modules.remove(mod)
|
||||
return posToRemove
|
||||
|
||||
def unfill(self):
|
||||
for i in range(len(self.modules) - 1, -1, -1):
|
||||
@@ -1507,6 +1511,28 @@ class Fit(object):
|
||||
|
||||
return True
|
||||
|
||||
def getReleaseLimitForDrone(self, item):
|
||||
if not item.isDrone:
|
||||
return 0
|
||||
bw = round(self.ship.getModifiedItemAttr("droneBandwidth"))
|
||||
volume = round(item.attribsWithOverrides['volume'].value)
|
||||
return int(bw / volume)
|
||||
|
||||
def getStoreLimitForDrone(self, item):
|
||||
if not item.isDrone:
|
||||
return 0
|
||||
bayTotal = round(self.ship.getModifiedItemAttr("droneCapacity"))
|
||||
bayUsed = round(self.droneBayUsed)
|
||||
volume = item.attribsWithOverrides['volume'].value
|
||||
return int((bayTotal - bayUsed) / volume)
|
||||
|
||||
def getSystemSecurity(self):
|
||||
secstatus = self.systemSecurity
|
||||
# Default to nullsec
|
||||
if secstatus is None:
|
||||
secstatus = FitSystemSecurity.NULLSEC
|
||||
return secstatus
|
||||
|
||||
def __deepcopy__(self, memo=None):
|
||||
fitCopy = Fit()
|
||||
# Character and owner are not copied
|
||||
@@ -1518,10 +1544,12 @@ class Fit(object):
|
||||
fitCopy.damagePattern = self.damagePattern
|
||||
fitCopy.targetResists = self.targetResists
|
||||
fitCopy.implantLocation = self.implantLocation
|
||||
fitCopy.systemSecurity = self.systemSecurity
|
||||
fitCopy.notes = self.notes
|
||||
|
||||
for i in self.modules:
|
||||
fitCopy.modules.appendIgnoreEmpty(deepcopy(i))
|
||||
toCopy = (
|
||||
"modules",
|
||||
"drones",
|
||||
"fighters",
|
||||
"cargo",
|
||||
|
||||
@@ -17,21 +17,22 @@
|
||||
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
||||
# ===============================================================================
|
||||
|
||||
from math import floor
|
||||
|
||||
from logbook import Logger
|
||||
import math
|
||||
from sqlalchemy.orm import reconstructor, validates
|
||||
|
||||
import eos.db
|
||||
from eos.const import FittingModuleState, FittingHardpoint, FittingSlot
|
||||
from eos.const import FittingHardpoint, FittingModuleState, FittingSlot
|
||||
from eos.effectHandlerHelpers import HandledCharge, HandledItem
|
||||
from eos.modifiedAttributeDict import ChargeAttrShortcut, ItemAttrShortcut, ModifiedAttributeDict
|
||||
from eos.saveddata.citadel import Citadel
|
||||
from eos.saveddata.mutator import Mutator
|
||||
from eos.utils.cycles import CycleInfo, CycleSequence
|
||||
from eos.utils.float import floatUnerr
|
||||
from eos.utils.spoolSupport import calculateSpoolup, resolveSpoolOptions
|
||||
from eos.utils.stats import DmgTypes
|
||||
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
ProjectedMap = {
|
||||
@@ -287,7 +288,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
# numcycles = math.floor(module_capacity / (module_volume * module_chargerate))
|
||||
chargeRate = self.getModifiedItemAttr("chargeRate")
|
||||
numCharges = self.numCharges
|
||||
numShots = floor(numCharges / chargeRate)
|
||||
numShots = math.floor(numCharges / chargeRate)
|
||||
else:
|
||||
numShots = None
|
||||
return numShots
|
||||
@@ -300,7 +301,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
chance = self.getModifiedChargeAttr("crystalVolatilityChance")
|
||||
damage = self.getModifiedChargeAttr("crystalVolatilityDamage")
|
||||
crystals = self.numCharges
|
||||
numShots = floor((crystals * hp) / (damage * chance))
|
||||
numShots = math.floor((crystals * hp) / (damage * chance))
|
||||
else:
|
||||
# Set 0 (infinite) for permanent crystals like t1 laser crystals
|
||||
numShots = 0
|
||||
@@ -400,8 +401,12 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
volley = self.getModifiedItemAttr("specialtyMiningAmount") or self.getModifiedItemAttr(
|
||||
"miningAmount") or 0
|
||||
if volley:
|
||||
cycleTime = self.cycleTime
|
||||
self.__miningyield = volley / (cycleTime / 1000.0)
|
||||
cycleParams = self.getCycleParameters()
|
||||
if cycleParams is None:
|
||||
self.__miningyield = 0
|
||||
else:
|
||||
cycleTime = cycleParams.averageTime
|
||||
self.__miningyield = volley / (cycleTime / 1000.0)
|
||||
else:
|
||||
self.__miningyield = 0
|
||||
else:
|
||||
@@ -409,42 +414,64 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
|
||||
return self.__miningyield
|
||||
|
||||
def getVolley(self, spoolOptions=None, targetResists=None, ignoreState=False):
|
||||
def getVolleyParameters(self, spoolOptions=None, targetResists=None, ignoreState=False):
|
||||
if self.isEmpty or (self.state < FittingModuleState.ACTIVE and not ignoreState):
|
||||
return DmgTypes(0, 0, 0, 0)
|
||||
return {0: DmgTypes(0, 0, 0, 0)}
|
||||
if self.__baseVolley is None:
|
||||
self.__baseVolley = {}
|
||||
dmgGetter = self.getModifiedChargeAttr if self.charge else self.getModifiedItemAttr
|
||||
dmgMult = self.getModifiedItemAttr("damageMultiplier", 1)
|
||||
self.__baseVolley = DmgTypes(
|
||||
em=(dmgGetter("emDamage", 0)) * dmgMult,
|
||||
thermal=(dmgGetter("thermalDamage", 0)) * dmgMult,
|
||||
kinetic=(dmgGetter("kineticDamage", 0)) * dmgMult,
|
||||
explosive=(dmgGetter("explosiveDamage", 0)) * dmgMult)
|
||||
dmgDelay = self.getModifiedItemAttr("damageDelayDuration", 0) or self.getModifiedItemAttr("doomsdayWarningDuration", 0)
|
||||
dmgDuration = self.getModifiedItemAttr("doomsdayDamageDuration", 0)
|
||||
dmgSubcycle = self.getModifiedItemAttr("doomsdayDamageCycleTime", 0)
|
||||
if dmgDuration != 0 and dmgSubcycle != 0:
|
||||
subcycles = math.floor(floatUnerr(dmgDuration / dmgSubcycle))
|
||||
else:
|
||||
subcycles = 1
|
||||
for i in range(subcycles):
|
||||
self.__baseVolley[dmgDelay + dmgSubcycle * i] = DmgTypes(
|
||||
em=(dmgGetter("emDamage", 0)) * dmgMult,
|
||||
thermal=(dmgGetter("thermalDamage", 0)) * dmgMult,
|
||||
kinetic=(dmgGetter("kineticDamage", 0)) * dmgMult,
|
||||
explosive=(dmgGetter("explosiveDamage", 0)) * dmgMult)
|
||||
spoolType, spoolAmount = resolveSpoolOptions(spoolOptions, self)
|
||||
spoolBoost = calculateSpoolup(
|
||||
self.getModifiedItemAttr("damageMultiplierBonusMax", 0),
|
||||
self.getModifiedItemAttr("damageMultiplierBonusPerCycle", 0),
|
||||
self.rawCycleTime / 1000, spoolType, spoolAmount)[0]
|
||||
spoolMultiplier = 1 + spoolBoost
|
||||
volley = DmgTypes(
|
||||
em=self.__baseVolley.em * spoolMultiplier * (1 - getattr(targetResists, "emAmount", 0)),
|
||||
thermal=self.__baseVolley.thermal * spoolMultiplier * (1 - getattr(targetResists, "thermalAmount", 0)),
|
||||
kinetic=self.__baseVolley.kinetic * spoolMultiplier * (1 - getattr(targetResists, "kineticAmount", 0)),
|
||||
explosive=self.__baseVolley.explosive * spoolMultiplier * (1 - getattr(targetResists, "explosiveAmount", 0)))
|
||||
return volley
|
||||
adjustedVolley = {}
|
||||
for volleyTime, volleyValue in self.__baseVolley.items():
|
||||
adjustedVolley[volleyTime] = DmgTypes(
|
||||
em=volleyValue.em * spoolMultiplier * (1 - getattr(targetResists, "emAmount", 0)),
|
||||
thermal=volleyValue.thermal * spoolMultiplier * (1 - getattr(targetResists, "thermalAmount", 0)),
|
||||
kinetic=volleyValue.kinetic * spoolMultiplier * (1 - getattr(targetResists, "kineticAmount", 0)),
|
||||
explosive=volleyValue.explosive * spoolMultiplier * (1 - getattr(targetResists, "explosiveAmount", 0)))
|
||||
return adjustedVolley
|
||||
|
||||
def getVolley(self, spoolOptions=None, targetResists=None, ignoreState=False):
|
||||
volleyParams = self.getVolleyParameters(spoolOptions=spoolOptions, targetResists=targetResists, ignoreState=ignoreState)
|
||||
if len(volleyParams) == 0:
|
||||
return DmgTypes(0, 0, 0, 0)
|
||||
return volleyParams[min(volleyParams)]
|
||||
|
||||
def getDps(self, spoolOptions=None, targetResists=None, ignoreState=False):
|
||||
volley = self.getVolley(spoolOptions=spoolOptions, targetResists=targetResists, ignoreState=ignoreState)
|
||||
if not volley:
|
||||
return DmgTypes(0, 0, 0, 0)
|
||||
# Some weapons repeat multiple times in one cycle (bosonic doomsdays). Get the number of times it fires off
|
||||
volleysPerCycle = max(self.getModifiedItemAttr("doomsdayDamageDuration", 1) / self.getModifiedItemAttr("doomsdayDamageCycleTime", 1), 1)
|
||||
dpsFactor = volleysPerCycle / (self.cycleTime / 1000)
|
||||
dmgDuringCycle = DmgTypes(0, 0, 0, 0)
|
||||
cycleParams = self.getCycleParameters()
|
||||
if cycleParams is None:
|
||||
return dmgDuringCycle
|
||||
volleyParams = self.getVolleyParameters(spoolOptions=spoolOptions, targetResists=targetResists, ignoreState=ignoreState)
|
||||
avgCycleTime = cycleParams.averageTime
|
||||
if len(volleyParams) == 0 or avgCycleTime == 0:
|
||||
return dmgDuringCycle
|
||||
for volleyValue in volleyParams.values():
|
||||
dmgDuringCycle += volleyValue
|
||||
dpsFactor = 1 / (avgCycleTime / 1000)
|
||||
dps = DmgTypes(
|
||||
em=volley.em * dpsFactor,
|
||||
thermal=volley.thermal * dpsFactor,
|
||||
kinetic=volley.kinetic * dpsFactor,
|
||||
explosive=volley.explosive * dpsFactor)
|
||||
em=dmgDuringCycle.em * dpsFactor,
|
||||
thermal=dmgDuringCycle.thermal * dpsFactor,
|
||||
kinetic=dmgDuringCycle.kinetic * dpsFactor,
|
||||
explosive=dmgDuringCycle.explosive * dpsFactor)
|
||||
return dps
|
||||
|
||||
def getRemoteReps(self, spoolOptions=None, ignoreState=False):
|
||||
@@ -474,7 +501,10 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
else:
|
||||
return None, 0
|
||||
if rrAmount:
|
||||
rrAmount *= 1 / (self.cycleTime / 1000)
|
||||
cycleParams = self.getCycleParameters()
|
||||
if cycleParams is None:
|
||||
return None, 0
|
||||
rrAmount *= 1 / (cycleParams.averageTime / 1000)
|
||||
if module.item.group.name == "Ancillary Remote Armor Repairer" and module.charge:
|
||||
rrAmount *= module.getModifiedItemAttr("chargedArmorDamageMultiplier", 1)
|
||||
|
||||
@@ -550,6 +580,8 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
if not fits and fit.ignoreRestrictions:
|
||||
self.restrictionOverridden = True
|
||||
fits = True
|
||||
elif fits and fit.ignoreRestrictions:
|
||||
self.restrictionOverridden = False
|
||||
|
||||
return fits
|
||||
|
||||
@@ -567,6 +599,8 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
if self.slot == FittingSlot.SUBSYSTEM:
|
||||
subSlot = self.getModifiedItemAttr("subSystemSlot")
|
||||
for mod in fit.modules:
|
||||
if mod is self:
|
||||
continue
|
||||
if mod.getModifiedItemAttr("subSystemSlot") == subSlot:
|
||||
return False
|
||||
|
||||
@@ -601,7 +635,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
# Check if we're within bounds
|
||||
if state < -1 or state > 2:
|
||||
return False
|
||||
elif state >= FittingModuleState.ACTIVE and not self.item.isType("active"):
|
||||
elif state >= FittingModuleState.ACTIVE and (not self.item.isType("active") or self.getModifiedItemAttr('activationBlocked') > 0):
|
||||
return False
|
||||
elif state == FittingModuleState.OVERHEATED and not self.item.isType("overheat"):
|
||||
return False
|
||||
@@ -771,28 +805,26 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
context = ("module",)
|
||||
projected = False
|
||||
|
||||
# if gang:
|
||||
# context += ("commandRun",)
|
||||
|
||||
if self.charge is not None:
|
||||
# fix for #82 and it's regression #106
|
||||
if not projected or (self.projected and not forceProjected) or gang:
|
||||
for effect in self.charge.effects.values():
|
||||
if effect.runTime == runTime and \
|
||||
effect.activeByDefault and \
|
||||
(effect.isType("offline") or
|
||||
(effect.isType("passive") and self.state >= FittingModuleState.ONLINE) or
|
||||
(effect.isType("active") and self.state >= FittingModuleState.ACTIVE)) and \
|
||||
(not gang or (gang and effect.isType("gang"))):
|
||||
|
||||
chargeContext = ("moduleCharge",)
|
||||
# For gang effects, we pass in the effect itself as an argument. However, to avoid going through
|
||||
# all the effect files and defining this argument, do a simple try/catch here and be done with it.
|
||||
if (
|
||||
effect.runTime == runTime and
|
||||
effect.activeByDefault and (
|
||||
effect.isType("offline") or
|
||||
(effect.isType("passive") and self.state >= FittingModuleState.ONLINE) or
|
||||
(effect.isType("active") and self.state >= FittingModuleState.ACTIVE)) and
|
||||
(not gang or (gang and effect.isType("gang")))
|
||||
):
|
||||
contexts = ("moduleCharge",)
|
||||
# For gang effects, we pass in the effect itself as an argument. However, to avoid going through all
|
||||
# the effect definitions and defining this argument, do a simple try/catch here and be done with it.
|
||||
# @todo: possibly fix this
|
||||
try:
|
||||
effect.handler(fit, self, chargeContext, effect=effect)
|
||||
effect.handler(fit, self, contexts, effect=effect)
|
||||
except:
|
||||
effect.handler(fit, self, chargeContext)
|
||||
effect.handler(fit, self, contexts)
|
||||
|
||||
if self.item:
|
||||
if self.state >= FittingModuleState.OVERHEATED:
|
||||
@@ -817,49 +849,61 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
except:
|
||||
effect.handler(fit, self, context)
|
||||
|
||||
@property
|
||||
def cycleTime(self):
|
||||
def getCycleParameters(self, reloadOverride=None):
|
||||
"""Copied from new eos as well"""
|
||||
# Determine if we'll take into account reload time or not
|
||||
factorReload = self.owner.factorReload if self.forceReload is None else self.forceReload
|
||||
|
||||
numShots = self.numShots
|
||||
speed = self.rawCycleTime
|
||||
|
||||
if factorReload and self.charge:
|
||||
raw_reload_time = self.reloadTime
|
||||
if reloadOverride is not None:
|
||||
factorReload = reloadOverride
|
||||
else:
|
||||
raw_reload_time = 0.0
|
||||
factorReload = self.owner.factorReload if self.forceReload is None else self.forceReload
|
||||
|
||||
# Module can only fire one shot at a time, think bomb launchers or defender launchers
|
||||
if self.disallowRepeatingAction:
|
||||
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)
|
||||
so your effective reload time depends on where you are at in the cycle.
|
||||
cycles_until_reload = self.numShots
|
||||
if cycles_until_reload == 0:
|
||||
cycles_until_reload = math.inf
|
||||
|
||||
We can't do that, so instead we'll average it out.
|
||||
|
||||
Currently would apply to bomb launchers and defender missiles
|
||||
"""
|
||||
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
|
||||
active_time = self.rawCycleTime
|
||||
if active_time == 0:
|
||||
return None
|
||||
forced_inactive_time = self.reactivationDelay
|
||||
reload_time = self.reloadTime
|
||||
# Effects which cannot be reloaded have the same processing whether
|
||||
# caller wants to take reload time into account or not
|
||||
if reload_time is None and cycles_until_reload < math.inf:
|
||||
final_cycles = 1
|
||||
early_cycles = cycles_until_reload - final_cycles
|
||||
# Single cycle until effect cannot run anymore
|
||||
if early_cycles == 0:
|
||||
return CycleInfo(active_time, 0, 1)
|
||||
# Multiple cycles with the same parameters
|
||||
if forced_inactive_time == 0:
|
||||
return CycleInfo(active_time, 0, cycles_until_reload)
|
||||
# Multiple cycles with different parameters
|
||||
return CycleSequence((
|
||||
CycleInfo(active_time, forced_inactive_time, early_cycles),
|
||||
CycleInfo(active_time, 0, final_cycles)
|
||||
), 1)
|
||||
# Module cycles the same way all the time in 3 cases:
|
||||
# 1) caller doesn't want to take into account reload time
|
||||
# 2) effect does not have to reload anything to keep running
|
||||
# 3) effect has enough time to reload during inactivity periods
|
||||
if (
|
||||
not factorReload or
|
||||
cycles_until_reload == math.inf or
|
||||
forced_inactive_time >= reload_time
|
||||
):
|
||||
return CycleInfo(active_time, forced_inactive_time, math.inf)
|
||||
# We've got to take reload into consideration
|
||||
else:
|
||||
"""
|
||||
Currently no other modules would have a reactivation delay, so for sanities sake don't try and account for it.
|
||||
Okay, technically cloaks do, but they also have 0 cycle time and cap usage so why do you care?
|
||||
"""
|
||||
effective_reload_time = raw_reload_time
|
||||
|
||||
if numShots > 0 and self.charge:
|
||||
speed = (speed * numShots + effective_reload_time) / numShots
|
||||
|
||||
return speed
|
||||
final_cycles = 1
|
||||
early_cycles = cycles_until_reload - final_cycles
|
||||
# If effect has to reload after each its cycle, then its parameters
|
||||
# are the same all the time
|
||||
if early_cycles == 0:
|
||||
return CycleInfo(active_time, reload_time, math.inf)
|
||||
return CycleSequence((
|
||||
CycleInfo(active_time, forced_inactive_time, early_cycles),
|
||||
CycleInfo(active_time, reload_time, final_cycles)
|
||||
), math.inf)
|
||||
|
||||
@property
|
||||
def rawCycleTime(self):
|
||||
@@ -885,7 +929,10 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
def capUse(self):
|
||||
capNeed = self.getModifiedItemAttr("capacitorNeed")
|
||||
if capNeed and self.state >= FittingModuleState.ACTIVE:
|
||||
cycleTime = self.cycleTime
|
||||
cycleParams = self.getCycleParameters()
|
||||
if cycleParams is None:
|
||||
return 0
|
||||
cycleTime = cycleParams.averageTime
|
||||
if cycleTime > 0:
|
||||
capUsed = capNeed / (cycleTime / 1000.0)
|
||||
return capUsed
|
||||
@@ -894,7 +941,6 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
|
||||
@staticmethod
|
||||
def getProposedState(mod, click, proposedState=None):
|
||||
# todo: instead of passing in module, make this a instanced function.
|
||||
pyfalog.debug("Get proposed state for module.")
|
||||
if mod.slot == FittingSlot.SUBSYSTEM or mod.isEmpty:
|
||||
return FittingModuleState.ONLINE
|
||||
@@ -914,13 +960,12 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
state = FittingModuleState.OFFLINE
|
||||
else:
|
||||
state = transitionMap[currState]
|
||||
if not mod.isValidState(state):
|
||||
state = -1
|
||||
# If passive module tries to transition into online and fails,
|
||||
# put it to passive instead
|
||||
if not mod.isValidState(state) and currState == FittingModuleState.ONLINE:
|
||||
state = FittingModuleState.OFFLINE
|
||||
|
||||
if mod.isValidState(state):
|
||||
return state
|
||||
else:
|
||||
return currState
|
||||
return mod.getMaxState(proposedState=state)
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
item = self.item
|
||||
|
||||
68
eos/utils/cycles.py
Normal file
68
eos/utils/cycles.py
Normal file
@@ -0,0 +1,68 @@
|
||||
# Borrowed from new eos
|
||||
|
||||
|
||||
from utils.repr import makeReprStr
|
||||
|
||||
|
||||
class CycleInfo:
|
||||
|
||||
def __init__(self, activeTime, inactiveTime, quantity):
|
||||
self.activeTime = activeTime
|
||||
self.inactiveTime = inactiveTime
|
||||
self.quantity = quantity
|
||||
|
||||
@property
|
||||
def averageTime(self):
|
||||
return self.activeTime + self.inactiveTime
|
||||
|
||||
def iterCycles(self):
|
||||
i = 0
|
||||
while i < self.quantity:
|
||||
yield self.activeTime, self.inactiveTime
|
||||
i += 1
|
||||
|
||||
def _getCycleQuantity(self):
|
||||
return self.quantity
|
||||
|
||||
def _getTime(self):
|
||||
return (self.activeTime + self.inactiveTime) * self.quantity
|
||||
|
||||
def __repr__(self):
|
||||
spec = ['activeTime', 'inactiveTime', 'quantity']
|
||||
return makeReprStr(self, spec)
|
||||
|
||||
|
||||
class CycleSequence:
|
||||
|
||||
def __init__(self, sequence, quantity):
|
||||
self.sequence = sequence
|
||||
self.quantity = quantity
|
||||
|
||||
@property
|
||||
def averageTime(self):
|
||||
"""Get average time between cycles."""
|
||||
return self._getTime() / self._getCycleQuantity()
|
||||
|
||||
def iterCycles(self):
|
||||
i = 0
|
||||
while i < self.quantity:
|
||||
for cycleInfo in self.sequence:
|
||||
for cycleTime, inactiveTime in cycleInfo.iterCycles():
|
||||
yield cycleTime, inactiveTime
|
||||
i += 1
|
||||
|
||||
def _getCycleQuantity(self):
|
||||
quantity = 0
|
||||
for item in self.sequence:
|
||||
quantity += item._getCycleQuantity()
|
||||
return quantity
|
||||
|
||||
def _getTime(self):
|
||||
time = 0
|
||||
for item in self.sequence:
|
||||
time += item._getTime()
|
||||
return time
|
||||
|
||||
def __repr__(self):
|
||||
spec = ['sequence', 'quantity']
|
||||
return makeReprStr(self, spec)
|
||||
@@ -34,6 +34,7 @@ from gui.toggle_panel import TogglePanel
|
||||
|
||||
|
||||
class AdditionsPane(TogglePanel):
|
||||
|
||||
def __init__(self, parent):
|
||||
|
||||
TogglePanel.__init__(self, parent, force_layout=1)
|
||||
@@ -86,8 +87,8 @@ class AdditionsPane(TogglePanel):
|
||||
|
||||
PANES = ["Drones", "Fighters", "Cargo", "Implants", "Boosters", "Projected", "Command", "Notes"]
|
||||
|
||||
def select(self, name):
|
||||
self.notebook.SetSelection(self.PANES.index(name))
|
||||
def select(self, name, focus=True):
|
||||
self.notebook.SetSelection(self.PANES.index(name), focus=focus)
|
||||
|
||||
def getName(self, idx):
|
||||
return self.PANES[idx]
|
||||
|
||||
@@ -50,8 +50,11 @@ class BitmapLoader(object):
|
||||
|
||||
@classmethod
|
||||
def getStaticBitmap(cls, name, parent, location):
|
||||
bitmap = cls.getBitmap(name or 0, location)
|
||||
if bitmap is None:
|
||||
return None
|
||||
static = wx.StaticBitmap(parent)
|
||||
static.SetBitmap(cls.getBitmap(name or 0, location))
|
||||
static.SetBitmap(bitmap)
|
||||
return static
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -48,6 +48,7 @@ class BoosterViewDrop(wx.DropTarget):
|
||||
|
||||
|
||||
class BoosterView(d.Display):
|
||||
|
||||
DEFAULT_COLS = [
|
||||
"State",
|
||||
"attr:boosterness",
|
||||
@@ -58,14 +59,14 @@ class BoosterView(d.Display):
|
||||
]
|
||||
|
||||
def __init__(self, parent):
|
||||
d.Display.__init__(self, parent, style=wx.LC_SINGLE_SEL | wx.BORDER_NONE)
|
||||
d.Display.__init__(self, parent, style=wx.BORDER_NONE)
|
||||
|
||||
self.lastFitId = None
|
||||
|
||||
self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged)
|
||||
self.mainFrame.Bind(ITEM_SELECTED, self.addItem)
|
||||
|
||||
self.Bind(wx.EVT_LEFT_DCLICK, self.removeItem)
|
||||
self.Bind(wx.EVT_LEFT_DCLICK, self.onLeftDoubleClick)
|
||||
self.Bind(wx.EVT_LEFT_DOWN, self.click)
|
||||
self.Bind(wx.EVT_KEY_UP, self.kbEvent)
|
||||
|
||||
@@ -87,11 +88,14 @@ class BoosterView(d.Display):
|
||||
|
||||
def kbEvent(self, event):
|
||||
keycode = event.GetKeyCode()
|
||||
if keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE):
|
||||
row = self.GetFirstSelected()
|
||||
if row != -1:
|
||||
self.removeBooster(self.boosters[self.GetItemData(row)])
|
||||
|
||||
mstate = wx.GetMouseState()
|
||||
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
self.unselectAll()
|
||||
elif keycode == 65 and mstate.GetModifiers() == wx.MOD_CONTROL:
|
||||
self.selectAll()
|
||||
elif keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE) and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
boosters = self.getSelectedBoosters()
|
||||
self.removeBoosters(boosters)
|
||||
event.Skip()
|
||||
|
||||
def fitChanged(self, event):
|
||||
@@ -120,7 +124,7 @@ class BoosterView(d.Display):
|
||||
if item != -1:
|
||||
self.EnsureVisible(item)
|
||||
|
||||
self.deselectItems()
|
||||
self.unselectAll()
|
||||
|
||||
self.update(self.boosters)
|
||||
event.Skip()
|
||||
@@ -143,32 +147,78 @@ class BoosterView(d.Display):
|
||||
self.mainFrame.additionsPane.select('Boosters')
|
||||
event.Skip()
|
||||
|
||||
def removeItem(self, event):
|
||||
def onLeftDoubleClick(self, event):
|
||||
row, _ = self.HitTest(event.Position)
|
||||
if row != -1:
|
||||
col = self.getColumn(event.Position)
|
||||
if col != self.getColIndex(State):
|
||||
self.removeBooster(self.boosters[self.GetItemData(row)])
|
||||
try:
|
||||
booster = self.boosters[row]
|
||||
except IndexError:
|
||||
return
|
||||
self.removeBoosters([booster])
|
||||
|
||||
def removeBooster(self, booster):
|
||||
def removeBoosters(self, boosters):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveBoosterCommand(fitID=fitID, position=self.original.index(booster)))
|
||||
positions = []
|
||||
for booster in boosters:
|
||||
if booster in self.original:
|
||||
positions.append(self.original.index(booster))
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveBoostersCommand(fitID=fitID, positions=positions))
|
||||
|
||||
def click(self, event):
|
||||
event.Skip()
|
||||
row, _ = self.HitTest(event.Position)
|
||||
if row != -1:
|
||||
mainRow, _ = self.HitTest(event.Position)
|
||||
if mainRow != -1:
|
||||
col = self.getColumn(event.Position)
|
||||
if col == self.getColIndex(State):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
booster = self.boosters[self.GetItemData(row)]
|
||||
self.mainFrame.command.Submit(cmd.GuiToggleBoosterStateCommand(fitID=fitID, position=self.original.index(booster)))
|
||||
try:
|
||||
mainBooster = self.boosters[mainRow]
|
||||
except IndexError:
|
||||
return
|
||||
if mainBooster in self.original:
|
||||
mainPosition = self.original.index(mainBooster)
|
||||
positions = []
|
||||
for row in self.getSelectedRows():
|
||||
try:
|
||||
booster = self.boosters[row]
|
||||
except IndexError:
|
||||
continue
|
||||
if booster in self.original:
|
||||
positions.append(self.original.index(booster))
|
||||
if mainPosition not in positions:
|
||||
positions = [mainPosition]
|
||||
self.mainFrame.command.Submit(cmd.GuiToggleBoosterStatesCommand(
|
||||
fitID=fitID,
|
||||
mainPosition=mainPosition,
|
||||
positions=positions))
|
||||
return
|
||||
event.Skip()
|
||||
|
||||
def spawnMenu(self, event):
|
||||
sel = self.GetFirstSelected()
|
||||
if sel != -1:
|
||||
booster = self.boosters[sel]
|
||||
srcContext = "boosterItem"
|
||||
itemContext = "Booster"
|
||||
menu = ContextMenu.getMenu((booster,), (srcContext, itemContext))
|
||||
selection = self.getSelectedBoosters()
|
||||
clickedPos = self.getRowByAbs(event.Position)
|
||||
mainBooster = None
|
||||
if clickedPos != -1:
|
||||
try:
|
||||
booster = self.boosters[clickedPos]
|
||||
except IndexError:
|
||||
pass
|
||||
else:
|
||||
if booster in self.original:
|
||||
mainBooster = booster
|
||||
sourceContext = "boosterItem"
|
||||
itemContext = None if mainBooster is None else "Booster"
|
||||
menu = ContextMenu.getMenu(mainBooster, selection, (sourceContext, itemContext))
|
||||
if menu:
|
||||
self.PopupMenu(menu)
|
||||
|
||||
def getSelectedBoosters(self):
|
||||
boosters = []
|
||||
for row in self.getSelectedRows():
|
||||
try:
|
||||
booster = self.boosters[row]
|
||||
except IndexError:
|
||||
continue
|
||||
boosters.append(booster)
|
||||
return boosters
|
||||
|
||||
@@ -19,14 +19,14 @@
|
||||
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
|
||||
import gui.display as d
|
||||
from gui.builtinViewColumns.state import State
|
||||
from gui.contextMenu import ContextMenu
|
||||
import gui.fitCommands as cmd
|
||||
import gui.globalEvents as GE
|
||||
from gui.contextMenu import ContextMenu
|
||||
from gui.utils.staticHelpers import DragDropHelper
|
||||
from service.fit import Fit
|
||||
from service.market import Market
|
||||
import gui.fitCommands as cmd
|
||||
|
||||
|
||||
class CargoViewDrop(wx.DropTarget):
|
||||
@@ -53,12 +53,12 @@ class CargoView(d.Display):
|
||||
"Price"]
|
||||
|
||||
def __init__(self, parent):
|
||||
d.Display.__init__(self, parent, style=wx.LC_SINGLE_SEL | wx.BORDER_NONE)
|
||||
d.Display.__init__(self, parent, style=wx.BORDER_NONE)
|
||||
|
||||
self.lastFitId = None
|
||||
|
||||
self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged)
|
||||
self.Bind(wx.EVT_LEFT_DCLICK, self.removeItem)
|
||||
self.Bind(wx.EVT_LEFT_DCLICK, self.onLeftDoubleClick)
|
||||
self.Bind(wx.EVT_KEY_UP, self.kbEvent)
|
||||
|
||||
self.SetDropTarget(CargoViewDrop(self.handleListDrag))
|
||||
@@ -78,18 +78,25 @@ class CargoView(d.Display):
|
||||
if data[0] == "fitting":
|
||||
self.swapModule(x, y, int(data[1]))
|
||||
elif data[0] == "market":
|
||||
fit = self.mainFrame.getActiveFit()
|
||||
if fit:
|
||||
self.mainFrame.command.Submit(cmd.GuiAddCargoCommand(fit, int(data[1]), 1))
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
if fitID:
|
||||
self.mainFrame.command.Submit(cmd.GuiAddCargoCommand(
|
||||
fitID=fitID, itemID=int(data[1]), amount=1))
|
||||
|
||||
def startDrag(self, event):
|
||||
row = event.GetIndex()
|
||||
|
||||
if row != -1:
|
||||
data = wx.TextDataObject()
|
||||
dataStr = "cargo:{}".format(self.cargo[row].itemID)
|
||||
try:
|
||||
dataStr = "cargo:{}".format(self.cargo[row].itemID)
|
||||
except IndexError:
|
||||
return
|
||||
data.SetText(dataStr)
|
||||
|
||||
self.unselectAll()
|
||||
self.Select(row, True)
|
||||
|
||||
dropSource = wx.DropSource(self)
|
||||
dropSource.SetData(data)
|
||||
DragDropHelper.data = dataStr
|
||||
@@ -97,12 +104,14 @@ class CargoView(d.Display):
|
||||
|
||||
def kbEvent(self, event):
|
||||
keycode = event.GetKeyCode()
|
||||
if keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE):
|
||||
row = self.GetFirstSelected()
|
||||
if row != -1:
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
cargo = self.cargo[self.GetItemData(row)]
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveCargoCommand(fitID=fitID, itemID=cargo.itemID))
|
||||
mstate = wx.GetMouseState()
|
||||
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
self.unselectAll()
|
||||
elif keycode == 65 and mstate.GetModifiers() == wx.MOD_CONTROL:
|
||||
self.selectAll()
|
||||
elif keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE) and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
cargos = self.getSelectedCargos()
|
||||
self.removeCargos(cargos)
|
||||
event.Skip()
|
||||
|
||||
def swapModule(self, x, y, modIdx):
|
||||
@@ -111,11 +120,19 @@ class CargoView(d.Display):
|
||||
fit = sFit.getFit(self.mainFrame.getActiveFit())
|
||||
dstRow, _ = self.HitTest((x, y))
|
||||
|
||||
if dstRow > -1:
|
||||
try:
|
||||
dstCargoItemID = getattr(self.cargo[dstRow], 'itemID', None)
|
||||
except IndexError:
|
||||
dstCargoItemID = None
|
||||
else:
|
||||
dstCargoItemID = None
|
||||
|
||||
self.mainFrame.command.Submit(cmd.GuiLocalModuleToCargoCommand(
|
||||
fitID=self.mainFrame.getActiveFit(),
|
||||
modPosition=fit.modules[modIdx].modPosition,
|
||||
cargoItemID=self.cargo[dstRow].itemID if dstRow > -1 else None,
|
||||
copy=wx.GetMouseState().CmdDown()))
|
||||
modPosition=modIdx,
|
||||
cargoItemID=dstCargoItemID,
|
||||
copy=wx.GetMouseState().GetModifiers() == wx.MOD_CONTROL))
|
||||
|
||||
def fitChanged(self, event):
|
||||
sFit = Fit.getInstance()
|
||||
@@ -143,28 +160,53 @@ class CargoView(d.Display):
|
||||
if item != -1:
|
||||
self.EnsureVisible(item)
|
||||
|
||||
self.deselectItems()
|
||||
self.unselectAll()
|
||||
|
||||
self.populate(self.cargo)
|
||||
self.refresh(self.cargo)
|
||||
event.Skip()
|
||||
|
||||
def removeItem(self, event):
|
||||
def onLeftDoubleClick(self, event):
|
||||
row, _ = self.HitTest(event.Position)
|
||||
if row != -1:
|
||||
col = self.getColumn(event.Position)
|
||||
if col != self.getColIndex(State):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
cargo = self.cargo[self.GetItemData(row)]
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveCargoCommand(fitID, cargo.itemID))
|
||||
try:
|
||||
cargo = self.cargo[row]
|
||||
except IndexError:
|
||||
return
|
||||
self.removeCargos([cargo])
|
||||
|
||||
def removeCargos(self, cargos):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
itemIDs = []
|
||||
for cargo in cargos:
|
||||
if cargo in self.original:
|
||||
itemIDs.append(cargo.itemID)
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveCargosCommand(fitID=fitID, itemIDs=itemIDs))
|
||||
|
||||
def spawnMenu(self, event):
|
||||
sel = self.GetFirstSelected()
|
||||
if sel != -1:
|
||||
cargo = self.cargo[sel]
|
||||
sMkt = Market.getInstance()
|
||||
sourceContext = "cargoItem"
|
||||
itemContext = sMkt.getCategoryByItem(cargo.item).name
|
||||
|
||||
menu = ContextMenu.getMenu((cargo,), (sourceContext, itemContext))
|
||||
selection = self.getSelectedCargos()
|
||||
clickedPos = self.getRowByAbs(event.Position)
|
||||
mainCargo = None
|
||||
if clickedPos != -1:
|
||||
try:
|
||||
cargo = self.cargo[clickedPos]
|
||||
except IndexError:
|
||||
pass
|
||||
else:
|
||||
if cargo in self.original:
|
||||
mainCargo = cargo
|
||||
sourceContext = "cargoItem"
|
||||
itemContext = None if mainCargo is None else Market.getInstance().getCategoryByItem(mainCargo.item).name
|
||||
menu = ContextMenu.getMenu(mainCargo, selection, (sourceContext, itemContext))
|
||||
if menu:
|
||||
self.PopupMenu(menu)
|
||||
|
||||
def getSelectedCargos(self):
|
||||
cargos = []
|
||||
for row in self.getSelectedRows():
|
||||
try:
|
||||
cargo = self.cargo[row]
|
||||
except IndexError:
|
||||
continue
|
||||
cargos.append(cargo)
|
||||
return cargos
|
||||
|
||||
@@ -22,24 +22,25 @@ import wx
|
||||
|
||||
import gui.builtinAdditionPanes.droneView
|
||||
import gui.display as d
|
||||
import gui.fitCommands as cmd
|
||||
import gui.globalEvents as GE
|
||||
from gui.builtinShipBrowser.events import EVT_FIT_REMOVED
|
||||
from eos.saveddata.drone import Drone as es_Drone
|
||||
from gui.builtinContextMenus.commandFitAdd import AddCommandFit
|
||||
from gui.builtinShipBrowser.events import EVT_FIT_REMOVED
|
||||
from gui.builtinViewColumns.state import State
|
||||
from gui.contextMenu import ContextMenu
|
||||
from gui.utils.staticHelpers import DragDropHelper
|
||||
from service.fit import Fit
|
||||
import gui.fitCommands as cmd
|
||||
|
||||
|
||||
class DummyItem(object):
|
||||
class DummyItem:
|
||||
|
||||
def __init__(self, txt):
|
||||
self.name = txt
|
||||
self.iconID = None
|
||||
|
||||
|
||||
class DummyEntry(object):
|
||||
class DummyEntry:
|
||||
|
||||
def __init__(self, txt):
|
||||
self.item = DummyItem(txt)
|
||||
|
||||
@@ -61,10 +62,11 @@ class CommandViewDrop(wx.DropTarget):
|
||||
|
||||
|
||||
class CommandView(d.Display):
|
||||
|
||||
DEFAULT_COLS = ["State", "Base Name"]
|
||||
|
||||
def __init__(self, parent):
|
||||
d.Display.__init__(self, parent, style=wx.LC_SINGLE_SEL | wx.BORDER_NONE)
|
||||
d.Display.__init__(self, parent, style=wx.BORDER_NONE)
|
||||
|
||||
self.lastFitId = None
|
||||
|
||||
@@ -72,14 +74,13 @@ class CommandView(d.Display):
|
||||
self.mainFrame.Bind(EVT_FIT_REMOVED, AddCommandFit.populateFits)
|
||||
self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged)
|
||||
self.Bind(wx.EVT_LEFT_DOWN, self.click)
|
||||
self.Bind(wx.EVT_LEFT_DCLICK, self.remove)
|
||||
self.Bind(wx.EVT_LEFT_DCLICK, self.onLeftDoubleClick)
|
||||
self.Bind(wx.EVT_KEY_UP, self.kbEvent)
|
||||
|
||||
self.droneView = gui.builtinAdditionPanes.droneView.DroneView
|
||||
|
||||
self.Bind(wx.EVT_RIGHT_UP, self.spawnMenu)
|
||||
self.Bind(wx.EVT_CONTEXT_MENU, self.spawnMenu)
|
||||
|
||||
self.Bind(wx.EVT_LIST_BEGIN_DRAG, self.startDrag)
|
||||
self.SetDropTarget(CommandViewDrop(self.handleListDrag))
|
||||
|
||||
@staticmethod
|
||||
@@ -95,30 +96,22 @@ class CommandView(d.Display):
|
||||
|
||||
def kbEvent(self, event):
|
||||
keycode = event.GetKeyCode()
|
||||
if keycode == wx.WXK_DELETE or keycode == wx.WXK_NUMPAD_DELETE:
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
row = self.GetFirstSelected()
|
||||
if row != -1:
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveCommandFitCommand(fitID, self.get(row).ID))
|
||||
mstate = wx.GetMouseState()
|
||||
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
self.unselectAll()
|
||||
elif keycode == 65 and mstate.GetModifiers() == wx.MOD_CONTROL:
|
||||
self.selectAll()
|
||||
elif keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE) and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
commandFits = self.getSelectedCommandFits()
|
||||
self.removeCommandFits(commandFits)
|
||||
event.Skip()
|
||||
|
||||
def handleDrag(self, type, fitID):
|
||||
# Those are drags coming from pyfa sources, NOT builtin wx drags
|
||||
if type == "fit":
|
||||
activeFit = self.mainFrame.getActiveFit()
|
||||
if activeFit:
|
||||
self.mainFrame.command.Submit(cmd.GuiAddCommandFitCommand(activeFit, fitID))
|
||||
|
||||
def startDrag(self, event):
|
||||
row = event.GetIndex()
|
||||
if row != -1 and isinstance(self.get(row), es_Drone):
|
||||
data = wx.TextDataObject()
|
||||
dataStr = "command:" + str(self.GetItemData(row))
|
||||
data.SetText(dataStr)
|
||||
|
||||
dropSource = wx.DropSource(self)
|
||||
dropSource.SetData(data)
|
||||
DragDropHelper.data = dataStr
|
||||
dropSource.DoDragDrop()
|
||||
self.mainFrame.command.Submit(cmd.GuiAddCommandFitCommand(fitID=activeFit, commandFitID=fitID))
|
||||
|
||||
@staticmethod
|
||||
def fitSort(fit):
|
||||
@@ -153,9 +146,8 @@ class CommandView(d.Display):
|
||||
if item != -1:
|
||||
self.EnsureVisible(item)
|
||||
|
||||
self.deselectItems()
|
||||
self.unselectAll()
|
||||
|
||||
# todo: verify
|
||||
if not stuff:
|
||||
stuff = [DummyEntry("Drag a fit to this area")]
|
||||
|
||||
@@ -163,52 +155,68 @@ class CommandView(d.Display):
|
||||
|
||||
event.Skip()
|
||||
|
||||
def get(self, row):
|
||||
if row == -1:
|
||||
return None
|
||||
|
||||
numFits = len(self.fits)
|
||||
|
||||
if numFits == 0:
|
||||
return None
|
||||
|
||||
return self.fits[row]
|
||||
|
||||
def click(self, event):
|
||||
event.Skip()
|
||||
row, _ = self.HitTest(event.Position)
|
||||
if row != -1:
|
||||
item = self.get(row)
|
||||
mainRow, _ = self.HitTest(event.Position)
|
||||
if mainRow != -1:
|
||||
col = self.getColumn(event.Position)
|
||||
if col == self.getColIndex(State):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
self.mainFrame.command.Submit(cmd.GuiToggleCommandFitStateCommand(fitID, item.ID))
|
||||
try:
|
||||
mainCommandFitID = self.fits[mainRow].ID
|
||||
except IndexError:
|
||||
return
|
||||
commandFitIDs = []
|
||||
for commandFit in self.getSelectedCommandFits():
|
||||
commandFitIDs.append(commandFit.ID)
|
||||
if mainCommandFitID not in commandFitIDs:
|
||||
commandFitIDs = [mainCommandFitID]
|
||||
self.mainFrame.command.Submit(cmd.GuiToggleCommandFitStatesCommand(
|
||||
fitID=fitID,
|
||||
mainCommandFitID=mainCommandFitID,
|
||||
commandFitIDs=commandFitIDs))
|
||||
return
|
||||
event.Skip()
|
||||
|
||||
def spawnMenu(self, event):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
if fitID is None:
|
||||
return
|
||||
|
||||
sel = self.GetFirstSelected()
|
||||
context = ()
|
||||
item = self.get(sel)
|
||||
|
||||
if item is not None:
|
||||
fitSrcContext = "commandFit"
|
||||
fitItemContext = item.name
|
||||
context = ((fitSrcContext, fitItemContext),)
|
||||
|
||||
context += (("commandView",),)
|
||||
menu = ContextMenu.getMenu((item,) if item is not None else [], *context)
|
||||
if menu is not None:
|
||||
selection = self.getSelectedCommandFits()
|
||||
clickedPos = self.getRowByAbs(event.Position)
|
||||
mainCommandFit = None
|
||||
if clickedPos != -1:
|
||||
try:
|
||||
mainCommandFit = self.fits[clickedPos]
|
||||
except IndexError:
|
||||
pass
|
||||
contexts = []
|
||||
if mainCommandFit is not None:
|
||||
contexts.append(('commandFit', 'Command Fit'))
|
||||
contexts.append(('commandView',))
|
||||
menu = ContextMenu.getMenu(mainCommandFit, selection, *contexts)
|
||||
if menu:
|
||||
self.PopupMenu(menu)
|
||||
|
||||
def remove(self, event):
|
||||
def onLeftDoubleClick(self, event):
|
||||
row, _ = self.HitTest(event.Position)
|
||||
if row != -1:
|
||||
col = self.getColumn(event.Position)
|
||||
if col != self.getColIndex(State):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
thing = self.get(row)
|
||||
if thing: # thing doesn't exist if it's the dummy value
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveCommandFitCommand(fitID, thing.ID))
|
||||
try:
|
||||
commandFit = self.fits[row]
|
||||
except IndexError:
|
||||
return
|
||||
self.removeCommandFits([commandFit])
|
||||
|
||||
def removeCommandFits(self, commandFits):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
commandFitIDs = []
|
||||
for commandFit in commandFits:
|
||||
if commandFit in self.fits:
|
||||
commandFitIDs.append(commandFit.ID)
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveCommandFitsCommand(fitID=fitID, commandFitIDs=commandFitIDs))
|
||||
|
||||
def getSelectedCommandFits(self):
|
||||
commandFits = []
|
||||
for row in self.getSelectedRows():
|
||||
try:
|
||||
commandFit = self.fits[row]
|
||||
except IndexError:
|
||||
continue
|
||||
commandFits.append(commandFit)
|
||||
return commandFits
|
||||
|
||||
@@ -33,6 +33,7 @@ from gui.utils.staticHelpers import DragDropHelper
|
||||
from service.fit import Fit
|
||||
from service.market import Market
|
||||
import gui.fitCommands as cmd
|
||||
from gui.fitCommands.helpers import droneStackLimit
|
||||
|
||||
|
||||
class DroneViewDrop(wx.DropTarget):
|
||||
@@ -64,7 +65,7 @@ class DroneView(Display):
|
||||
]
|
||||
|
||||
def __init__(self, parent):
|
||||
Display.__init__(self, parent, style=wx.LC_SINGLE_SEL | wx.BORDER_NONE)
|
||||
Display.__init__(self, parent, style=wx.BORDER_NONE)
|
||||
|
||||
self.lastFitId = None
|
||||
|
||||
@@ -75,7 +76,7 @@ class DroneView(Display):
|
||||
|
||||
self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged)
|
||||
self.mainFrame.Bind(ITEM_SELECTED, self.addItem)
|
||||
self.Bind(wx.EVT_LEFT_DCLICK, self.removeItem)
|
||||
self.Bind(wx.EVT_LEFT_DCLICK, self.onLeftDoubleClick)
|
||||
self.Bind(wx.EVT_LEFT_DOWN, self.click)
|
||||
self.Bind(wx.EVT_KEY_UP, self.kbEvent)
|
||||
self.Bind(wx.EVT_MOTION, self.OnMouseMove)
|
||||
@@ -101,7 +102,10 @@ class DroneView(Display):
|
||||
self.hoveredRow = row
|
||||
self.hoveredColumn = col
|
||||
if row != -1 and col != -1 and col < len(self.DEFAULT_COLS):
|
||||
mod = self.drones[self.GetItemData(row)]
|
||||
try:
|
||||
mod = self.drones[row]
|
||||
except IndexError:
|
||||
return
|
||||
if self.DEFAULT_COLS[col] == "Miscellanea":
|
||||
tooltip = self.activeColumns[col].getToolTip(mod)
|
||||
if tooltip is not None:
|
||||
@@ -116,17 +120,22 @@ class DroneView(Display):
|
||||
|
||||
def kbEvent(self, event):
|
||||
keycode = event.GetKeyCode()
|
||||
if keycode == wx.WXK_DELETE or keycode == wx.WXK_NUMPAD_DELETE:
|
||||
row = self.GetFirstSelected()
|
||||
if row != -1:
|
||||
drone = self.drones[self.GetItemData(row)]
|
||||
self.removeDroneStack(drone)
|
||||
|
||||
mstate = wx.GetMouseState()
|
||||
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
self.unselectAll()
|
||||
elif keycode == 65 and mstate.GetModifiers() == wx.MOD_CONTROL:
|
||||
self.selectAll()
|
||||
elif keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE) and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
drones = self.getSelectedDrones()
|
||||
self.removeDroneStacks(drones)
|
||||
event.Skip()
|
||||
|
||||
def startDrag(self, event):
|
||||
row = event.GetIndex()
|
||||
if row != -1:
|
||||
self.unselectAll()
|
||||
self.Select(row, True)
|
||||
|
||||
data = wx.TextDataObject()
|
||||
dataStr = "drone:" + str(row)
|
||||
data.SetText(dataStr)
|
||||
@@ -146,19 +155,36 @@ class DroneView(Display):
|
||||
"""
|
||||
if data[0] == "drone":
|
||||
srcRow = int(data[1])
|
||||
dstRow, _ = self.HitTest((x, y))
|
||||
if srcRow != -1 and dstRow != -1:
|
||||
self._merge(srcRow, dstRow)
|
||||
if srcRow != -1:
|
||||
if wx.GetMouseState().GetModifiers() == wx.MOD_CONTROL:
|
||||
try:
|
||||
srcDrone = self.drones[srcRow]
|
||||
except IndexError:
|
||||
return
|
||||
if srcDrone not in self.original:
|
||||
return
|
||||
self.mainFrame.command.Submit(cmd.GuiCloneLocalDroneCommand(
|
||||
fitID=self.mainFrame.getActiveFit(),
|
||||
position=self.original.index(srcDrone)))
|
||||
else:
|
||||
dstRow, _ = self.HitTest((x, y))
|
||||
if dstRow != -1:
|
||||
self._merge(srcRow, dstRow)
|
||||
elif data[0] == "market":
|
||||
wx.PostEvent(self.mainFrame, ItemSelected(itemID=int(data[1])))
|
||||
|
||||
def _merge(self, srcRow, dstRow):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
self.mainFrame.command.Submit(cmd.GuiMergeLocalDroneStacksCommand(
|
||||
fitID=fitID,
|
||||
srcPosition=fit.drones.index(self.drones[srcRow]),
|
||||
dstPosition=fit.drones.index(self.drones[dstRow])))
|
||||
try:
|
||||
srcDrone = self.drones[srcRow]
|
||||
dstDrone = self.drones[dstRow]
|
||||
except IndexError:
|
||||
return
|
||||
if srcDrone in self.original and dstDrone in self.original:
|
||||
srcPosition = self.original.index(srcDrone)
|
||||
dstPosition = self.original.index(dstDrone)
|
||||
self.mainFrame.command.Submit(cmd.GuiMergeLocalDroneStacksCommand(
|
||||
fitID=fitID, srcPosition=srcPosition, dstPosition=dstPosition))
|
||||
|
||||
DRONE_ORDER = ('Light Scout Drones', 'Medium Scout Drones',
|
||||
'Heavy Attack Drones', 'Sentry Drones', 'Combat Utility Drones',
|
||||
@@ -199,7 +225,7 @@ class DroneView(Display):
|
||||
if item != -1:
|
||||
self.EnsureVisible(item)
|
||||
|
||||
self.deselectItems()
|
||||
self.unselectAll()
|
||||
|
||||
self.update(self.drones)
|
||||
event.Skip()
|
||||
@@ -216,44 +242,94 @@ class DroneView(Display):
|
||||
event.Skip()
|
||||
return
|
||||
|
||||
if self.mainFrame.command.Submit(cmd.GuiAddLocalDroneCommand(fitID=fitID, itemID=event.itemID, amount=1)):
|
||||
amount = droneStackLimit(fit, event.itemID) if wx.GetMouseState().GetModifiers() == wx.MOD_ALT else 1
|
||||
if self.mainFrame.command.Submit(cmd.GuiAddLocalDroneCommand(fitID=fitID, itemID=event.itemID, amount=amount)):
|
||||
self.mainFrame.additionsPane.select('Drones')
|
||||
|
||||
event.Skip()
|
||||
|
||||
def removeItem(self, event):
|
||||
def onLeftDoubleClick(self, event):
|
||||
row, _ = self.HitTest(event.Position)
|
||||
if row != -1:
|
||||
col = self.getColumn(event.Position)
|
||||
if col != self.getColIndex(State):
|
||||
drone = self.drones[self.GetItemData(row)]
|
||||
self.removeDrone(drone)
|
||||
try:
|
||||
drone = self.drones[row]
|
||||
except IndexError:
|
||||
return
|
||||
if wx.GetMouseState().GetModifiers() == wx.MOD_ALT:
|
||||
self.removeDroneStacks([drone])
|
||||
else:
|
||||
self.removeDrone(drone)
|
||||
|
||||
def removeDrone(self, drone):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveLocalDroneCommand(fitID, self.original.index(drone), 1))
|
||||
if drone in self.original:
|
||||
position = self.original.index(drone)
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveLocalDronesCommand(
|
||||
fitID=fitID, positions=[position], amount=1))
|
||||
|
||||
def removeDroneStack(self, drone):
|
||||
def removeDroneStacks(self, drones):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveLocalDroneCommand(fitID, self.original.index(drone), math.inf))
|
||||
positions = []
|
||||
for drone in drones:
|
||||
if drone in self.original:
|
||||
positions.append(self.original.index(drone))
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveLocalDronesCommand(
|
||||
fitID=fitID, positions=positions, amount=math.inf))
|
||||
|
||||
def click(self, event):
|
||||
event.Skip()
|
||||
row, _ = self.HitTest(event.Position)
|
||||
if row != -1:
|
||||
mainRow, _ = self.HitTest(event.Position)
|
||||
if mainRow != -1:
|
||||
col = self.getColumn(event.Position)
|
||||
if col == self.getColIndex(State):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
drone = self.drones[row]
|
||||
self.mainFrame.command.Submit(cmd.GuiToggleLocalDroneStateCommand(fitID, self.original.index(drone)))
|
||||
try:
|
||||
mainDrone = self.drones[mainRow]
|
||||
except IndexError:
|
||||
return
|
||||
if mainDrone in self.original:
|
||||
mainPosition = self.original.index(mainDrone)
|
||||
positions = []
|
||||
for row in self.getSelectedRows():
|
||||
try:
|
||||
drone = self.drones[row]
|
||||
except IndexError:
|
||||
continue
|
||||
if drone in self.original:
|
||||
positions.append(self.original.index(drone))
|
||||
if mainPosition not in positions:
|
||||
positions = [mainPosition]
|
||||
self.mainFrame.command.Submit(cmd.GuiToggleLocalDroneStatesCommand(
|
||||
fitID=self.mainFrame.getActiveFit(),
|
||||
mainPosition=mainPosition,
|
||||
positions=positions))
|
||||
return
|
||||
event.Skip()
|
||||
|
||||
def spawnMenu(self, event):
|
||||
sel = self.GetFirstSelected()
|
||||
if sel != -1:
|
||||
drone = self.drones[sel]
|
||||
|
||||
sMkt = Market.getInstance()
|
||||
sourceContext = "droneItem"
|
||||
itemContext = sMkt.getCategoryByItem(drone.item).name
|
||||
menu = ContextMenu.getMenu((drone,), (sourceContext, itemContext))
|
||||
clickedPos = self.getRowByAbs(event.Position)
|
||||
mainDrone = None
|
||||
if clickedPos != -1:
|
||||
try:
|
||||
drone = self.drones[clickedPos]
|
||||
except IndexError:
|
||||
pass
|
||||
else:
|
||||
if drone in self.original:
|
||||
mainDrone = drone
|
||||
selection = self.getSelectedDrones()
|
||||
sourceContext = "droneItem"
|
||||
itemContext = None if mainDrone is None else Market.getInstance().getCategoryByItem(mainDrone.item).name
|
||||
menu = ContextMenu.getMenu(mainDrone, selection, (sourceContext, itemContext))
|
||||
if menu:
|
||||
self.PopupMenu(menu)
|
||||
|
||||
def getSelectedDrones(self):
|
||||
drones = []
|
||||
for row in self.getSelectedRows():
|
||||
try:
|
||||
drone = self.drones[row]
|
||||
except IndexError:
|
||||
continue
|
||||
drones.append(drone)
|
||||
return drones
|
||||
|
||||
@@ -20,17 +20,18 @@
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
|
||||
import gui.globalEvents as GE
|
||||
from gui.builtinMarketBrowser.events import ItemSelected, ITEM_SELECTED
|
||||
import gui.mainFrame
|
||||
import gui.display as d
|
||||
from gui.builtinViewColumns.state import State
|
||||
import gui.fitCommands as cmd
|
||||
import gui.globalEvents as GE
|
||||
import gui.mainFrame
|
||||
from eos.const import FittingSlot
|
||||
from gui.builtinMarketBrowser.events import ItemSelected, ITEM_SELECTED
|
||||
from gui.builtinViewColumns.state import State
|
||||
from gui.contextMenu import ContextMenu
|
||||
from gui.fitCommands.helpers import getSimilarFighters
|
||||
from gui.utils.staticHelpers import DragDropHelper
|
||||
from service.fit import Fit
|
||||
from service.market import Market
|
||||
import gui.fitCommands as cmd
|
||||
|
||||
|
||||
class FighterViewDrop(wx.DropTarget):
|
||||
@@ -50,6 +51,7 @@ class FighterViewDrop(wx.DropTarget):
|
||||
|
||||
|
||||
class FighterView(wx.Panel):
|
||||
|
||||
def __init__(self, parent):
|
||||
wx.Panel.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, style=wx.TAB_TRAVERSAL)
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
@@ -115,6 +117,7 @@ class FighterView(wx.Panel):
|
||||
|
||||
|
||||
class FighterDisplay(d.Display):
|
||||
|
||||
DEFAULT_COLS = ["State",
|
||||
# "Base Icon",
|
||||
"Base Name",
|
||||
@@ -127,7 +130,7 @@ class FighterDisplay(d.Display):
|
||||
]
|
||||
|
||||
def __init__(self, parent):
|
||||
d.Display.__init__(self, parent, style=wx.LC_SINGLE_SEL | wx.BORDER_NONE)
|
||||
d.Display.__init__(self, parent, style=wx.BORDER_NONE)
|
||||
|
||||
self.lastFitId = None
|
||||
|
||||
@@ -136,7 +139,7 @@ class FighterDisplay(d.Display):
|
||||
|
||||
self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged)
|
||||
self.mainFrame.Bind(ITEM_SELECTED, self.addItem)
|
||||
self.Bind(wx.EVT_LEFT_DCLICK, self.removeItem)
|
||||
self.Bind(wx.EVT_LEFT_DCLICK, self.onLeftDoubleClick)
|
||||
self.Bind(wx.EVT_LEFT_DOWN, self.click)
|
||||
self.Bind(wx.EVT_KEY_UP, self.kbEvent)
|
||||
self.Bind(wx.EVT_MOTION, self.OnMouseMove)
|
||||
@@ -162,7 +165,10 @@ class FighterDisplay(d.Display):
|
||||
self.hoveredRow = row
|
||||
self.hoveredColumn = col
|
||||
if row != -1 and col != -1 and col < len(self.DEFAULT_COLS):
|
||||
mod = self.fighters[self.GetItemData(row)]
|
||||
try:
|
||||
mod = self.fighters[row]
|
||||
except IndexError:
|
||||
return
|
||||
if self.DEFAULT_COLS[col] == "Miscellanea":
|
||||
tooltip = self.activeColumns[col].getToolTip(mod)
|
||||
if tooltip is not None:
|
||||
@@ -177,17 +183,22 @@ class FighterDisplay(d.Display):
|
||||
|
||||
def kbEvent(self, event):
|
||||
keycode = event.GetKeyCode()
|
||||
if keycode == wx.WXK_DELETE or keycode == wx.WXK_NUMPAD_DELETE:
|
||||
row = self.GetFirstSelected()
|
||||
if row != -1:
|
||||
fighter = self.fighters[self.GetItemData(row)]
|
||||
self.removeFighter(fighter)
|
||||
|
||||
mstate = wx.GetMouseState()
|
||||
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
self.unselectAll()
|
||||
elif keycode == 65 and mstate.GetModifiers() == wx.MOD_CONTROL:
|
||||
self.selectAll()
|
||||
elif keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE) and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
fighters = self.getSelectedFighters()
|
||||
self.removeFighters(fighters)
|
||||
event.Skip()
|
||||
|
||||
def startDrag(self, event):
|
||||
row = event.GetIndex()
|
||||
if row != -1:
|
||||
self.unselectAll()
|
||||
self.Select(row, True)
|
||||
|
||||
data = wx.TextDataObject()
|
||||
dataStr = "fighter:" + str(row)
|
||||
data.SetText(dataStr)
|
||||
@@ -217,12 +228,18 @@ class FighterDisplay(d.Display):
|
||||
def _merge(src, dst):
|
||||
return
|
||||
|
||||
FIGHTER_ORDER = ('Heavy Fighter', 'Light Fighter', 'Support Fighter')
|
||||
FIGHTER_ORDER = ('Light Fighter', 'Heavy Fighter', 'Support Fighter')
|
||||
|
||||
def fighterKey(self, fighter):
|
||||
sMkt = Market.getInstance()
|
||||
groupName = sMkt.getGroupByItem(fighter.item).name
|
||||
return (self.FIGHTER_ORDER.index(groupName), fighter.item.name)
|
||||
groupName = Market.getInstance().getGroupByItem(fighter.item).name
|
||||
orderPos = self.FIGHTER_ORDER.index(groupName)
|
||||
# Sort support fighters by name, ignore their abilities
|
||||
if groupName == 'Support Fighter':
|
||||
abilityEffectIDs = ()
|
||||
# Group up fighters from various roles
|
||||
else:
|
||||
abilityEffectIDs = sorted(a.effectID for a in fighter.abilities)
|
||||
return orderPos, abilityEffectIDs, fighter.item.name
|
||||
|
||||
def fitChanged(self, event):
|
||||
sFit = Fit.getInstance()
|
||||
@@ -251,7 +268,7 @@ class FighterDisplay(d.Display):
|
||||
if item != -1:
|
||||
self.EnsureVisible(item)
|
||||
|
||||
self.deselectItems()
|
||||
self.unselectAll()
|
||||
|
||||
self.update(self.fighters)
|
||||
event.Skip()
|
||||
@@ -268,35 +285,87 @@ class FighterDisplay(d.Display):
|
||||
|
||||
event.Skip()
|
||||
|
||||
def removeItem(self, event):
|
||||
def onLeftDoubleClick(self, event):
|
||||
row, _ = self.HitTest(event.Position)
|
||||
if row != -1:
|
||||
col = self.getColumn(event.Position)
|
||||
if col != self.getColIndex(State):
|
||||
fighter = self.fighters[self.GetItemData(row)]
|
||||
self.removeFighter(fighter)
|
||||
mstate = wx.GetMouseState()
|
||||
try:
|
||||
fighter = self.fighters[row]
|
||||
except IndexError:
|
||||
return
|
||||
if mstate.GetModifiers() == wx.MOD_ALT:
|
||||
fighters = getSimilarFighters(self.original, fighter)
|
||||
else:
|
||||
fighters = [fighter]
|
||||
self.removeFighters(fighters)
|
||||
|
||||
def removeFighter(self, fighter):
|
||||
def removeFighters(self, fighters):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveLocalFighterCommand(fitID, self.original.index(fighter)))
|
||||
positions = []
|
||||
for fighter in fighters:
|
||||
if fighter in self.original:
|
||||
positions.append(self.original.index(fighter))
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveLocalFightersCommand(fitID=fitID, positions=positions))
|
||||
|
||||
def click(self, event):
|
||||
event.Skip()
|
||||
row, _ = self.HitTest(event.Position)
|
||||
if row != -1:
|
||||
mainRow, _ = self.HitTest(event.Position)
|
||||
if mainRow != -1:
|
||||
col = self.getColumn(event.Position)
|
||||
if col == self.getColIndex(State):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
fighter = self.fighters[row]
|
||||
self.mainFrame.command.Submit(cmd.GuiToggleLocalFighterStateCommand(fitID, self.original.index(fighter)))
|
||||
try:
|
||||
mainFighter = self.fighters[mainRow]
|
||||
except IndexError:
|
||||
return
|
||||
if mainFighter in self.original:
|
||||
mainPosition = self.original.index(mainFighter)
|
||||
positions = []
|
||||
if event.GetModifiers() == wx.MOD_ALT:
|
||||
for fighter in getSimilarFighters(self.original, mainFighter):
|
||||
positions.append(self.original.index(fighter))
|
||||
else:
|
||||
for row in self.getSelectedRows():
|
||||
try:
|
||||
fighter = self.fighters[row]
|
||||
except IndexError:
|
||||
continue
|
||||
if fighter in self.original:
|
||||
positions.append(self.original.index(fighter))
|
||||
if mainPosition not in positions:
|
||||
positions = [mainPosition]
|
||||
self.mainFrame.command.Submit(cmd.GuiToggleLocalFighterStatesCommand(
|
||||
fitID=fitID,
|
||||
mainPosition=mainPosition,
|
||||
positions=positions))
|
||||
return
|
||||
event.Skip()
|
||||
|
||||
def spawnMenu(self, event):
|
||||
sel = self.GetFirstSelected()
|
||||
if sel != -1:
|
||||
fighter = self.fighters[sel]
|
||||
|
||||
sMkt = Market.getInstance()
|
||||
sourceContext = "fighterItem"
|
||||
itemContext = sMkt.getCategoryByItem(fighter.item).name
|
||||
menu = ContextMenu.getMenu((fighter,), (sourceContext, itemContext))
|
||||
selection = self.getSelectedFighters()
|
||||
clickedPos = self.getRowByAbs(event.Position)
|
||||
mainFighter = None
|
||||
if clickedPos != -1:
|
||||
try:
|
||||
fighter = self.fighters[clickedPos]
|
||||
except IndexError:
|
||||
pass
|
||||
else:
|
||||
if fighter in self.original:
|
||||
mainFighter = fighter
|
||||
sourceContext = "fighterItem"
|
||||
itemContext = None if mainFighter is None else Market.getInstance().getCategoryByItem(mainFighter.item).name
|
||||
menu = ContextMenu.getMenu(mainFighter, selection, (sourceContext, itemContext))
|
||||
if menu:
|
||||
self.PopupMenu(menu)
|
||||
|
||||
def getSelectedFighters(self):
|
||||
fighters = []
|
||||
for row in self.getSelectedRows():
|
||||
try:
|
||||
fighter = self.fighters[row]
|
||||
except IndexError:
|
||||
continue
|
||||
fighters.append(fighter)
|
||||
return fighters
|
||||
|
||||
@@ -19,17 +19,18 @@
|
||||
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
|
||||
import gui.display as d
|
||||
from gui.builtinMarketBrowser.events import ITEM_SELECTED
|
||||
import gui.mainFrame
|
||||
from gui.builtinViewColumns.state import State
|
||||
from gui.utils.staticHelpers import DragDropHelper
|
||||
from gui.contextMenu import ContextMenu
|
||||
import gui.fitCommands as cmd
|
||||
import gui.globalEvents as GE
|
||||
import gui.mainFrame
|
||||
from eos.const import ImplantLocation
|
||||
from gui.builtinMarketBrowser.events import ITEM_SELECTED
|
||||
from gui.builtinViewColumns.state import State
|
||||
from gui.contextMenu import ContextMenu
|
||||
from gui.utils.staticHelpers import DragDropHelper
|
||||
from service.fit import Fit
|
||||
from service.market import Market
|
||||
import gui.fitCommands as cmd
|
||||
|
||||
|
||||
class ImplantViewDrop(wx.DropTarget):
|
||||
@@ -49,6 +50,7 @@ class ImplantViewDrop(wx.DropTarget):
|
||||
|
||||
|
||||
class ImplantView(wx.Panel):
|
||||
|
||||
def __init__(self, parent):
|
||||
wx.Panel.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, style=wx.TAB_TRAVERSAL)
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
@@ -99,6 +101,7 @@ class ImplantView(wx.Panel):
|
||||
|
||||
|
||||
class ImplantDisplay(d.Display):
|
||||
|
||||
DEFAULT_COLS = [
|
||||
"State",
|
||||
"attr:implantness",
|
||||
@@ -108,13 +111,13 @@ class ImplantDisplay(d.Display):
|
||||
]
|
||||
|
||||
def __init__(self, parent):
|
||||
d.Display.__init__(self, parent, style=wx.LC_SINGLE_SEL | wx.BORDER_NONE)
|
||||
d.Display.__init__(self, parent, style=wx.BORDER_NONE)
|
||||
|
||||
self.lastFitId = None
|
||||
|
||||
self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged)
|
||||
self.mainFrame.Bind(ITEM_SELECTED, self.addItem)
|
||||
self.Bind(wx.EVT_LEFT_DCLICK, self.removeItem)
|
||||
self.Bind(wx.EVT_LEFT_DCLICK, self.onLeftDoubleClick)
|
||||
self.Bind(wx.EVT_LEFT_DOWN, self.click)
|
||||
self.Bind(wx.EVT_KEY_UP, self.kbEvent)
|
||||
self.SetDropTarget(ImplantViewDrop(self.handleListDrag))
|
||||
@@ -132,15 +135,20 @@ class ImplantDisplay(d.Display):
|
||||
"""
|
||||
|
||||
if data[0] == "market":
|
||||
if self.mainFrame.command.Submit(cmd.GuiAddImplantCommand(self.mainFrame.getActiveFit(), int(data[1]))):
|
||||
if self.mainFrame.command.Submit(cmd.GuiAddImplantCommand(
|
||||
fitID=self.mainFrame.getActiveFit(), itemID=int(data[1]))):
|
||||
self.mainFrame.additionsPane.select("Implants")
|
||||
|
||||
def kbEvent(self, event):
|
||||
keycode = event.GetKeyCode()
|
||||
if keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE):
|
||||
row = self.GetFirstSelected()
|
||||
if row != -1:
|
||||
self.removeImplant(self.implants[self.GetItemData(row)])
|
||||
mstate = wx.GetMouseState()
|
||||
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
self.unselectAll()
|
||||
elif keycode == 65 and mstate.GetModifiers() == wx.MOD_CONTROL:
|
||||
self.selectAll()
|
||||
elif keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE) and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
implants = self.getSelectedImplants()
|
||||
self.removeImplants(implants)
|
||||
event.Skip()
|
||||
|
||||
def fitChanged(self, event):
|
||||
@@ -169,7 +177,7 @@ class ImplantDisplay(d.Display):
|
||||
if item != -1:
|
||||
self.EnsureVisible(item)
|
||||
|
||||
self.deselectItems()
|
||||
self.unselectAll()
|
||||
|
||||
self.update(self.implants)
|
||||
event.Skip()
|
||||
@@ -187,67 +195,94 @@ class ImplantDisplay(d.Display):
|
||||
event.Skip()
|
||||
return
|
||||
|
||||
self.mainFrame.command.Submit(cmd.GuiAddImplantCommand(fitID, event.itemID))
|
||||
self.mainFrame.command.Submit(cmd.GuiAddImplantCommand(
|
||||
fitID=fitID, itemID=event.itemID))
|
||||
# Select in any case - as we might've added implant which has been there already and command failed
|
||||
self.mainFrame.additionsPane.select('Implants')
|
||||
|
||||
event.Skip()
|
||||
|
||||
def removeItem(self, event):
|
||||
# Character implants can't be changed here...
|
||||
if self.Parent.source == ImplantLocation.CHARACTER:
|
||||
return
|
||||
|
||||
def onLeftDoubleClick(self, event):
|
||||
row, _ = self.HitTest(event.Position)
|
||||
if row != -1:
|
||||
col = self.getColumn(event.Position)
|
||||
if col != self.getColIndex(State):
|
||||
self.removeImplant(self.implants[self.GetItemData(row)])
|
||||
try:
|
||||
implant = self.implants[row]
|
||||
except IndexError:
|
||||
return
|
||||
self.removeImplants([implant])
|
||||
|
||||
def removeImplant(self, implant):
|
||||
def removeImplants(self, implants):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
sFit = Fit.getInstance()
|
||||
fit = sFit.getFit(fitID)
|
||||
if fit.implantLocation == ImplantLocation.FIT:
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveImplantCommand(fitID, self.original.index(implant)))
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
if fit.implantLocation != ImplantLocation.FIT:
|
||||
return
|
||||
positions = []
|
||||
for implant in implants:
|
||||
if implant in self.original:
|
||||
positions.append(self.original.index(implant))
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveImplantsCommand(fitID=fitID, positions=positions))
|
||||
|
||||
def click(self, event):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
if fit.implantLocation == ImplantLocation.FIT:
|
||||
mainRow, _ = self.HitTest(event.Position)
|
||||
if mainRow != -1:
|
||||
col = self.getColumn(event.Position)
|
||||
if col == self.getColIndex(State):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
try:
|
||||
mainImplant = self.implants[mainRow]
|
||||
except IndexError:
|
||||
return
|
||||
if mainImplant in self.original:
|
||||
mainPosition = self.original.index(mainImplant)
|
||||
positions = []
|
||||
for row in self.getSelectedRows():
|
||||
try:
|
||||
implant = self.implants[row]
|
||||
except IndexError:
|
||||
continue
|
||||
if implant in self.original:
|
||||
positions.append(self.original.index(implant))
|
||||
if mainPosition not in positions:
|
||||
positions = [mainPosition]
|
||||
self.mainFrame.command.Submit(cmd.GuiToggleImplantStatesCommand(
|
||||
fitID=fitID,
|
||||
mainPosition=mainPosition,
|
||||
positions=positions))
|
||||
return
|
||||
event.Skip()
|
||||
|
||||
# Character implants can't be changed here...
|
||||
if self.Parent.source == ImplantLocation.CHARACTER:
|
||||
return
|
||||
|
||||
row, _ = self.HitTest(event.Position)
|
||||
if row != -1:
|
||||
col = self.getColumn(event.Position)
|
||||
if col == self.getColIndex(State):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
implant = self.implants[self.GetItemData(row)]
|
||||
self.mainFrame.command.Submit(cmd.GuiToggleImplantStateCommand(fitID=fitID, position=self.original.index(implant)))
|
||||
|
||||
def spawnMenu(self, event):
|
||||
sel = self.GetFirstSelected()
|
||||
menu = None
|
||||
|
||||
sFit = Fit.getInstance()
|
||||
fit = sFit.getFit(self.mainFrame.getActiveFit())
|
||||
|
||||
if not fit:
|
||||
return
|
||||
|
||||
if sel != -1:
|
||||
implant = self.implants[sel]
|
||||
sMkt = Market.getInstance()
|
||||
sourceContext = "implantItem" if fit.implantSource == ImplantLocation.FIT else "implantItemChar"
|
||||
itemContext = sMkt.getCategoryByItem(implant.item).name
|
||||
fullContext = ((sourceContext, itemContext), ("implantView",))
|
||||
menu = ContextMenu.getMenu((implant,), *fullContext)
|
||||
elif sel == -1 and fit.implantSource == ImplantLocation.FIT:
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
if fitID is None:
|
||||
return
|
||||
context = ("implantView",)
|
||||
menu = ContextMenu.getMenu([], context)
|
||||
if menu is not None:
|
||||
selection = self.getSelectedImplants()
|
||||
clickedPos = self.getRowByAbs(event.Position)
|
||||
mainImplant = None
|
||||
if clickedPos != -1:
|
||||
try:
|
||||
implant = self.implants[clickedPos]
|
||||
except IndexError:
|
||||
pass
|
||||
else:
|
||||
if implant in self.original:
|
||||
mainImplant = implant
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
sourceContext1 = "implantItem" if fit.implantSource == ImplantLocation.FIT else "implantItemChar"
|
||||
sourceContext2 = "implantView" if fit.implantSource == ImplantLocation.FIT else "implantViewChar"
|
||||
itemContext = None if mainImplant is None else Market.getInstance().getCategoryByItem(mainImplant.item).name
|
||||
menu = ContextMenu.getMenu(mainImplant, selection, (sourceContext1, itemContext), (sourceContext2, itemContext))
|
||||
if menu:
|
||||
self.PopupMenu(menu)
|
||||
|
||||
def getSelectedImplants(self):
|
||||
implants = []
|
||||
for row in self.getSelectedRows():
|
||||
try:
|
||||
implant = self.implants[row]
|
||||
except IndexError:
|
||||
continue
|
||||
implants.append(implant)
|
||||
return implants
|
||||
|
||||
@@ -25,17 +25,19 @@ from logbook import Logger
|
||||
|
||||
import gui.builtinAdditionPanes.droneView
|
||||
import gui.display as d
|
||||
import gui.fitCommands as cmd
|
||||
import gui.globalEvents as GE
|
||||
from eos.saveddata.drone import Drone as es_Drone
|
||||
from eos.saveddata.fighter import Fighter as es_Fighter
|
||||
from eos.saveddata.fit import Fit as es_Fit
|
||||
from eos.saveddata.module import Module as es_Module
|
||||
from eos.saveddata.drone import Drone as EosDrone
|
||||
from eos.saveddata.fighter import Fighter as EosFighter
|
||||
from eos.saveddata.fit import Fit as EosFit
|
||||
from eos.saveddata.module import Module as EosModule
|
||||
from gui.builtinViewColumns.state import State
|
||||
from gui.contextMenu import ContextMenu
|
||||
from gui.fitCommands.helpers import getSimilarFighters, getSimilarModPositions
|
||||
from gui.utils.staticHelpers import DragDropHelper
|
||||
from service.fit import Fit
|
||||
from service.market import Market
|
||||
import gui.fitCommands as cmd
|
||||
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
@@ -68,28 +70,27 @@ class ProjectedViewDrop(wx.DropTarget):
|
||||
|
||||
|
||||
class ProjectedView(d.Display):
|
||||
DEFAULT_COLS = ["State",
|
||||
"Ammo Icon",
|
||||
"Base Icon",
|
||||
"Base Name",
|
||||
"Ammo"]
|
||||
DEFAULT_COLS = ['State',
|
||||
'Ammo Icon',
|
||||
'Base Icon',
|
||||
'Base Name',
|
||||
'Ammo']
|
||||
|
||||
def __init__(self, parent):
|
||||
d.Display.__init__(self, parent, style=wx.LC_SINGLE_SEL | wx.BORDER_NONE)
|
||||
d.Display.__init__(self, parent, style=wx.BORDER_NONE)
|
||||
|
||||
self.lastFitId = None
|
||||
|
||||
self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged)
|
||||
self.Bind(wx.EVT_LEFT_DOWN, self.click)
|
||||
self.Bind(wx.EVT_RIGHT_DOWN, self.click)
|
||||
self.Bind(wx.EVT_LEFT_DCLICK, self.remove)
|
||||
self.Bind(wx.EVT_LEFT_DCLICK, self.onLeftDoubleClick)
|
||||
self.Bind(wx.EVT_KEY_UP, self.kbEvent)
|
||||
|
||||
self.droneView = gui.builtinAdditionPanes.droneView.DroneView
|
||||
|
||||
self.Bind(wx.EVT_CONTEXT_MENU, self.spawnMenu)
|
||||
|
||||
self.Bind(wx.EVT_LIST_BEGIN_DRAG, self.startDrag)
|
||||
self.SetDropTarget(ProjectedViewDrop(self.handleListDrag))
|
||||
|
||||
def handleListDrag(self, x, y, data):
|
||||
@@ -109,7 +110,7 @@ class ProjectedView(d.Display):
|
||||
fitID=fitID, itemID=fit.modules[int(data[1])].itemID))
|
||||
elif data[0] == 'market':
|
||||
itemID = int(data[1])
|
||||
category = Market.getInstance().getItem(itemID, eager=("group.category")).category.name
|
||||
category = Market.getInstance().getItem(itemID, eager=('group.category')).category.name
|
||||
if category == 'Module':
|
||||
self.mainFrame.command.Submit(cmd.GuiAddProjectedModuleCommand(fitID=fitID, itemID=itemID))
|
||||
elif category == 'Drone':
|
||||
@@ -119,42 +120,25 @@ class ProjectedView(d.Display):
|
||||
|
||||
def kbEvent(self, event):
|
||||
keycode = event.GetKeyCode()
|
||||
if keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE):
|
||||
row = self.GetFirstSelected()
|
||||
if row != -1:
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
thing = self.get(row)
|
||||
if isinstance(thing, es_Fit):
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedFitCommand(
|
||||
fitID=fitID, projectedFitID=thing.ID))
|
||||
elif isinstance(thing, es_Module):
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedModuleCommand(
|
||||
fitID=fitID, position=Fit.getInstance().getFit(fitID).projectedModules.index(thing)))
|
||||
elif isinstance(thing, es_Drone):
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedDroneCommand(
|
||||
fitID=fitID, itemID=thing.itemID, amount=math.inf))
|
||||
elif isinstance(thing, es_Fighter):
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedFighterCommand(
|
||||
fitID=fitID, position=Fit.getInstance().getFit(fitID).projectedFighters.index(thing)))
|
||||
mstate = wx.GetMouseState()
|
||||
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
self.unselectAll()
|
||||
elif keycode == 65 and mstate.GetModifiers() == wx.MOD_CONTROL:
|
||||
self.selectAll()
|
||||
elif keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE) and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedItemsCommand(
|
||||
fitID=self.mainFrame.getActiveFit(),
|
||||
items=self.getSelectedProjectors(),
|
||||
amount=math.inf))
|
||||
event.Skip()
|
||||
|
||||
def handleDrag(self, type, fitID):
|
||||
# Those are drags coming from pyfa sources, NOT builtin wx drags
|
||||
if type == "fit":
|
||||
if type == 'fit':
|
||||
activeFit = self.mainFrame.getActiveFit()
|
||||
if activeFit:
|
||||
self.mainFrame.command.Submit(cmd.GuiAddProjectedFitCommand(fitID=activeFit, projectedFitID=fitID))
|
||||
|
||||
def startDrag(self, event):
|
||||
row = event.GetIndex()
|
||||
if row != -1 and isinstance(self.get(row), es_Drone):
|
||||
data = wx.TextDataObject()
|
||||
dataStr = "projected:" + str(self.GetItemData(row))
|
||||
data.SetText(dataStr)
|
||||
|
||||
dropSource = wx.DropSource(self)
|
||||
dropSource.SetData(data)
|
||||
DragDropHelper.data = dataStr
|
||||
dropSource.DoDragDrop()
|
||||
self.mainFrame.command.Submit(cmd.GuiAddProjectedFitCommand(
|
||||
fitID=activeFit, projectedFitID=fitID, amount=1))
|
||||
|
||||
@staticmethod
|
||||
def moduleSort(module):
|
||||
@@ -179,7 +163,7 @@ class ProjectedView(d.Display):
|
||||
def fitChanged(self, event):
|
||||
sFit = Fit.getInstance()
|
||||
fit = sFit.getFit(event.fitID)
|
||||
# pyfalog.debug("ProjectedView::fitChanged: {}", repr(fit))
|
||||
# pyfalog.debug('ProjectedView::fitChanged: {}', repr(fit))
|
||||
|
||||
self.Parent.Parent.DisablePage(self, not fit or fit.isStructure)
|
||||
|
||||
@@ -192,10 +176,13 @@ class ProjectedView(d.Display):
|
||||
|
||||
stuff = []
|
||||
if fit is not None:
|
||||
# pyfalog.debug(" Collecting list of stuff to display in ProjectedView")
|
||||
self.originalFits = fit.projectedFits
|
||||
self.fits = fit.projectedFits[:]
|
||||
self.originalModules = fit.projectedModules
|
||||
self.modules = fit.projectedModules[:]
|
||||
self.originalDrones = fit.projectedDrones
|
||||
self.drones = fit.projectedDrones[:]
|
||||
self.originalFighters = fit.projectedFighters
|
||||
self.fighters = fit.projectedFighters[:]
|
||||
|
||||
self.fits.sort(key=self.fitSort)
|
||||
@@ -216,10 +203,10 @@ class ProjectedView(d.Display):
|
||||
if item != -1:
|
||||
self.EnsureVisible(item)
|
||||
|
||||
self.deselectItems()
|
||||
self.unselectAll()
|
||||
|
||||
if not stuff:
|
||||
stuff = [DummyEntry("Drag an item or fit, or use right-click menu for wormhole effects")]
|
||||
stuff = [DummyEntry('Drag an item or fit, or use right-click menu for wormhole effects')]
|
||||
|
||||
self.update(stuff)
|
||||
|
||||
@@ -238,101 +225,140 @@ class ProjectedView(d.Display):
|
||||
return None
|
||||
|
||||
if row < numFits:
|
||||
stuff = self.fits[row]
|
||||
fit = self.fits[row]
|
||||
if fit in self.originalFits:
|
||||
return fit
|
||||
elif row - numFits < numMods:
|
||||
stuff = self.modules[row - numFits]
|
||||
mod = self.modules[row - numFits]
|
||||
if mod in self.originalModules:
|
||||
return mod
|
||||
elif row - numFits - numMods < numDrones:
|
||||
stuff = self.drones[row - numFits - numMods]
|
||||
drone = self.drones[row - numFits - numMods]
|
||||
if drone in self.originalDrones:
|
||||
return drone
|
||||
else:
|
||||
stuff = self.fighters[row - numFits - numMods - numDrones]
|
||||
|
||||
return stuff
|
||||
fighter = self.fighters[row - numFits - numMods - numDrones]
|
||||
if fighter in self.originalFighters:
|
||||
return fighter
|
||||
return None
|
||||
|
||||
def click(self, event):
|
||||
event.Skip()
|
||||
row, _ = self.HitTest(event.Position)
|
||||
if row != -1:
|
||||
mainRow, _ = self.HitTest(event.Position)
|
||||
if mainRow != -1:
|
||||
col = self.getColumn(event.Position)
|
||||
if col == self.getColIndex(State):
|
||||
mainItem = self.get(mainRow)
|
||||
if mainItem is None:
|
||||
return
|
||||
selection = self.getSelectedProjectors()
|
||||
if mainItem not in selection:
|
||||
selection = [mainItem]
|
||||
modPressed = wx.GetMouseState().GetModifiers() == wx.MOD_ALT
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
thing = self.get(row)
|
||||
button = event.GetButton()
|
||||
if isinstance(thing, es_Fit) and button != 3:
|
||||
self.mainFrame.command.Submit(cmd.GuiToggleProjectedFitStateCommand(
|
||||
fitID=fitID, projectedFitID=thing.ID))
|
||||
elif isinstance(thing, es_Module):
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeProjectedModuleStateCommand(
|
||||
fitID=fitID,
|
||||
position=Fit.getInstance().getFit(fitID).projectedModules.index(thing),
|
||||
click='right' if button == 3 else 'left'))
|
||||
elif isinstance(thing, es_Drone) and button != 3:
|
||||
self.mainFrame.command.Submit(cmd.GuiToggleProjectedDroneStateCommand(
|
||||
fitID=fitID, itemID=thing.itemID))
|
||||
elif isinstance(thing, es_Fighter) and button != 3:
|
||||
self.mainFrame.command.Submit(cmd.GuiToggleProjectedFighterStateCommand(
|
||||
fitID=fitID, position=Fit.getInstance().getFit(fitID).projectedFighters.index(thing)))
|
||||
if isinstance(mainItem, EosModule) and modPressed:
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
positions = getSimilarModPositions(fit.projectedModules, mainItem)
|
||||
selection = [fit.projectedModules[p] for p in positions]
|
||||
elif isinstance(mainItem, EosFighter) and modPressed:
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
selection = getSimilarFighters(fit.projectedFighters, mainItem)
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeProjectedItemStatesCommand(
|
||||
fitID=fitID,
|
||||
mainItem=mainItem,
|
||||
items=selection,
|
||||
click='right' if event.GetButton() == 3 else 'left'))
|
||||
return
|
||||
event.Skip()
|
||||
|
||||
def spawnMenu(self, event):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
if fitID is None:
|
||||
return
|
||||
|
||||
if self.getColumn(self.ScreenToClient(event.Position)) == self.getColIndex(State):
|
||||
if self.getColumn(self.screenToClientFixed(event.Position)) == self.getColIndex(State):
|
||||
return
|
||||
|
||||
sel = self.GetFirstSelected()
|
||||
context = ()
|
||||
item = self.get(sel)
|
||||
clickedPos = self.getRowByAbs(event.Position)
|
||||
mainItem = self.get(clickedPos)
|
||||
|
||||
if item is not None:
|
||||
contexts = []
|
||||
if mainItem is not None:
|
||||
sMkt = Market.getInstance()
|
||||
|
||||
if isinstance(item, es_Drone):
|
||||
srcContext = "projectedDrone"
|
||||
itemContext = sMkt.getCategoryByItem(item.item).name
|
||||
context = ((srcContext, itemContext),)
|
||||
elif isinstance(item, es_Fighter):
|
||||
srcContext = "projectedFighter"
|
||||
itemContext = sMkt.getCategoryByItem(item.item).name
|
||||
context = ((srcContext, itemContext),)
|
||||
elif isinstance(item, es_Module):
|
||||
modSrcContext = "projectedModule"
|
||||
modItemContext = sMkt.getCategoryByItem(item.item).name
|
||||
if isinstance(mainItem, EosModule):
|
||||
modSrcContext = 'projectedModule'
|
||||
modItemContext = 'Projected Item'
|
||||
modFullContext = (modSrcContext, modItemContext)
|
||||
if item.charge is not None:
|
||||
chgSrcContext = "projectedCharge"
|
||||
chgItemContext = sMkt.getCategoryByItem(item.charge).name
|
||||
chgFullContext = (chgSrcContext, chgItemContext)
|
||||
context = (modFullContext, chgFullContext)
|
||||
else:
|
||||
context = (modFullContext,)
|
||||
contexts.append(modFullContext)
|
||||
if mainItem.charge is not None:
|
||||
chargeSrcContext = 'projectedCharge'
|
||||
chargeItemContext = sMkt.getCategoryByItem(mainItem.charge).name
|
||||
chargeFullContext = (chargeSrcContext, chargeItemContext)
|
||||
contexts.append(chargeFullContext)
|
||||
elif isinstance(mainItem, EosDrone):
|
||||
srcContext = 'projectedDrone'
|
||||
itemContext = 'Projected Item'
|
||||
droneFullContext = (srcContext, itemContext)
|
||||
contexts.append(droneFullContext)
|
||||
elif isinstance(mainItem, EosFighter):
|
||||
srcContext = 'projectedFighter'
|
||||
itemContext = 'Projected Item'
|
||||
fighterFullContext = (srcContext, itemContext)
|
||||
contexts.append(fighterFullContext)
|
||||
else:
|
||||
fitSrcContext = "projectedFit"
|
||||
fitItemContext = item.name
|
||||
context = ((fitSrcContext, fitItemContext),)
|
||||
|
||||
context += (("projected",),)
|
||||
menu = ContextMenu.getMenu((item,) if item is not None else [], *context)
|
||||
fitSrcContext = 'projectedFit'
|
||||
fitItemContext = 'Projected Item'
|
||||
fitFullContext = (fitSrcContext, fitItemContext)
|
||||
contexts.append(fitFullContext)
|
||||
contexts.append(('projected',))
|
||||
|
||||
selection = self.getSelectedProjectors()
|
||||
menu = ContextMenu.getMenu(mainItem, selection, *contexts)
|
||||
if menu is not None:
|
||||
self.PopupMenu(menu)
|
||||
|
||||
def remove(self, event):
|
||||
def onLeftDoubleClick(self, event):
|
||||
row, _ = self.HitTest(event.Position)
|
||||
if row != -1:
|
||||
col = self.getColumn(event.Position)
|
||||
if col != self.getColIndex(State):
|
||||
mainItem = self.get(row)
|
||||
if mainItem is None:
|
||||
return
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
thing = self.get(row)
|
||||
if isinstance(thing, es_Fit):
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedFitCommand(
|
||||
fitID=fitID, projectedFitID=thing.ID))
|
||||
elif isinstance(thing, es_Module):
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedModuleCommand(
|
||||
fitID=fitID, position=Fit.getInstance().getFit(fitID).projectedModules.index(thing)))
|
||||
elif isinstance(thing, es_Drone):
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedDroneCommand(
|
||||
fitID=fitID, itemID=thing.itemID, amount=1))
|
||||
elif isinstance(thing, es_Fighter):
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedFighterCommand(
|
||||
fitID=fitID, position=Fit.getInstance().getFit(fitID).projectedFighters.index(thing)))
|
||||
modPressed = wx.GetMouseState().GetModifiers() == wx.MOD_ALT
|
||||
if isinstance(mainItem, EosFit):
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedItemsCommand(
|
||||
fitID=fitID, items=[mainItem], amount=math.inf if modPressed else 1))
|
||||
elif isinstance(mainItem, EosModule):
|
||||
if modPressed:
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
positions = getSimilarModPositions(fit.projectedModules, mainItem)
|
||||
items = [fit.projectedModules[p] for p in positions]
|
||||
else:
|
||||
items = [mainItem]
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedItemsCommand(
|
||||
fitID=fitID, items=items, amount=1))
|
||||
elif isinstance(mainItem, EosDrone):
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedItemsCommand(
|
||||
fitID=fitID, items=[mainItem], amount=math.inf if modPressed else 1))
|
||||
elif isinstance(mainItem, EosFighter):
|
||||
if modPressed:
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
items = getSimilarFighters(fit.projectedFighters, mainItem)
|
||||
else:
|
||||
items = [mainItem]
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedItemsCommand(
|
||||
fitID=fitID, items=items, amount=1))
|
||||
else:
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedItemsCommand(
|
||||
fitID=fitID, items=[mainItem], amount=math.inf if modPressed else 1))
|
||||
|
||||
def getSelectedProjectors(self):
|
||||
projectors = []
|
||||
for row in self.getSelectedRows():
|
||||
projector = self.get(row)
|
||||
if projector is None:
|
||||
continue
|
||||
projectors.append(projector)
|
||||
return projectors
|
||||
|
||||
@@ -15,16 +15,18 @@ from gui.builtinContextMenus import ( # noqa: E402,F401
|
||||
# Item info
|
||||
itemStats,
|
||||
itemMarketJump,
|
||||
fitSystemSecurity, # Not really an item info but want to keep it here
|
||||
shipJump,
|
||||
# Generic item manipulations
|
||||
itemRemove,
|
||||
moduleMutations,
|
||||
itemAmountChange,
|
||||
droneSplitStack,
|
||||
itemVariationChange,
|
||||
moduleMutations,
|
||||
moduleFill,
|
||||
skillAffectors,
|
||||
# Market stuff
|
||||
itemFill,
|
||||
droneAddStack,
|
||||
cargoAdd,
|
||||
cargoAddAmmo,
|
||||
|
||||
@@ -1,42 +1,44 @@
|
||||
from gui.contextMenu import ContextMenu
|
||||
import gui.mainFrame
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
|
||||
import gui.globalEvents as GE
|
||||
import gui.mainFrame
|
||||
from gui.contextMenu import ContextMenuSingle
|
||||
from service.fit import Fit
|
||||
from service.settings import ContextMenuSettings
|
||||
|
||||
|
||||
class AmmoToDmgPattern(ContextMenu):
|
||||
class AmmoToDmgPattern(ContextMenuSingle):
|
||||
|
||||
def __init__(self):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
self.settings = ContextMenuSettings.getInstance()
|
||||
|
||||
def display(self, srcContext, selection):
|
||||
def display(self, srcContext, mainItem):
|
||||
if not self.settings.get('ammoPattern'):
|
||||
return False
|
||||
|
||||
if srcContext not in ("marketItemGroup", "marketItemMisc") or self.mainFrame.getActiveFit() is None:
|
||||
return False
|
||||
|
||||
item = selection[0]
|
||||
if mainItem is None:
|
||||
return False
|
||||
|
||||
for attr in ("emDamage", "thermalDamage", "explosiveDamage", "kineticDamage"):
|
||||
if item.getAttribute(attr) is not None:
|
||||
if mainItem.getAttribute(attr) is not None:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def getText(self, itmContext, selection):
|
||||
return "Set {0} as Damage Pattern".format(itmContext if itmContext is not None else "Item")
|
||||
def getText(self, itmContext, mainItem):
|
||||
return "Set {} as Damage Pattern".format(itmContext if itmContext is not None else "Item")
|
||||
|
||||
def activate(self, fullContext, selection, i):
|
||||
item = selection[0]
|
||||
fit = self.mainFrame.getActiveFit()
|
||||
sFit = Fit.getInstance()
|
||||
sFit.setAsPattern(fit, item)
|
||||
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fit))
|
||||
def activate(self, fullContext, mainItem, i):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
Fit.getInstance().setAsPattern(fitID, mainItem)
|
||||
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
|
||||
|
||||
def getBitmap(self, context, selection):
|
||||
def getBitmap(self, context, mainItem):
|
||||
return None
|
||||
|
||||
|
||||
|
||||
@@ -3,24 +3,25 @@ import wx
|
||||
|
||||
import gui.mainFrame
|
||||
from gui import fitCommands as cmd
|
||||
from gui.contextMenu import ContextMenu
|
||||
from gui.contextMenu import ContextMenuSingle
|
||||
from service.fit import Fit
|
||||
from service.settings import ContextMenuSettings
|
||||
|
||||
|
||||
class BoosterSideEffect(ContextMenu):
|
||||
class BoosterSideEffects(ContextMenuSingle):
|
||||
|
||||
def __init__(self):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
self.settings = ContextMenuSettings.getInstance()
|
||||
|
||||
def display(self, srcContext, selection):
|
||||
# if not self.settings.get('fighterAbilities'):
|
||||
# return False
|
||||
|
||||
def display(self, srcContext, mainItem):
|
||||
if self.mainFrame.getActiveFit() is None or srcContext not in "boosterItem":
|
||||
return False
|
||||
|
||||
self.booster = selection[0]
|
||||
if mainItem is None:
|
||||
return False
|
||||
|
||||
self.booster = mainItem
|
||||
|
||||
for effect in self.booster.sideEffects:
|
||||
if effect.effect.isImplemented:
|
||||
@@ -28,19 +29,19 @@ class BoosterSideEffect(ContextMenu):
|
||||
|
||||
return False
|
||||
|
||||
def getText(self, itmContext, selection):
|
||||
def getText(self, itmContext, mainItem):
|
||||
return "Side Effects"
|
||||
|
||||
def addEffect(self, menu, ability):
|
||||
label = ability.name
|
||||
id = ContextMenu.nextID()
|
||||
id = ContextMenuSingle.nextID()
|
||||
self.effectIds[id] = ability
|
||||
|
||||
menuItem = wx.MenuItem(menu, id, label, kind=wx.ITEM_CHECK)
|
||||
menu.Bind(wx.EVT_MENU, self.handleMode, menuItem)
|
||||
return menuItem
|
||||
|
||||
def getSubMenu(self, context, selection, rootMenu, i, pitem):
|
||||
def getSubMenu(self, context, mainItem, rootMenu, i, pitem):
|
||||
msw = True if "wxMSW" in wx.PlatformInfo else False
|
||||
self.context = context
|
||||
self.effectIds = {}
|
||||
@@ -58,14 +59,17 @@ class BoosterSideEffect(ContextMenu):
|
||||
|
||||
def handleMode(self, event):
|
||||
effect = self.effectIds[event.Id]
|
||||
if effect is False or effect not in self.booster.sideEffects:
|
||||
booster = self.booster
|
||||
if effect is False or effect not in booster.sideEffects:
|
||||
event.Skip()
|
||||
return
|
||||
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
index = fit.boosters.index(self.booster)
|
||||
self.mainFrame.command.Submit(cmd.GuiToggleBoosterSideEffectStateCommand(fitID, index, effect.effectID))
|
||||
if booster in fit.boosters:
|
||||
index = fit.boosters.index(booster)
|
||||
self.mainFrame.command.Submit(cmd.GuiToggleBoosterSideEffectStateCommand(
|
||||
fitID=fitID, position=index, effectID=effect.effectID))
|
||||
|
||||
|
||||
BoosterSideEffect.register()
|
||||
BoosterSideEffects.register()
|
||||
|
||||
@@ -1,19 +1,23 @@
|
||||
import gui.fitCommands as cmd
|
||||
import gui.mainFrame
|
||||
from gui.contextMenu import ContextMenu
|
||||
from gui.contextMenu import ContextMenuSingle
|
||||
from service.fit import Fit
|
||||
from service.settings import ContextMenuSettings
|
||||
|
||||
|
||||
class AddToCargo(ContextMenu):
|
||||
class AddToCargo(ContextMenuSingle):
|
||||
|
||||
def __init__(self):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
self.settings = ContextMenuSettings.getInstance()
|
||||
|
||||
def display(self, srcContext, selection):
|
||||
def display(self, srcContext, mainItem):
|
||||
if srcContext not in ("marketItemGroup", "marketItemMisc"):
|
||||
return False
|
||||
|
||||
if mainItem is None:
|
||||
return False
|
||||
|
||||
sFit = Fit.getInstance()
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
fit = sFit.getFit(fitID)
|
||||
@@ -23,16 +27,15 @@ class AddToCargo(ContextMenu):
|
||||
|
||||
return True
|
||||
|
||||
def getText(self, itmContext, selection):
|
||||
def getText(self, itmContext, mainItem):
|
||||
return "Add {} to Cargo".format(itmContext)
|
||||
|
||||
def activate(self, fullContext, selection, i):
|
||||
def activate(self, fullContext, mainItem, i):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
|
||||
typeID = int(selection[0].ID)
|
||||
|
||||
self.mainFrame.command.Submit(cmd.GuiAddCargoCommand(fitID, typeID, 1))
|
||||
self.mainFrame.additionsPane.select("Cargo")
|
||||
typeID = int(mainItem.ID)
|
||||
command = cmd.GuiAddCargoCommand(fitID=fitID, itemID=typeID, amount=1)
|
||||
if self.mainFrame.command.Submit(command):
|
||||
self.mainFrame.additionsPane.select("Cargo", focus=False)
|
||||
|
||||
|
||||
AddToCargo.register()
|
||||
|
||||
@@ -1,32 +1,36 @@
|
||||
import gui.fitCommands as cmd
|
||||
import gui.mainFrame
|
||||
from gui.contextMenu import ContextMenu
|
||||
from gui.contextMenu import ContextMenuSingle
|
||||
from service.settings import ContextMenuSettings
|
||||
|
||||
|
||||
class AddToCargoAmmo(ContextMenu):
|
||||
class AddToCargoAmmo(ContextMenuSingle):
|
||||
|
||||
def __init__(self):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
self.settings = ContextMenuSettings.getInstance()
|
||||
|
||||
def display(self, srcContext, selection):
|
||||
def display(self, srcContext, mainItem):
|
||||
if srcContext not in ("marketItemGroup", "marketItemMisc") or self.mainFrame.getActiveFit() is None:
|
||||
return False
|
||||
|
||||
for selected_item in selection:
|
||||
if selected_item.category.ID in (
|
||||
8, # Charge
|
||||
):
|
||||
return True
|
||||
if mainItem is None:
|
||||
return False
|
||||
|
||||
def getText(self, itmContext, selection):
|
||||
if mainItem.category.ID != 8:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def getText(self, itmContext, mainItem):
|
||||
return "Add {0} to Cargo (x1000)".format(itmContext)
|
||||
|
||||
def activate(self, fullContext, selection, i):
|
||||
def activate(self, fullContext, mainItem, i):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
typeID = int(selection[0].ID)
|
||||
self.mainFrame.command.Submit(cmd.GuiAddCargoCommand(fitID, typeID, 1000))
|
||||
self.mainFrame.additionsPane.select("Cargo")
|
||||
typeID = int(mainItem.ID)
|
||||
command = cmd.GuiAddCargoCommand(fitID=fitID, itemID=typeID, amount=1000)
|
||||
if self.mainFrame.command.Submit(command):
|
||||
self.mainFrame.additionsPane.select("Cargo", focus=False)
|
||||
|
||||
|
||||
AddToCargoAmmo.register()
|
||||
|
||||
@@ -3,13 +3,14 @@ import wx
|
||||
|
||||
import gui.fitCommands as cmd
|
||||
import gui.mainFrame
|
||||
from gui.contextMenu import ContextMenu
|
||||
from gui.contextMenu import ContextMenuUnconditional
|
||||
from service.fit import Fit
|
||||
from service.market import Market
|
||||
from service.settings import ContextMenuSettings
|
||||
|
||||
|
||||
class AddCommandFit(ContextMenu):
|
||||
class AddCommandFit(ContextMenuUnconditional):
|
||||
|
||||
# Get list of items that define a command fit
|
||||
sMkt = Market.getInstance()
|
||||
grp = sMkt.getGroup(1770) # Command burst group
|
||||
@@ -41,24 +42,24 @@ class AddCommandFit(ContextMenu):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
self.settings = ContextMenuSettings.getInstance()
|
||||
|
||||
def display(self, srcContext, selection):
|
||||
def display(self, srcContext):
|
||||
if self.mainFrame.getActiveFit() is None or len(self.__class__.commandFits) == 0 or srcContext != "commandView":
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def getText(self, itmContext, selection):
|
||||
def getText(self, itmContext):
|
||||
return "Command Fits"
|
||||
|
||||
def addFit(self, menu, fit, includeShip=False):
|
||||
label = fit.name if not includeShip else "({}) {}".format(fit.ship.item.name, fit.name)
|
||||
id = ContextMenu.nextID()
|
||||
id = ContextMenuUnconditional.nextID()
|
||||
self.fitMenuItemIds[id] = fit
|
||||
menuItem = wx.MenuItem(menu, id, label)
|
||||
menu.Bind(wx.EVT_MENU, self.handleSelection, menuItem)
|
||||
return menuItem
|
||||
|
||||
def getSubMenu(self, context, selection, rootMenu, i, pitem):
|
||||
def getSubMenu(self, context, rootMenu, i, pitem):
|
||||
msw = True if "wxMSW" in wx.PlatformInfo else False
|
||||
self.context = context
|
||||
self.fitMenuItemIds = {}
|
||||
@@ -79,7 +80,7 @@ class AddCommandFit(ContextMenu):
|
||||
typeDict[shipName].append(fit)
|
||||
|
||||
for ship in sorted(typeDict.keys()):
|
||||
shipItem = wx.MenuItem(sub, ContextMenu.nextID(), ship)
|
||||
shipItem = wx.MenuItem(sub, ContextMenuUnconditional.nextID(), ship)
|
||||
grandSub = wx.Menu()
|
||||
shipItem.SetSubMenu(grandSub)
|
||||
|
||||
@@ -98,7 +99,7 @@ class AddCommandFit(ContextMenu):
|
||||
return
|
||||
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
self.mainFrame.command.Submit(cmd.GuiAddCommandFitCommand(fitID, fit.ID))
|
||||
self.mainFrame.command.Submit(cmd.GuiAddCommandFitCommand(fitID=fitID, commandFitID=fit.ID))
|
||||
|
||||
|
||||
AddCommandFit.populateFits(None)
|
||||
|
||||
@@ -6,25 +6,26 @@ import wx
|
||||
import gui.globalEvents as GE
|
||||
import gui.mainFrame
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
from gui.contextMenu import ContextMenu
|
||||
from gui.contextMenu import ContextMenuUnconditional
|
||||
from service.damagePattern import DamagePattern as import_DamagePattern
|
||||
from service.fit import Fit
|
||||
from service.settings import ContextMenuSettings
|
||||
|
||||
|
||||
class ChangeDamagePattern(ContextMenu):
|
||||
class ChangeDamagePattern(ContextMenuUnconditional):
|
||||
|
||||
def __init__(self):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
self.settings = ContextMenuSettings.getInstance()
|
||||
|
||||
def display(self, srcContext, selection):
|
||||
def display(self, srcContext):
|
||||
return srcContext == "resistancesViewFull"
|
||||
|
||||
@property
|
||||
def enabled(self):
|
||||
return self.mainFrame.getActiveFit() is not None
|
||||
|
||||
def getText(self, itmContext, selection):
|
||||
def getText(self, itmContext):
|
||||
sDP = import_DamagePattern.getInstance()
|
||||
sFit = Fit.getInstance()
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
@@ -55,7 +56,7 @@ class ChangeDamagePattern(ContextMenu):
|
||||
return self.m
|
||||
|
||||
def addPattern(self, rootMenu, pattern):
|
||||
id = ContextMenu.nextID()
|
||||
id = ContextMenuUnconditional.nextID()
|
||||
name = getattr(pattern, "_name", pattern.name) if pattern is not None else "No Profile"
|
||||
|
||||
self.patternIds[id] = pattern
|
||||
@@ -76,7 +77,7 @@ class ChangeDamagePattern(ContextMenu):
|
||||
menuItem.SetBitmap(bitmap)
|
||||
return menuItem
|
||||
|
||||
def getSubMenu(self, context, selection, rootMenu, i, pitem):
|
||||
def getSubMenu(self, context, rootMenu, i, pitem):
|
||||
msw = True if "wxMSW" in wx.PlatformInfo else False
|
||||
|
||||
if self.m[i] not in self.subMenus:
|
||||
|
||||
@@ -1,34 +1,50 @@
|
||||
import gui.fitCommands as cmd
|
||||
import gui.mainFrame
|
||||
from gui.contextMenu import ContextMenu
|
||||
from gui.contextMenu import ContextMenuSingle
|
||||
from gui.fitCommands.helpers import droneStackLimit
|
||||
from service.fit import Fit
|
||||
from service.settings import ContextMenuSettings
|
||||
|
||||
|
||||
class DroneAddStack(ContextMenu):
|
||||
class DroneAddStack(ContextMenuSingle):
|
||||
|
||||
def __init__(self):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
self.settings = ContextMenuSettings.getInstance()
|
||||
|
||||
def display(self, srcContext, selection):
|
||||
if srcContext not in ('marketItemGroup', 'marketItemMisc') or self.mainFrame.getActiveFit() is None:
|
||||
def display(self, srcContext, mainItem):
|
||||
if srcContext not in ('marketItemGroup', 'marketItemMisc'):
|
||||
return False
|
||||
|
||||
for selected_item in selection:
|
||||
if selected_item.category.ID in (
|
||||
18, # Drones
|
||||
):
|
||||
return True
|
||||
if self.mainFrame.getActiveFit() is None:
|
||||
return False
|
||||
|
||||
return False
|
||||
if mainItem is None:
|
||||
return False
|
||||
|
||||
def getText(self, itmContext, selection):
|
||||
return "Add {0} to Drone Bay (x5)".format(itmContext)
|
||||
if mainItem.category.name != 'Drone':
|
||||
return False
|
||||
|
||||
def activate(self, fullContext, selection, i):
|
||||
self.mainFrame.command.Submit(cmd.GuiAddLocalDroneCommand(
|
||||
fitID=self.mainFrame.getActiveFit(), itemID=int(selection[0].ID), amount=5))
|
||||
self.mainFrame.additionsPane.select('Drones')
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
amount = droneStackLimit(fit, mainItem)
|
||||
if amount < 1:
|
||||
return False
|
||||
|
||||
self.amount = amount
|
||||
return True
|
||||
|
||||
def getText(self, itmContext, mainItem):
|
||||
return 'Add {} to Drone Bay{}'.format(
|
||||
itmContext, '' if self.amount == 1 else ' (x{})'.format(self.amount))
|
||||
|
||||
def activate(self, fullContext, mainItem, i):
|
||||
command = cmd.GuiAddLocalDroneCommand(
|
||||
fitID=self.mainFrame.getActiveFit(),
|
||||
itemID=int(mainItem.ID),
|
||||
amount=self.amount)
|
||||
if self.mainFrame.command.Submit(command):
|
||||
self.mainFrame.additionsPane.select('Drones', focus=False)
|
||||
|
||||
|
||||
DroneAddStack.register()
|
||||
|
||||
@@ -5,26 +5,31 @@ import wx
|
||||
|
||||
import gui.fitCommands as cmd
|
||||
import gui.mainFrame
|
||||
from gui.contextMenu import ContextMenu
|
||||
from gui.contextMenu import ContextMenuSingle
|
||||
from service.fit import Fit
|
||||
from service.settings import ContextMenuSettings
|
||||
|
||||
|
||||
class DroneSplitStack(ContextMenu):
|
||||
class DroneSplitStack(ContextMenuSingle):
|
||||
|
||||
def __init__(self):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
self.settings = ContextMenuSettings.getInstance()
|
||||
|
||||
def display(self, srcContext, selection):
|
||||
return srcContext == "droneItem" and selection[0].amount > 1
|
||||
def display(self, srcContext, mainItem):
|
||||
if srcContext != "droneItem":
|
||||
return False
|
||||
|
||||
def getText(self, itmContext, selection):
|
||||
return "Split {0} Stack".format(itmContext)
|
||||
if mainItem is None:
|
||||
return False
|
||||
|
||||
def activate(self, fullContext, selection, i):
|
||||
srcContext = fullContext[0]
|
||||
drone = selection[0]
|
||||
dlg = DroneStackSplit(self.mainFrame, drone.amount)
|
||||
return mainItem.amount > 1
|
||||
|
||||
def getText(self, itmContext, mainItem):
|
||||
return "Split {} Stack".format(itmContext)
|
||||
|
||||
def activate(self, fullContext, mainItem, i):
|
||||
dlg = DroneStackSplit(self.mainFrame, mainItem.amount)
|
||||
|
||||
if dlg.ShowModal() == wx.ID_OK:
|
||||
|
||||
@@ -35,10 +40,10 @@ class DroneSplitStack(ContextMenu):
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
cleanInput = re.sub(r'[^0-9.]', '', dlg.input.GetLineText(0).strip())
|
||||
|
||||
self.mainFrame.command.Submit(cmd.GuiSplitLocalDroneStackCommand(
|
||||
fitID=fitID,
|
||||
position=fit.drones.index(drone),
|
||||
amount=int(cleanInput)))
|
||||
if mainItem in fit.drones:
|
||||
position = fit.drones.index(mainItem)
|
||||
self.mainFrame.command.Submit(cmd.GuiSplitLocalDroneStackCommand(
|
||||
fitID=fitID, position=position, amount=int(cleanInput)))
|
||||
|
||||
|
||||
DroneSplitStack.register()
|
||||
|
||||
@@ -6,12 +6,12 @@ import wx
|
||||
|
||||
import gui.fitCommands as cmd
|
||||
import gui.mainFrame
|
||||
from gui.contextMenu import ContextMenu
|
||||
from gui.contextMenu import ContextMenuUnconditional
|
||||
from service.market import Market
|
||||
from service.settings import ContextMenuSettings
|
||||
|
||||
|
||||
class AddEnvironmentEffect(ContextMenu):
|
||||
class AddEnvironmentEffect(ContextMenuUnconditional):
|
||||
|
||||
# CCP doesn't currently provide a mapping between the general Environment, and the specific environment effect
|
||||
# (which can be random when going into Abyssal space). This is how we currently define it:
|
||||
@@ -28,13 +28,13 @@ class AddEnvironmentEffect(ContextMenu):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
self.settings = ContextMenuSettings.getInstance()
|
||||
|
||||
def display(self, srcContext, selection):
|
||||
def display(self, srcContext):
|
||||
return srcContext == "projected"
|
||||
|
||||
def getText(self, itmContext, selection):
|
||||
def getText(self, itmContext):
|
||||
return "Add Environmental Effect"
|
||||
|
||||
def getSubMenu(self, context, selection, rootMenu, i, pitem):
|
||||
def getSubMenu(self, context, rootMenu, i, pitem):
|
||||
msw = True if "wxMSW" in wx.PlatformInfo else False
|
||||
|
||||
# Wormholes
|
||||
@@ -92,7 +92,7 @@ class AddEnvironmentEffect(ContextMenu):
|
||||
|
||||
def processFlat(data, root, sub):
|
||||
for swData in sorted(data, key=lambda tpl: tpl[2]):
|
||||
wxid = ContextMenu.nextID()
|
||||
wxid = ContextMenuUnconditional.nextID()
|
||||
swObj, swName, swClass = swData
|
||||
self.idmap[wxid] = (swObj, swName)
|
||||
subItem = wx.MenuItem(sub, wxid, swClass)
|
||||
@@ -123,7 +123,8 @@ class AddEnvironmentEffect(ContextMenu):
|
||||
|
||||
# Expressions for matching when detecting effects we're looking for
|
||||
if incursions:
|
||||
validgroups = ("Incursion ship attributes effects",)
|
||||
validgroups = ("Incursion ship attributes effects",
|
||||
"Invasion Effects")
|
||||
else:
|
||||
validgroups = ("Black Hole Effect Beacon",
|
||||
"Cataclysmic Variable Effect Beacon",
|
||||
@@ -133,7 +134,7 @@ class AddEnvironmentEffect(ContextMenu):
|
||||
"Wolf Rayet Effect Beacon")
|
||||
|
||||
# Stuff we don't want to see in names
|
||||
garbages = ("Effect", "Beacon", "ship attributes effects")
|
||||
garbages = ("Effects?", "Beacon", "ship attributes effects")
|
||||
|
||||
# Get group with all the system-wide beacons
|
||||
grp = sMkt.getGroup("Effect Beacon")
|
||||
|
||||
@@ -3,27 +3,28 @@ import wx
|
||||
|
||||
import gui.globalEvents as GE
|
||||
import gui.mainFrame
|
||||
from gui.contextMenu import ContextMenu
|
||||
from gui.contextMenu import ContextMenuUnconditional
|
||||
from service.fit import Fit
|
||||
from service.settings import ContextMenuSettings
|
||||
|
||||
|
||||
class FactorReload(ContextMenu):
|
||||
class FactorReload(ContextMenuUnconditional):
|
||||
|
||||
def __init__(self):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
self.settings = ContextMenuSettings.getInstance()
|
||||
|
||||
def display(self, srcContext, selection):
|
||||
def display(self, srcContext):
|
||||
return srcContext == "firepowerViewFull"
|
||||
|
||||
@property
|
||||
def enabled(self):
|
||||
return self.mainFrame.getActiveFit() is not None
|
||||
|
||||
def getText(self, itmContext, selection):
|
||||
def getText(self, itmContext):
|
||||
return "Factor in Reload Time"
|
||||
|
||||
def activate(self, fullContext, selection, i):
|
||||
def activate(self, fullContext, i):
|
||||
sFit = Fit.getInstance()
|
||||
sFit.serviceFittingOptions["useGlobalForceReload"] = not sFit.serviceFittingOptions["useGlobalForceReload"]
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
|
||||
@@ -3,37 +3,43 @@ import wx
|
||||
|
||||
import gui.mainFrame
|
||||
from gui import fitCommands as cmd
|
||||
from gui.contextMenu import ContextMenu
|
||||
from gui.fitCommands.helpers import getSimilarFighters
|
||||
from gui.contextMenu import ContextMenuCombined
|
||||
from service.fit import Fit
|
||||
from service.settings import ContextMenuSettings
|
||||
|
||||
|
||||
class FighterAbilities(ContextMenu):
|
||||
class FighterAbilities(ContextMenuCombined):
|
||||
|
||||
def __init__(self):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
self.settings = ContextMenuSettings.getInstance()
|
||||
self.isProjected = None
|
||||
|
||||
def display(self, srcContext, selection):
|
||||
def display(self, srcContext, mainItem, selection):
|
||||
if self.mainFrame.getActiveFit() is None or srcContext not in ("fighterItem", "projectedFighter"):
|
||||
return False
|
||||
|
||||
self.fighter = selection[0]
|
||||
if mainItem is None:
|
||||
return False
|
||||
|
||||
self.fighter = mainItem
|
||||
self.selection = selection
|
||||
self.isProjected = True if srcContext == "projectedFighter" else False
|
||||
return True
|
||||
|
||||
def getText(self, itmContext, selection):
|
||||
def getText(self, itmContext, mainItem, selection):
|
||||
return "Abilities"
|
||||
|
||||
def addAbility(self, menu, ability):
|
||||
label = ability.name
|
||||
id = ContextMenu.nextID()
|
||||
id = ContextMenuCombined.nextID()
|
||||
self.abilityIds[id] = ability
|
||||
menuItem = wx.MenuItem(menu, id, label, kind=wx.ITEM_CHECK)
|
||||
menu.Bind(wx.EVT_MENU, self.handleMode, menuItem)
|
||||
return menuItem
|
||||
|
||||
def getSubMenu(self, context, selection, rootMenu, i, pitem):
|
||||
def getSubMenu(self, context, mainItem, selection, rootMenu, i, pitem):
|
||||
msw = True if "wxMSW" in wx.PlatformInfo else False
|
||||
self.context = context
|
||||
self.abilityIds = {}
|
||||
@@ -58,12 +64,26 @@ class FighterAbilities(ContextMenu):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
if self.isProjected:
|
||||
self.mainFrame.command.Submit(cmd.GuiToggleProjectedFighterAbilityStateCommand(
|
||||
fitID=fitID, position=fit.projectedFighters.index(self.fighter), effectID=ability.effectID))
|
||||
container = fit.projectedFighters
|
||||
command = cmd.GuiToggleProjectedFighterAbilityStateCommand
|
||||
else:
|
||||
self.mainFrame.command.Submit(cmd.GuiToggleLocalFighterAbilityStateCommand(
|
||||
fitID=fitID, position=fit.fighters.index(self.fighter), effectID=ability.effectID))
|
||||
|
||||
container = fit.fighters
|
||||
command = cmd.GuiToggleLocalFighterAbilityStateCommand
|
||||
if self.fighter in container:
|
||||
mainPosition = container.index(self.fighter)
|
||||
if wx.GetMouseState().GetModifiers() == wx.MOD_ALT:
|
||||
fighters = getSimilarFighters(container, self.fighter)
|
||||
else:
|
||||
fighters = self.selection
|
||||
positions = []
|
||||
for fighter in fighters:
|
||||
if fighter in container:
|
||||
positions.append(container.index(fighter))
|
||||
self.mainFrame.command.Submit(command(
|
||||
fitID=fitID,
|
||||
mainPosition=mainPosition,
|
||||
positions=positions,
|
||||
effectID=ability.effectID))
|
||||
|
||||
|
||||
FighterAbilities.register()
|
||||
|
||||
@@ -4,25 +4,26 @@ import wx
|
||||
import gui.fitCommands as cmd
|
||||
import gui.mainFrame
|
||||
from gui.builtinViews.emptyView import BlankPage
|
||||
from gui.contextMenu import ContextMenu
|
||||
from gui.contextMenu import ContextMenuUnconditional
|
||||
from service.fit import Fit
|
||||
|
||||
|
||||
class AddCurrentlyOpenFit(ContextMenu):
|
||||
class AddCurrentlyOpenFit(ContextMenuUnconditional):
|
||||
|
||||
def __init__(self):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
|
||||
def display(self, srcContext, selection):
|
||||
def display(self, srcContext):
|
||||
|
||||
if self.mainFrame.getActiveFit() is None or srcContext not in ('projected', 'commandView'):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def getText(self, itmContext, selection):
|
||||
def getText(self, itmContext):
|
||||
return 'Add Currently Open Fit'
|
||||
|
||||
def getSubMenu(self, context, selection, rootMenu, i, pitem):
|
||||
def getSubMenu(self, context, rootMenu, i, pitem):
|
||||
self.fitLookup = {}
|
||||
self.context = context
|
||||
sFit = Fit.getInstance()
|
||||
@@ -40,7 +41,7 @@ class AddCurrentlyOpenFit(ContextMenu):
|
||||
if isinstance(page, BlankPage):
|
||||
continue
|
||||
fit = sFit.getFit(page.activeFitID, basic=True)
|
||||
id = ContextMenu.nextID()
|
||||
id = ContextMenuUnconditional.nextID()
|
||||
mitem = wx.MenuItem(rootMenu, id, "{}: {}".format(fit.ship.item.name, fit.name))
|
||||
bindmenu.Bind(wx.EVT_MENU, self.handleSelection, mitem)
|
||||
self.fitLookup[id] = fit
|
||||
@@ -56,7 +57,7 @@ class AddCurrentlyOpenFit(ContextMenu):
|
||||
if self.context == 'commandView':
|
||||
self.mainFrame.command.Submit(cmd.GuiAddCommandFitCommand(fitID=fitID, commandFitID=fit.ID))
|
||||
elif self.context == 'projected':
|
||||
self.mainFrame.command.Submit(cmd.GuiAddProjectedFitCommand(fitID=fitID, projectedFitID=fit.ID))
|
||||
self.mainFrame.command.Submit(cmd.GuiAddProjectedFitCommand(fitID=fitID, projectedFitID=fit.ID, amount=1))
|
||||
|
||||
|
||||
AddCurrentlyOpenFit.register()
|
||||
|
||||
@@ -3,30 +3,34 @@ import wx
|
||||
|
||||
import gui.mainFrame
|
||||
from gui.builtinShipBrowser.events import FitSelected
|
||||
from gui.contextMenu import ContextMenu
|
||||
from gui.contextMenu import ContextMenuSingle
|
||||
from service.settings import ContextMenuSettings
|
||||
|
||||
|
||||
class OpenFitInNewTab(ContextMenu):
|
||||
class OpenFitInNewTab(ContextMenuSingle):
|
||||
|
||||
def __init__(self):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
self.settings = ContextMenuSettings.getInstance()
|
||||
|
||||
def display(self, srcContext, selection):
|
||||
def display(self, srcContext, mainItem):
|
||||
if srcContext not in ("projectedFit", "commandFit"):
|
||||
return False
|
||||
|
||||
if mainItem is None:
|
||||
return False
|
||||
|
||||
currentFitID = self.mainFrame.getActiveFit()
|
||||
selectedFitID = selection[0].ID
|
||||
selectedFitID = mainItem.ID
|
||||
if currentFitID == selectedFitID:
|
||||
return False
|
||||
return True
|
||||
|
||||
def getText(self, itmContext, selection):
|
||||
def getText(self, itmContext, mainItem):
|
||||
return "Open Fit in New Tab"
|
||||
|
||||
def activate(self, fullContext, selection, i):
|
||||
fit = selection[0]
|
||||
wx.PostEvent(self.mainFrame, FitSelected(fitID=fit.ID, startup=2))
|
||||
def activate(self, fullContext, mainItem, i):
|
||||
wx.PostEvent(self.mainFrame, FitSelected(fitID=mainItem.ID, startup=2))
|
||||
|
||||
|
||||
OpenFitInNewTab.register()
|
||||
|
||||
67
gui/builtinContextMenus/fitSystemSecurity.py
Normal file
67
gui/builtinContextMenus/fitSystemSecurity.py
Normal file
@@ -0,0 +1,67 @@
|
||||
from collections import OrderedDict
|
||||
|
||||
import wx
|
||||
|
||||
import gui.fitCommands as cmd
|
||||
import gui.mainFrame
|
||||
from eos.const import FitSystemSecurity
|
||||
from gui.contextMenu import ContextMenuUnconditional
|
||||
from service.fit import Fit
|
||||
|
||||
|
||||
optionMap = OrderedDict((
|
||||
('High Security', FitSystemSecurity.HISEC),
|
||||
('Low Security', FitSystemSecurity.LOWSEC),
|
||||
('Null Security', FitSystemSecurity.NULLSEC),
|
||||
('W-Space', FitSystemSecurity.WSPACE)))
|
||||
|
||||
|
||||
class FitSystemSecurityMenu(ContextMenuUnconditional):
|
||||
|
||||
def __init__(self):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
|
||||
def display(self, srcContext):
|
||||
if srcContext != "fittingShip":
|
||||
return False
|
||||
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
|
||||
if not fit.isStructure:
|
||||
return
|
||||
|
||||
return True
|
||||
|
||||
def getText(self, itmContext):
|
||||
return "Citadel System Security"
|
||||
|
||||
def addOption(self, menu, optionLabel):
|
||||
id = ContextMenuUnconditional.nextID()
|
||||
self.optionIds[id] = optionLabel
|
||||
menuItem = wx.MenuItem(menu, id, optionLabel, kind=wx.ITEM_CHECK)
|
||||
menu.Bind(wx.EVT_MENU, self.handleMode, menuItem)
|
||||
return menuItem
|
||||
|
||||
def getSubMenu(self, context, rootMenu, i, pitem):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
msw = True if "wxMSW" in wx.PlatformInfo else False
|
||||
self.optionIds = {}
|
||||
sub = wx.Menu()
|
||||
for optionLabel, optionValue in optionMap.items():
|
||||
menuItem = self.addOption(rootMenu if msw else sub, optionLabel)
|
||||
sub.Append(menuItem)
|
||||
menuItem.Check(fit.getSystemSecurity() == optionValue)
|
||||
|
||||
return sub
|
||||
|
||||
def handleMode(self, event):
|
||||
optionLabel = self.optionIds[event.Id]
|
||||
optionValue = optionMap[optionLabel]
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeFitSystemSecurityCommand(
|
||||
fitID=self.mainFrame.getActiveFit(),
|
||||
secStatus=optionValue))
|
||||
|
||||
|
||||
FitSystemSecurityMenu.register()
|
||||
@@ -4,18 +4,19 @@ import wx
|
||||
import gui.fitCommands as cmd
|
||||
import gui.globalEvents as GE
|
||||
import gui.mainFrame
|
||||
from gui.contextMenu import ContextMenu
|
||||
from gui.contextMenu import ContextMenuSingle
|
||||
from service.character import Character
|
||||
from service.implantSet import ImplantSets as s_ImplantSets
|
||||
from service.settings import ContextMenuSettings
|
||||
|
||||
|
||||
class addImplantSet(ContextMenu):
|
||||
class AddImplantSet(ContextMenuSingle):
|
||||
|
||||
def __init__(self):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
self.settings = ContextMenuSettings.getInstance()
|
||||
|
||||
def display(self, srcContext, selection):
|
||||
def display(self, srcContext, mainItem):
|
||||
|
||||
sIS = s_ImplantSets.getInstance()
|
||||
implantSets = sIS.getImplantSetList()
|
||||
@@ -24,17 +25,17 @@ class addImplantSet(ContextMenu):
|
||||
return False
|
||||
return srcContext in ("implantView", "implantEditor")
|
||||
|
||||
def getText(self, itmContext, selection):
|
||||
def getText(self, itmContext, mainItem):
|
||||
return "Add Implant Set"
|
||||
|
||||
def getSubMenu(self, context, selection, rootMenu, i, pitem):
|
||||
def getSubMenu(self, context, mainItem, rootMenu, i, pitem):
|
||||
"""
|
||||
A note on the selection here: Most context menus act on a fit, so it's easy enough to get the active fit from
|
||||
A note on the mainItem here: Most context menus act on a fit, so it's easy enough to get the active fit from
|
||||
the MainFrame instance. There's never been a reason to get info from another window, so there's not common
|
||||
way of doing this. However, we use this context menu within the Character Editor to apply implant sets to a
|
||||
character, so we need to access the character editor.
|
||||
|
||||
It is for these reasons that I hijack the selection parameter when calling the menu and pass a pointer to the
|
||||
It is for these reasons that I hijack the mainItem parameter when calling the menu and pass a pointer to the
|
||||
Character Editor. This way we can use it to get current editing character ID and apply the implants.
|
||||
|
||||
It would probably be better to have a function on the MainFrame to get the currently open Character Editor (as
|
||||
@@ -49,13 +50,12 @@ class addImplantSet(ContextMenu):
|
||||
implantSets = sIS.getImplantSetList()
|
||||
|
||||
self.context = context
|
||||
if len(selection) == 1:
|
||||
self.selection = selection[0] # dirty hack here
|
||||
self.mainItem = mainItem # dirty hack here
|
||||
|
||||
self.idmap = {}
|
||||
|
||||
for set in implantSets:
|
||||
id = ContextMenu.nextID()
|
||||
for set in sorted(implantSets, key=lambda i: i.name):
|
||||
id = ContextMenuSingle.nextID()
|
||||
mitem = wx.MenuItem(rootMenu, id, set.name)
|
||||
bindmenu.Bind(wx.EVT_MENU, self.handleSelection, mitem)
|
||||
self.idmap[id] = set
|
||||
@@ -73,16 +73,16 @@ class addImplantSet(ContextMenu):
|
||||
if self.context == "implantEditor":
|
||||
# we are calling from character editor, the implant source is different
|
||||
sChar = Character.getInstance()
|
||||
char = self.selection.entityEditor.getActiveEntity()
|
||||
char = self.mainItem.entityEditor.getActiveEntity()
|
||||
|
||||
for implant in set.implants:
|
||||
sChar.addImplant(char.ID, implant.item.ID)
|
||||
|
||||
wx.PostEvent(self.selection, GE.CharChanged())
|
||||
wx.PostEvent(self.mainItem, GE.CharChanged())
|
||||
else:
|
||||
self.mainFrame.command.Submit(cmd.GuiAddImplantSetCommand(
|
||||
fitID=self.mainFrame.getActiveFit(),
|
||||
itemIDs=[i.itemID for i in set.implants]))
|
||||
|
||||
|
||||
addImplantSet.register()
|
||||
AddImplantSet.register()
|
||||
|
||||
@@ -9,35 +9,43 @@ from eos.saveddata.cargo import Cargo as es_Cargo
|
||||
from eos.saveddata.drone import Drone
|
||||
from eos.saveddata.fighter import Fighter as es_Fighter
|
||||
from eos.saveddata.fit import Fit as es_Fit
|
||||
from gui.contextMenu import ContextMenu
|
||||
from gui.contextMenu import ContextMenuSingle
|
||||
from service.fit import Fit
|
||||
from service.settings import ContextMenuSettings
|
||||
|
||||
|
||||
class ChangeItemAmount(ContextMenu):
|
||||
class ChangeItemAmount(ContextMenuSingle):
|
||||
|
||||
def __init__(self):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
self.settings = ContextMenuSettings.getInstance()
|
||||
|
||||
def display(self, srcContext, selection):
|
||||
return srcContext in ("droneItem", "projectedDrone", "cargoItem", "projectedFit", "fighterItem", "projectedFighter")
|
||||
def display(self, srcContext, mainItem):
|
||||
if srcContext not in ("droneItem", "projectedDrone", "cargoItem", "projectedFit", "fighterItem", "projectedFighter"):
|
||||
return False
|
||||
|
||||
def getText(self, itmContext, selection):
|
||||
return u"Change {0} Quantity".format(itmContext)
|
||||
if mainItem is None:
|
||||
return False
|
||||
|
||||
def activate(self, fullContext, selection, i):
|
||||
thing = selection[0]
|
||||
mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
fitID = mainFrame.getActiveFit()
|
||||
return True
|
||||
|
||||
def getText(self, itmContext, mainItem):
|
||||
return "Change {0} Quantity".format(itmContext)
|
||||
|
||||
def activate(self, fullContext, mainItem, i):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
srcContext = fullContext[0]
|
||||
if isinstance(thing, es_Fit):
|
||||
value = thing.getProjectionInfo(fitID).amount
|
||||
elif isinstance(thing, es_Fighter):
|
||||
value = thing.amountActive
|
||||
if isinstance(mainItem, es_Fit):
|
||||
try:
|
||||
value = mainItem.getProjectionInfo(fitID).amount
|
||||
except AttributeError:
|
||||
return
|
||||
elif isinstance(mainItem, es_Fighter):
|
||||
value = mainItem.amountActive
|
||||
else:
|
||||
value = thing.amount
|
||||
value = mainItem.amount
|
||||
|
||||
dlg = AmountChanger(self.mainFrame, value, (0, 20)) if isinstance(thing, es_Fit) else AmountChanger(self.mainFrame, value)
|
||||
dlg = AmountChanger(self.mainFrame, value, (0, 20)) if isinstance(mainItem, es_Fit) else AmountChanger(self.mainFrame, value)
|
||||
if dlg.ShowModal() == wx.ID_OK:
|
||||
|
||||
if dlg.input.GetLineText(0).strip() == '':
|
||||
@@ -47,20 +55,32 @@ class ChangeItemAmount(ContextMenu):
|
||||
fit = sFit.getFit(fitID)
|
||||
cleanInput = int(float(re.sub(r'[^0-9.]', '', dlg.input.GetLineText(0).strip())))
|
||||
|
||||
if isinstance(thing, es_Cargo):
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeCargoAmountCommand(fitID, thing.itemID, cleanInput))
|
||||
elif isinstance(thing, Drone):
|
||||
if isinstance(mainItem, es_Cargo):
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeCargoAmountCommand(
|
||||
fitID=fitID, itemID=mainItem.itemID, amount=cleanInput))
|
||||
elif isinstance(mainItem, Drone):
|
||||
if srcContext == "projectedDrone":
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeProjectedDroneAmountCommand(fitID, thing.itemID, cleanInput))
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeProjectedDroneAmountCommand(
|
||||
fitID=fitID, itemID=mainItem.itemID, amount=cleanInput))
|
||||
else:
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeLocalDroneAmountCommand(fitID, fit.drones.index(thing), cleanInput))
|
||||
elif isinstance(thing, es_Fit):
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeProjectedFitAmountCommand(fitID, thing.ID, cleanInput))
|
||||
elif isinstance(thing, es_Fighter):
|
||||
if mainItem in fit.drones:
|
||||
position = fit.drones.index(mainItem)
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeLocalDroneAmountCommand(
|
||||
fitID=fitID, position=position, amount=cleanInput))
|
||||
elif isinstance(mainItem, es_Fit):
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeProjectedFitAmountCommand(
|
||||
fitID=fitID, projectedFitID=mainItem.ID, amount=cleanInput))
|
||||
elif isinstance(mainItem, es_Fighter):
|
||||
if srcContext == "projectedFighter":
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeProjectedFighterAmountCommand(fitID, fit.projectedFighters.index(thing), cleanInput))
|
||||
if mainItem in fit.projectedFighters:
|
||||
position = fit.projectedFighters.index(mainItem)
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeProjectedFighterAmountCommand(
|
||||
fitID=fitID, position=position, amount=cleanInput))
|
||||
else:
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeLocalFighterAmountCommand(fitID, fit.fighters.index(thing), cleanInput))
|
||||
if mainItem in fit.fighters:
|
||||
position = fit.fighters.index(mainItem)
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeLocalFighterAmountCommand(
|
||||
fitID=fitID, position=position, amount=cleanInput))
|
||||
|
||||
|
||||
ChangeItemAmount.register()
|
||||
|
||||
40
gui/builtinContextMenus/itemFill.py
Normal file
40
gui/builtinContextMenus/itemFill.py
Normal file
@@ -0,0 +1,40 @@
|
||||
import gui.fitCommands as cmd
|
||||
import gui.mainFrame
|
||||
from gui.contextMenu import ContextMenuSingle
|
||||
from service.settings import ContextMenuSettings
|
||||
|
||||
|
||||
class FillWithItem(ContextMenuSingle):
|
||||
|
||||
def __init__(self):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
self.settings = ContextMenuSettings.getInstance()
|
||||
|
||||
def display(self, srcContext, mainItem):
|
||||
if not self.settings.get('moduleFill'):
|
||||
return False
|
||||
|
||||
if srcContext not in ('marketItemGroup', 'marketItemMisc'):
|
||||
return False
|
||||
|
||||
if self.mainFrame.getActiveFit() is None:
|
||||
return False
|
||||
|
||||
if mainItem is None:
|
||||
return False
|
||||
|
||||
if mainItem.category.name != 'Module':
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def getText(self, itmContext, mainItem):
|
||||
return "Fill With Module"
|
||||
|
||||
def activate(self, fullContext, mainItem, i):
|
||||
self.mainFrame.command.Submit(cmd.GuiFillWithNewLocalModulesCommand(
|
||||
fitID=self.mainFrame.getActiveFit(),
|
||||
itemID=int(mainItem.ID)))
|
||||
|
||||
|
||||
FillWithItem.register()
|
||||
@@ -1,15 +1,15 @@
|
||||
import gui.mainFrame
|
||||
from gui.contextMenu import ContextMenu
|
||||
from gui.contextMenu import ContextMenuSingle
|
||||
from service.market import Market
|
||||
from service.settings import ContextMenuSettings
|
||||
|
||||
|
||||
class JumpToMarketItem(ContextMenu):
|
||||
class JumpToMarketItem(ContextMenuSingle):
|
||||
def __init__(self):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
self.settings = ContextMenuSettings.getInstance()
|
||||
|
||||
def display(self, srcContext, selection):
|
||||
def display(self, srcContext, mainItem):
|
||||
validContexts = ("marketItemMisc", "fittingModule",
|
||||
"fittingCharge", "droneItem",
|
||||
"implantItem", "boosterItem",
|
||||
@@ -18,37 +18,40 @@ class JumpToMarketItem(ContextMenu):
|
||||
"implantItemChar", "fighterItem",
|
||||
"projectedFighter")
|
||||
|
||||
if srcContext not in validContexts or selection is None or len(selection) < 1:
|
||||
if srcContext not in validContexts or mainItem is None:
|
||||
return False
|
||||
|
||||
if mainItem is None or getattr(mainItem, "isEmpty", False):
|
||||
return False
|
||||
|
||||
sMkt = Market.getInstance()
|
||||
item = getattr(selection[0], "item", selection[0])
|
||||
isMutated = getattr(selection[0], "isMutated", False)
|
||||
item = getattr(mainItem, "item", mainItem)
|
||||
isMutated = getattr(mainItem, "isMutated", False)
|
||||
mktGrp = sMkt.getMarketGroupByItem(item)
|
||||
if mktGrp is None and isMutated:
|
||||
mktGrp = sMkt.getMarketGroupByItem(selection[0].baseItem)
|
||||
mktGrp = sMkt.getMarketGroupByItem(mainItem.baseItem)
|
||||
|
||||
# 1663 is Special Edition Festival Assets, we don't have root group for it
|
||||
if mktGrp is None or mktGrp.ID == 1663:
|
||||
return False
|
||||
|
||||
doit = not selection[0].isEmpty if srcContext == "fittingModule" else True
|
||||
doit = not mainItem.isEmpty if srcContext == "fittingModule" else True
|
||||
return doit
|
||||
|
||||
def getText(self, itmContext, selection):
|
||||
def getText(self, itmContext, mainItem):
|
||||
return "{0} Market Group".format(itmContext if itmContext is not None else "Item")
|
||||
|
||||
def activate(self, fullContext, selection, i):
|
||||
def activate(self, fullContext, mainItem, i):
|
||||
srcContext = fullContext[0]
|
||||
if srcContext in ("fittingCharge", "projectedCharge"):
|
||||
item = selection[0].charge
|
||||
elif hasattr(selection[0], "item"):
|
||||
if getattr(selection[0], "isMutated", False):
|
||||
item = selection[0].baseItem
|
||||
item = mainItem.charge
|
||||
elif hasattr(mainItem, "item"):
|
||||
if getattr(mainItem, "isMutated", False):
|
||||
item = mainItem.baseItem
|
||||
else:
|
||||
item = selection[0].item
|
||||
item = mainItem.item
|
||||
else:
|
||||
item = selection[0]
|
||||
item = mainItem
|
||||
|
||||
self.mainFrame.notebookBrowsers.SetSelection(0)
|
||||
self.mainFrame.marketBrowser.jump(item)
|
||||
|
||||
@@ -1,22 +1,26 @@
|
||||
import gui.fitCommands as cmd
|
||||
import gui.mainFrame
|
||||
from gui.contextMenu import ContextMenu
|
||||
from gui.contextMenu import ContextMenuSingle
|
||||
from service.fit import Fit
|
||||
from service.settings import ContextMenuSettings
|
||||
|
||||
|
||||
class ProjectItem(ContextMenu):
|
||||
class ProjectItem(ContextMenuSingle):
|
||||
|
||||
def __init__(self):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
self.settings = ContextMenuSettings.getInstance()
|
||||
|
||||
def display(self, srcContext, selection):
|
||||
def display(self, srcContext, mainItem):
|
||||
if not self.settings.get('project'):
|
||||
return False
|
||||
|
||||
if srcContext not in ("marketItemGroup", "marketItemMisc") or self.mainFrame.getActiveFit() is None:
|
||||
return False
|
||||
|
||||
if mainItem is None:
|
||||
return False
|
||||
|
||||
sFit = Fit.getInstance()
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
fit = sFit.getFit(fitID)
|
||||
@@ -24,25 +28,23 @@ class ProjectItem(ContextMenu):
|
||||
if fit.isStructure:
|
||||
return False
|
||||
|
||||
item = selection[0]
|
||||
return item.isType("projected")
|
||||
return mainItem.isType("projected")
|
||||
|
||||
def getText(self, itmContext, selection):
|
||||
def getText(self, itmContext, mainItem):
|
||||
return "Project {0} onto Fit".format(itmContext)
|
||||
|
||||
def activate(self, fullContext, selection, i):
|
||||
def activate(self, fullContext, mainItem, i):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
category = selection[0].category.name
|
||||
if category == 'Module':
|
||||
success = self.mainFrame.command.Submit(cmd.GuiAddProjectedModuleCommand(fitID=fitID, itemID=selection[0].ID))
|
||||
elif category == 'Drone':
|
||||
success = self.mainFrame.command.Submit(cmd.GuiAddProjectedDroneCommand(fitID=fitID, itemID=selection[0].ID))
|
||||
elif category == 'Fighter':
|
||||
success = self.mainFrame.command.Submit(cmd.GuiAddProjectedFighterCommand(fitID=fitID, itemID=selection[0].ID))
|
||||
if mainItem.isModule:
|
||||
success = self.mainFrame.command.Submit(cmd.GuiAddProjectedModuleCommand(fitID=fitID, itemID=mainItem.ID))
|
||||
elif mainItem.isDrone:
|
||||
success = self.mainFrame.command.Submit(cmd.GuiAddProjectedDroneCommand(fitID=fitID, itemID=mainItem.ID))
|
||||
elif mainItem.isFighter:
|
||||
success = self.mainFrame.command.Submit(cmd.GuiAddProjectedFighterCommand(fitID=fitID, itemID=mainItem.ID))
|
||||
else:
|
||||
success = False
|
||||
if success:
|
||||
self.mainFrame.additionsPane.select('Projected')
|
||||
self.mainFrame.additionsPane.select('Projected', focus=False)
|
||||
|
||||
|
||||
ProjectItem.register()
|
||||
|
||||
@@ -1,71 +1,163 @@
|
||||
import math
|
||||
|
||||
import wx
|
||||
|
||||
import gui.fitCommands as cmd
|
||||
import gui.mainFrame
|
||||
from gui.contextMenu import ContextMenu
|
||||
from eos.saveddata.drone import Drone as EosDrone
|
||||
from eos.saveddata.fighter import Fighter as EosFighter
|
||||
from eos.saveddata.fit import Fit as EosFit
|
||||
from eos.saveddata.module import Module as EosModule
|
||||
from gui.contextMenu import ContextMenuCombined
|
||||
from gui.fitCommands.helpers import getSimilarFighters, getSimilarModPositions
|
||||
from service.fit import Fit
|
||||
from service.settings import ContextMenuSettings
|
||||
|
||||
|
||||
class RemoveItem(ContextMenu):
|
||||
class RemoveItem(ContextMenuCombined):
|
||||
|
||||
def __init__(self):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
self.settings = ContextMenuSettings.getInstance()
|
||||
|
||||
def display(self, srcContext, selection):
|
||||
return srcContext in ("fittingModule", "droneItem",
|
||||
"implantItem", "boosterItem",
|
||||
"projectedModule", "cargoItem",
|
||||
"projectedFit", "projectedDrone",
|
||||
"fighterItem", "projectedFighter",
|
||||
"commandFit")
|
||||
def display(self, srcContext, mainItem, selection):
|
||||
if srcContext not in (
|
||||
"fittingModule", "droneItem",
|
||||
"implantItem", "boosterItem",
|
||||
"projectedModule", "cargoItem",
|
||||
"projectedFit", "projectedDrone",
|
||||
"fighterItem", "projectedFighter",
|
||||
"commandFit"
|
||||
):
|
||||
return False
|
||||
|
||||
def getText(self, itmContext, selection):
|
||||
return u"Remove {0}".format(itmContext if itmContext is not None else "Item")
|
||||
if mainItem is None or getattr(mainItem, "isEmpty", False):
|
||||
return False
|
||||
|
||||
def activate(self, fullContext, selection, i):
|
||||
self.srcContext = srcContext
|
||||
return True
|
||||
|
||||
def getText(self, itmContext, mainItem, selection):
|
||||
return 'Remove {}{}'.format(
|
||||
itmContext if itmContext is not None else 'Item',
|
||||
' Stack' if self.srcContext in ('droneItem', 'projectedDrone', 'cargoItem', 'projectedFit') else '')
|
||||
|
||||
def activate(self, fullContext, mainItem, selection, i):
|
||||
handlerMap = {
|
||||
'fittingModule': self.__handleModule,
|
||||
'droneItem': self.__handleDrone,
|
||||
'fighterItem': self.__handleFighter,
|
||||
'implantItem': self.__handleImplant,
|
||||
'boosterItem': self.__handleBooster,
|
||||
'cargoItem': self.__handleCargo,
|
||||
'projectedFit': self.__handleProjectedItem,
|
||||
'projectedModule': self.__handleProjectedItem,
|
||||
'projectedDrone': self.__handleProjectedItem,
|
||||
'projectedFighter': self.__handleProjectedItem,
|
||||
'commandFit': self.__handleCommandFit}
|
||||
srcContext = fullContext[0]
|
||||
sFit = Fit.getInstance()
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
fit = sFit.getFit(fitID)
|
||||
handler = handlerMap.get(srcContext)
|
||||
if handler is None:
|
||||
return
|
||||
handler(mainItem, selection)
|
||||
|
||||
if srcContext == "fittingModule":
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveLocalModuleCommand(
|
||||
fitID=fitID, modules=[module for module in selection if module is not None]))
|
||||
elif srcContext == "fittingCharge":
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeLocalModuleChargesCommand(
|
||||
fitID=fitID, modules=selection, chargeItemID=None))
|
||||
elif srcContext == "droneItem":
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveLocalDroneCommand(
|
||||
fitID=fitID, position=fit.drones.index(selection[0]), amount=math.inf))
|
||||
elif srcContext == "fighterItem":
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveLocalFighterCommand(
|
||||
fitID=fitID, position=fit.fighters.index(selection[0])))
|
||||
elif srcContext == "implantItem":
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveImplantCommand(
|
||||
fitID=fitID, position=fit.implants.index(selection[0])))
|
||||
elif srcContext == "boosterItem":
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveBoosterCommand(
|
||||
fitID=fitID, position=fit.boosters.index(selection[0])))
|
||||
elif srcContext == "cargoItem":
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveCargoCommand(
|
||||
fitID=fitID, itemID=selection[0].itemID))
|
||||
elif srcContext == "projectedFit":
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedFitCommand(
|
||||
fitID=fitID, projectedFitID=selection[0].ID))
|
||||
elif srcContext == "projectedModule":
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedModuleCommand(
|
||||
fitID=fitID, position=fit.projectedModules.index(selection[0])))
|
||||
elif srcContext == "projectedDrone":
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedDroneCommand(
|
||||
fitID=fitID, itemID=selection[0].itemID, amount=math.inf))
|
||||
elif srcContext == "projectedFighter":
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedFighterCommand(
|
||||
fitID=fitID, position=fit.projectedFighters.index(selection[0])))
|
||||
elif srcContext == "commandFit":
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveCommandFitCommand(
|
||||
fitID=fitID, commandFitID=selection[0].ID))
|
||||
def __handleModule(self, mainItem, selection):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
if wx.GetMouseState().GetModifiers() == wx.MOD_ALT:
|
||||
positions = getSimilarModPositions(fit.modules, mainItem)
|
||||
else:
|
||||
positions = []
|
||||
for mod in selection:
|
||||
if mod in fit.modules:
|
||||
positions.append(fit.modules.index(mod))
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveLocalModuleCommand(
|
||||
fitID=fitID, positions=positions))
|
||||
|
||||
def __handleDrone(self, mainItem, selection):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
positions = []
|
||||
for drone in selection:
|
||||
if drone in fit.drones:
|
||||
positions.append(fit.drones.index(drone))
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveLocalDronesCommand(
|
||||
fitID=fitID, positions=positions, amount=math.inf))
|
||||
|
||||
def __handleFighter(self, mainItem, selection):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
if wx.GetMouseState().GetModifiers() == wx.MOD_ALT:
|
||||
fighters = getSimilarFighters(fit.fighters, mainItem)
|
||||
else:
|
||||
fighters = selection
|
||||
positions = []
|
||||
for fighter in fighters:
|
||||
if fighter in fit.fighters:
|
||||
positions.append(fit.fighters.index(fighter))
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveLocalFightersCommand(
|
||||
fitID=fitID, positions=positions))
|
||||
|
||||
def __handleImplant(self, mainItem, selection):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
positions = []
|
||||
for implant in selection:
|
||||
if implant in fit.implants:
|
||||
positions.append(fit.implants.index(implant))
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveImplantsCommand(
|
||||
fitID=fitID, positions=positions))
|
||||
|
||||
def __handleBooster(self, mainItem, selection):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
positions = []
|
||||
for booster in selection:
|
||||
if booster in fit.boosters:
|
||||
positions.append(fit.boosters.index(booster))
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveBoostersCommand(
|
||||
fitID=fitID, positions=positions))
|
||||
|
||||
def __handleCargo(self, mainItem, selection):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
itemIDs = [c.itemID for c in selection]
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveCargosCommand(
|
||||
fitID=fitID, itemIDs=itemIDs))
|
||||
|
||||
def __handleProjectedItem(self, mainItem, selection):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
if isinstance(mainItem, EosFit):
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedItemsCommand(
|
||||
fitID=fitID, items=selection, amount=math.inf))
|
||||
elif isinstance(mainItem, EosModule):
|
||||
if wx.GetMouseState().GetModifiers() == wx.MOD_ALT:
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
positions = getSimilarModPositions(fit.projectedModules, mainItem)
|
||||
items = [fit.projectedModules[p] for p in positions]
|
||||
else:
|
||||
items = selection
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedItemsCommand(
|
||||
fitID=fitID, items=items, amount=math.inf))
|
||||
elif isinstance(mainItem, EosDrone):
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedItemsCommand(
|
||||
fitID=fitID, items=selection, amount=math.inf))
|
||||
elif isinstance(mainItem, EosFighter):
|
||||
if wx.GetMouseState().GetModifiers() == wx.MOD_ALT:
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
items = getSimilarFighters(fit.projectedFighters, mainItem)
|
||||
else:
|
||||
items = selection
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedItemsCommand(
|
||||
fitID=fitID, items=items, amount=math.inf))
|
||||
else:
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedItemsCommand(
|
||||
fitID=fitID, items=selection, amount=math.inf))
|
||||
|
||||
def __handleCommandFit(self, mainItem, selection):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
commandFitIDs = [cf.ID for cf in selection]
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveCommandFitsCommand(
|
||||
fitID=fitID, commandFitIDs=commandFitIDs))
|
||||
|
||||
|
||||
RemoveItem.register()
|
||||
|
||||
@@ -2,42 +2,51 @@
|
||||
import wx
|
||||
|
||||
import gui.mainFrame
|
||||
from gui.contextMenu import ContextMenu
|
||||
from gui.contextMenu import ContextMenuSingle
|
||||
from gui.itemStats import ItemStatsDialog
|
||||
from service.fit import Fit
|
||||
from eos.saveddata.mode import Mode
|
||||
from service.settings import ContextMenuSettings
|
||||
|
||||
|
||||
class ItemStats(ContextMenu):
|
||||
class ItemStats(ContextMenuSingle):
|
||||
def __init__(self):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
self.settings = ContextMenuSettings.getInstance()
|
||||
|
||||
def display(self, srcContext, selection):
|
||||
return srcContext in ("marketItemGroup", "marketItemMisc",
|
||||
"fittingModule", "fittingCharge",
|
||||
"fittingShip", "baseShip",
|
||||
"cargoItem", "droneItem",
|
||||
"implantItem", "boosterItem",
|
||||
"skillItem", "projectedModule",
|
||||
"projectedDrone", "projectedCharge",
|
||||
"itemStats", "fighterItem",
|
||||
"implantItemChar", "projectedFighter",
|
||||
"fittingMode")
|
||||
def display(self, srcContext, mainItem):
|
||||
if srcContext not in (
|
||||
"marketItemGroup", "marketItemMisc",
|
||||
"fittingModule", "fittingCharge",
|
||||
"fittingShip", "baseShip",
|
||||
"cargoItem", "droneItem",
|
||||
"implantItem", "boosterItem",
|
||||
"skillItem", "projectedModule",
|
||||
"projectedDrone", "projectedCharge",
|
||||
"itemStats", "fighterItem",
|
||||
"implantItemChar", "projectedFighter",
|
||||
"fittingMode"
|
||||
):
|
||||
return False
|
||||
|
||||
def getText(self, itmContext, selection):
|
||||
return "{0} Stats".format(itmContext if itmContext is not None else "Item")
|
||||
if (mainItem is None or getattr(mainItem, "isEmpty", False)) and srcContext != "fittingShip":
|
||||
return False
|
||||
|
||||
def activate(self, fullContext, selection, i):
|
||||
return True
|
||||
|
||||
def getText(self, itmContext, mainItem):
|
||||
return "{} Stats".format(itmContext if itmContext is not None else "Item")
|
||||
|
||||
def activate(self, fullContext, mainItem, i):
|
||||
srcContext = fullContext[0]
|
||||
if srcContext == "fittingShip":
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
sFit = Fit.getInstance()
|
||||
stuff = sFit.getFit(fitID).ship
|
||||
elif srcContext == "fittingMode":
|
||||
stuff = selection[0].item
|
||||
stuff = mainItem.item
|
||||
else:
|
||||
stuff = selection[0]
|
||||
stuff = mainItem
|
||||
|
||||
if srcContext == "fittingModule" and stuff.isEmpty:
|
||||
return
|
||||
@@ -45,7 +54,7 @@ class ItemStats(ContextMenu):
|
||||
mstate = wx.GetMouseState()
|
||||
reuse = False
|
||||
|
||||
if mstate.cmdDown:
|
||||
if mstate.GetModifiers() == wx.MOD_SHIFT:
|
||||
reuse = True
|
||||
|
||||
if self.mainFrame.GetActiveStatsWindow() is None and reuse:
|
||||
|
||||
@@ -3,18 +3,20 @@ import wx
|
||||
|
||||
import gui.fitCommands as cmd
|
||||
import gui.mainFrame
|
||||
from gui.contextMenu import ContextMenu
|
||||
from gui.contextMenu import ContextMenuCombined
|
||||
from gui.fitCommands.helpers import getSimilarModPositions, getSimilarFighters
|
||||
from service.fit import Fit
|
||||
from service.market import Market
|
||||
from service.settings import ContextMenuSettings
|
||||
|
||||
|
||||
class ChangeItemToVariation(ContextMenu):
|
||||
class ChangeItemToVariation(ContextMenuCombined):
|
||||
|
||||
def __init__(self):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
self.settings = ContextMenuSettings.getInstance()
|
||||
|
||||
def display(self, srcContext, selection):
|
||||
def display(self, srcContext, mainItem, selection):
|
||||
if not self.settings.get('metaSwap'):
|
||||
return False
|
||||
|
||||
@@ -31,29 +33,22 @@ class ChangeItemToVariation(ContextMenu):
|
||||
):
|
||||
return False
|
||||
|
||||
# Check if list of variations is same for all of selection
|
||||
# If not - don't show the menu
|
||||
mkt = Market.getInstance()
|
||||
self.variations = None
|
||||
for i in selection:
|
||||
variations = mkt.getVariationsByItems([i.item])
|
||||
if self.variations is None:
|
||||
self.variations = variations
|
||||
else:
|
||||
if variations != self.variations:
|
||||
return False
|
||||
if mainItem is None or getattr(mainItem, 'isEmpty', False):
|
||||
return False
|
||||
|
||||
self.mainVariations = Market.getInstance().getVariationsByItems((mainItem.item,))
|
||||
# No variations from current module
|
||||
if len(self.mainVariations) < 2:
|
||||
return False
|
||||
|
||||
self.mainItem = mainItem
|
||||
self.selection = selection
|
||||
|
||||
if len(self.variations) == 1:
|
||||
return False # no variations from current module
|
||||
|
||||
return True
|
||||
|
||||
def getText(self, itmContext, selection):
|
||||
def getText(self, itmContext, mainItem, selection):
|
||||
return 'Variations'
|
||||
|
||||
def getSubMenu(self, context, selection, rootMenu, i, pitem):
|
||||
def getSubMenu(self, context, mainItem, selection, rootMenu, i, pitem):
|
||||
self.moduleLookup = {}
|
||||
sFit = Fit.getInstance()
|
||||
fit = sFit.getFit(self.mainFrame.getActiveFit())
|
||||
@@ -64,11 +59,13 @@ class ChangeItemToVariation(ContextMenu):
|
||||
return x.attributes['metaLevel'].value
|
||||
|
||||
def get_metagroup(x):
|
||||
return x.metaGroup.ID if x.metaGroup is not None else 0
|
||||
# We want deadspace before officer mods
|
||||
remap = {5: 6, 6: 5}
|
||||
return remap.get(x.metaGroup.ID, x.metaGroup.ID) if x.metaGroup is not None else 0
|
||||
|
||||
def get_boosterrank(x):
|
||||
# If we're returning a lot of items, sort my name
|
||||
if len(self.variations) > 7:
|
||||
if len(self.mainVariations) > 7:
|
||||
return x.name
|
||||
# Sort by booster chance to get some sort of pseudorank.
|
||||
elif 'boosterEffectChance1' in x.attributes:
|
||||
@@ -87,13 +84,14 @@ class ChangeItemToVariation(ContextMenu):
|
||||
bindmenu = m
|
||||
|
||||
# Sort items by metalevel, and group within that metalevel
|
||||
items = list(self.variations)
|
||||
|
||||
items = list(self.mainVariations)
|
||||
# Sort all items by name first
|
||||
items.sort(key=lambda x: x.name)
|
||||
# Do not do any extra sorting for implants
|
||||
if 'implantItem' in context:
|
||||
# sort implants based on name
|
||||
items.sort(key=lambda x: x.name)
|
||||
pass
|
||||
# Boosters don't have meta or anything concrete that we can rank by. Go by chance to inflict side effect
|
||||
elif 'boosterItem' in context:
|
||||
# boosters don't have meta or anything concrete that we can rank by. Go by chance to inflict side effect
|
||||
items.sort(key=get_boosterrank)
|
||||
else:
|
||||
# sort by group and meta level
|
||||
@@ -112,13 +110,13 @@ class ChangeItemToVariation(ContextMenu):
|
||||
|
||||
if thisgroup != group and context not in ('implantItem', 'boosterItem'):
|
||||
group = thisgroup
|
||||
id = ContextMenu.nextID()
|
||||
id = ContextMenuCombined.nextID()
|
||||
m.Append(id, '─ %s ─' % group)
|
||||
m.Enable(id, False)
|
||||
|
||||
id = ContextMenu.nextID()
|
||||
id = ContextMenuCombined.nextID()
|
||||
mitem = wx.MenuItem(rootMenu, id, item.name)
|
||||
bindmenu.Bind(wx.EVT_MENU, self.handleModule, mitem)
|
||||
bindmenu.Bind(wx.EVT_MENU, self.handleSwitch, mitem)
|
||||
|
||||
self.moduleLookup[id] = item, context
|
||||
m.Append(mitem)
|
||||
@@ -126,48 +124,175 @@ class ChangeItemToVariation(ContextMenu):
|
||||
|
||||
return m
|
||||
|
||||
def handleModule(self, event):
|
||||
def handleSwitch(self, event):
|
||||
item, context = self.moduleLookup.get(event.Id, None)
|
||||
if item is None:
|
||||
event.Skip()
|
||||
return
|
||||
handlerMap = {
|
||||
'fittingModule': self.__handleModule,
|
||||
'droneItem': self.__handleDrone,
|
||||
'fighterItem': self.__handleFighter,
|
||||
'cargoItem': self.__handleCargo,
|
||||
'implantItem': self.__handleImplant,
|
||||
'boosterItem': self.__handleBooster,
|
||||
'projectedModule': self.__handleProjectedModule,
|
||||
'projectedDrone': self.__handleProjectedDrone,
|
||||
'projectedFighter': self.__handleProjectedFighter}
|
||||
handler = handlerMap.get(context)
|
||||
if handler is None:
|
||||
return
|
||||
handler(item)
|
||||
|
||||
def __handleModule(self, varItem):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
if context == 'fittingModule':
|
||||
positions = [mod.modPosition for mod in self.selection]
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeLocalModuleMetasCommand(
|
||||
fitID=fitID, positions=positions, newItemID=item.ID))
|
||||
elif context == 'droneItem':
|
||||
position = fit.drones.index(self.selection[0])
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeLocalDroneMetaCommand(
|
||||
fitID=fitID, position=position, newItemID=item.ID))
|
||||
elif context == 'fighterItem':
|
||||
position = fit.fighters.index(self.selection[0])
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeLocalFighterMetaCommand(
|
||||
fitID=fitID, position=position, newItemID=item.ID))
|
||||
elif context == 'implantItem':
|
||||
position = fit.implants.index(self.selection[0])
|
||||
if wx.GetMouseState().GetModifiers() == wx.MOD_ALT:
|
||||
positions = getSimilarModPositions(fit.modules, self.mainItem)
|
||||
else:
|
||||
sMkt = Market.getInstance()
|
||||
positions = []
|
||||
for mod in self.selection:
|
||||
if mod.isEmpty:
|
||||
continue
|
||||
if mod is self.mainItem:
|
||||
positions.append(fit.modules.index(mod))
|
||||
continue
|
||||
if mod not in fit.modules:
|
||||
continue
|
||||
modVariations = sMkt.getVariationsByItems((mod.item,))
|
||||
if modVariations == self.mainVariations:
|
||||
positions.append(fit.modules.index(mod))
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeLocalModuleMetasCommand(
|
||||
fitID=fitID, positions=positions, newItemID=varItem.ID))
|
||||
|
||||
def __handleDrone(self, varItem):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
sMkt = Market.getInstance()
|
||||
positions = []
|
||||
for drone in self.selection:
|
||||
if drone not in fit.drones:
|
||||
continue
|
||||
if drone is self.mainItem:
|
||||
positions.append(fit.drones.index(drone))
|
||||
continue
|
||||
droneVariations = sMkt.getVariationsByItems((drone.item,))
|
||||
if droneVariations == self.mainVariations:
|
||||
positions.append(fit.drones.index(drone))
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeLocalDroneMetasCommand(
|
||||
fitID=fitID, positions=positions, newItemID=varItem.ID))
|
||||
|
||||
def __handleFighter(self, varItem):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
if wx.GetMouseState().GetModifiers() == wx.MOD_ALT:
|
||||
fighters = getSimilarFighters(fit.fighters, self.mainItem)
|
||||
else:
|
||||
fighters = self.selection
|
||||
sMkt = Market.getInstance()
|
||||
positions = []
|
||||
for fighter in fighters:
|
||||
if fighter not in fit.fighters:
|
||||
continue
|
||||
if fighter is self.mainItem:
|
||||
positions.append(fit.fighters.index(fighter))
|
||||
continue
|
||||
fighterVariations = sMkt.getVariationsByItems((fighter.item,))
|
||||
if fighterVariations == self.mainVariations:
|
||||
positions.append(fit.fighters.index(fighter))
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeLocalFighterMetasCommand(
|
||||
fitID=fitID, positions=positions, newItemID=varItem.ID))
|
||||
|
||||
def __handleCargo(self, varItem):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
sMkt = Market.getInstance()
|
||||
itemIDs = []
|
||||
for cargo in self.selection:
|
||||
if cargo is self.mainItem:
|
||||
itemIDs.append(cargo.itemID)
|
||||
continue
|
||||
cargoVariations = sMkt.getVariationsByItems((cargo.item,))
|
||||
if cargoVariations == self.mainVariations:
|
||||
itemIDs.append(cargo.itemID)
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeCargoMetasCommand(
|
||||
fitID=fitID, itemIDs=itemIDs, newItemID=varItem.ID))
|
||||
|
||||
def __handleImplant(self, varItem):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
implant = self.mainItem
|
||||
if implant in fit.implants:
|
||||
position = fit.implants.index(implant)
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeImplantMetaCommand(
|
||||
fitID=fitID, position=position, newItemID=item.ID))
|
||||
elif context == 'boosterItem':
|
||||
position = fit.boosters.index(self.selection[0])
|
||||
fitID=fitID, position=position, newItemID=varItem.ID))
|
||||
|
||||
def __handleBooster(self, varItem):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
booster = self.mainItem
|
||||
if booster in fit.boosters:
|
||||
position = fit.boosters.index(booster)
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeBoosterMetaCommand(
|
||||
fitID=fitID, position=position, newItemID=item.ID))
|
||||
elif context == 'cargoItem':
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeCargoMetaCommand(
|
||||
fitID=fitID, itemID=self.selection[0].itemID, newItemID=item.ID))
|
||||
elif context == 'projectedModule':
|
||||
position = fit.projectedModules.index(self.selection[0])
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeProjectedModuleMetaCommand(
|
||||
fitID=fitID, position=position, newItemID=item.ID))
|
||||
elif context == 'projectedDrone':
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeProjectedDroneMetaCommand(
|
||||
fitID=fitID, itemID=self.selection[0].itemID, newItemID=item.ID))
|
||||
elif context == 'projectedFighter':
|
||||
position = fit.projectedFighters.index(self.selection[0])
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeProjectedFighterMetaCommand(
|
||||
fitID=fitID, position=position, newItemID=item.ID))
|
||||
fitID=fitID, position=position, newItemID=varItem.ID))
|
||||
|
||||
def __handleProjectedModule(self, varItem):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
if wx.GetMouseState().GetModifiers() == wx.MOD_ALT:
|
||||
positions = getSimilarModPositions(fit.projectedModules, self.mainItem)
|
||||
else:
|
||||
sMkt = Market.getInstance()
|
||||
positions = []
|
||||
for mod in self.selection:
|
||||
if mod is self.mainItem:
|
||||
positions.append(fit.projectedModules.index(mod))
|
||||
continue
|
||||
if mod not in fit.projectedModules:
|
||||
continue
|
||||
modVariations = sMkt.getVariationsByItems((mod.item,))
|
||||
if modVariations == self.mainVariations:
|
||||
positions.append(fit.projectedModules.index(mod))
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeProjectedModuleMetasCommand(
|
||||
fitID=fitID, positions=positions, newItemID=varItem.ID))
|
||||
|
||||
def __handleProjectedDrone(self, varItem):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
sMkt = Market.getInstance()
|
||||
itemIDs = []
|
||||
for drone in self.selection:
|
||||
if drone not in fit.projectedDrones:
|
||||
continue
|
||||
if drone is self.mainItem:
|
||||
itemIDs.append(drone.itemID)
|
||||
continue
|
||||
droneVariations = sMkt.getVariationsByItems((drone.item,))
|
||||
if droneVariations == self.mainVariations:
|
||||
itemIDs.append(drone.itemID)
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeProjectedDroneMetasCommand(
|
||||
fitID=fitID, itemIDs=itemIDs, newItemID=varItem.ID))
|
||||
|
||||
def __handleProjectedFighter(self, varItem):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
if wx.GetMouseState().GetModifiers() == wx.MOD_ALT:
|
||||
fighters = getSimilarFighters(fit.projectedFighters, self.mainItem)
|
||||
else:
|
||||
fighters = self.selection
|
||||
sMkt = Market.getInstance()
|
||||
positions = []
|
||||
for fighter in fighters:
|
||||
if fighter not in fit.projectedFighters:
|
||||
continue
|
||||
if fighter is self.mainItem:
|
||||
positions.append(fit.projectedFighters.index(fighter))
|
||||
continue
|
||||
fighterVariations = sMkt.getVariationsByItems((fighter.item,))
|
||||
if fighterVariations == self.mainVariations:
|
||||
positions.append(fit.projectedFighters.index(fighter))
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeProjectedFighterMetasCommand(
|
||||
fitID=fitID, positions=positions, newItemID=varItem.ID))
|
||||
|
||||
|
||||
ChangeItemToVariation.register()
|
||||
|
||||
@@ -4,54 +4,60 @@ import wx
|
||||
import gui.fitCommands as cmd
|
||||
import gui.mainFrame
|
||||
from eos.const import FittingHardpoint
|
||||
from eos.saveddata.module import Module
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
from gui.contextMenu import ContextMenu
|
||||
from gui.contextMenu import ContextMenuCombined
|
||||
from gui.fitCommands.helpers import getSimilarModPositions
|
||||
from service.fit import Fit
|
||||
from service.market import Market
|
||||
from service.settings import ContextMenuSettings
|
||||
|
||||
|
||||
class ChangeModuleAmmo(ContextMenu):
|
||||
class ChangeModuleAmmo(ContextMenuCombined):
|
||||
|
||||
DAMAGE_TYPES = ("em", "explosive", "kinetic", "thermal")
|
||||
MISSILE_ORDER = ("em", "thermal", "kinetic", "explosive", "mixed")
|
||||
|
||||
def __init__(self):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
self.settings = ContextMenuSettings.getInstance()
|
||||
# Format: {type ID: set(loadable, charges)}
|
||||
self.loadableCharges = {}
|
||||
|
||||
def display(self, srcContext, selection):
|
||||
if self.mainFrame.getActiveFit() is None or srcContext not in ("fittingModule", "projectedModule"):
|
||||
def display(self, srcContext, mainItem, selection):
|
||||
if srcContext not in ("fittingModule", "projectedModule"):
|
||||
return False
|
||||
|
||||
modules = selection if srcContext == "fittingModule" else (selection[0],)
|
||||
|
||||
validCharges = None
|
||||
checkedTypes = set()
|
||||
|
||||
for mod in modules:
|
||||
# loop through modules and gather list of valid charges
|
||||
if mod.item.ID in checkedTypes:
|
||||
continue
|
||||
checkedTypes.add(mod.item.ID)
|
||||
currCharges = mod.getValidCharges()
|
||||
if len(currCharges) > 0:
|
||||
if validCharges is not None and validCharges != currCharges:
|
||||
return False
|
||||
|
||||
validCharges = currCharges
|
||||
self.module = mod
|
||||
|
||||
if validCharges is None:
|
||||
if self.mainFrame.getActiveFit() is None:
|
||||
return False
|
||||
|
||||
self.modules = modules
|
||||
self.charges = list([charge for charge in validCharges if Market.getInstance().getPublicityByItem(charge)])
|
||||
self.context = srcContext
|
||||
return len(self.charges) > 0
|
||||
self.mainCharges = self.getChargesForMod(mainItem)
|
||||
if not self.mainCharges:
|
||||
return False
|
||||
|
||||
def getText(self, itmContext, selection):
|
||||
self.module = mainItem
|
||||
self.selection = selection
|
||||
self.srcContext = srcContext
|
||||
return True
|
||||
|
||||
def getText(self, itmContext, mainItem, selection):
|
||||
return "Charge"
|
||||
|
||||
def getChargesForMod(self, mod):
|
||||
sMkt = Market.getInstance()
|
||||
if mod is None or mod.isEmpty:
|
||||
return set()
|
||||
typeID = mod.item.ID
|
||||
if typeID in self.loadableCharges:
|
||||
return self.loadableCharges[typeID]
|
||||
chargeSet = self.loadableCharges.setdefault(typeID, set())
|
||||
# Do not try to grab it for modes which can also be passed as part of selection
|
||||
if isinstance(mod, Module):
|
||||
for charge in mod.getValidCharges():
|
||||
if sMkt.getPublicityByItem(charge):
|
||||
chargeSet.add(charge)
|
||||
return chargeSet
|
||||
|
||||
def turretSorter(self, charge):
|
||||
damage = 0
|
||||
range_ = (self.module.item.getAttribute("maxRange")) * \
|
||||
@@ -107,7 +113,7 @@ class ChangeModuleAmmo(ContextMenu):
|
||||
return list(map(self.numericConverter, parts))
|
||||
|
||||
def addCharge(self, menu, charge):
|
||||
id_ = ContextMenu.nextID()
|
||||
id_ = ContextMenuCombined.nextID()
|
||||
name = charge.name if charge is not None else "Empty"
|
||||
self.chargeIds[id_] = charge
|
||||
item = wx.MenuItem(menu, id_, name)
|
||||
@@ -122,11 +128,11 @@ class ChangeModuleAmmo(ContextMenu):
|
||||
|
||||
@staticmethod
|
||||
def addSeperator(m, text):
|
||||
id_ = ContextMenu.nextID()
|
||||
id_ = ContextMenuCombined.nextID()
|
||||
m.Append(id_, '─ %s ─' % text)
|
||||
m.Enable(id_, False)
|
||||
|
||||
def getSubMenu(self, context, selection, rootMenu, i, pitem):
|
||||
def getSubMenu(self, context, mainItem, selection, rootMenu, i, pitem):
|
||||
msw = True if "wxMSW" in wx.PlatformInfo else False
|
||||
m = wx.Menu()
|
||||
self.chargeIds = {}
|
||||
@@ -139,8 +145,8 @@ class ChangeModuleAmmo(ContextMenu):
|
||||
range_ = None
|
||||
nameBase = None
|
||||
sub = None
|
||||
self.charges.sort(key=self.turretSorter)
|
||||
for charge in self.charges:
|
||||
chargesSorted = sorted(self.mainCharges, key=self.turretSorter)
|
||||
for charge in chargesSorted:
|
||||
if "civilian" in charge.name.lower():
|
||||
continue
|
||||
currBase = charge.name.rsplit()[-2:]
|
||||
@@ -173,11 +179,11 @@ class ChangeModuleAmmo(ContextMenu):
|
||||
|
||||
self.addSeperator(m, "Short Range")
|
||||
elif hardpoint == FittingHardpoint.MISSILE and moduleName != 'Festival Launcher':
|
||||
self.charges.sort(key=self.missileSorter)
|
||||
type_ = None
|
||||
sub = None
|
||||
defender = None
|
||||
for charge in self.charges:
|
||||
chargesSorted = sorted(self.mainCharges, key=self.missileSorter)
|
||||
for charge in chargesSorted:
|
||||
currType = self.damageInfo(charge)[0]
|
||||
|
||||
if currType != type_ or type_ is None:
|
||||
@@ -206,8 +212,8 @@ class ChangeModuleAmmo(ContextMenu):
|
||||
if sub is not None:
|
||||
self.addSeperator(sub, "More Damage")
|
||||
else:
|
||||
self.charges.sort(key=self.nameSorter)
|
||||
for charge in self.charges:
|
||||
chargesSorted = sorted(self.mainCharges, key=self.nameSorter)
|
||||
for charge in chargesSorted:
|
||||
m.Append(self.addCharge(rootMenu if msw else m, charge))
|
||||
|
||||
m.Append(self.addCharge(rootMenu if msw else m, None))
|
||||
@@ -221,33 +227,43 @@ class ChangeModuleAmmo(ContextMenu):
|
||||
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
sFit = Fit.getInstance()
|
||||
switchAll = sFit.serviceFittingOptions['ammoChangeAll'] is not wx.GetMouseState().CmdDown()
|
||||
# Switch in selection or all modules, depending on ctrl key state and settings
|
||||
fit = sFit.getFit(fitID)
|
||||
mstate = wx.GetMouseState()
|
||||
# Switch in selection or all modules, depending on modifier key state and settings
|
||||
switchAll = sFit.serviceFittingOptions['ammoChangeAll'] is not mstate.GetModifiers() in (wx.MOD_ALT, wx.MOD_CONTROL)
|
||||
if switchAll:
|
||||
fit = sFit.getFit(fitID)
|
||||
selectedModule = self.modules[0]
|
||||
if self.context == 'fittingModule':
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeLocalModuleChargesCommand(
|
||||
fitID=fitID,
|
||||
modules=[m for m in fit.modules if m.itemID is not None and m.itemID == selectedModule.itemID],
|
||||
chargeItemID=charge.ID if charge is not None else None))
|
||||
elif self.context == 'projectedModule':
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeProjectedModuleChargesCommand(
|
||||
fitID=fitID,
|
||||
modules=[m for m in fit.projectedModules if
|
||||
m.itemID is not None and m.itemID == selectedModule.itemID],
|
||||
chargeItemID=charge.ID if charge is not None else None))
|
||||
if self.srcContext == 'fittingModule':
|
||||
command = cmd.GuiChangeLocalModuleChargesCommand
|
||||
modContainer = fit.modules
|
||||
elif self.srcContext == 'projectedModule':
|
||||
command = cmd.GuiChangeProjectedModuleChargesCommand
|
||||
modContainer = fit.projectedModules
|
||||
else:
|
||||
return
|
||||
positions = getSimilarModPositions(modContainer, self.module)
|
||||
self.mainFrame.command.Submit(command(
|
||||
fitID=fitID,
|
||||
positions=positions,
|
||||
chargeItemID=charge.ID if charge is not None else None))
|
||||
else:
|
||||
if self.context == 'fittingModule':
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeLocalModuleChargesCommand(
|
||||
fitID=fitID,
|
||||
modules=self.modules,
|
||||
chargeItemID=charge.ID if charge is not None else None))
|
||||
elif self.context == 'projectedModule':
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeProjectedModuleChargesCommand(
|
||||
fitID=fitID,
|
||||
modules=self.modules,
|
||||
chargeItemID=charge.ID if charge is not None else None))
|
||||
if self.srcContext == 'fittingModule':
|
||||
command = cmd.GuiChangeLocalModuleChargesCommand
|
||||
modContainer = fit.modules
|
||||
elif self.srcContext == 'projectedModule':
|
||||
command = cmd.GuiChangeProjectedModuleChargesCommand
|
||||
modContainer = fit.projectedModules
|
||||
else:
|
||||
return
|
||||
positions = []
|
||||
for position, mod in enumerate(modContainer):
|
||||
if mod in self.selection:
|
||||
modCharges = self.getChargesForMod(mod)
|
||||
if modCharges.issubset(self.mainCharges):
|
||||
positions.append(position)
|
||||
self.mainFrame.command.Submit(command(
|
||||
fitID=fitID,
|
||||
positions=positions,
|
||||
chargeItemID=charge.ID if charge is not None else None))
|
||||
|
||||
|
||||
ChangeModuleAmmo.register()
|
||||
|
||||
@@ -1,35 +1,40 @@
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
|
||||
import gui.fitCommands as cmd
|
||||
import gui.globalEvents as GE
|
||||
import gui.mainFrame
|
||||
from gui.contextMenu import ContextMenu
|
||||
from gui.contextMenu import ContextMenuSingle
|
||||
from service.fit import Fit
|
||||
from service.settings import ContextMenuSettings
|
||||
|
||||
|
||||
class FillWithModule(ContextMenu):
|
||||
class FillWithModule(ContextMenuSingle):
|
||||
|
||||
def __init__(self):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
self.settings = ContextMenuSettings.getInstance()
|
||||
|
||||
def display(self, srcContext, selection):
|
||||
def display(self, srcContext, mainItem):
|
||||
|
||||
if not self.settings.get('moduleFill'):
|
||||
return False
|
||||
|
||||
if mainItem is None or getattr(mainItem, 'isEmpty', False):
|
||||
return False
|
||||
|
||||
return srcContext == "fittingModule"
|
||||
|
||||
def getText(self, itmContext, selection):
|
||||
return u"Fill With {0}".format(itmContext if itmContext is not None else "Module")
|
||||
def getText(self, itmContext, mainItem):
|
||||
return "Fill With {0}".format(itmContext if itmContext is not None else "Module")
|
||||
|
||||
def activate(self, fullContext, selection, i):
|
||||
def activate(self, fullContext, mainItem, i):
|
||||
|
||||
srcContext = fullContext[0]
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
|
||||
if srcContext == "fittingModule":
|
||||
self.mainFrame.command.Submit(cmd.GuiFillWithLocalModulesCommand(fitID, selection[0].itemID))
|
||||
return # the command takes care of the PostEvent
|
||||
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
if mainItem in fit.modules:
|
||||
position = fit.modules.index(mainItem)
|
||||
self.mainFrame.command.Submit(cmd.GuiFillWithClonedLocalModulesCommand(
|
||||
fitID=fitID, position=position))
|
||||
|
||||
|
||||
FillWithModule.register()
|
||||
|
||||
@@ -2,51 +2,49 @@
|
||||
import wx
|
||||
|
||||
import gui.mainFrame
|
||||
from gui.contextMenu import ContextMenu
|
||||
from gui.contextMenu import ContextMenuSingle
|
||||
from gui.fitCommands import GuiConvertMutatedLocalModuleCommand, GuiRevertMutatedLocalModuleCommand
|
||||
from service.fit import Fit
|
||||
from service.settings import ContextMenuSettings
|
||||
|
||||
|
||||
class ChangeModuleMutation(ContextMenu):
|
||||
class ChangeModuleMutation(ContextMenuSingle):
|
||||
|
||||
def __init__(self):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
self.settings = ContextMenuSettings.getInstance()
|
||||
self.eventIDs = {}
|
||||
|
||||
def display(self, srcContext, selection):
|
||||
|
||||
# if not self.settings.get('ammoPattern'):
|
||||
# return False
|
||||
def display(self, srcContext, mainItem):
|
||||
|
||||
if srcContext != "fittingModule" or self.mainFrame.getActiveFit() is None:
|
||||
return False
|
||||
|
||||
mod = selection[0]
|
||||
if len(mod.item.mutaplasmids) == 0 and not mod.isMutated:
|
||||
if mainItem is None or mainItem.isEmpty:
|
||||
return False
|
||||
|
||||
if len(mainItem.item.mutaplasmids) == 0 and not mainItem.isMutated:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def getText(self, itmContext, selection):
|
||||
mod = selection[0]
|
||||
return "Apply Mutaplasmid" if not mod.isMutated else "Revert to {}".format(mod.baseItem.name)
|
||||
def getText(self, itmContext, mainItem):
|
||||
return "Apply Mutaplasmid" if not mainItem.isMutated else "Revert to {}".format(mainItem.baseItem.name)
|
||||
|
||||
def getSubMenu(self, context, selection, rootMenu, i, pitem):
|
||||
if selection[0].isMutated:
|
||||
def getSubMenu(self, context, mainItem, rootMenu, i, pitem):
|
||||
if mainItem.isMutated:
|
||||
return None
|
||||
|
||||
msw = True if "wxMSW" in wx.PlatformInfo else False
|
||||
self.skillIds = {}
|
||||
sub = wx.Menu()
|
||||
|
||||
mod = selection[0]
|
||||
|
||||
menu = rootMenu if msw else sub
|
||||
|
||||
for item in mod.item.mutaplasmids:
|
||||
for item in mainItem.item.mutaplasmids:
|
||||
label = item.item.name
|
||||
id = ContextMenu.nextID()
|
||||
self.eventIDs[id] = (item, mod)
|
||||
id = ContextMenuSingle.nextID()
|
||||
self.eventIDs[id] = (item, mainItem)
|
||||
skillItem = wx.MenuItem(menu, id, label)
|
||||
menu.Bind(wx.EVT_MENU, self.handleMenu, skillItem)
|
||||
sub.Append(skillItem)
|
||||
@@ -55,19 +53,22 @@ class ChangeModuleMutation(ContextMenu):
|
||||
|
||||
def handleMenu(self, event):
|
||||
mutaplasmid, mod = self.eventIDs[event.Id]
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
if mod in fit.modules:
|
||||
position = fit.modules.index(mod)
|
||||
self.mainFrame.command.Submit(GuiConvertMutatedLocalModuleCommand(
|
||||
fitID=fitID, position=position, mutaplasmid=mutaplasmid))
|
||||
|
||||
self.mainFrame.command.Submit(GuiConvertMutatedLocalModuleCommand(
|
||||
fitID=self.mainFrame.getActiveFit(),
|
||||
position=mod.modPosition,
|
||||
mutaplasmid=mutaplasmid))
|
||||
def activate(self, fullContext, mainItem, i):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
if mainItem in fit.modules:
|
||||
position = fit.modules.index(mainItem)
|
||||
self.mainFrame.command.Submit(GuiRevertMutatedLocalModuleCommand(
|
||||
fitID=fitID, position=position))
|
||||
|
||||
def activate(self, fullContext, selection, i):
|
||||
mod = selection[0]
|
||||
self.mainFrame.command.Submit(GuiRevertMutatedLocalModuleCommand(
|
||||
fitID=self.mainFrame.getActiveFit(),
|
||||
position=mod.modPosition))
|
||||
|
||||
def getBitmap(self, context, selection):
|
||||
def getBitmap(self, context, mainItem):
|
||||
return None
|
||||
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import math
|
||||
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
|
||||
@@ -5,33 +7,38 @@ import eos.config
|
||||
import gui.fitCommands as cmd
|
||||
import gui.mainFrame
|
||||
from eos.utils.spoolSupport import SpoolType, SpoolOptions
|
||||
from gui.contextMenu import ContextMenu
|
||||
from gui.contextMenu import ContextMenuSingle
|
||||
from service.fit import Fit
|
||||
from service.settings import ContextMenuSettings
|
||||
|
||||
|
||||
class ChangeModuleSpool(ContextMenu):
|
||||
class ChangeModuleSpool(ContextMenuSingle):
|
||||
|
||||
def __init__(self):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
self.settings = ContextMenuSettings.getInstance()
|
||||
self.cycleMap = {}
|
||||
self.resetId = None
|
||||
|
||||
def display(self, srcContext, selection):
|
||||
def display(self, srcContext, mainItem):
|
||||
if not self.settings.get('spoolup'):
|
||||
return False
|
||||
|
||||
if srcContext not in ('fittingModule', 'projectedModule') or self.mainFrame.getActiveFit() is None:
|
||||
return False
|
||||
|
||||
self.mod = selection[0]
|
||||
if mainItem is None or mainItem.isEmpty:
|
||||
return False
|
||||
|
||||
self.mod = mainItem
|
||||
self.context = srcContext
|
||||
|
||||
return self.mod.item.group.name in ("Precursor Weapon", "Mutadaptive Remote Armor Repairer")
|
||||
|
||||
def getText(self, itmContext, selection):
|
||||
def getText(self, itmContext, mainItem):
|
||||
return "Spoolup Cycles"
|
||||
|
||||
def getSubMenu(self, context, selection, rootMenu, i, pitem):
|
||||
def getSubMenu(self, context, mainItem, rootMenu, i, pitem):
|
||||
m = wx.Menu()
|
||||
if "wxMSW" in wx.PlatformInfo:
|
||||
bindmenu = rootMenu
|
||||
@@ -43,15 +50,44 @@ class ChangeModuleSpool(ContextMenu):
|
||||
cycleCurrent = self.mod.getSpoolData(spoolOptions=SpoolOptions(SpoolType.SCALE, eos.config.settings['globalDefaultSpoolupPercentage'], False))[0]
|
||||
cycleMin = self.mod.getSpoolData(spoolOptions=SpoolOptions(SpoolType.SCALE, 0, True))[0]
|
||||
cycleMax = self.mod.getSpoolData(spoolOptions=SpoolOptions(SpoolType.SCALE, 1, True))[0]
|
||||
cycleTotalMin = min(cycleDefault, cycleCurrent, cycleMin)
|
||||
cycleTotalMax = max(cycleDefault, cycleCurrent, cycleMax)
|
||||
|
||||
for cycle in range(cycleMin, cycleMax + 1):
|
||||
menuId = ContextMenu.nextID()
|
||||
def findCycles(val1, val2):
|
||||
# Try to compose list of 21 steps max (0-20)
|
||||
maxSteps = 20
|
||||
valDiff = val2 - val1
|
||||
valScale = valDiff / maxSteps
|
||||
minStep = math.ceil(round(valScale, 9))
|
||||
maxStep = math.floor(round(valDiff / 4, 9))
|
||||
# Check steps from smallest to highest and see if we can go from min value
|
||||
# to max value using those
|
||||
for currentStep in range(minStep, maxStep + 1):
|
||||
if valDiff % currentStep == 0:
|
||||
return set(range(val1, val2 + currentStep, currentStep))
|
||||
# Otherwise just split range in halves and go both ends using min values
|
||||
else:
|
||||
cycles = set()
|
||||
while val2 >= val1:
|
||||
cycles.add(val1)
|
||||
cycles.add(val2)
|
||||
val1 += minStep
|
||||
val2 -= minStep
|
||||
return cycles
|
||||
|
||||
cyclesToShow = findCycles(cycleMin, cycleMax)
|
||||
for cycle in range(cycleTotalMin, cycleTotalMax + 1):
|
||||
menuId = ContextMenuSingle.nextID()
|
||||
|
||||
# Show default only for current value and when not overriden
|
||||
if not isNotDefault and cycle == cycleDefault:
|
||||
text = "{} (default)".format(cycle)
|
||||
else:
|
||||
# Always show current selection and stuff which we decided to show via the cycles function
|
||||
elif cycle == cycleCurrent or cycle in cyclesToShow:
|
||||
text = "{}".format(cycle)
|
||||
# Ignore the rest to not have very long menu
|
||||
else:
|
||||
continue
|
||||
|
||||
item = wx.MenuItem(m, menuId, text, kind=wx.ITEM_CHECK)
|
||||
bindmenu.Bind(wx.EVT_MENU, self.handleSpoolChange, item)
|
||||
@@ -59,7 +95,7 @@ class ChangeModuleSpool(ContextMenu):
|
||||
item.Check(isNotDefault and cycle == cycleCurrent)
|
||||
self.cycleMap[menuId] = cycle
|
||||
|
||||
self.resetId = ContextMenu.nextID()
|
||||
self.resetId = ContextMenuSingle.nextID()
|
||||
item = wx.MenuItem(m, self.resetId, "Reset")
|
||||
bindmenu.Bind(wx.EVT_MENU, self.handleSpoolChange, item)
|
||||
m.Append(item)
|
||||
@@ -75,18 +111,18 @@ class ChangeModuleSpool(ContextMenu):
|
||||
spoolAmount = self.cycleMap[event.Id]
|
||||
else:
|
||||
return
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
if self.context == 'fittingModule':
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeLocalModuleSpoolCommand(
|
||||
fitID=self.mainFrame.getActiveFit(),
|
||||
position=self.mod.modPosition,
|
||||
spoolType=spoolType,
|
||||
spoolAmount=spoolAmount))
|
||||
if self.mod in fit.modules:
|
||||
position = fit.modules.index(self.mod)
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeLocalModuleSpoolCommand(
|
||||
fitID=fitID, position=position, spoolType=spoolType, spoolAmount=spoolAmount))
|
||||
elif self.context == 'projectedModule':
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeProjectedModuleSpoolCommand(
|
||||
fitID=self.mainFrame.getActiveFit(),
|
||||
position=self.mod.modPosition,
|
||||
spoolType=spoolType,
|
||||
spoolAmount=spoolAmount))
|
||||
if self.mod in fit.projectedModules:
|
||||
position = fit.projectedModules.index(self.mod)
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeProjectedModuleSpoolCommand(
|
||||
fitID=fitID, position=position, spoolType=spoolType, spoolAmount=spoolAmount))
|
||||
|
||||
|
||||
ChangeModuleSpool.register()
|
||||
|
||||
@@ -4,11 +4,11 @@ import wx
|
||||
|
||||
import gui.globalEvents as GE
|
||||
import gui.mainFrame
|
||||
from gui.contextMenu import ContextMenu
|
||||
from gui.contextMenu import ContextMenuUnconditional
|
||||
from service.settings import MarketPriceSettings
|
||||
|
||||
|
||||
class ItemGroupPrice(ContextMenu, metaclass=ABCMeta):
|
||||
class ItemGroupPrice(ContextMenuUnconditional, metaclass=ABCMeta):
|
||||
|
||||
def __init__(self):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
@@ -24,13 +24,13 @@ class ItemGroupPrice(ContextMenu, metaclass=ABCMeta):
|
||||
def optionName(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def display(self, srcContext, selection):
|
||||
def display(self, srcContext):
|
||||
return srcContext in ("priceViewFull", "priceViewMinimal")
|
||||
|
||||
def getText(self, itmContext, selection):
|
||||
def getText(self, itmContext):
|
||||
return self.label
|
||||
|
||||
def activate(self, fullContext, selection, i):
|
||||
def activate(self, fullContext, i):
|
||||
self.settings.set(self.optionName, not self.settings.get(self.optionName))
|
||||
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit()))
|
||||
|
||||
|
||||
@@ -3,30 +3,41 @@ import wx
|
||||
|
||||
import gui.mainFrame
|
||||
from gui.builtinShipBrowser.events import Stage3Selected
|
||||
from gui.contextMenu import ContextMenu
|
||||
from gui.contextMenu import ContextMenuUnconditional
|
||||
from service.fit import Fit
|
||||
from service.settings import ContextMenuSettings
|
||||
|
||||
|
||||
class JumpToShip(ContextMenu):
|
||||
class JumpToShip(ContextMenuUnconditional):
|
||||
|
||||
def __init__(self):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
self.settings = ContextMenuSettings.getInstance()
|
||||
|
||||
def display(self, srcContext, selection):
|
||||
return srcContext == "fittingShip"
|
||||
def display(self, srcContext):
|
||||
if srcContext != "fittingShip":
|
||||
return False
|
||||
fitTabSelected = self.mainFrame.notebookBrowsers.GetSelection() == 1
|
||||
if not fitTabSelected:
|
||||
return True
|
||||
browsingStage = self.mainFrame.shipBrowser.GetActiveStage()
|
||||
if browsingStage != 3:
|
||||
return True
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
ship = Fit.getInstance().getFit(fitID).ship
|
||||
browsingShipID = self.mainFrame.shipBrowser.GetStageData(browsingStage)
|
||||
if browsingShipID != ship.item.ID:
|
||||
return True
|
||||
return False
|
||||
|
||||
def getText(self, itmContext, selection):
|
||||
def getText(self, itmContext):
|
||||
return "Open in Fitting Browser"
|
||||
|
||||
def activate(self, fullContext, selection, i):
|
||||
def activate(self, fullContext, i):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
sFit = Fit.getInstance()
|
||||
stuff = sFit.getFit(fitID).ship
|
||||
groupID = stuff.item.group.ID
|
||||
|
||||
ship = Fit.getInstance().getFit(fitID).ship
|
||||
self.mainFrame.notebookBrowsers.SetSelection(1)
|
||||
wx.PostEvent(self.mainFrame.shipBrowser, Stage3Selected(shipID=stuff.item.ID, back=groupID))
|
||||
wx.PostEvent(self.mainFrame.shipBrowser, Stage3Selected(shipID=ship.item.ID, back=True))
|
||||
|
||||
|
||||
JumpToShip.register()
|
||||
|
||||
@@ -3,17 +3,18 @@ import wx
|
||||
|
||||
import gui.fitCommands as cmd
|
||||
import gui.mainFrame
|
||||
from gui.contextMenu import ContextMenu
|
||||
from gui.contextMenu import ContextMenuUnconditional
|
||||
from service.fit import Fit
|
||||
from service.settings import ContextMenuSettings
|
||||
|
||||
|
||||
class ChangeShipTacticalMode(ContextMenu):
|
||||
class ChangeShipTacticalMode(ContextMenuUnconditional):
|
||||
|
||||
def __init__(self):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
self.settings = ContextMenuSettings.getInstance()
|
||||
|
||||
def display(self, srcContext, selection):
|
||||
def display(self, srcContext):
|
||||
if self.mainFrame.getActiveFit() is None or srcContext != "fittingShip":
|
||||
return False
|
||||
|
||||
@@ -26,18 +27,18 @@ class ChangeShipTacticalMode(ContextMenu):
|
||||
|
||||
return srcContext == "fittingShip" and self.modes is not None
|
||||
|
||||
def getText(self, itmContext, selection):
|
||||
def getText(self, itmContext):
|
||||
return "Tactical Mode"
|
||||
|
||||
def addMode(self, menu, mode):
|
||||
label = mode.item.name.rsplit()[-2]
|
||||
id = ContextMenu.nextID()
|
||||
id = ContextMenuUnconditional.nextID()
|
||||
self.modeIds[id] = mode
|
||||
menuItem = wx.MenuItem(menu, id, label, kind=wx.ITEM_RADIO)
|
||||
menu.Bind(wx.EVT_MENU, self.handleMode, menuItem)
|
||||
return menuItem
|
||||
|
||||
def getSubMenu(self, context, selection, rootMenu, i, pitem):
|
||||
def getSubMenu(self, context, rootMenu, i, pitem):
|
||||
msw = True if "wxMSW" in wx.PlatformInfo else False
|
||||
self.context = context
|
||||
self.modeIds = {}
|
||||
|
||||
@@ -5,28 +5,39 @@ import gui.globalEvents as GE
|
||||
import gui.mainFrame
|
||||
from eos.saveddata.character import Skill
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
from gui.contextMenu import ContextMenu
|
||||
from gui.contextMenu import ContextMenuSingle
|
||||
from service.character import Character
|
||||
from service.fit import Fit
|
||||
from service.settings import ContextMenuSettings
|
||||
|
||||
|
||||
class ChangeAffectingSkills(ContextMenu):
|
||||
class ChangeAffectingSkills(ContextMenuSingle):
|
||||
|
||||
def __init__(self):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
self.settings = ContextMenuSettings.getInstance()
|
||||
|
||||
def display(self, srcContext, selection):
|
||||
def display(self, srcContext, mainItem):
|
||||
if not self.settings.get('changeAffectingSkills'):
|
||||
return False
|
||||
|
||||
if self.mainFrame.getActiveFit() is None or srcContext not in (
|
||||
"fittingModule", "fittingCharge", "fittingShip", "droneItem", "fighterItem"):
|
||||
if srcContext not in (
|
||||
"fittingModule", "fittingCharge",
|
||||
"fittingShip", "droneItem",
|
||||
"fighterItem"
|
||||
):
|
||||
return False
|
||||
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
if fitID is None:
|
||||
return False
|
||||
|
||||
if (mainItem is None or getattr(mainItem, "isEmpty", False)) and srcContext != "fittingShip":
|
||||
return False
|
||||
|
||||
self.sChar = Character.getInstance()
|
||||
self.sFit = Fit.getInstance()
|
||||
fit = self.sFit.getFit(self.mainFrame.getActiveFit())
|
||||
fit = self.sFit.getFit(fitID)
|
||||
|
||||
self.charID = fit.character.ID
|
||||
|
||||
@@ -34,14 +45,13 @@ class ChangeAffectingSkills(ContextMenu):
|
||||
# return False
|
||||
|
||||
if srcContext == "fittingShip":
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
sFit = Fit.getInstance()
|
||||
self.stuff = sFit.getFit(fitID).ship
|
||||
cont = sFit.getFit(fitID).ship.itemModifiedAttributes
|
||||
elif srcContext == "fittingCharge":
|
||||
cont = selection[0].chargeModifiedAttributes
|
||||
cont = mainItem.chargeModifiedAttributes
|
||||
else:
|
||||
cont = selection[0].itemModifiedAttributes
|
||||
cont = mainItem.itemModifiedAttributes
|
||||
|
||||
skills = set()
|
||||
|
||||
@@ -60,7 +70,7 @@ class ChangeAffectingSkills(ContextMenu):
|
||||
self.skills = sorted(skills, key=lambda x: x.item.name)
|
||||
return len(self.skills) > 0
|
||||
|
||||
def getText(self, itmContext, selection):
|
||||
def getText(self, itmContext, mainItem):
|
||||
return "Change %s Skills" % itmContext
|
||||
|
||||
def addSkill(self, rootMenu, skill, i):
|
||||
@@ -69,19 +79,19 @@ class ChangeAffectingSkills(ContextMenu):
|
||||
else:
|
||||
label = "Level %s" % i
|
||||
|
||||
id = ContextMenu.nextID()
|
||||
id = ContextMenuSingle.nextID()
|
||||
self.skillIds[id] = (skill, i)
|
||||
menuItem = wx.MenuItem(rootMenu, id, label, kind=wx.ITEM_RADIO)
|
||||
rootMenu.Bind(wx.EVT_MENU, self.handleSkillChange, menuItem)
|
||||
return menuItem
|
||||
|
||||
def getSubMenu(self, context, selection, rootMenu, i, pitem):
|
||||
def getSubMenu(self, context, mainItem, rootMenu, i, pitem):
|
||||
msw = True if "wxMSW" in wx.PlatformInfo else False
|
||||
self.skillIds = {}
|
||||
sub = wx.Menu()
|
||||
|
||||
for skill in self.skills:
|
||||
skillItem = wx.MenuItem(sub, ContextMenu.nextID(), skill.item.name)
|
||||
skillItem = wx.MenuItem(sub, ContextMenuSingle.nextID(), skill.item.name)
|
||||
grandSub = wx.Menu()
|
||||
skillItem.SetSubMenu(grandSub)
|
||||
if skill.learned:
|
||||
|
||||
@@ -6,18 +6,19 @@ import wx
|
||||
import gui.globalEvents as GE
|
||||
import gui.mainFrame
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
from gui.contextMenu import ContextMenu
|
||||
from gui.contextMenu import ContextMenuUnconditional
|
||||
from service.fit import Fit
|
||||
from service.settings import ContextMenuSettings
|
||||
from service.targetResists import TargetResists as svc_TargetResists
|
||||
|
||||
|
||||
class TargetResists(ContextMenu):
|
||||
class TargetResists(ContextMenuUnconditional):
|
||||
|
||||
def __init__(self):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
self.settings = ContextMenuSettings.getInstance()
|
||||
|
||||
def display(self, srcContext, selection):
|
||||
def display(self, srcContext):
|
||||
if self.mainFrame.getActiveFit() is None or srcContext != "firepowerViewFull":
|
||||
return False
|
||||
|
||||
@@ -27,7 +28,7 @@ class TargetResists(ContextMenu):
|
||||
|
||||
return len(self.patterns) > 0
|
||||
|
||||
def getText(self, itmContext, selection):
|
||||
def getText(self, itmContext):
|
||||
return "Target Resists"
|
||||
|
||||
def handleResistSwitch(self, event):
|
||||
@@ -42,7 +43,7 @@ class TargetResists(ContextMenu):
|
||||
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
|
||||
|
||||
def addPattern(self, rootMenu, pattern):
|
||||
id = ContextMenu.nextID()
|
||||
id = ContextMenuUnconditional.nextID()
|
||||
name = getattr(pattern, "_name", pattern.name) if pattern is not None else "No Profile"
|
||||
|
||||
self.patternIds[id] = pattern
|
||||
@@ -63,7 +64,7 @@ class TargetResists(ContextMenu):
|
||||
item.SetBitmap(bitmap)
|
||||
return item
|
||||
|
||||
def getSubMenu(self, context, selection, rootMenu, i, pitem):
|
||||
def getSubMenu(self, context, rootMenu, i, pitem):
|
||||
msw = True if "wxMSW" in wx.PlatformInfo else False
|
||||
self.patternIds = {}
|
||||
self.subMenus = OrderedDict()
|
||||
@@ -92,7 +93,7 @@ class TargetResists(ContextMenu):
|
||||
# Items that have a parent
|
||||
for menuName, patterns in list(self.subMenus.items()):
|
||||
# Create parent item for root menu that is simply name of parent
|
||||
item = wx.MenuItem(rootMenu, ContextMenu.nextID(), menuName)
|
||||
item = wx.MenuItem(rootMenu, ContextMenuUnconditional.nextID(), menuName)
|
||||
|
||||
# Create menu for child items
|
||||
grandSub = wx.Menu()
|
||||
|
||||
@@ -1 +1,12 @@
|
||||
__all__ = ["fitDps"]
|
||||
__all__ = [
|
||||
'fitDpsRange',
|
||||
'fitDpsTime',
|
||||
'fitDmgTime',
|
||||
'fitShieldRegenAmount',
|
||||
'fitShieldAmountTime',
|
||||
'fitCapRegenAmount',
|
||||
'fitCapAmountTime',
|
||||
'fitSpeedTime',
|
||||
'fitDistanceTime',
|
||||
'fitWarpTimeDistance'
|
||||
]
|
||||
|
||||
81
gui/builtinGraphs/fitCapAmountTime.py
Normal file
81
gui/builtinGraphs/fitCapAmountTime.py
Normal file
@@ -0,0 +1,81 @@
|
||||
# =============================================================================
|
||||
# Copyright (C) 2010 Diego Duclos
|
||||
#
|
||||
# This file is part of pyfa.
|
||||
#
|
||||
# pyfa is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# pyfa is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with pyfa. If not, see <http://www.gnu.org/licenses/>.
|
||||
# =============================================================================
|
||||
|
||||
import gui.mainFrame
|
||||
from eos.graph import Data
|
||||
from eos.graph.fitCapAmountTime import FitCapAmountTimeGraph as EosFitCapAmountTimeGraph
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
from gui.graph import Graph
|
||||
from service.attribute import Attribute
|
||||
|
||||
|
||||
class FitCapAmountTimeGraph(Graph):
|
||||
|
||||
propertyLabelMap = {"time": "Time (seconds)"}
|
||||
|
||||
defaults = EosFitCapAmountTimeGraph.defaults.copy()
|
||||
|
||||
def __init__(self):
|
||||
Graph.__init__(self)
|
||||
self.defaults["time"] = "0-300"
|
||||
self.name = "Cap Amount vs Time"
|
||||
self.eosGraph = None
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
|
||||
def getFields(self):
|
||||
return self.defaults
|
||||
|
||||
def getLabels(self):
|
||||
return self.propertyLabelMap
|
||||
|
||||
def getIcons(self):
|
||||
iconFile = Attribute.getInstance().getAttributeInfo('duration').iconID
|
||||
bitmap = BitmapLoader.getBitmap(iconFile, "icons")
|
||||
return {"time": bitmap}
|
||||
|
||||
def getPoints(self, fit, fields):
|
||||
eosGraph = getattr(self, "eosGraph", None)
|
||||
if eosGraph is None or eosGraph.fit != fit:
|
||||
eosGraph = self.eosGraph = EosFitCapAmountTimeGraph(fit)
|
||||
|
||||
eosGraph.clearData()
|
||||
variable = None
|
||||
for fieldName, value in fields.items():
|
||||
d = Data(fieldName, value)
|
||||
if not d.isConstant():
|
||||
if variable is None:
|
||||
variable = fieldName
|
||||
else:
|
||||
# We can't handle more then one variable atm, OOPS FUCK OUT
|
||||
return False, "Can only handle 1 variable"
|
||||
|
||||
eosGraph.setData(d)
|
||||
|
||||
if variable is None:
|
||||
return False, "No variable"
|
||||
|
||||
x = []
|
||||
y = []
|
||||
for point, val in eosGraph.getIterator():
|
||||
x.append(point[variable])
|
||||
y.append(val)
|
||||
return x, y
|
||||
|
||||
|
||||
FitCapAmountTimeGraph.register()
|
||||
82
gui/builtinGraphs/fitCapRegenAmount.py
Normal file
82
gui/builtinGraphs/fitCapRegenAmount.py
Normal file
@@ -0,0 +1,82 @@
|
||||
# =============================================================================
|
||||
# Copyright (C) 2010 Diego Duclos
|
||||
#
|
||||
# This file is part of pyfa.
|
||||
#
|
||||
# pyfa is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# pyfa is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with pyfa. If not, see <http://www.gnu.org/licenses/>.
|
||||
# =============================================================================
|
||||
|
||||
import gui.mainFrame
|
||||
from eos.graph import Data
|
||||
from eos.graph.fitCapRegenAmount import FitCapRegenAmountGraph as EosFitCapRegenAmountGraph
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
from gui.graph import Graph
|
||||
from service.attribute import Attribute
|
||||
|
||||
|
||||
class FitCapRegenAmountGraph(Graph):
|
||||
|
||||
propertyLabelMap = {"percentage": "Cap Amount (percent)"}
|
||||
|
||||
defaults = EosFitCapRegenAmountGraph.defaults.copy()
|
||||
|
||||
def __init__(self):
|
||||
Graph.__init__(self)
|
||||
self.defaults["percentage"] = "0-100"
|
||||
self.name = "Cap Regen vs Cap Amount"
|
||||
self.eosGraph = None
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
|
||||
def getFields(self):
|
||||
return self.defaults
|
||||
|
||||
def getLabels(self):
|
||||
return self.propertyLabelMap
|
||||
|
||||
def getIcons(self):
|
||||
iconFile = Attribute.getInstance().getAttributeInfo('capacitorCapacity').iconID
|
||||
bitmap = BitmapLoader.getBitmap(iconFile, "icons")
|
||||
return {"percentage": bitmap}
|
||||
|
||||
def getPoints(self, fit, fields):
|
||||
eosGraph = getattr(self, "eosGraph", None)
|
||||
if eosGraph is None or eosGraph.fit != fit:
|
||||
eosGraph = self.eosGraph = EosFitCapRegenAmountGraph(fit)
|
||||
|
||||
eosGraph.clearData()
|
||||
variable = None
|
||||
for fieldName, value in fields.items():
|
||||
d = Data(fieldName, value)
|
||||
if not d.isConstant():
|
||||
if variable is None:
|
||||
variable = fieldName
|
||||
else:
|
||||
# We can't handle more then one variable atm, OOPS FUCK OUT
|
||||
return False, "Can only handle 1 variable"
|
||||
|
||||
eosGraph.setData(d)
|
||||
|
||||
if variable is None:
|
||||
return False, "No variable"
|
||||
|
||||
x = []
|
||||
y = []
|
||||
for point, val in eosGraph.getIterator():
|
||||
x.append(point[variable])
|
||||
y.append(val)
|
||||
|
||||
return x, y
|
||||
|
||||
|
||||
FitCapRegenAmountGraph.register()
|
||||
81
gui/builtinGraphs/fitDistanceTime.py
Normal file
81
gui/builtinGraphs/fitDistanceTime.py
Normal file
@@ -0,0 +1,81 @@
|
||||
# =============================================================================
|
||||
# Copyright (C) 2010 Diego Duclos
|
||||
#
|
||||
# This file is part of pyfa.
|
||||
#
|
||||
# pyfa is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# pyfa is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with pyfa. If not, see <http://www.gnu.org/licenses/>.
|
||||
# =============================================================================
|
||||
|
||||
import gui.mainFrame
|
||||
from eos.graph import Data
|
||||
from eos.graph.fitDistanceTime import FitDistanceTimeGraph as EosFitDistanceTimeGraph
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
from gui.graph import Graph
|
||||
from service.attribute import Attribute
|
||||
|
||||
|
||||
class FitDistanceTimeGraph(Graph):
|
||||
|
||||
propertyLabelMap = {"time": "Time (seconds)"}
|
||||
|
||||
defaults = EosFitDistanceTimeGraph.defaults.copy()
|
||||
|
||||
def __init__(self):
|
||||
Graph.__init__(self)
|
||||
self.defaults["time"] = "0-80"
|
||||
self.name = "Distance Travelled vs Time"
|
||||
self.eosGraph = None
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
|
||||
def getFields(self):
|
||||
return self.defaults
|
||||
|
||||
def getLabels(self):
|
||||
return self.propertyLabelMap
|
||||
|
||||
def getIcons(self):
|
||||
iconFile = Attribute.getInstance().getAttributeInfo('duration').iconID
|
||||
bitmap = BitmapLoader.getBitmap(iconFile, "icons")
|
||||
return {"time": bitmap}
|
||||
|
||||
def getPoints(self, fit, fields):
|
||||
eosGraph = getattr(self, "eosGraph", None)
|
||||
if eosGraph is None or eosGraph.fit != fit:
|
||||
eosGraph = self.eosGraph = EosFitDistanceTimeGraph(fit)
|
||||
|
||||
eosGraph.clearData()
|
||||
variable = None
|
||||
for fieldName, value in fields.items():
|
||||
d = Data(fieldName, value)
|
||||
if not d.isConstant():
|
||||
if variable is None:
|
||||
variable = fieldName
|
||||
else:
|
||||
# We can't handle more then one variable atm, OOPS FUCK OUT
|
||||
return False, "Can only handle 1 variable"
|
||||
|
||||
eosGraph.setData(d)
|
||||
|
||||
if variable is None:
|
||||
return False, "No variable"
|
||||
|
||||
x = []
|
||||
y = []
|
||||
for point, val in eosGraph.getIterator():
|
||||
x.append(point[variable])
|
||||
y.append(val)
|
||||
return x, y
|
||||
|
||||
|
||||
FitDistanceTimeGraph.register()
|
||||
82
gui/builtinGraphs/fitDmgTime.py
Normal file
82
gui/builtinGraphs/fitDmgTime.py
Normal file
@@ -0,0 +1,82 @@
|
||||
# =============================================================================
|
||||
# Copyright (C) 2010 Diego Duclos
|
||||
#
|
||||
# This file is part of pyfa.
|
||||
#
|
||||
# pyfa is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# pyfa is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with pyfa. If not, see <http://www.gnu.org/licenses/>.
|
||||
# =============================================================================
|
||||
|
||||
import gui.mainFrame
|
||||
from eos.graph import Data
|
||||
from eos.graph.fitDmgTime import FitDmgTimeGraph as EosFitDmgTimeGraph
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
from gui.graph import Graph
|
||||
from service.attribute import Attribute
|
||||
|
||||
|
||||
class FitDmgTimeGraph(Graph):
|
||||
|
||||
propertyLabelMap = {"time": "Time (seconds)"}
|
||||
|
||||
defaults = EosFitDmgTimeGraph.defaults.copy()
|
||||
|
||||
def __init__(self):
|
||||
Graph.__init__(self)
|
||||
self.defaults["time"] = "0-80"
|
||||
self.name = "Damage Inflicted vs Time"
|
||||
self.eosGraph = None
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
|
||||
def getFields(self):
|
||||
return self.defaults
|
||||
|
||||
def getLabels(self):
|
||||
return self.propertyLabelMap
|
||||
|
||||
def getIcons(self):
|
||||
iconFile = Attribute.getInstance().getAttributeInfo('duration').iconID
|
||||
bitmap = BitmapLoader.getBitmap(iconFile, "icons")
|
||||
return {"time": bitmap}
|
||||
|
||||
def getPoints(self, fit, fields):
|
||||
eosGraph = getattr(self, "eosGraph", None)
|
||||
if eosGraph is None or eosGraph.fit != fit:
|
||||
eosGraph = self.eosGraph = EosFitDmgTimeGraph(fit)
|
||||
|
||||
eosGraph.clearData()
|
||||
variable = None
|
||||
for fieldName, value in fields.items():
|
||||
d = Data(fieldName, value)
|
||||
if not d.isConstant():
|
||||
if variable is None:
|
||||
variable = fieldName
|
||||
else:
|
||||
# We can't handle more then one variable atm, OOPS FUCK OUT
|
||||
return False, "Can only handle 1 variable"
|
||||
|
||||
eosGraph.setData(d)
|
||||
|
||||
if variable is None:
|
||||
return False, "No variable"
|
||||
|
||||
x = []
|
||||
y = []
|
||||
eosGraph.recalc()
|
||||
for point, val in eosGraph.getIterator():
|
||||
x.append(point[variable])
|
||||
y.append(val)
|
||||
return x, y
|
||||
|
||||
|
||||
FitDmgTimeGraph.register()
|
||||
@@ -17,15 +17,16 @@
|
||||
# along with pyfa. If not, see <http://www.gnu.org/licenses/>.
|
||||
# =============================================================================
|
||||
|
||||
from gui.graph import Graph
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
from eos.graph.fitDps import FitDpsGraph as FitDps
|
||||
from eos.graph import Data
|
||||
import gui.mainFrame
|
||||
from eos.graph import Data
|
||||
from eos.graph.fitDpsRange import FitDpsRangeGraph as EosFitDpsRangeGraph
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
from gui.graph import Graph
|
||||
from service.attribute import Attribute
|
||||
|
||||
|
||||
class FitDpsGraph(Graph):
|
||||
class FitDpsRangeGraph(Graph):
|
||||
|
||||
propertyAttributeMap = {"angle": "maxVelocity",
|
||||
"distance": "maxRange",
|
||||
"signatureRadius": "signatureRadius",
|
||||
@@ -36,13 +37,13 @@ class FitDpsGraph(Graph):
|
||||
"signatureRadius": "Target Signature Radius (m)",
|
||||
"velocity": "Target Velocity (m/s)"}
|
||||
|
||||
defaults = FitDps.defaults.copy()
|
||||
defaults = EosFitDpsRangeGraph.defaults.copy()
|
||||
|
||||
def __init__(self):
|
||||
Graph.__init__(self)
|
||||
self.defaults["distance"] = "0-100"
|
||||
self.name = "DPS"
|
||||
self.fitDps = None
|
||||
self.name = "DPS vs Range"
|
||||
self.eosGraph = None
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
|
||||
def getFields(self):
|
||||
@@ -63,11 +64,11 @@ class FitDpsGraph(Graph):
|
||||
return icons
|
||||
|
||||
def getPoints(self, fit, fields):
|
||||
fitDps = getattr(self, "fitDps", None)
|
||||
if fitDps is None or fitDps.fit != fit:
|
||||
fitDps = self.fitDps = FitDps(fit)
|
||||
eosGraph = getattr(self, "eosGraph", None)
|
||||
if eosGraph is None or eosGraph.fit != fit:
|
||||
eosGraph = self.eosGraph = EosFitDpsRangeGraph(fit)
|
||||
|
||||
fitDps.clearData()
|
||||
eosGraph.clearData()
|
||||
variable = None
|
||||
for fieldName, value in fields.items():
|
||||
d = Data(fieldName, value)
|
||||
@@ -78,18 +79,18 @@ class FitDpsGraph(Graph):
|
||||
# We can't handle more then one variable atm, OOPS FUCK OUT
|
||||
return False, "Can only handle 1 variable"
|
||||
|
||||
fitDps.setData(d)
|
||||
eosGraph.setData(d)
|
||||
|
||||
if variable is None:
|
||||
return False, "No variable"
|
||||
|
||||
x = []
|
||||
y = []
|
||||
for point, val in fitDps.getIterator():
|
||||
for point, val in eosGraph.getIterator():
|
||||
x.append(point[variable])
|
||||
y.append(val)
|
||||
|
||||
return x, y
|
||||
|
||||
|
||||
FitDpsGraph.register()
|
||||
FitDpsRangeGraph.register()
|
||||
83
gui/builtinGraphs/fitDpsTime.py
Normal file
83
gui/builtinGraphs/fitDpsTime.py
Normal file
@@ -0,0 +1,83 @@
|
||||
# =============================================================================
|
||||
# Copyright (C) 2010 Diego Duclos
|
||||
#
|
||||
# This file is part of pyfa.
|
||||
#
|
||||
# pyfa is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# pyfa is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with pyfa. If not, see <http://www.gnu.org/licenses/>.
|
||||
# =============================================================================
|
||||
|
||||
import gui.mainFrame
|
||||
from eos.graph import Data
|
||||
from eos.graph.fitDpsTime import FitDpsTimeGraph as EosFitDpsTimeGraph
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
from gui.graph import Graph
|
||||
from service.attribute import Attribute
|
||||
|
||||
|
||||
class FitDpsTimeGraph(Graph):
|
||||
|
||||
propertyLabelMap = {"time": "Time (seconds)"}
|
||||
|
||||
defaults = EosFitDpsTimeGraph.defaults.copy()
|
||||
|
||||
def __init__(self):
|
||||
Graph.__init__(self)
|
||||
self.defaults["time"] = "0-80"
|
||||
self.name = "DPS vs Time"
|
||||
self.eosGraph = None
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
|
||||
def getFields(self):
|
||||
return self.defaults
|
||||
|
||||
def getLabels(self):
|
||||
return self.propertyLabelMap
|
||||
|
||||
def getIcons(self):
|
||||
iconFile = Attribute.getInstance().getAttributeInfo('duration').iconID
|
||||
bitmap = BitmapLoader.getBitmap(iconFile, "icons")
|
||||
return {"time": bitmap}
|
||||
|
||||
def getPoints(self, fit, fields):
|
||||
eosGraph = getattr(self, "eosGraph", None)
|
||||
if eosGraph is None or eosGraph.fit != fit:
|
||||
eosGraph = self.eosGraph = EosFitDpsTimeGraph(fit)
|
||||
|
||||
eosGraph.clearData()
|
||||
variable = None
|
||||
for fieldName, value in fields.items():
|
||||
d = Data(fieldName, value)
|
||||
if not d.isConstant():
|
||||
if variable is None:
|
||||
variable = fieldName
|
||||
else:
|
||||
# We can't handle more then one variable atm, OOPS FUCK OUT
|
||||
return False, "Can only handle 1 variable"
|
||||
|
||||
eosGraph.setData(d)
|
||||
|
||||
if variable is None:
|
||||
return False, "No variable"
|
||||
|
||||
x = []
|
||||
y = []
|
||||
eosGraph.recalc()
|
||||
for point, val in eosGraph.getIterator():
|
||||
x.append(point[variable])
|
||||
y.append(val)
|
||||
|
||||
return x, y
|
||||
|
||||
|
||||
FitDpsTimeGraph.register()
|
||||
85
gui/builtinGraphs/fitShieldAmountTime.py
Normal file
85
gui/builtinGraphs/fitShieldAmountTime.py
Normal file
@@ -0,0 +1,85 @@
|
||||
# =============================================================================
|
||||
# Copyright (C) 2010 Diego Duclos
|
||||
#
|
||||
# This file is part of pyfa.
|
||||
#
|
||||
# pyfa is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# pyfa is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with pyfa. If not, see <http://www.gnu.org/licenses/>.
|
||||
# =============================================================================
|
||||
|
||||
import gui.mainFrame
|
||||
from eos.graph import Data
|
||||
from eos.graph.fitShieldAmountTime import FitShieldAmountTimeGraph as EosFitShieldAmountTimeGraph
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
from gui.graph import Graph
|
||||
from service.attribute import Attribute
|
||||
|
||||
|
||||
class FitShieldAmountTimeGraph(Graph):
|
||||
|
||||
propertyLabelMap = {"time": "Time (seconds)"}
|
||||
|
||||
defaults = EosFitShieldAmountTimeGraph.defaults.copy()
|
||||
|
||||
def __init__(self):
|
||||
Graph.__init__(self)
|
||||
self.defaults["time"] = "0-300"
|
||||
self.name = "Shield Amount vs Time"
|
||||
self.eosGraph = None
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
|
||||
def getFields(self):
|
||||
return self.defaults
|
||||
|
||||
def getLabels(self):
|
||||
return self.propertyLabelMap
|
||||
|
||||
def getIcons(self):
|
||||
iconFile = Attribute.getInstance().getAttributeInfo('duration').iconID
|
||||
bitmap = BitmapLoader.getBitmap(iconFile, "icons")
|
||||
return {"time": bitmap}
|
||||
|
||||
def getPoints(self, fit, fields):
|
||||
eosGraph = getattr(self, "eosGraph", None)
|
||||
if eosGraph is None or eosGraph.fit != fit:
|
||||
eosGraph = self.eosGraph = EosFitShieldAmountTimeGraph(fit)
|
||||
|
||||
eosGraph.clearData()
|
||||
variable = None
|
||||
for fieldName, value in fields.items():
|
||||
d = Data(fieldName, value)
|
||||
if not d.isConstant():
|
||||
if variable is None:
|
||||
variable = fieldName
|
||||
else:
|
||||
# We can't handle more then one variable atm, OOPS FUCK OUT
|
||||
return False, "Can only handle 1 variable"
|
||||
|
||||
eosGraph.setData(d)
|
||||
|
||||
if variable is None:
|
||||
return False, "No variable"
|
||||
|
||||
x = []
|
||||
y = []
|
||||
for point, val in eosGraph.getIterator():
|
||||
x.append(point[variable])
|
||||
y.append(val)
|
||||
return x, y
|
||||
|
||||
@property
|
||||
def redrawOnEffectiveChange(self):
|
||||
return True
|
||||
|
||||
|
||||
FitShieldAmountTimeGraph.register()
|
||||
86
gui/builtinGraphs/fitShieldRegenAmount.py
Normal file
86
gui/builtinGraphs/fitShieldRegenAmount.py
Normal file
@@ -0,0 +1,86 @@
|
||||
# =============================================================================
|
||||
# Copyright (C) 2010 Diego Duclos
|
||||
#
|
||||
# This file is part of pyfa.
|
||||
#
|
||||
# pyfa is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# pyfa is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with pyfa. If not, see <http://www.gnu.org/licenses/>.
|
||||
# =============================================================================
|
||||
|
||||
import gui.mainFrame
|
||||
from eos.graph import Data
|
||||
from eos.graph.fitShieldRegenAmount import FitShieldRegenAmountGraph as EosFitShieldRegenAmountGraph
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
from gui.graph import Graph
|
||||
from service.attribute import Attribute
|
||||
|
||||
|
||||
class FitShieldRegenAmountGraph(Graph):
|
||||
|
||||
propertyLabelMap = {"percentage": "Shield Capacity (percent)"}
|
||||
|
||||
defaults = EosFitShieldRegenAmountGraph.defaults.copy()
|
||||
|
||||
def __init__(self):
|
||||
Graph.__init__(self)
|
||||
self.defaults["percentage"] = "0-100"
|
||||
self.name = "Shield Regen vs Shield Amount"
|
||||
self.eosGraph = None
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
|
||||
def getFields(self):
|
||||
return self.defaults
|
||||
|
||||
def getLabels(self):
|
||||
return self.propertyLabelMap
|
||||
|
||||
def getIcons(self):
|
||||
iconFile = Attribute.getInstance().getAttributeInfo('shieldCapacity').iconID
|
||||
bitmap = BitmapLoader.getBitmap(iconFile, "icons")
|
||||
return {"percentage": bitmap}
|
||||
|
||||
def getPoints(self, fit, fields):
|
||||
eosGraph = getattr(self, "eosGraph", None)
|
||||
if eosGraph is None or eosGraph.fit != fit:
|
||||
eosGraph = self.eosGraph = EosFitShieldRegenAmountGraph(fit)
|
||||
|
||||
eosGraph.clearData()
|
||||
variable = None
|
||||
for fieldName, value in fields.items():
|
||||
d = Data(fieldName, value)
|
||||
if not d.isConstant():
|
||||
if variable is None:
|
||||
variable = fieldName
|
||||
else:
|
||||
# We can't handle more then one variable atm, OOPS FUCK OUT
|
||||
return False, "Can only handle 1 variable"
|
||||
|
||||
eosGraph.setData(d)
|
||||
|
||||
if variable is None:
|
||||
return False, "No variable"
|
||||
|
||||
x = []
|
||||
y = []
|
||||
for point, val in eosGraph.getIterator():
|
||||
x.append(point[variable])
|
||||
y.append(val)
|
||||
|
||||
return x, y
|
||||
|
||||
@property
|
||||
def redrawOnEffectiveChange(self):
|
||||
return True
|
||||
|
||||
|
||||
FitShieldRegenAmountGraph.register()
|
||||
81
gui/builtinGraphs/fitSpeedTime.py
Normal file
81
gui/builtinGraphs/fitSpeedTime.py
Normal file
@@ -0,0 +1,81 @@
|
||||
# =============================================================================
|
||||
# Copyright (C) 2010 Diego Duclos
|
||||
#
|
||||
# This file is part of pyfa.
|
||||
#
|
||||
# pyfa is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# pyfa is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with pyfa. If not, see <http://www.gnu.org/licenses/>.
|
||||
# =============================================================================
|
||||
|
||||
import gui.mainFrame
|
||||
from eos.graph import Data
|
||||
from eos.graph.fitSpeedTime import FitSpeedTimeGraph as EosFitSpeedTimeGraph
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
from gui.graph import Graph
|
||||
from service.attribute import Attribute
|
||||
|
||||
|
||||
class FitSpeedTimeGraph(Graph):
|
||||
|
||||
propertyLabelMap = {"time": "Time (seconds)"}
|
||||
|
||||
defaults = EosFitSpeedTimeGraph.defaults.copy()
|
||||
|
||||
def __init__(self):
|
||||
Graph.__init__(self)
|
||||
self.defaults["time"] = "0-80"
|
||||
self.name = "Speed vs Time"
|
||||
self.eosGraph = None
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
|
||||
def getFields(self):
|
||||
return self.defaults
|
||||
|
||||
def getLabels(self):
|
||||
return self.propertyLabelMap
|
||||
|
||||
def getIcons(self):
|
||||
iconFile = Attribute.getInstance().getAttributeInfo('duration').iconID
|
||||
bitmap = BitmapLoader.getBitmap(iconFile, "icons")
|
||||
return {"time": bitmap}
|
||||
|
||||
def getPoints(self, fit, fields):
|
||||
eosGraph = getattr(self, "eosGraph", None)
|
||||
if eosGraph is None or eosGraph.fit != fit:
|
||||
eosGraph = self.eosGraph = EosFitSpeedTimeGraph(fit)
|
||||
|
||||
eosGraph.clearData()
|
||||
variable = None
|
||||
for fieldName, value in fields.items():
|
||||
d = Data(fieldName, value)
|
||||
if not d.isConstant():
|
||||
if variable is None:
|
||||
variable = fieldName
|
||||
else:
|
||||
# We can't handle more then one variable atm, OOPS FUCK OUT
|
||||
return False, "Can only handle 1 variable"
|
||||
|
||||
eosGraph.setData(d)
|
||||
|
||||
if variable is None:
|
||||
return False, "No variable"
|
||||
|
||||
x = []
|
||||
y = []
|
||||
for point, val in eosGraph.getIterator():
|
||||
x.append(point[variable])
|
||||
y.append(val)
|
||||
return x, y
|
||||
|
||||
|
||||
FitSpeedTimeGraph.register()
|
||||
82
gui/builtinGraphs/fitWarpTimeDistance.py
Normal file
82
gui/builtinGraphs/fitWarpTimeDistance.py
Normal file
@@ -0,0 +1,82 @@
|
||||
# =============================================================================
|
||||
# Copyright (C) 2010 Diego Duclos
|
||||
#
|
||||
# This file is part of pyfa.
|
||||
#
|
||||
# pyfa is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# pyfa is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with pyfa. If not, see <http://www.gnu.org/licenses/>.
|
||||
# =============================================================================
|
||||
|
||||
import gui.mainFrame
|
||||
from eos.graph import Data
|
||||
from eos.graph.fitWarpTimeDistance import FitWarpTimeDistanceGraph as EosFitWarpTimeDistanceGraph
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
from gui.graph import Graph
|
||||
from service.attribute import Attribute
|
||||
|
||||
|
||||
class FitWarpTimeDistanceGraph(Graph):
|
||||
|
||||
propertyLabelMap = {"distance": "Distance (AU)"}
|
||||
|
||||
defaults = EosFitWarpTimeDistanceGraph.defaults.copy()
|
||||
|
||||
def __init__(self):
|
||||
Graph.__init__(self)
|
||||
self.defaults["distance"] = "0-50"
|
||||
self.name = "Warp Time vs Distance"
|
||||
self.eosGraph = None
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
|
||||
def getFields(self):
|
||||
return self.defaults
|
||||
|
||||
def getLabels(self):
|
||||
return self.propertyLabelMap
|
||||
|
||||
def getIcons(self):
|
||||
iconFile = Attribute.getInstance().getAttributeInfo('maxRange').iconID
|
||||
bitmap = BitmapLoader.getBitmap(iconFile, "icons")
|
||||
return {"distance": bitmap}
|
||||
|
||||
def getPoints(self, fit, fields):
|
||||
eosGraph = getattr(self, "eosGraph", None)
|
||||
if eosGraph is None or eosGraph.fit != fit:
|
||||
eosGraph = self.eosGraph = EosFitWarpTimeDistanceGraph(fit)
|
||||
|
||||
eosGraph.clearData()
|
||||
variable = None
|
||||
for fieldName, value in fields.items():
|
||||
d = Data(fieldName, value)
|
||||
if not d.isConstant():
|
||||
if variable is None:
|
||||
variable = fieldName
|
||||
else:
|
||||
# We can't handle more then one variable atm, OOPS FUCK OUT
|
||||
return False, "Can only handle 1 variable"
|
||||
|
||||
eosGraph.setData(d)
|
||||
|
||||
if variable is None:
|
||||
return False, "No variable"
|
||||
|
||||
x = []
|
||||
y = []
|
||||
for point, val in eosGraph.getIterator():
|
||||
if val is not None:
|
||||
x.append(point[variable])
|
||||
y.append(val)
|
||||
return x, y
|
||||
|
||||
|
||||
FitWarpTimeDistanceGraph.register()
|
||||
@@ -96,8 +96,10 @@ class AttributeSlider(wx.Panel):
|
||||
self.ctrl = wx.SpinCtrlDouble(self, min=minValue, max=maxValue, inc=getStep(maxValue - minValue))
|
||||
self.ctrl.SetDigits(getDigitPlaces(minValue, maxValue))
|
||||
|
||||
|
||||
self.ctrl.Bind(wx.EVT_SPINCTRLDOUBLE, self.UpdateValue)
|
||||
# GTK scrolls spinboxes with mousewheel, others do not
|
||||
if "wxGTK" not in wx.PlatformInfo:
|
||||
self.ctrl.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
|
||||
|
||||
self.slider = AttributeGauge(self, size=(-1, 8))
|
||||
|
||||
@@ -124,6 +126,16 @@ class AttributeSlider(wx.Panel):
|
||||
if post_event:
|
||||
wx.PostEvent(self, ValueChanged(self, None, value, None, slider_percentage))
|
||||
|
||||
def OnMouseWheel(self, evt):
|
||||
if evt.GetWheelRotation() > 0 and evt.GetWheelAxis() == wx.MOUSE_WHEEL_VERTICAL:
|
||||
self.ctrl.Value = self.ctrl.Value + self.ctrl.Increment
|
||||
self.SetValue(self.ctrl.GetValue())
|
||||
elif evt.GetWheelRotation() < 0 and evt.GetWheelAxis() == wx.MOUSE_WHEEL_VERTICAL:
|
||||
self.ctrl.Value = self.ctrl.Value - self.ctrl.Increment
|
||||
self.SetValue(self.ctrl.GetValue())
|
||||
else:
|
||||
evt.Skip()
|
||||
|
||||
|
||||
class TestAttributeSlider(wx.Frame):
|
||||
|
||||
|
||||
@@ -82,7 +82,8 @@ class ItemAffectedBy(wx.Panel):
|
||||
# instead, we send the item.
|
||||
type_ = stuff.__class__.__name__
|
||||
contexts.append(("itemStats", type_))
|
||||
menu = ContextMenu.getMenu(stuff if type_ != "Skill" else stuff.item, *contexts)
|
||||
stuff = stuff if type_ != "Skill" else stuff.item
|
||||
menu = ContextMenu.getMenu(stuff, (stuff,), *contexts)
|
||||
self.PopupMenu(menu)
|
||||
|
||||
def ExpandCollapseTree(self):
|
||||
|
||||
@@ -4,6 +4,7 @@ import random
|
||||
import wx
|
||||
from logbook import Logger
|
||||
|
||||
import gui.fitCommands as cmd
|
||||
import gui.globalEvents as GE
|
||||
import gui.mainFrame
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
@@ -11,44 +12,91 @@ from service.fit import Fit
|
||||
from .attributeSlider import AttributeSlider, EVT_VALUE_CHANGED
|
||||
from .itemAttributes import ItemParams
|
||||
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
|
||||
class ItemMutator(wx.Panel):
|
||||
class ItemMutatorPanel(wx.Panel):
|
||||
|
||||
def __init__(self, parent, stuff, item):
|
||||
def __init__(self, parent, mod):
|
||||
wx.Panel.__init__(self, parent)
|
||||
self.stuff = stuff
|
||||
self.item = item
|
||||
self.timer = None
|
||||
self.activeFit = gui.mainFrame.MainFrame.getInstance().getActiveFit()
|
||||
|
||||
font = parent.GetFont()
|
||||
font.SetWeight(wx.BOLD)
|
||||
self.stuff = mod
|
||||
|
||||
mainSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
|
||||
sourceItemsSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
sourceItemsSizer.Add(BitmapLoader.getStaticBitmap(stuff.item.iconID, self, "icons"), 0, wx.LEFT, 5)
|
||||
sourceItemsSizer.Add(BitmapLoader.getStaticBitmap(stuff.mutaplasmid.item.iconID, self, "icons"), 0, wx.LEFT, 0)
|
||||
sourceItemShort = "{} {}".format(stuff.mutaplasmid.item.name.split(" ")[0], stuff.baseItem.name)
|
||||
headerSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
headerSizer.AddStretchSpacer()
|
||||
itemIcon = BitmapLoader.getStaticBitmap(mod.item.iconID, self, "icons")
|
||||
if itemIcon is not None:
|
||||
headerSizer.Add(itemIcon, 0, 0, 0)
|
||||
mutaIcon = BitmapLoader.getStaticBitmap(mod.mutaplasmid.item.iconID, self, "icons")
|
||||
if mutaIcon is not None:
|
||||
headerSizer.Add(mutaIcon, 0, wx.LEFT, 0)
|
||||
sourceItemShort = "{} {}".format(mod.mutaplasmid.item.name.split(" ")[0], mod.baseItem.name)
|
||||
sourceItemText = wx.StaticText(self, wx.ID_ANY, sourceItemShort)
|
||||
font = parent.GetFont()
|
||||
font.SetWeight(wx.BOLD)
|
||||
sourceItemText.SetFont(font)
|
||||
sourceItemsSizer.Add(sourceItemText, 0, wx.LEFT, 10)
|
||||
mainSizer.Add(sourceItemsSizer, 0, wx.TOP | wx.EXPAND, 10)
|
||||
headerSizer.Add(sourceItemText, 0, wx.LEFT, 10)
|
||||
headerSizer.AddStretchSpacer()
|
||||
mainSizer.Add(headerSizer, 0, wx.ALL | wx.EXPAND, 5)
|
||||
mainSizer.Add(wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, wx.EXPAND, 0)
|
||||
|
||||
mainSizer.Add(wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, wx.ALL | wx.EXPAND, 5)
|
||||
self.mutaList = ItemMutatorList(self, mod)
|
||||
mainSizer.Add(self.mutaList, 1, wx.EXPAND | wx.ALL, 0)
|
||||
|
||||
self.goodColor = wx.Colour(96, 191, 0)
|
||||
self.badColor = wx.Colour(255, 64, 0)
|
||||
mainSizer.Add(wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, wx.EXPAND, 0)
|
||||
footerSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
self.refreshBtn = wx.Button(self, wx.ID_ANY, "Reset defaults", wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
footerSizer.Add(self.refreshBtn, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5)
|
||||
self.refreshBtn.Bind(wx.EVT_BUTTON, self.mutaList.resetMutatedValues)
|
||||
self.randomBtn = wx.Button(self, wx.ID_ANY, "Random stats", wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
footerSizer.Add(self.randomBtn, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5)
|
||||
self.randomBtn.Bind(wx.EVT_BUTTON, self.mutaList.randomMutatedValues)
|
||||
self.revertBtn = wx.Button(self, wx.ID_ANY, "Revert changes", wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
footerSizer.Add(self.revertBtn, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5)
|
||||
self.revertBtn.Bind(wx.EVT_BUTTON, self.mutaList.revertChanges)
|
||||
mainSizer.Add(footerSizer, 0, wx.ALL | wx.EXPAND, 5)
|
||||
|
||||
self.SetSizer(mainSizer)
|
||||
self.Layout()
|
||||
|
||||
def submitMutationChanges(self):
|
||||
self.mutaList.submitMutationChanges()
|
||||
|
||||
|
||||
class ItemMutatorList(wx.ScrolledWindow):
|
||||
|
||||
def __init__(self, parent, mod):
|
||||
wx.ScrolledWindow.__init__(self, parent)
|
||||
self.SetScrollRate(0, 15)
|
||||
self.carryingFitID = gui.mainFrame.MainFrame.getInstance().getActiveFit()
|
||||
self.initialMutations = {}
|
||||
self.mod = mod
|
||||
self.timer = None
|
||||
|
||||
goodColor = wx.Colour(96, 191, 0)
|
||||
badColor = wx.Colour(255, 64, 0)
|
||||
font = parent.GetFont()
|
||||
font.SetWeight(wx.BOLD)
|
||||
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
|
||||
self.event_mapping = {}
|
||||
higOverrides = {
|
||||
('Stasis Web', 'speedFactor'): False,
|
||||
('Damage Control', 'duration'): True,
|
||||
}
|
||||
|
||||
for m in sorted(stuff.mutators.values(), key=lambda x: x.attribute.displayName):
|
||||
highIsGood = higOverrides.get((stuff.item.group.name, m.attribute.name), m.highIsGood)
|
||||
first = True
|
||||
for m in sorted(mod.mutators.values(), key=lambda x: x.attribute.displayName):
|
||||
if not first:
|
||||
sizer.Add(wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, wx.ALL | wx.EXPAND, 5)
|
||||
first = False
|
||||
|
||||
self.initialMutations[m.attrID] = m.value
|
||||
|
||||
highIsGood = higOverrides.get((mod.item.group.name, m.attribute.name), m.highIsGood)
|
||||
# Format: [raw value, modifier applied to base raw value, display value]
|
||||
range1 = (m.minValue, m.attribute.unit.SimplifyValue(m.minValue))
|
||||
range2 = (m.maxValue, m.attribute.unit.SimplifyValue(m.maxValue))
|
||||
@@ -96,17 +144,17 @@ class ItemMutator(wx.Panel):
|
||||
|
||||
worseVal = ItemParams.FormatValue(*m.attribute.unit.PreformatValue(worseRange[0]), rounding='dec')
|
||||
worseText = wx.StaticText(self, wx.ID_ANY, worseVal)
|
||||
worseText.SetForegroundColour(self.badColor)
|
||||
worseText.SetForegroundColour(badColor)
|
||||
|
||||
betterVal = ItemParams.FormatValue(*m.attribute.unit.PreformatValue(betterRange[0]), rounding='dec')
|
||||
betterText = wx.StaticText(self, wx.ID_ANY, betterVal)
|
||||
betterText.SetForegroundColour(self.goodColor)
|
||||
betterText.SetForegroundColour(goodColor)
|
||||
|
||||
headingSizer.Add(worseText, 0, wx.ALL | wx.EXPAND, 0)
|
||||
headingSizer.Add(wx.StaticText(self, wx.ID_ANY, " ─ "), 0, wx.RIGHT | wx.LEFT | wx.EXPAND, 5)
|
||||
headingSizer.Add(betterText, 0, wx.RIGHT | wx.EXPAND, 10)
|
||||
|
||||
mainSizer.Add(headingSizer, 0, wx.ALL | wx.EXPAND, 5)
|
||||
sizer.Add(headingSizer, 0, wx.ALL | wx.EXPAND, 5)
|
||||
|
||||
slider = AttributeSlider(parent=self,
|
||||
baseValue=m.attribute.unit.SimplifyValue(sliderBaseValue),
|
||||
@@ -116,29 +164,9 @@ class ItemMutator(wx.Panel):
|
||||
slider.SetValue(m.attribute.unit.SimplifyValue(m.value), False)
|
||||
slider.Bind(EVT_VALUE_CHANGED, self.changeMutatedValue)
|
||||
self.event_mapping[slider] = m
|
||||
mainSizer.Add(slider, 0, wx.RIGHT | wx.LEFT | wx.EXPAND, 10)
|
||||
sizer.Add(slider, 0, wx.RIGHT | wx.LEFT | wx.EXPAND, 10)
|
||||
|
||||
mainSizer.Add(wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, wx.ALL | wx.EXPAND, 5)
|
||||
|
||||
mainSizer.AddStretchSpacer()
|
||||
|
||||
self.m_staticline = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL)
|
||||
mainSizer.Add(self.m_staticline, 0, wx.EXPAND)
|
||||
|
||||
bSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
|
||||
self.refreshBtn = wx.Button(self, wx.ID_ANY, "Reset defaults", wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
bSizer.Add(self.refreshBtn, 0, wx.ALIGN_CENTER_VERTICAL)
|
||||
self.refreshBtn.Bind(wx.EVT_BUTTON, self.resetMutatedValues)
|
||||
|
||||
self.randomBtn = wx.Button(self, wx.ID_ANY, "Random stats", wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
bSizer.Add(self.randomBtn, 0, wx.ALIGN_CENTER_VERTICAL)
|
||||
self.randomBtn.Bind(wx.EVT_BUTTON, self.randomMutatedValues)
|
||||
|
||||
mainSizer.Add(bSizer, 0, wx.RIGHT | wx.LEFT | wx.EXPAND, 0)
|
||||
|
||||
self.SetSizer(mainSizer)
|
||||
self.Layout()
|
||||
self.SetSizer(sizer)
|
||||
|
||||
def changeMutatedValue(self, evt):
|
||||
m = self.event_mapping[evt.Object]
|
||||
@@ -146,12 +174,12 @@ class ItemMutator(wx.Panel):
|
||||
value = m.attribute.unit.ComplicateValue(value)
|
||||
sFit = Fit.getInstance()
|
||||
|
||||
sFit.changeMutatedValue(m, value)
|
||||
sFit.changeMutatedValuePrelim(m, value)
|
||||
if self.timer:
|
||||
self.timer.Stop()
|
||||
self.timer = None
|
||||
|
||||
for x in self.Parent.Children:
|
||||
for x in self.Parent.Parent.Children:
|
||||
if isinstance(x, ItemParams):
|
||||
x.RefreshValues(None)
|
||||
break
|
||||
@@ -159,36 +187,54 @@ class ItemMutator(wx.Panel):
|
||||
|
||||
def resetMutatedValues(self, evt):
|
||||
sFit = Fit.getInstance()
|
||||
|
||||
for slider, m in self.event_mapping.items():
|
||||
value = sFit.changeMutatedValue(m, m.baseValue)
|
||||
value = sFit.changeMutatedValuePrelim(m, m.baseValue)
|
||||
value = m.attribute.unit.SimplifyValue(value)
|
||||
slider.SetValue(value)
|
||||
|
||||
evt.Skip()
|
||||
|
||||
def randomMutatedValues(self, evt):
|
||||
sFit = Fit.getInstance()
|
||||
|
||||
for slider, m in self.event_mapping.items():
|
||||
value = random.uniform(m.minValue, m.maxValue)
|
||||
value = sFit.changeMutatedValue(m, value)
|
||||
value = sFit.changeMutatedValuePrelim(m, value)
|
||||
value = m.attribute.unit.SimplifyValue(value)
|
||||
slider.SetValue(value)
|
||||
|
||||
evt.Skip()
|
||||
|
||||
def revertChanges(self, evt):
|
||||
sFit = Fit.getInstance()
|
||||
for slider, m in self.event_mapping.items():
|
||||
if m.attrID in self.initialMutations:
|
||||
value = sFit.changeMutatedValuePrelim(m, self.initialMutations[m.attrID])
|
||||
value = m.attribute.unit.SimplifyValue(value)
|
||||
slider.SetValue(value)
|
||||
evt.Skip()
|
||||
|
||||
def submitMutationChanges(self):
|
||||
fit = Fit.getInstance().getFit(self.carryingFitID)
|
||||
if self.mod in fit.modules:
|
||||
currentMutation = {}
|
||||
for m in self.event_mapping.values():
|
||||
currentMutation[m.attrID] = m.value
|
||||
mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
mainFrame.getCommandForFit(self.carryingFitID).Submit(cmd.GuiChangeLocalModuleMutationCommand(
|
||||
fitID=self.carryingFitID,
|
||||
position=fit.modules.index(self.mod),
|
||||
mutation=currentMutation,
|
||||
oldMutation=self.initialMutations))
|
||||
|
||||
def callLater(self):
|
||||
self.timer = None
|
||||
sFit = Fit.getInstance()
|
||||
|
||||
# recalc the fit that this module affects. This is not necessarily the currently active fit
|
||||
sFit.refreshFit(self.activeFit)
|
||||
sFit.refreshFit(self.carryingFitID)
|
||||
|
||||
mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
activeFit = mainFrame.getActiveFit()
|
||||
|
||||
if activeFit != self.activeFit:
|
||||
if activeFit != self.carryingFitID:
|
||||
# if we're no longer on the fit this module is affecting, simulate a "switch fit" so that the active fit
|
||||
# can be recalculated (if needed)
|
||||
sFit.switchFit(activeFit)
|
||||
|
||||
@@ -21,7 +21,7 @@ class ItemView(Display):
|
||||
"attr:cpu,,,True"]
|
||||
|
||||
def __init__(self, parent, marketBrowser):
|
||||
Display.__init__(self, parent)
|
||||
Display.__init__(self, parent, style=wx.LC_SINGLE_SEL)
|
||||
pyfalog.debug("Initialize ItemView")
|
||||
marketBrowser.Bind(wx.EVT_TREE_SEL_CHANGED, self.treeSelectionChanged)
|
||||
|
||||
@@ -51,6 +51,7 @@ class ItemView(Display):
|
||||
|
||||
# Make reverse map, used by sorter
|
||||
self.metaMap = self.makeReverseMetaMap()
|
||||
self.active = []
|
||||
|
||||
# Fill up recently used modules set
|
||||
pyfalog.debug("Fill up recently used modules set")
|
||||
@@ -247,7 +248,7 @@ class ItemView(Display):
|
||||
sourceContext = "marketItemMisc" if self.marketBrowser.mode in ("search", "recent") else "marketItemGroup"
|
||||
itemContext = sMkt.getCategoryByItem(item).name
|
||||
|
||||
menu = ContextMenu.getMenu((item,), (sourceContext, itemContext))
|
||||
menu = ContextMenu.getMenu(item, (item,), (sourceContext, itemContext))
|
||||
self.PopupMenu(menu)
|
||||
|
||||
def populate(self, items):
|
||||
@@ -258,7 +259,7 @@ class ItemView(Display):
|
||||
sMkt = self.sMkt
|
||||
self.metalvls = sMkt.directAttrRequest(items, attrs)
|
||||
# Clear selection
|
||||
self.deselectItems()
|
||||
self.unselectAll()
|
||||
# Perform sorting, using item's meta levels besides other stuff
|
||||
items.sort(key=self.itemSort)
|
||||
# Mark current item list as active
|
||||
|
||||
@@ -37,6 +37,7 @@ class MarketTree(wx.TreeCtrl):
|
||||
|
||||
# Bind our lookup method to when the tree gets expanded
|
||||
self.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.expandLookup)
|
||||
self.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.OnCollapsed)
|
||||
|
||||
def addImage(self, iconFile, location="icons"):
|
||||
if iconFile is None:
|
||||
@@ -71,6 +72,10 @@ class MarketTree(wx.TreeCtrl):
|
||||
|
||||
self.SortChildren(root)
|
||||
|
||||
def OnCollapsed(self, event):
|
||||
self.CollapseAllChildren(event.Item)
|
||||
event.Skip()
|
||||
|
||||
def jump(self, item):
|
||||
"""Open market group and meta tab of given item"""
|
||||
sMkt = self.sMkt
|
||||
|
||||
@@ -80,10 +80,10 @@ class PFFittingEnginePref(PreferenceView):
|
||||
|
||||
# Future code once new cap sim is implemented
|
||||
'''
|
||||
self.cbGlobalForceReactivationTimer = wx.CheckBox( panel, wx.ID_ANY, u"Factor in reactivation timer", wx.DefaultPosition, wx.DefaultSize, 0 )
|
||||
self.cbGlobalForceReactivationTimer = wx.CheckBox( panel, wx.ID_ANY, "Factor in reactivation timer", wx.DefaultPosition, wx.DefaultSize, 0 )
|
||||
mainSizer.Add( self.cbGlobalForceReactivationTimer, 0, wx.ALL|wx.EXPAND, 5 )
|
||||
|
||||
text = u" Ignores reactivation timer when calculating capacitor usage,\n damage, and tank."
|
||||
text = " Ignores reactivation timer when calculating capacitor usage,\n damage, and tank."
|
||||
self.cbGlobalForceReactivationTimerText = wx.StaticText( panel, wx.ID_ANY, text, wx.DefaultPosition, wx.DefaultSize, 0 )
|
||||
self.cbGlobalForceReactivationTimerText.Wrap( -1 )
|
||||
self.cbGlobalForceReactivationTimerText.SetFont( wx.Font( 10, 70, 90, 90, False, wx.EmptyString ) )
|
||||
@@ -92,10 +92,10 @@ class PFFittingEnginePref(PreferenceView):
|
||||
|
||||
# Future code for mining laser crystal
|
||||
'''
|
||||
self.cbGlobalMiningSpecialtyCrystal = wx.CheckBox( panel, wx.ID_ANY, u"Factor in reactivation timer", wx.DefaultPosition, wx.DefaultSize, 0 )
|
||||
self.cbGlobalMiningSpecialtyCrystal = wx.CheckBox( panel, wx.ID_ANY, "Factor in reactivation timer", wx.DefaultPosition, wx.DefaultSize, 0 )
|
||||
mainSizer.Add( self.cbGlobalMiningSpecialtyCrystal, 0, wx.ALL|wx.EXPAND, 5 )
|
||||
|
||||
text = u" If enabled, displays the Specialty Crystal mining amount.\n This is the amount mined when using crystals and mining the matching asteroid."
|
||||
text = " If enabled, displays the Specialty Crystal mining amount.\n This is the amount mined when using crystals and mining the matching asteroid."
|
||||
self.cbGlobalMiningSpecialtyCrystalText = wx.StaticText( panel, wx.ID_ANY, text, wx.DefaultPosition, wx.DefaultSize, 0 )
|
||||
self.cbGlobalMiningSpecialtyCrystalText.Wrap( -1 )
|
||||
self.cbGlobalMiningSpecialtyCrystalText.SetFont( wx.Font( 10, 70, 90, 90, False, wx.EmptyString ) )
|
||||
|
||||
@@ -75,7 +75,7 @@ class PFEsiPref(PreferenceView):
|
||||
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 = 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)
|
||||
|
||||
@@ -84,7 +84,7 @@ class PFEsiPref(PreferenceView):
|
||||
|
||||
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 = 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)
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
from wx.lib.intctrl import IntCtrl
|
||||
|
||||
from gui.preferenceView import PreferenceView
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
|
||||
import gui.mainFrame
|
||||
import gui.globalEvents as GE
|
||||
from service.settings import SettingsProvider
|
||||
import gui.mainFrame
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
from gui.preferenceView import PreferenceView
|
||||
from service.fit import Fit
|
||||
from service.price import Price
|
||||
from service.settings import SettingsProvider
|
||||
|
||||
|
||||
class PFGeneralPref(PreferenceView):
|
||||
|
||||
title = "General"
|
||||
|
||||
def populatePanel(self, panel):
|
||||
@@ -85,7 +83,7 @@ class PFGeneralPref(PreferenceView):
|
||||
if "wxGTK" not in wx.PlatformInfo:
|
||||
self.cbReloadAll.SetCursor(helpCursor)
|
||||
self.cbReloadAll.SetToolTip(wx.ToolTip(
|
||||
'When disabled, reloads charges just in selected modules. Action can be reversed by holding Ctrl key while changing charge.'))
|
||||
'When disabled, reloads charges just in selected modules. Action can be reversed by holding Ctrl or Alt key while changing charge.'))
|
||||
mainSizer.Add(self.cbReloadAll, 0, wx.ALL | wx.EXPAND, 5)
|
||||
|
||||
self.sFit = Fit.getInstance()
|
||||
|
||||
@@ -185,13 +185,14 @@ class FitItem(SFItem.SFBrowserItem):
|
||||
if activeFit:
|
||||
sFit = Fit.getInstance()
|
||||
projectedFit = sFit.getFit(self.fitID)
|
||||
if self.mainFrame.command.Submit(cmd.GuiAddProjectedCommand(activeFit, projectedFit.ID, 'fit')):
|
||||
command = cmd.GuiAddProjectedFitCommand(fitID=activeFit, projectedFitID=projectedFit.ID, amount=1)
|
||||
if self.mainFrame.command.Submit(command):
|
||||
self.mainFrame.additionsPane.select("Projected")
|
||||
|
||||
def OnAddCommandFit(self, event):
|
||||
activeFit = self.mainFrame.getActiveFit()
|
||||
if activeFit:
|
||||
if self.mainFrame.command.Submit(cmd.GuiAddCommandFitCommand(activeFit, self.fitID)):
|
||||
if self.mainFrame.command.Submit(cmd.GuiAddCommandFitCommand(fitID=activeFit, commandFitID=self.fitID)):
|
||||
self.mainFrame.additionsPane.select("Command")
|
||||
|
||||
def OnMouseCaptureLost(self, event):
|
||||
@@ -290,7 +291,9 @@ class FitItem(SFItem.SFBrowserItem):
|
||||
event.Skip()
|
||||
|
||||
def editCheckEsc(self, event):
|
||||
if event.GetKeyCode() == wx.WXK_ESCAPE:
|
||||
keycode = event.GetKeyCode()
|
||||
mstate = wx.GetMouseState()
|
||||
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
self.RestoreEditButton()
|
||||
else:
|
||||
event.Skip()
|
||||
@@ -345,7 +348,8 @@ class FitItem(SFItem.SFBrowserItem):
|
||||
return
|
||||
|
||||
# to prevent accidental deletion, give dialog confirmation unless shift is depressed
|
||||
if wx.GetMouseState().ShiftDown() or wx.GetMouseState().MiddleIsDown():
|
||||
mstate = wx.GetMouseState()
|
||||
if mstate.GetModifiers() == wx.MOD_SHIFT or mstate.MiddleIsDown():
|
||||
self.deleteFit()
|
||||
else:
|
||||
dlg = wx.MessageDialog(
|
||||
|
||||
@@ -105,7 +105,9 @@ class NavigationPanel(SFItem.SFBrowserItem):
|
||||
self.BrowserSearchBox.Show(False)
|
||||
|
||||
def OnBrowserSearchBoxKeyPress(self, event):
|
||||
if event.GetKeyCode() == wx.WXK_ESCAPE:
|
||||
keycode = event.GetKeyCode()
|
||||
mstate = wx.GetMouseState()
|
||||
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
self.BrowserSearchBox.Show(False)
|
||||
elif event.RawControlDown() and event.GetKeyCode() == wx.WXK_BACK:
|
||||
HandleCtrlBackspace(self.BrowserSearchBox)
|
||||
|
||||
@@ -22,6 +22,7 @@ import wx
|
||||
|
||||
|
||||
class PFListPane(wx.ScrolledWindow):
|
||||
|
||||
def __init__(self, parent):
|
||||
wx.ScrolledWindow.__init__(self, parent, pos=wx.DefaultPosition, style=wx.TAB_TRAVERSAL)
|
||||
|
||||
@@ -150,6 +151,11 @@ class PFListPane(wx.ScrolledWindow):
|
||||
self._wList[i].Refresh()
|
||||
self.itemsHeight = max(self.itemsHeight, iheight - 1)
|
||||
|
||||
# This is needed as under GTK wx does not emit scroll up/scroll down
|
||||
# events, see issue #1909 for more info
|
||||
if 'wxGTK' in wx.PlatformInfo:
|
||||
self.SetScrollRate(0, self.itemsHeight)
|
||||
|
||||
def RemoveWidget(self, child):
|
||||
child.Destroy()
|
||||
self._wList.remove(child)
|
||||
|
||||
@@ -108,7 +108,7 @@ class ShipItem(SFItem.SFBrowserItem):
|
||||
pos = event.GetPosition()
|
||||
pos = self.ScreenToClient(pos)
|
||||
contexts = [("baseShip", "Ship Basic")]
|
||||
menu = ContextMenu.getMenu(self.baseItem, *contexts)
|
||||
menu = ContextMenu.getMenu(self.baseItem, (self.baseItem,), *contexts)
|
||||
self.PopupMenu(menu, pos)
|
||||
|
||||
def OnTimer(self, event):
|
||||
@@ -175,7 +175,9 @@ class ShipItem(SFItem.SFBrowserItem):
|
||||
self.Refresh()
|
||||
|
||||
def editCheckEsc(self, event):
|
||||
if event.GetKeyCode() == wx.WXK_ESCAPE:
|
||||
keycode = event.GetKeyCode()
|
||||
mstate = wx.GetMouseState()
|
||||
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
self.tcFitName.Show(False)
|
||||
else:
|
||||
event.Skip()
|
||||
|
||||
@@ -49,6 +49,7 @@ class ResourcesViewFull(StatsView):
|
||||
self.toggleContext("fighter")
|
||||
else:
|
||||
self.toggleContext("drone")
|
||||
event.Skip()
|
||||
|
||||
def toggleContext(self, context):
|
||||
# Apparently you cannot .Hide(True) on a Window, otherwise I would just .Hide(context !== x).
|
||||
|
||||
@@ -1,259 +0,0 @@
|
||||
# =============================================================================
|
||||
# Copyright (C) 2010 Diego Duclos
|
||||
#
|
||||
# This file is part of pyfa.
|
||||
#
|
||||
# pyfa is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# pyfa is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with pyfa. If not, see <http://www.gnu.org/licenses/>.
|
||||
# =============================================================================
|
||||
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
from gui.statsView import StatsView
|
||||
from gui.utils.numberFormatter import formatAmount
|
||||
from collections import OrderedDict
|
||||
|
||||
|
||||
class TargetingMiscViewFull(StatsView):
|
||||
name = "targetingMiscViewFull"
|
||||
|
||||
def __init__(self, parent):
|
||||
StatsView.__init__(self)
|
||||
self.parent = parent
|
||||
self._cachedValues = []
|
||||
|
||||
def getHeaderText(self, fit):
|
||||
return "Targeting && Misc"
|
||||
|
||||
def getTextExtentW(self, text):
|
||||
width, height = self.parent.GetTextExtent(text)
|
||||
return width
|
||||
|
||||
def populatePanel(self, contentPanel, headerPanel):
|
||||
contentSizer = contentPanel.GetSizer()
|
||||
|
||||
self.panel = contentPanel
|
||||
self.headerPanel = headerPanel
|
||||
gridTargetingMisc = wx.FlexGridSizer(1, 3, 0, 0)
|
||||
contentSizer.Add(gridTargetingMisc, 0, wx.EXPAND | wx.ALL, 0)
|
||||
gridTargetingMisc.AddGrowableCol(0)
|
||||
gridTargetingMisc.AddGrowableCol(2)
|
||||
# Targeting
|
||||
|
||||
gridTargeting = wx.FlexGridSizer(5, 2, 0, 0)
|
||||
gridTargeting.AddGrowableCol(1)
|
||||
|
||||
gridTargetingMisc.Add(gridTargeting, 0, wx.ALIGN_LEFT | wx.ALL, 5)
|
||||
|
||||
labels = (("Targets", "Targets", ""),
|
||||
("Range", "Range", "km"),
|
||||
("Scan res.", "ScanRes", "mm"),
|
||||
("Sensor str.", "SensorStr", ""),
|
||||
("Drone range", "CtrlRange", "km"))
|
||||
|
||||
for header, labelShort, unit in labels:
|
||||
gridTargeting.Add(wx.StaticText(contentPanel, wx.ID_ANY, "%s: " % header), 0, wx.ALIGN_LEFT)
|
||||
|
||||
box = wx.BoxSizer(wx.HORIZONTAL)
|
||||
gridTargeting.Add(box, 0, wx.ALIGN_LEFT)
|
||||
|
||||
lbl = wx.StaticText(contentPanel, wx.ID_ANY, "0 %s" % unit)
|
||||
setattr(self, "label%s" % labelShort, lbl)
|
||||
box.Add(lbl, 0, wx.ALIGN_LEFT)
|
||||
|
||||
self._cachedValues.append({"main": 0})
|
||||
|
||||
# Misc
|
||||
gridTargetingMisc.Add(wx.StaticLine(contentPanel, wx.ID_ANY, style=wx.VERTICAL), 0, wx.EXPAND, 3)
|
||||
gridMisc = wx.FlexGridSizer(5, 2, 0, 0)
|
||||
gridMisc.AddGrowableCol(1)
|
||||
gridTargetingMisc.Add(gridMisc, 0, wx.ALIGN_LEFT | wx.ALL, 5)
|
||||
|
||||
labels = (("Speed", "Speed", "m/s"),
|
||||
("Align time", "AlignTime", "s"),
|
||||
("Signature", "SigRadius", "m"),
|
||||
("Warp Speed", "WarpSpeed", "AU/s"),
|
||||
("Cargo", "Cargo", "m\u00B3"))
|
||||
|
||||
for header, labelShort, unit in labels:
|
||||
gridMisc.Add(wx.StaticText(contentPanel, wx.ID_ANY, "%s: " % header), 0, wx.ALIGN_LEFT)
|
||||
|
||||
box = wx.BoxSizer(wx.HORIZONTAL)
|
||||
gridMisc.Add(box, 0, wx.ALIGN_LEFT)
|
||||
|
||||
lbl = wx.StaticText(contentPanel, wx.ID_ANY, "0 %s" % unit)
|
||||
setattr(self, "labelFull%s" % labelShort, lbl)
|
||||
box.Add(lbl, 0, wx.ALIGN_LEFT)
|
||||
|
||||
self._cachedValues.append({"main": 0})
|
||||
|
||||
def refreshPanel(self, fit):
|
||||
# If we did anything interesting, we'd update our labels to reflect the new fit's stats here
|
||||
|
||||
cargoNamesOrder = OrderedDict((
|
||||
("fleetHangarCapacity", "Fleet hangar"),
|
||||
("shipMaintenanceBayCapacity", "Maintenance bay"),
|
||||
("specialAmmoHoldCapacity", "Ammo hold"),
|
||||
("specialFuelBayCapacity", "Fuel bay"),
|
||||
("specialShipHoldCapacity", "Ship hold"),
|
||||
("specialSmallShipHoldCapacity", "Small ship hold"),
|
||||
("specialMediumShipHoldCapacity", "Medium ship hold"),
|
||||
("specialLargeShipHoldCapacity", "Large ship hold"),
|
||||
("specialIndustrialShipHoldCapacity", "Industrial ship hold"),
|
||||
("specialOreHoldCapacity", "Ore hold"),
|
||||
("specialMineralHoldCapacity", "Mineral hold"),
|
||||
("specialMaterialBayCapacity", "Material bay"),
|
||||
("specialGasHoldCapacity", "Gas hold"),
|
||||
("specialSalvageHoldCapacity", "Salvage hold"),
|
||||
("specialCommandCenterHoldCapacity", "Command center hold"),
|
||||
("specialPlanetaryCommoditiesHoldCapacity", "Planetary goods hold"),
|
||||
("specialQuafeHoldCapacity", "Quafe hold")
|
||||
))
|
||||
|
||||
cargoValues = {
|
||||
"main": lambda: fit.ship.getModifiedItemAttr("capacity"),
|
||||
"fleetHangarCapacity": lambda: fit.ship.getModifiedItemAttr("fleetHangarCapacity"),
|
||||
"shipMaintenanceBayCapacity": lambda: fit.ship.getModifiedItemAttr("shipMaintenanceBayCapacity"),
|
||||
"specialAmmoHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialAmmoHoldCapacity"),
|
||||
"specialFuelBayCapacity": lambda: fit.ship.getModifiedItemAttr("specialFuelBayCapacity"),
|
||||
"specialShipHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialShipHoldCapacity"),
|
||||
"specialSmallShipHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialSmallShipHoldCapacity"),
|
||||
"specialMediumShipHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialMediumShipHoldCapacity"),
|
||||
"specialLargeShipHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialLargeShipHoldCapacity"),
|
||||
"specialIndustrialShipHoldCapacity": lambda: fit.ship.getModifiedItemAttr(
|
||||
"specialIndustrialShipHoldCapacity"),
|
||||
"specialOreHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialOreHoldCapacity"),
|
||||
"specialMineralHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialMineralHoldCapacity"),
|
||||
"specialMaterialBayCapacity": lambda: fit.ship.getModifiedItemAttr("specialMaterialBayCapacity"),
|
||||
"specialGasHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialGasHoldCapacity"),
|
||||
"specialSalvageHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialSalvageHoldCapacity"),
|
||||
"specialCommandCenterHoldCapacity": lambda: fit.ship.getModifiedItemAttr(
|
||||
"specialCommandCenterHoldCapacity"),
|
||||
"specialPlanetaryCommoditiesHoldCapacity": lambda: fit.ship.getModifiedItemAttr(
|
||||
"specialPlanetaryCommoditiesHoldCapacity"),
|
||||
"specialQuafeHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialQuafeHoldCapacity")
|
||||
}
|
||||
|
||||
stats = (("labelTargets", {"main": lambda: fit.maxTargets}, 3, 0, 0, ""),
|
||||
("labelRange", {"main": lambda: fit.maxTargetRange / 1000}, 3, 0, 0, "km"),
|
||||
("labelScanRes", {"main": lambda: fit.ship.getModifiedItemAttr("scanResolution")}, 3, 0, 0, "mm"),
|
||||
("labelSensorStr", {"main": lambda: fit.scanStrength}, 3, 0, 0, ""),
|
||||
("labelCtrlRange", {"main": lambda: fit.extraAttributes["droneControlRange"] / 1000}, 3, 0, 0, "km"),
|
||||
("labelFullSpeed", {"main": lambda: fit.maxSpeed}, 3, 0, 0, "m/s"),
|
||||
("labelFullAlignTime", {"main": lambda: fit.alignTime}, 3, 0, 0, "s"),
|
||||
("labelFullSigRadius", {"main": lambda: fit.ship.getModifiedItemAttr("signatureRadius")}, 3, 0, 9, ""),
|
||||
("labelFullWarpSpeed", {"main": lambda: fit.warpSpeed}, 3, 0, 0, "AU/s"),
|
||||
("labelFullCargo", cargoValues, 4, 0, 9, "m\u00B3"))
|
||||
|
||||
counter = 0
|
||||
RADII = [("Pod", 25), ("Interceptor", 33), ("Frigate", 38),
|
||||
("Destroyer", 83), ("Cruiser", 130),
|
||||
("Battlecruiser", 265), ("Battleship", 420),
|
||||
("Carrier", 3000)]
|
||||
for labelName, valueDict, prec, lowest, highest, unit in stats:
|
||||
label = getattr(self, labelName)
|
||||
newValues = {}
|
||||
for valueAlias, value in list(valueDict.items()):
|
||||
value = value() if fit is not None else 0
|
||||
value = value if value is not None else 0
|
||||
newValues[valueAlias] = value
|
||||
if self._cachedValues[counter] != newValues:
|
||||
mainValue = newValues["main"]
|
||||
otherValues = dict((k, newValues[k]) for k in [k for k in newValues if k != "main"])
|
||||
if labelName == "labelFullCargo":
|
||||
# Get sum of all cargoholds except for maintenance bay
|
||||
additionalCargo = sum(otherValues.values())
|
||||
if additionalCargo > 0:
|
||||
label.SetLabel("%s+%s %s" % (formatAmount(mainValue, prec, lowest, highest),
|
||||
formatAmount(additionalCargo, prec, lowest, highest),
|
||||
unit))
|
||||
else:
|
||||
label.SetLabel("%s %s" % (formatAmount(mainValue, prec, lowest, highest), unit))
|
||||
else:
|
||||
label.SetLabel("%s %s" % (formatAmount(mainValue, prec, lowest, highest), unit))
|
||||
# Tooltip stuff
|
||||
if fit:
|
||||
if labelName == "labelScanRes":
|
||||
lockTime = "%s\n" % "Lock Times".center(30)
|
||||
for size, radius in RADII:
|
||||
left = "%.1fs" % fit.calculateLockTime(radius)
|
||||
right = "%s [%d]" % (size, radius)
|
||||
lockTime += "%5s\t%s\n" % (left, right)
|
||||
label.SetToolTip(wx.ToolTip(lockTime))
|
||||
elif labelName == "labelFullSigRadius":
|
||||
label.SetToolTip(wx.ToolTip("Probe Size: %.3f" % (fit.probeSize or 0)))
|
||||
elif labelName == "labelFullWarpSpeed":
|
||||
maxWarpDistance = "Max Warp Distance: %.1f AU" % fit.maxWarpDistance
|
||||
if fit.ship.getModifiedItemAttr("warpScrambleStatus"):
|
||||
warpScrambleStatus = "Warp Core Strength: %.1f" % (fit.ship.getModifiedItemAttr("warpScrambleStatus") * -1)
|
||||
else:
|
||||
warpScrambleStatus = "Warp Core Strength: %.1f" % 0
|
||||
label.SetToolTip(wx.ToolTip("%s\n%s" % (maxWarpDistance, warpScrambleStatus)))
|
||||
elif labelName == "labelSensorStr":
|
||||
if fit.jamChance > 0:
|
||||
label.SetToolTip(
|
||||
wx.ToolTip("Type: %s\n%.1f%% Chance of Jam" % (fit.scanType, fit.jamChance)))
|
||||
else:
|
||||
label.SetToolTip(wx.ToolTip("Type: %s" % fit.scanType))
|
||||
elif labelName == "labelFullAlignTime":
|
||||
alignTime = "Align:\t%.3fs" % mainValue
|
||||
mass = 'Mass:\t{:,.0f}kg'.format(fit.ship.getModifiedItemAttr("mass"))
|
||||
agility = "Agility:\t%.3fx" % (fit.ship.getModifiedItemAttr("agility") or 0)
|
||||
label.SetToolTip(wx.ToolTip("%s\n%s\n%s" % (alignTime, mass, agility)))
|
||||
elif labelName == "labelFullCargo":
|
||||
tipLines = ["Cargohold: {:,.2f}m\u00B3 / {:,.2f}m\u00B3".format(fit.cargoBayUsed, newValues["main"])]
|
||||
for attrName, tipAlias in list(cargoNamesOrder.items()):
|
||||
if newValues[attrName] > 0:
|
||||
tipLines.append("{}: {:,.2f}m\u00B3".format(tipAlias, newValues[attrName]))
|
||||
label.SetToolTip(wx.ToolTip("\n".join(tipLines)))
|
||||
else:
|
||||
label.SetToolTip(wx.ToolTip("%.1f" % mainValue))
|
||||
else:
|
||||
label.SetToolTip(wx.ToolTip(""))
|
||||
self._cachedValues[counter] = newValues
|
||||
elif labelName == "labelFullWarpSpeed":
|
||||
if fit:
|
||||
maxWarpDistance = "Max Warp Distance: %.1f AU" % fit.maxWarpDistance
|
||||
if fit.ship.getModifiedItemAttr("warpScrambleStatus"):
|
||||
warpScrambleStatus = "Warp Core Strength: %.1f" % (fit.ship.getModifiedItemAttr("warpScrambleStatus") * -1)
|
||||
else:
|
||||
warpScrambleStatus = "Warp Core Strength: %.1f" % 0
|
||||
label.SetToolTip(wx.ToolTip("%s\n%s" % (maxWarpDistance, warpScrambleStatus)))
|
||||
elif labelName == "labelSensorStr":
|
||||
if fit:
|
||||
if fit.jamChance > 0:
|
||||
label.SetToolTip(wx.ToolTip("Type: %s\n%.1f%% Chance of Jam" % (fit.scanType, fit.jamChance)))
|
||||
else:
|
||||
label.SetToolTip(wx.ToolTip("Type: %s" % fit.scanType))
|
||||
else:
|
||||
label.SetToolTip(wx.ToolTip(""))
|
||||
elif labelName == "labelFullCargo":
|
||||
if fit:
|
||||
cachedCargo = self._cachedValues[counter]
|
||||
# if you add stuff to cargo, the capacity doesn't change and thus it is still cached
|
||||
# This assures us that we force refresh of cargo tooltip
|
||||
tipLines = ["Cargohold: {:,.2f}m\u00B3 / {:,.2f}m\u00B3".format(fit.cargoBayUsed, cachedCargo["main"])]
|
||||
for attrName, tipAlias in list(cargoNamesOrder.items()):
|
||||
if cachedCargo[attrName] > 0:
|
||||
tipLines.append("{}: {:,.2f}m\u00B3".format(tipAlias, cachedCargo[attrName]))
|
||||
label.SetToolTip(wx.ToolTip("\n".join(tipLines)))
|
||||
else:
|
||||
label.SetToolTip(wx.ToolTip(""))
|
||||
|
||||
counter += 1
|
||||
|
||||
self.panel.Layout()
|
||||
self.headerPanel.Layout()
|
||||
|
||||
|
||||
TargetingMiscViewFull.register()
|
||||
@@ -140,7 +140,10 @@ class Miscellanea(ViewColumn):
|
||||
return "+ " + ", ".join(info), "Slot Modifiers"
|
||||
elif itemGroup == "Energy Neutralizer":
|
||||
neutAmount = stuff.getModifiedItemAttr("energyNeutralizerAmount")
|
||||
cycleTime = stuff.cycleTime
|
||||
cycleParams = stuff.getCycleParameters()
|
||||
if cycleParams is None:
|
||||
return "", None
|
||||
cycleTime = cycleParams.averageTime
|
||||
if not neutAmount or not cycleTime:
|
||||
return "", None
|
||||
capPerSec = float(-neutAmount) * 1000 / cycleTime
|
||||
@@ -149,7 +152,10 @@ class Miscellanea(ViewColumn):
|
||||
return text, tooltip
|
||||
elif itemGroup == "Energy Nosferatu":
|
||||
neutAmount = stuff.getModifiedItemAttr("powerTransferAmount")
|
||||
cycleTime = stuff.cycleTime
|
||||
cycleParams = stuff.getCycleParameters()
|
||||
if cycleParams is None:
|
||||
return "", None
|
||||
cycleTime = cycleParams.averageTime
|
||||
if not neutAmount or not cycleTime:
|
||||
return "", None
|
||||
capPerSec = float(-neutAmount) * 1000 / cycleTime
|
||||
@@ -258,6 +264,35 @@ class Miscellanea(ViewColumn):
|
||||
|
||||
tooltip = "{0} disruption".format(formatList(ttEntries)).capitalize()
|
||||
return text, tooltip
|
||||
elif itemGroup in ("Gyrostabilizer", "Magnetic Field Stabilizer", "Heat Sink", "Ballistic Control system", "Entropic Radiation Sink"):
|
||||
attrMap = {
|
||||
"Gyrostabilizer": ("damageMultiplier", "speedMultiplier", "Projectile weapon"),
|
||||
"Magnetic Field Stabilizer": ("damageMultiplier", "speedMultiplier", "Hybrid weapon"),
|
||||
"Heat Sink": ("damageMultiplier", "speedMultiplier", "Energy weapon"),
|
||||
"Ballistic Control system": ("missileDamageMultiplierBonus", "speedMultiplier", "Missile"),
|
||||
"Entropic Radiation Sink": ("damageMultiplier", "speedMultiplier", "Precursor weapon")}
|
||||
dmgAttr, rofAttr, weaponName = attrMap[itemGroup]
|
||||
dmg = stuff.getModifiedItemAttr(dmgAttr)
|
||||
rof = stuff.getModifiedItemAttr(rofAttr)
|
||||
if not dmg or not rof:
|
||||
return "", None
|
||||
texts = []
|
||||
tooltips = []
|
||||
cumulative = (dmg / rof - 1) * 100
|
||||
texts.append("{}%".format(formatAmount(cumulative, 3, 0, 3, forceSign=True)))
|
||||
tooltips.append("{} DPS boost".format(weaponName))
|
||||
droneDmg = stuff.getModifiedItemAttr("droneDamageBonus")
|
||||
if droneDmg:
|
||||
texts.append("{}%".format(formatAmount(droneDmg, 3, 0, 3, forceSign=True)))
|
||||
tooltips.append("drone DPS boost".format(weaponName))
|
||||
return ' | '.join(texts), ' and '.join(tooltips)
|
||||
elif itemGroup == "Drone Damage Modules":
|
||||
dmg = stuff.getModifiedItemAttr("droneDamageBonus")
|
||||
if not dmg:
|
||||
return
|
||||
text = "{}%".format(formatAmount(dmg, 3, 0, 3, forceSign=True))
|
||||
tooltip = "Drone DPS boost"
|
||||
return text, tooltip
|
||||
elif itemGroup in ("ECM", "Burst Jammer", "Burst Projectors"):
|
||||
grav = stuff.getModifiedItemAttr("scanGravimetricStrengthBonus")
|
||||
ladar = stuff.getModifiedItemAttr("scanLadarStrengthBonus")
|
||||
@@ -499,9 +534,9 @@ class Miscellanea(ViewColumn):
|
||||
text = "{0}/s".format(formatAmount(capPerSec, 3, 0, 3))
|
||||
tooltip = "Energy neutralization per second"
|
||||
return text, tooltip
|
||||
elif itemGroup == "Micro Jump Drive":
|
||||
elif itemGroup in ("Micro Jump Drive", "Micro Jump Field Generators"):
|
||||
cycleTime = stuff.getModifiedItemAttr("duration") / 1000
|
||||
text = "{0}s".format(cycleTime)
|
||||
text = "{0}s".format(formatAmount(cycleTime, 3, 0, 3))
|
||||
tooltip = "Spoolup time"
|
||||
return text, tooltip
|
||||
elif itemGroup in ("Siege Module", "Cynosural Field Generator"):
|
||||
|
||||
@@ -42,6 +42,7 @@ from gui.utils.staticHelpers import DragDropHelper
|
||||
from service.fit import Fit
|
||||
from service.market import Market
|
||||
from config import slotColourMap
|
||||
from gui.fitCommands.helpers import getSimilarModPositions
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
@@ -74,8 +75,8 @@ class FitSpawner(gui.multiSwitch.TabSpawner):
|
||||
sFit = Fit.getInstance()
|
||||
openFitInNew = sFit.serviceFittingOptions["openFitInNew"]
|
||||
mstate = wx.GetMouseState()
|
||||
|
||||
if from_import or (not openFitInNew and mstate.CmdDown()) or startup or (openFitInNew and not mstate.CmdDown()):
|
||||
modifierKey = mstate.GetModifiers() == wx.MOD_CONTROL
|
||||
if from_import or (not openFitInNew and modifierKey) or startup or (openFitInNew and not modifierKey):
|
||||
self.multiSwitch.AddPage()
|
||||
|
||||
view = self.multiSwitch.GetSelectedPage()
|
||||
@@ -163,7 +164,7 @@ class FittingView(d.Display):
|
||||
self.hoveredRow = None
|
||||
self.hoveredColumn = None
|
||||
|
||||
self.Bind(wx.EVT_KEY_UP, self.kbEvent)
|
||||
self.Bind(wx.EVT_KEY_DOWN, self.kbEvent)
|
||||
self.Bind(wx.EVT_LEFT_DOWN, self.click)
|
||||
self.Bind(wx.EVT_RIGHT_DOWN, self.click)
|
||||
self.Bind(wx.EVT_MIDDLE_DOWN, self.click)
|
||||
@@ -189,7 +190,7 @@ class FittingView(d.Display):
|
||||
self.hoveredRow = row
|
||||
self.hoveredColumn = col
|
||||
if row != -1 and row not in self.blanks and col != -1 and col < len(self.DEFAULT_COLS):
|
||||
mod = self.mods[self.GetItemData(row)]
|
||||
mod = self.mods[row]
|
||||
tooltip = self.activeColumns[col].getToolTip(mod)
|
||||
if tooltip is not None:
|
||||
self.SetToolTip(tooltip)
|
||||
@@ -207,7 +208,6 @@ class FittingView(d.Display):
|
||||
data[0] is hard-coded str of originating source
|
||||
data[1] is typeID or index of data we want to manipulate
|
||||
"""
|
||||
|
||||
if data[0] == "fitting":
|
||||
self.swapItems(x, y, int(data[1]))
|
||||
elif data[0] == "cargo":
|
||||
@@ -237,44 +237,57 @@ class FittingView(d.Display):
|
||||
return self.activeFitID
|
||||
|
||||
def startDrag(self, event):
|
||||
row = event.GetIndex()
|
||||
srcRow = event.GetIndex()
|
||||
|
||||
if row != -1 and row not in self.blanks and isinstance(self.mods[row], Module) and not self.mods[row].isEmpty:
|
||||
data = wx.TextDataObject()
|
||||
dataStr = "fitting:" + str(self.mods[row].modPosition)
|
||||
data.SetText(dataStr)
|
||||
if srcRow == -1:
|
||||
return
|
||||
if srcRow in self.blanks:
|
||||
return
|
||||
try:
|
||||
mod = self.mods[srcRow]
|
||||
except IndexError:
|
||||
return
|
||||
if not isinstance(self.mods[srcRow], Module):
|
||||
return
|
||||
if mod.isEmpty:
|
||||
return
|
||||
fit = Fit.getInstance().getFit(self.activeFitID)
|
||||
if mod not in fit.modules:
|
||||
return
|
||||
|
||||
dropSource = wx.DropSource(self)
|
||||
dropSource.SetData(data)
|
||||
DragDropHelper.data = dataStr
|
||||
dropSource.DoDragDrop()
|
||||
self.unselectAll()
|
||||
self.Select(srcRow, True)
|
||||
|
||||
data = wx.TextDataObject()
|
||||
dataStr = "fitting:" + str(fit.modules.index(mod))
|
||||
data.SetText(dataStr)
|
||||
|
||||
dropSource = wx.DropSource(self)
|
||||
dropSource.SetData(data)
|
||||
DragDropHelper.data = dataStr
|
||||
dropSource.DoDragDrop()
|
||||
|
||||
def getSelectedMods(self):
|
||||
sel = []
|
||||
row = self.GetFirstSelected()
|
||||
while row != -1:
|
||||
mod = self.mods[self.GetItemData(row)]
|
||||
mods = []
|
||||
for row in self.getSelectedRows():
|
||||
try:
|
||||
mod = self.mods[row]
|
||||
except IndexError:
|
||||
continue
|
||||
if mod and not isinstance(mod, Rack):
|
||||
sel.append(mod)
|
||||
row = self.GetNextSelected(row)
|
||||
|
||||
return sel
|
||||
mods.append(mod)
|
||||
return mods
|
||||
|
||||
def kbEvent(self, event):
|
||||
keycode = event.GetKeyCode()
|
||||
if keycode == wx.WXK_DELETE or keycode == wx.WXK_NUMPAD_DELETE:
|
||||
row = self.GetFirstSelected()
|
||||
modules = []
|
||||
|
||||
while row != -1:
|
||||
if row not in self.blanks:
|
||||
mod = self.mods[row]
|
||||
if isinstance(mod, Module) and not mod.isEmpty:
|
||||
modules.append(self.mods[row])
|
||||
self.Select(row, 0)
|
||||
row = self.GetNextSelected(row)
|
||||
mstate = wx.GetMouseState()
|
||||
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
self.unselectAll()
|
||||
elif keycode == 65 and mstate.GetModifiers() == wx.MOD_CONTROL:
|
||||
self.selectAll()
|
||||
elif keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE) and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
modules = [m for m in self.getSelectedMods() if not m.isEmpty]
|
||||
self.removeModule(modules)
|
||||
|
||||
event.Skip()
|
||||
|
||||
def fitRemoved(self, event):
|
||||
@@ -358,35 +371,54 @@ class FittingView(d.Display):
|
||||
fitID = self.activeFitID
|
||||
if fitID is not None:
|
||||
item = Market.getInstance().getItem(itemID, eager='group.category')
|
||||
if item is None or not (item.isModule or item.isSubsystem):
|
||||
if item is None:
|
||||
event.Skip()
|
||||
return
|
||||
batchOp = wx.GetMouseState().GetModifiers() == wx.MOD_ALT and getattr(event, 'allowBatch', None) is not False
|
||||
# If we've selected ammo, then apply to the selected module(s)
|
||||
if item.isCharge:
|
||||
# If we've selected ammo, then apply to the selected module(s)
|
||||
modules = []
|
||||
sel = self.GetFirstSelected()
|
||||
while sel != -1 and sel not in self.blanks:
|
||||
mod = self.mods[self.GetItemData(sel)]
|
||||
if isinstance(mod, Module) and not mod.isEmpty:
|
||||
modules.append(self.mods[self.GetItemData(sel)])
|
||||
sel = self.GetNextSelected(sel)
|
||||
|
||||
if len(modules) > 0:
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeLocalModuleChargesCommand(fitID, modules, itemID))
|
||||
else:
|
||||
self.mainFrame.command.Submit(cmd.GuiAddLocalModuleCommand(fitID, itemID))
|
||||
positions = []
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
if batchOp:
|
||||
for position, mod in enumerate(fit.modules):
|
||||
if isinstance(mod, Module) and not mod.isEmpty:
|
||||
positions.append(position)
|
||||
else:
|
||||
for mod in self.getSelectedMods():
|
||||
if mod.isEmpty or mod not in fit.modules:
|
||||
continue
|
||||
positions.append(fit.modules.index(mod))
|
||||
if len(positions) > 0:
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeLocalModuleChargesCommand(
|
||||
fitID=fitID, positions=positions, chargeItemID=itemID))
|
||||
elif (item.isModule and not batchOp) or item.isSubsystem:
|
||||
self.mainFrame.command.Submit(cmd.GuiAddLocalModuleCommand(fitID=fitID, itemID=itemID))
|
||||
elif item.isModule and batchOp:
|
||||
self.mainFrame.command.Submit(cmd.GuiFillWithNewLocalModulesCommand(fitID=fitID, itemID=itemID))
|
||||
|
||||
event.Skip()
|
||||
|
||||
def removeItem(self, event):
|
||||
"""Double Left Click - remove module"""
|
||||
if event.CmdDown():
|
||||
if event.GetModifiers() == wx.MOD_CONTROL:
|
||||
return
|
||||
row, _ = self.HitTest(event.Position)
|
||||
if row != -1 and row not in self.blanks and isinstance(self.mods[row], Module):
|
||||
col = self.getColumn(event.Position)
|
||||
if col != self.getColIndex(State):
|
||||
self.removeModule(self.mods[row])
|
||||
try:
|
||||
mod = self.mods[row]
|
||||
except IndexError:
|
||||
return
|
||||
if not isinstance(mod, Module) or mod.isEmpty:
|
||||
return
|
||||
if event.GetModifiers() == wx.MOD_ALT:
|
||||
fit = Fit.getInstance().getFit(self.activeFitID)
|
||||
positions = getSimilarModPositions(fit.modules, mod)
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveLocalModuleCommand(
|
||||
fitID=self.activeFitID, positions=positions))
|
||||
else:
|
||||
self.removeModule(mod)
|
||||
else:
|
||||
if "wxMSW" in wx.PlatformInfo:
|
||||
self.click(event)
|
||||
@@ -396,19 +428,66 @@ class FittingView(d.Display):
|
||||
if not isinstance(modules, list):
|
||||
modules = [modules]
|
||||
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveLocalModuleCommand(self.activeFitID, modules))
|
||||
fit = Fit.getInstance().getFit(self.activeFitID)
|
||||
positions = []
|
||||
for position, mod in enumerate(fit.modules):
|
||||
if mod in modules:
|
||||
positions.append(position)
|
||||
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveLocalModuleCommand(
|
||||
fitID=self.activeFitID, positions=positions))
|
||||
|
||||
def addModule(self, x, y, itemID):
|
||||
"""Add a module from the market browser (from dragging it)"""
|
||||
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
item = Market.getInstance().getItem(itemID)
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
dstRow, _ = self.HitTest((x, y))
|
||||
if dstRow != -1 and dstRow not in self.blanks:
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
mod = self.mods[dstRow]
|
||||
if not isinstance(mod, Module): # make sure we're not adding something to a T3D Mode
|
||||
return
|
||||
if dstRow == -1 or dstRow in self.blanks:
|
||||
dstMod = None
|
||||
else:
|
||||
try:
|
||||
dstMod = self.mods[dstRow]
|
||||
except IndexError:
|
||||
dstMod = None
|
||||
if not isinstance(dstMod, Module):
|
||||
dstMod = None
|
||||
if dstMod not in fit.modules:
|
||||
dstMod = None
|
||||
dstPos = fit.modules.index(dstMod) if dstMod is not None else None
|
||||
mstate = wx.GetMouseState()
|
||||
# If we dropping on a module, try to replace, or add if replacement fails
|
||||
if item.isModule and dstMod is not None and not dstMod.isEmpty:
|
||||
positions = getSimilarModPositions(fit.modules, dstMod) if mstate.GetModifiers() == wx.MOD_ALT else [dstPos]
|
||||
command = cmd.GuiReplaceLocalModuleCommand(fitID=fitID, itemID=itemID, positions=positions)
|
||||
if not self.mainFrame.command.Submit(command):
|
||||
if mstate.GetModifiers() == wx.MOD_ALT:
|
||||
self.mainFrame.command.Submit(cmd.GuiFillWithNewLocalModulesCommand(fitID=fitID, itemID=itemID))
|
||||
else:
|
||||
self.mainFrame.command.Submit(cmd.GuiAddLocalModuleCommand(fitID=fitID, itemID=itemID))
|
||||
elif item.isModule:
|
||||
if mstate.GetModifiers() == wx.MOD_ALT:
|
||||
self.mainFrame.command.Submit(cmd.GuiFillWithNewLocalModulesCommand(fitID=fitID, itemID=itemID))
|
||||
else:
|
||||
self.mainFrame.command.Submit(cmd.GuiAddLocalModuleCommand(fitID=fitID, itemID=itemID))
|
||||
elif item.isSubsystem:
|
||||
self.mainFrame.command.Submit(cmd.GuiAddLocalModuleCommand(fitID=fitID, itemID=itemID))
|
||||
elif item.isCharge:
|
||||
failoverToAll = False
|
||||
positionsAll = list(range(len(fit.modules)))
|
||||
if dstMod is None or dstMod.isEmpty:
|
||||
positions = positionsAll
|
||||
elif mstate.GetModifiers() == wx.MOD_ALT:
|
||||
positions = getSimilarModPositions(fit.modules, dstMod)
|
||||
failoverToAll = True
|
||||
else:
|
||||
positions = [fit.modules.index(dstMod)]
|
||||
if len(positions) > 0:
|
||||
command = cmd.GuiChangeLocalModuleChargesCommand(fitID=fitID, positions=positions, chargeItemID=itemID)
|
||||
if not self.mainFrame.command.Submit(command) and failoverToAll:
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeLocalModuleChargesCommand(
|
||||
fitID=fitID, positions=positionsAll, chargeItemID=itemID))
|
||||
|
||||
self.mainFrame.command.Submit(cmd.GuiAddLocalModuleCommand(fitID, itemID, self.mods[dstRow].modPosition))
|
||||
|
||||
def swapCargo(self, x, y, cargoItemID):
|
||||
"""Swap a module from cargo to fitting window"""
|
||||
@@ -420,11 +499,15 @@ class FittingView(d.Display):
|
||||
if not isinstance(mod, Module):
|
||||
return
|
||||
|
||||
self.mainFrame.command.Submit(cmd.GuiCargoToLocalModuleCommand(
|
||||
fitID=self.mainFrame.getActiveFit(),
|
||||
cargoItemID=cargoItemID,
|
||||
modPosition=mod.modPosition,
|
||||
copy=wx.GetMouseState().CmdDown()))
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
if mod in fit.modules:
|
||||
position = fit.modules.index(mod)
|
||||
self.mainFrame.command.Submit(cmd.GuiCargoToLocalModuleCommand(
|
||||
fitID=fitID,
|
||||
cargoItemID=cargoItemID,
|
||||
modPosition=position,
|
||||
copy=wx.GetMouseState().GetModifiers() == wx.MOD_CONTROL))
|
||||
|
||||
def swapItems(self, x, y, srcIdx):
|
||||
"""Swap two modules in fitting window"""
|
||||
@@ -434,27 +517,30 @@ class FittingView(d.Display):
|
||||
dstRow, _ = self.HitTest((x, y))
|
||||
|
||||
if dstRow != -1 and dstRow not in self.blanks:
|
||||
mod1 = fit.modules[srcIdx]
|
||||
mod2 = self.mods[dstRow]
|
||||
|
||||
try:
|
||||
mod1 = fit.modules[srcIdx]
|
||||
mod2 = self.mods[dstRow]
|
||||
except IndexError:
|
||||
return
|
||||
if not isinstance(mod2, Module):
|
||||
return
|
||||
|
||||
# can't swap modules to different racks
|
||||
if mod1.slot != mod2.slot:
|
||||
return
|
||||
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
if getattr(mod2, "modPosition") is not None:
|
||||
mstate = wx.GetMouseState()
|
||||
if mstate.CmdDown() and mod2.isEmpty:
|
||||
self.mainFrame.command.Submit(cmd.GuiCloneLocalModuleCommand(
|
||||
fitID=fitID, srcPosition=srcIdx, dstPosition=mod2.modPosition))
|
||||
elif not mstate.CmdDown():
|
||||
self.mainFrame.command.Submit(cmd.GuiSwapLocalModulesCommand(
|
||||
fitID=fitID, position1=srcIdx, position2=mod2.modPosition))
|
||||
else:
|
||||
if mod2 not in fit.modules:
|
||||
pyfalog.error("Missing module position for: {0}", str(getattr(mod2, "ID", "Unknown")))
|
||||
return
|
||||
mod2Position = fit.modules.index(mod2)
|
||||
mstate = wx.GetMouseState()
|
||||
if mstate.GetModifiers() == wx.MOD_CONTROL | wx.MOD_ALT:
|
||||
self.mainFrame.command.Submit(cmd.GuiFillWithClonedLocalModulesCommand(
|
||||
fitID=self.activeFitID, position=srcIdx))
|
||||
elif mstate.GetModifiers() == wx.MOD_CONTROL and mod2.isEmpty:
|
||||
self.mainFrame.command.Submit(cmd.GuiCloneLocalModuleCommand(
|
||||
fitID=self.activeFitID, srcPosition=srcIdx, dstPosition=mod2Position))
|
||||
elif mstate.GetModifiers() == wx.MOD_NONE:
|
||||
self.mainFrame.command.Submit(cmd.GuiSwapLocalModulesCommand(
|
||||
fitID=self.activeFitID, position1=srcIdx, position2=mod2Position))
|
||||
|
||||
def generateMods(self):
|
||||
"""
|
||||
@@ -518,7 +604,6 @@ class FittingView(d.Display):
|
||||
self.populate(self.mods)
|
||||
|
||||
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
|
||||
@@ -538,52 +623,52 @@ class FittingView(d.Display):
|
||||
event.Skip()
|
||||
|
||||
def spawnMenu(self, event):
|
||||
if self.activeFitID is None or self.getColumn(self.ScreenToClient(event.Position)) == self.getColIndex(State):
|
||||
if self.activeFitID is None or self.getColumn(self.screenToClientFixed(event.Position)) == self.getColIndex(State):
|
||||
return
|
||||
|
||||
sMkt = Market.getInstance()
|
||||
selection = []
|
||||
sel = self.GetFirstSelected()
|
||||
contexts = []
|
||||
|
||||
while sel != -1 and sel not in self.blanks:
|
||||
mod = self.mods[self.GetItemData(sel)]
|
||||
|
||||
for mod in self.getSelectedMods():
|
||||
# Test if this is a mode, which is a special snowflake of a Module
|
||||
if isinstance(mod, Mode):
|
||||
srcContext = "fittingMode"
|
||||
|
||||
itemContext = "Tactical Mode"
|
||||
fullContext = (srcContext, itemContext)
|
||||
if srcContext not in tuple(fCtxt[0] for fCtxt in contexts):
|
||||
contexts.append(fullContext)
|
||||
|
||||
selection.append(mod)
|
||||
|
||||
elif not mod.isEmpty:
|
||||
srcContext = "fittingModule"
|
||||
itemContext = sMkt.getCategoryByItem(mod.item).name
|
||||
selection.append(mod)
|
||||
|
||||
fit = Fit.getInstance().getFit(self.activeFitID)
|
||||
clickedPos = self.getRowByAbs(event.Position)
|
||||
mainMod = None
|
||||
if clickedPos != -1:
|
||||
try:
|
||||
mod = self.mods[clickedPos]
|
||||
except IndexError:
|
||||
pass
|
||||
else:
|
||||
if mod is not None and (mod in fit.modules or mod is fit.mode):
|
||||
mainMod = mod
|
||||
|
||||
sMkt = Market.getInstance()
|
||||
contexts = []
|
||||
if isinstance(mainMod, Module) and not mainMod.isEmpty:
|
||||
srcContext = "fittingModule"
|
||||
itemContext = sMkt.getCategoryByItem(mainMod.item).name
|
||||
fullContext = (srcContext, itemContext)
|
||||
if srcContext not in tuple(fCtx[0] for fCtx in contexts):
|
||||
contexts.append(fullContext)
|
||||
if mainMod.charge is not None:
|
||||
srcContext = "fittingCharge"
|
||||
itemContext = sMkt.getCategoryByItem(mainMod.charge).name
|
||||
fullContext = (srcContext, itemContext)
|
||||
if srcContext not in tuple(fCtxt[0] for fCtxt in contexts):
|
||||
contexts.append(fullContext)
|
||||
|
||||
if mod.charge is not None:
|
||||
srcContext = "fittingCharge"
|
||||
itemContext = sMkt.getCategoryByItem(mod.charge).name
|
||||
fullContext = (srcContext, itemContext)
|
||||
if srcContext not in tuple(fCtxt[0] for fCtxt in contexts):
|
||||
contexts.append(fullContext)
|
||||
|
||||
selection.append(mod)
|
||||
|
||||
sel = self.GetNextSelected(sel)
|
||||
|
||||
sFit = Fit.getInstance()
|
||||
fit = sFit.getFit(self.activeFitID)
|
||||
|
||||
elif isinstance(mainMod, Mode):
|
||||
srcContext = "fittingMode"
|
||||
itemContext = "Tactical Mode"
|
||||
fullContext = (srcContext, itemContext)
|
||||
if srcContext not in tuple(fCtx[0] for fCtx in contexts):
|
||||
contexts.append(fullContext)
|
||||
contexts.append(("fittingShip", "Ship" if not fit.isStructure else "Citadel"))
|
||||
|
||||
menu = ContextMenu.getMenu(selection, *contexts)
|
||||
menu = ContextMenu.getMenu(mainMod, selection, *contexts)
|
||||
self.PopupMenu(menu)
|
||||
|
||||
def click(self, event):
|
||||
@@ -595,34 +680,53 @@ class FittingView(d.Display):
|
||||
change State
|
||||
"""
|
||||
|
||||
row, _, col = self.HitTestSubItem(event.Position)
|
||||
clickedRow, _, col = self.HitTestSubItem(event.Position)
|
||||
|
||||
# only do State column and ignore invalid rows
|
||||
if row != -1 and row not in self.blanks and col == self.getColIndex(State):
|
||||
sel = []
|
||||
curr = self.GetFirstSelected()
|
||||
if clickedRow != -1 and clickedRow not in self.blanks and col == self.getColIndex(State):
|
||||
selectedRows = []
|
||||
currentRow = self.GetFirstSelected()
|
||||
|
||||
while curr != -1 and row not in self.blanks:
|
||||
sel.append(curr)
|
||||
curr = self.GetNextSelected(curr)
|
||||
while currentRow != -1 and clickedRow not in self.blanks:
|
||||
selectedRows.append(currentRow)
|
||||
currentRow = self.GetNextSelected(currentRow)
|
||||
|
||||
if row not in sel:
|
||||
mods = [self.mods[self.GetItemData(row)]]
|
||||
if clickedRow not in selectedRows:
|
||||
try:
|
||||
selectedMods = [self.mods[clickedRow]]
|
||||
except IndexError:
|
||||
return
|
||||
else:
|
||||
mods = self.getSelectedMods()
|
||||
selectedMods = self.getSelectedMods()
|
||||
|
||||
click = "ctrl" if event.GetModifiers() == wx.MOD_CONTROL or event.middleIsDown else "right" if event.GetButton() == 3 else "left"
|
||||
|
||||
try:
|
||||
mainMod = self.mods[clickedRow]
|
||||
except IndexError:
|
||||
return
|
||||
if mainMod.isEmpty:
|
||||
return
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
ctrl = event.cmdDown or event.middleIsDown
|
||||
click = "ctrl" if ctrl is True else "right" if event.GetButton() == 3 else "left"
|
||||
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
if mainMod not in fit.modules:
|
||||
return
|
||||
mainPosition = fit.modules.index(mainMod)
|
||||
if event.GetModifiers() == wx.MOD_ALT:
|
||||
positions = getSimilarModPositions(fit.modules, mainMod)
|
||||
else:
|
||||
positions = []
|
||||
for position, mod in enumerate(fit.modules):
|
||||
if mod in selectedMods:
|
||||
positions.append(position)
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeLocalModuleStatesCommand(
|
||||
fitID=fitID,
|
||||
mainPosition=self.mods[self.GetItemData(row)].modPosition,
|
||||
positions=[mod.modPosition for mod in mods],
|
||||
mainPosition=mainPosition,
|
||||
positions=positions,
|
||||
click=click))
|
||||
|
||||
# update state tooltip
|
||||
tooltip = self.activeColumns[col].getToolTip(self.mods[self.GetItemData(row)])
|
||||
tooltip = self.activeColumns[col].getToolTip(self.mods[clickedRow])
|
||||
if tooltip:
|
||||
self.SetToolTip(tooltip)
|
||||
|
||||
@@ -654,21 +758,25 @@ class FittingView(d.Display):
|
||||
self.SetItemBackgroundColour(i, self.GetBackgroundColour())
|
||||
|
||||
# only consider changing color if we're dealing with a Module
|
||||
if type(mod) is Module:
|
||||
hasRestrictionOverriden = getattr(mod, 'restrictionOverridden', None)
|
||||
# If module had broken fitting restrictions but now doesn't,
|
||||
# ensure it is now valid, and remove restrictionOverridden
|
||||
# variable. More in #1519
|
||||
if not fit.ignoreRestrictions and hasRestrictionOverriden:
|
||||
clean = False
|
||||
if mod.fits(fit, False):
|
||||
if not mod.hardpoint:
|
||||
clean = True
|
||||
elif fit.getHardpointsFree(mod.hardpoint) >= 0:
|
||||
clean = True
|
||||
if clean:
|
||||
del mod.restrictionOverridden
|
||||
hasRestrictionOverriden = not hasRestrictionOverriden
|
||||
if isinstance(mod, Module):
|
||||
hasRestrictionOverriden = False
|
||||
if not mod.isEmpty:
|
||||
fits = mod.fits(fit, False)
|
||||
hasRestrictionOverriden = getattr(mod, 'restrictionOverridden', None)
|
||||
# If module had broken fitting restrictions but now doesn't,
|
||||
# ensure it is now valid, and remove restrictionOverridden
|
||||
# variable. More in #1519
|
||||
if not fit.ignoreRestrictions and hasRestrictionOverriden:
|
||||
clean = False
|
||||
if fits:
|
||||
if not mod.hardpoint:
|
||||
clean = True
|
||||
elif fit.getHardpointsFree(mod.hardpoint) >= 0:
|
||||
clean = True
|
||||
if clean:
|
||||
del mod.restrictionOverridden
|
||||
hasRestrictionOverriden = not hasRestrictionOverriden
|
||||
|
||||
|
||||
if slotMap[mod.slot] or hasRestrictionOverriden: # Color too many modules as red
|
||||
self.SetItemBackgroundColour(i, wx.Colour(204, 51, 51))
|
||||
|
||||
@@ -11,6 +11,7 @@ from service.market import Market
|
||||
|
||||
|
||||
class BaseImplantEditorView(wx.Panel):
|
||||
|
||||
def addMarketViewImage(self, iconFile):
|
||||
if iconFile is None:
|
||||
return -1
|
||||
@@ -37,13 +38,6 @@ class BaseImplantEditorView(wx.Panel):
|
||||
availableSizer.Add(self.searchBox, 0, wx.EXPAND)
|
||||
availableSizer.Add(self.itemView, 1, wx.EXPAND)
|
||||
|
||||
'''
|
||||
self.availableImplantsSearch = wx.SearchCtrl(self, wx.ID_ANY, style=wx.TE_PROCESS_ENTER)
|
||||
self.availableImplantsSearch.ShowCancelButton(True)
|
||||
|
||||
availableSizer.Add(self.availableImplantsSearch, 0, wx.BOTTOM | wx.EXPAND, 2)
|
||||
'''
|
||||
|
||||
self.availableImplantsTree = wx.TreeCtrl(self, wx.ID_ANY, style=wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT)
|
||||
root = self.availableRoot = self.availableImplantsTree.AddRoot("Available")
|
||||
self.availableImplantsImageList = wx.ImageList(16, 16)
|
||||
@@ -202,7 +196,6 @@ class BaseImplantEditorView(wx.Panel):
|
||||
|
||||
class AvailableImplantsView(d.Display):
|
||||
DEFAULT_COLS = ["attr:implantness",
|
||||
"Base Icon",
|
||||
"Base Name"]
|
||||
|
||||
def __init__(self, parent):
|
||||
@@ -212,9 +205,7 @@ class AvailableImplantsView(d.Display):
|
||||
|
||||
class ItemView(d.Display):
|
||||
DEFAULT_COLS = ["Base Icon",
|
||||
"Base Name",
|
||||
"attr:power,,,True",
|
||||
"attr:cpu,,,True"]
|
||||
"Base Name"]
|
||||
|
||||
def __init__(self, parent):
|
||||
d.Display.__init__(self, parent)
|
||||
@@ -260,6 +251,7 @@ class ItemView(d.Display):
|
||||
self.Show()
|
||||
self.parent.Layout()
|
||||
|
||||
items = [i for i in items if i.group.name != 'Booster']
|
||||
self.items = sorted(list(items), key=lambda i: i.name)
|
||||
|
||||
self.update(self.items)
|
||||
|
||||
@@ -149,7 +149,7 @@ class CharacterEntityEditor(EntityEditor):
|
||||
|
||||
class CharacterEditor(wx.Frame):
|
||||
def __init__(self, parent):
|
||||
wx.Frame.__init__(self, parent, id=wx.ID_ANY, title="pyfa: Character Editor", pos=wx.DefaultPosition,
|
||||
wx.Frame.__init__(self, parent, id=wx.ID_ANY, title="Character Editor", pos=wx.DefaultPosition,
|
||||
size=wx.Size(640, 600), style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER | wx.FRAME_FLOAT_ON_PARENT)
|
||||
|
||||
i = wx.Icon(BitmapLoader.getBitmap("character_small", "gui"))
|
||||
@@ -208,6 +208,7 @@ class CharacterEditor(wx.Frame):
|
||||
self.Centre(wx.BOTH)
|
||||
|
||||
self.Bind(wx.EVT_CLOSE, self.closeEvent)
|
||||
self.Bind(wx.EVT_CHAR_HOOK, self.kbEvent)
|
||||
self.Bind(GE.CHAR_LIST_UPDATED, self.refreshCharacterList)
|
||||
self.entityEditor.Bind(wx.EVT_CHOICE, self.charChanged)
|
||||
|
||||
@@ -253,8 +254,18 @@ class CharacterEditor(wx.Frame):
|
||||
sChr.revertCharacter(char.ID)
|
||||
wx.PostEvent(self, GE.CharListUpdated())
|
||||
|
||||
def kbEvent(self, event):
|
||||
keycode = event.GetKeyCode()
|
||||
mstate = wx.GetMouseState()
|
||||
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
self.closeWindow()
|
||||
return
|
||||
event.Skip()
|
||||
|
||||
def closeEvent(self, event):
|
||||
# del self.disableWin
|
||||
self.closeWindow()
|
||||
|
||||
def closeWindow(self):
|
||||
wx.PostEvent(self.mainFrame, GE.CharListUpdated())
|
||||
self.Destroy()
|
||||
|
||||
@@ -404,31 +415,18 @@ class SkillTreeView(wx.Panel):
|
||||
self.charEditor.entityEditor.Bind(wx.EVT_CHOICE, self.charChanged)
|
||||
self.charEditor.Bind(GE.CHAR_LIST_UPDATED, self.populateSkillTree)
|
||||
|
||||
srcContext = "skillItem"
|
||||
itemContext = "Skill"
|
||||
context = (srcContext, itemContext)
|
||||
self.statsMenu = ContextMenu.getMenu(None, context)
|
||||
self.levelChangeMenu = ContextMenu.getMenu(None, context) or wx.Menu()
|
||||
self.levelChangeMenu.AppendSeparator()
|
||||
# Context menu stuff
|
||||
self.idUnlearned = wx.NewId()
|
||||
self.levelIds = {}
|
||||
|
||||
idUnlearned = wx.NewId()
|
||||
self.levelIds[idUnlearned] = "Not learned"
|
||||
self.levelChangeMenu.Append(idUnlearned, "Unlearn")
|
||||
|
||||
self.idLevels = {}
|
||||
self.levelIds[self.idUnlearned] = "Not learned"
|
||||
for level in range(6):
|
||||
id = wx.NewId()
|
||||
self.levelIds[id] = level
|
||||
self.levelChangeMenu.Append(id, "Level %d" % level)
|
||||
|
||||
self.levelChangeMenu.AppendSeparator()
|
||||
self.idLevels[level] = id
|
||||
self.revertID = wx.NewId()
|
||||
self.levelChangeMenu.Append(self.revertID, "Revert")
|
||||
|
||||
self.saveID = wx.NewId()
|
||||
self.levelChangeMenu.Append(self.saveID, "Save")
|
||||
|
||||
self.levelChangeMenu.Bind(wx.EVT_MENU, self.changeLevel)
|
||||
self.SetSizer(pmainSizer)
|
||||
|
||||
# This cuases issues with GTK, see #1866
|
||||
@@ -597,15 +595,27 @@ class SkillTreeView(wx.Panel):
|
||||
if thing:
|
||||
return
|
||||
|
||||
char = self.charEditor.entityEditor.getActiveEntity()
|
||||
sMkt = Market.getInstance()
|
||||
id = self.skillTreeListCtrl.GetItemData(item)[1]
|
||||
eveItem = Market.getInstance().getItem(id)
|
||||
|
||||
srcContext = "skillItem"
|
||||
itemContext = "Skill"
|
||||
context = (srcContext, itemContext)
|
||||
menu = ContextMenu.getMenu(eveItem, [eveItem], context)
|
||||
char = self.charEditor.entityEditor.getActiveEntity()
|
||||
if char.name not in ("All 0", "All 5"):
|
||||
self.levelChangeMenu.selection = sMkt.getItem(id)
|
||||
self.PopupMenu(self.levelChangeMenu)
|
||||
else:
|
||||
self.statsMenu.selection = sMkt.getItem(id)
|
||||
self.PopupMenu(self.statsMenu)
|
||||
menu.AppendSeparator()
|
||||
menu.Append(self.idUnlearned, "Unlearn")
|
||||
for level in range(6):
|
||||
menu.Append(self.idLevels[level], "Level %d" % level)
|
||||
# Doesn't make sense to have these menu items here, as they do not revert skill changes
|
||||
# done in an editor - because these changes are persisted anyway
|
||||
# menu.AppendSeparator()
|
||||
# menu.Append(self.revertID, "Revert")
|
||||
# menu.Append(self.saveID, "Save")
|
||||
menu.Bind(wx.EVT_MENU, self.changeLevel)
|
||||
|
||||
self.PopupMenu(menu)
|
||||
|
||||
def changeLevel(self, event):
|
||||
level = self.levelIds.get(event.Id)
|
||||
@@ -705,7 +715,8 @@ class ImplantEditorView(BaseImplantEditorView):
|
||||
context = (("implantEditor",),)
|
||||
# fuck good coding practices, passing a pointer to the character editor here for [reasons] =D
|
||||
# (see implantSets context class for info)
|
||||
menu = ContextMenu.getMenu((self.Parent.Parent,), *context)
|
||||
item = self.Parent.Parent
|
||||
menu = ContextMenu.getMenu(item, (item,), *context)
|
||||
|
||||
if menu:
|
||||
self.PopupMenu(menu)
|
||||
|
||||
@@ -64,8 +64,9 @@ class PageChanging(_PageChanging, NotebookTabChangeEvent, VetoAble):
|
||||
|
||||
|
||||
class PageChanged(_PageChanged, NotebookTabChangeEvent):
|
||||
def __init__(self, old, new):
|
||||
_PageChanged.__init__(self)
|
||||
|
||||
def __init__(self, old, new, *args, **kwargs):
|
||||
_PageChanged.__init__(self, *args, **kwargs)
|
||||
NotebookTabChangeEvent.__init__(self, old, new)
|
||||
|
||||
|
||||
@@ -236,13 +237,16 @@ class ChromeNotebook(wx.Panel):
|
||||
|
||||
self.tabs_container.DisableTab(idx, toggle)
|
||||
|
||||
def SetSelection(self, page):
|
||||
def SetSelection(self, page, focus=True):
|
||||
old_selection = self.GetSelection()
|
||||
if old_selection != page:
|
||||
self._active_page.Hide()
|
||||
self._active_page = self._pages[page]
|
||||
self.tabs_container.SetSelected(page)
|
||||
self.ShowActive()
|
||||
if focus:
|
||||
self._active_page.SetFocus()
|
||||
wx.PostEvent(self, PageChanged(old_selection, page))
|
||||
|
||||
def DeletePage(self, n):
|
||||
page = self._pages[n]
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user