Compare commits
74 Commits
v2.9.2
...
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 |
@@ -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,22 +240,22 @@ 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)
|
||||
return
|
||||
HandledList.insert(self, idx, implant)
|
||||
|
||||
def makeRoom(self, implant):
|
||||
@@ -292,22 +281,22 @@ 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)
|
||||
return
|
||||
HandledList.insert(self, idx, booster)
|
||||
|
||||
def makeRoom(self, booster):
|
||||
@@ -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):
|
||||
|
||||
401
eos/effects.py
401
eos/effects.py
@@ -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)
|
||||
"""
|
||||
|
||||
@@ -15232,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'))
|
||||
@@ -16285,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'
|
||||
@@ -25230,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
|
||||
"""
|
||||
|
||||
@@ -25812,8 +25773,7 @@ class Effect6315(BaseEffect):
|
||||
eliteBonusCommandDestroyerSkirmish1
|
||||
|
||||
Used by:
|
||||
Ship: Bifrost
|
||||
Ship: Magus
|
||||
Ships from group: Command Destroyer (3 of 5)
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
@@ -25862,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'
|
||||
@@ -26135,8 +26095,7 @@ class Effect6334(BaseEffect):
|
||||
eliteBonusCommandDestroyerInfo1
|
||||
|
||||
Used by:
|
||||
Ship: Pontifex
|
||||
Ship: Stork
|
||||
Ships from group: Command Destroyer (3 of 5)
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
@@ -32253,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'
|
||||
@@ -33802,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'
|
||||
@@ -33969,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'
|
||||
@@ -34853,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'
|
||||
@@ -34870,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'
|
||||
@@ -34919,8 +34879,8 @@ class Effect7085(BaseEffect):
|
||||
shipbonusPCTDamagePC1
|
||||
|
||||
Used by:
|
||||
Variations of ship: Vedmak (2 of 2)
|
||||
Ship: Tiamat
|
||||
Ship: Vedmak
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
@@ -34936,8 +34896,8 @@ class Effect7086(BaseEffect):
|
||||
shipbonusPCTTrackingPC2
|
||||
|
||||
Used by:
|
||||
Variations of ship: Vedmak (2 of 2)
|
||||
Ship: Tiamat
|
||||
Ship: Vedmak
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
@@ -34953,7 +34913,6 @@ class Effect7087(BaseEffect):
|
||||
shipbonusPCTOptimalPF2
|
||||
|
||||
Used by:
|
||||
Ship: Damavik
|
||||
Ship: Hydra
|
||||
"""
|
||||
|
||||
@@ -34970,7 +34929,7 @@ class Effect7088(BaseEffect):
|
||||
shipbonusPCTDamagePF1
|
||||
|
||||
Used by:
|
||||
Ship: Damavik
|
||||
Variations of ship: Damavik (2 of 2)
|
||||
Ship: Hydra
|
||||
"""
|
||||
|
||||
@@ -35002,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'
|
||||
@@ -35024,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'
|
||||
@@ -35047,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'
|
||||
@@ -35085,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'
|
||||
@@ -35103,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'
|
||||
@@ -35215,7 +35174,7 @@ class Effect7154(BaseEffect):
|
||||
shipBonusPD1DisintegratorDamage
|
||||
|
||||
Used by:
|
||||
Ship: Kikimora
|
||||
Variations of ship: Kikimora (2 of 2)
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
@@ -35265,7 +35224,7 @@ class Effect7157(BaseEffect):
|
||||
shipBonusPD2DisintegratorMaxRange
|
||||
|
||||
Used by:
|
||||
Ship: Kikimora
|
||||
Variations of ship: Kikimora (2 of 2)
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
@@ -35571,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):
|
||||
@@ -35621,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):
|
||||
...
|
||||
@@ -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
|
||||
@@ -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,
|
||||
|
||||
@@ -1547,8 +1547,9 @@ class Fit(object):
|
||||
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)
|
||||
|
||||
@@ -819,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):
|
||||
@@ -887,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
|
||||
|
||||
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)
|
||||
@@ -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
|
||||
|
||||
@@ -123,7 +123,8 @@ class AddEnvironmentEffect(ContextMenuUnconditional):
|
||||
|
||||
# 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(ContextMenuUnconditional):
|
||||
"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")
|
||||
|
||||
@@ -54,7 +54,7 @@ class AddImplantSet(ContextMenuSingle):
|
||||
|
||||
self.idmap = {}
|
||||
|
||||
for set in implantSets:
|
||||
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)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import math
|
||||
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
|
||||
@@ -48,15 +50,44 @@ class ChangeModuleSpool(ContextMenuSingle):
|
||||
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):
|
||||
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)
|
||||
|
||||
@@ -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):
|
||||
|
||||
|
||||
@@ -26,8 +26,12 @@ class ItemMutatorPanel(wx.Panel):
|
||||
|
||||
headerSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
headerSizer.AddStretchSpacer()
|
||||
headerSizer.Add(BitmapLoader.getStaticBitmap(mod.item.iconID, self, "icons"), 0, 0, 0)
|
||||
headerSizer.Add(BitmapLoader.getStaticBitmap(mod.mutaplasmid.item.iconID, self, "icons"), 0, wx.LEFT, 0)
|
||||
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()
|
||||
@@ -81,6 +85,7 @@ class ItemMutatorList(wx.ScrolledWindow):
|
||||
self.event_mapping = {}
|
||||
higOverrides = {
|
||||
('Stasis Web', 'speedFactor'): False,
|
||||
('Damage Control', 'duration'): True,
|
||||
}
|
||||
|
||||
first = True
|
||||
|
||||
@@ -291,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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
@@ -528,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"):
|
||||
|
||||
@@ -256,7 +256,8 @@ class CharacterEditor(wx.Frame):
|
||||
|
||||
def kbEvent(self, event):
|
||||
keycode = event.GetKeyCode()
|
||||
if keycode == wx.WXK_ESCAPE:
|
||||
mstate = wx.GetMouseState()
|
||||
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
self.closeWindow()
|
||||
return
|
||||
event.Skip()
|
||||
|
||||
@@ -67,8 +67,8 @@ class ContextMenu(metaclass=ABCMeta):
|
||||
(('marketItemGroup', 'Implant'),)
|
||||
(('fittingShip', 'Ship'),)
|
||||
"""
|
||||
cls._idxid = -1
|
||||
debug_start = len(cls._ids)
|
||||
ContextMenu._idxid = -1
|
||||
debug_start = len(ContextMenu._ids)
|
||||
|
||||
rootMenu = wx.Menu()
|
||||
rootMenu.info = {}
|
||||
@@ -95,7 +95,7 @@ class ContextMenu(metaclass=ABCMeta):
|
||||
bitmap = m._baseGetBitmap(srcContext, mainItem, selection)
|
||||
multiple = not isinstance(bitmap, wx.Bitmap)
|
||||
for it, text in enumerate(texts):
|
||||
id = cls.nextID()
|
||||
id = ContextMenu.nextID()
|
||||
check = m.checked
|
||||
rootItem = wx.MenuItem(rootMenu, id, text, kind=wx.ITEM_NORMAL if m.checked is None else wx.ITEM_CHECK)
|
||||
rootMenu.info[id] = (m, fullContext, it)
|
||||
@@ -104,7 +104,7 @@ class ContextMenu(metaclass=ABCMeta):
|
||||
|
||||
if sub is None:
|
||||
# if there is no sub menu, bind the handler to the rootItem
|
||||
rootMenu.Bind(wx.EVT_MENU, cls.handler, rootItem)
|
||||
rootMenu.Bind(wx.EVT_MENU, ContextMenu.handler, rootItem)
|
||||
elif sub:
|
||||
# If sub exists and is not False, set submenu.
|
||||
# Sub might return False when we have a mix of
|
||||
@@ -141,14 +141,14 @@ class ContextMenu(metaclass=ABCMeta):
|
||||
if display_amount > 0 and i != len(fullContexts) - 1:
|
||||
rootMenu.AppendSeparator()
|
||||
|
||||
debug_end = len(cls._ids)
|
||||
debug_end = len(ContextMenu._ids)
|
||||
if debug_end - debug_start:
|
||||
pyfalog.debug("{} new IDs created for this menu".format(debug_end - debug_start))
|
||||
|
||||
return rootMenu if empty is False else None
|
||||
|
||||
@classmethod
|
||||
def handler(cls, event):
|
||||
@staticmethod
|
||||
def handler(event):
|
||||
menu = event.EventObject
|
||||
stuff = menu.info.get(event.Id)
|
||||
if stuff is not None:
|
||||
@@ -162,21 +162,22 @@ class ContextMenu(metaclass=ABCMeta):
|
||||
else:
|
||||
event.Skip()
|
||||
|
||||
@classmethod
|
||||
def nextID(cls):
|
||||
@staticmethod
|
||||
def nextID():
|
||||
"""
|
||||
Fetches an ID from the pool of IDs allocated to Context Menu.
|
||||
If we don't have enough ID's to fulfill request, create new
|
||||
ID and add it to the pool.
|
||||
|
||||
See GH Issue #589
|
||||
See GH Issue #589.
|
||||
Has to be static method to properly handle modifications of primitives from subclasses (_idxid).
|
||||
"""
|
||||
cls._idxid += 1
|
||||
ContextMenu._idxid += 1
|
||||
|
||||
if cls._idxid >= len(cls._ids): # We don't ahve an ID for this index, create one
|
||||
cls._ids.append(wx.NewId())
|
||||
if ContextMenu._idxid >= len(ContextMenu._ids): # We don't ahve an ID for this index, create one
|
||||
ContextMenu._ids.append(wx.NewId())
|
||||
|
||||
return cls._ids[cls._idxid]
|
||||
return ContextMenu._ids[ContextMenu._idxid]
|
||||
|
||||
@property
|
||||
def checked(self):
|
||||
|
||||
@@ -23,13 +23,14 @@ from collections import OrderedDict
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
|
||||
from eos.db import getFit
|
||||
from gui.utils.clipboard import toClipboard
|
||||
from service.const import PortMultiBuyOptions
|
||||
from service.port import EfsPort, Port
|
||||
from service.port.dna import DNA_OPTIONS
|
||||
from service.port.eft import EFT_OPTIONS
|
||||
from service.port.multibuy import MULTIBUY_OPTIONS
|
||||
from service.settings import SettingsProvider
|
||||
from service.port import EfsPort, Port
|
||||
from service.const import PortMultiBuyOptions
|
||||
from eos.db import getFit
|
||||
from gui.utils.clipboard import toClipboard
|
||||
|
||||
|
||||
class CopySelectDialog(wx.Dialog):
|
||||
@@ -60,9 +61,9 @@ class CopySelectDialog(wx.Dialog):
|
||||
("EFT", (CopySelectDialog.copyFormatEft, EFT_OPTIONS)),
|
||||
("MultiBuy", (CopySelectDialog.copyFormatMultiBuy, MULTIBUY_OPTIONS)),
|
||||
("ESI", (CopySelectDialog.copyFormatEsi, None)),
|
||||
("DNA", (CopySelectDialog.copyFormatDna, DNA_OPTIONS)),
|
||||
("EFS", (CopySelectDialog.copyFormatEfs, None)),
|
||||
# ("XML", (CopySelectDialog.copyFormatXml, None)),
|
||||
# ("DNA", (CopySelectDialog.copyFormatDna, None)),
|
||||
))
|
||||
|
||||
defaultFormatOptions = {}
|
||||
@@ -99,6 +100,8 @@ class CopySelectDialog(wx.Dialog):
|
||||
|
||||
for optId, optName, optDesc, _ in formatOptions:
|
||||
checkbox = wx.CheckBox(self, -1, optName)
|
||||
if optDesc:
|
||||
checkbox.SetToolTip(wx.ToolTip(optDesc))
|
||||
self.options[formatId][optId] = checkbox
|
||||
if self.settings['options'].get(formatId, {}).get(optId, defaultFormatOptions.get(formatId, {}).get(optId)):
|
||||
checkbox.SetValue(True)
|
||||
@@ -166,7 +169,7 @@ class CopySelectDialog(wx.Dialog):
|
||||
|
||||
def exportDna(self, options, callback):
|
||||
fit = getFit(self.mainFrame.getActiveFit())
|
||||
Port.exportDna(fit, callback)
|
||||
Port.exportDna(fit, options, callback)
|
||||
|
||||
def exportEsi(self, options, callback):
|
||||
fit = getFit(self.mainFrame.getActiveFit())
|
||||
|
||||
@@ -96,7 +96,8 @@ class EveFittings(wx.Frame):
|
||||
|
||||
def kbEvent(self, event):
|
||||
keycode = event.GetKeyCode()
|
||||
if keycode == wx.WXK_ESCAPE:
|
||||
mstate = wx.GetMouseState()
|
||||
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
self.closeWindow()
|
||||
return
|
||||
event.Skip()
|
||||
@@ -250,7 +251,8 @@ class ExportToEve(wx.Frame):
|
||||
|
||||
def kbEvent(self, event):
|
||||
keycode = event.GetKeyCode()
|
||||
if keycode == wx.WXK_ESCAPE:
|
||||
mstate = wx.GetMouseState()
|
||||
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
self.closeWindow()
|
||||
return
|
||||
event.Skip()
|
||||
@@ -353,7 +355,8 @@ class SsoCharacterMgmt(wx.Dialog):
|
||||
|
||||
def kbEvent(self, event):
|
||||
keycode = event.GetKeyCode()
|
||||
if keycode == wx.WXK_ESCAPE:
|
||||
mstate = wx.GetMouseState()
|
||||
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
self.closeWindow()
|
||||
return
|
||||
event.Skip()
|
||||
|
||||
@@ -2,7 +2,6 @@ import wx
|
||||
from logbook import Logger
|
||||
|
||||
import eos.db
|
||||
from eos.exception import HandledListActionError
|
||||
from service.fit import Fit
|
||||
|
||||
|
||||
@@ -35,9 +34,8 @@ class CalcAddBoosterCommand(wx.Command):
|
||||
self.oldPosition, self.oldBoosterInfo = fit.boosters.makeRoom(newBooster)
|
||||
|
||||
if self.newPosition is not None:
|
||||
try:
|
||||
fit.boosters.insert(self.newPosition, newBooster)
|
||||
except HandledListActionError:
|
||||
fit.boosters.insert(self.newPosition, newBooster)
|
||||
if newBooster not in fit.boosters:
|
||||
pyfalog.warning('Failed to insert to list')
|
||||
cmd = CalcAddBoosterCommand(
|
||||
fitID=self.fitID,
|
||||
@@ -47,9 +45,8 @@ class CalcAddBoosterCommand(wx.Command):
|
||||
cmd.Do()
|
||||
return False
|
||||
else:
|
||||
try:
|
||||
fit.boosters.append(newBooster)
|
||||
except HandledListActionError:
|
||||
fit.boosters.append(newBooster)
|
||||
if newBooster not in fit.boosters:
|
||||
pyfalog.warning('Failed to append to list')
|
||||
cmd = CalcAddBoosterCommand(
|
||||
fitID=self.fitID,
|
||||
|
||||
@@ -2,7 +2,6 @@ import wx
|
||||
from logbook import Logger
|
||||
|
||||
import eos.db
|
||||
from eos.exception import HandledListActionError
|
||||
from service.fit import Fit
|
||||
|
||||
|
||||
@@ -25,9 +24,8 @@ class CalcAddCargoCommand(wx.Command):
|
||||
cargo.amount += self.cargoInfo.amount
|
||||
else:
|
||||
cargo = self.cargoInfo.toCargo()
|
||||
try:
|
||||
fit.cargo.append(cargo)
|
||||
except HandledListActionError:
|
||||
fit.cargo.append(cargo)
|
||||
if cargo not in fit.cargo:
|
||||
pyfalog.warning('Failed to append to list')
|
||||
if self.commit:
|
||||
eos.db.commit()
|
||||
|
||||
@@ -3,7 +3,6 @@ import wx
|
||||
from logbook import Logger
|
||||
|
||||
import eos.db
|
||||
from eos.exception import HandledListActionError
|
||||
from gui.fitCommands.helpers import DroneInfo, droneStackLimit
|
||||
from service.fit import Fit
|
||||
from service.market import Market
|
||||
@@ -50,9 +49,8 @@ class CalcAddLocalDroneCommand(wx.Command):
|
||||
if not self.ignoreRestrictions and not drone.fits(fit):
|
||||
pyfalog.warning('Drone does not fit')
|
||||
return False
|
||||
try:
|
||||
fit.drones.append(drone)
|
||||
except HandledListActionError:
|
||||
fit.drones.append(drone)
|
||||
if drone not in fit.drones:
|
||||
pyfalog.warning('Failed to append to list')
|
||||
if self.commit:
|
||||
eos.db.commit()
|
||||
|
||||
@@ -2,7 +2,6 @@ import wx
|
||||
from logbook import Logger
|
||||
|
||||
import eos.db
|
||||
from eos.exception import HandledListActionError
|
||||
from gui.fitCommands.helpers import DroneInfo
|
||||
from service.fit import Fit
|
||||
|
||||
@@ -48,9 +47,8 @@ class CalcRemoveLocalDroneCommand(wx.Command):
|
||||
drone = self.savedDroneInfo.toDrone()
|
||||
if drone is None:
|
||||
return False
|
||||
try:
|
||||
fit.drones.insert(self.position, drone)
|
||||
except HandledListActionError:
|
||||
fit.drones.insert(self.position, drone)
|
||||
if drone not in fit.drones:
|
||||
pyfalog.warning('Failed to insert to list')
|
||||
if self.commit:
|
||||
eos.db.commit()
|
||||
|
||||
@@ -4,7 +4,6 @@ import wx
|
||||
from logbook import Logger
|
||||
|
||||
import eos.db
|
||||
from eos.exception import HandledListActionError
|
||||
from gui.fitCommands.helpers import DroneInfo
|
||||
from service.fit import Fit
|
||||
|
||||
@@ -43,9 +42,8 @@ class CalcAddProjectedDroneCommand(wx.Command):
|
||||
if not drone.item.isType('projected'):
|
||||
pyfalog.debug('Drone is not projectable')
|
||||
return False
|
||||
try:
|
||||
fit.projectedDrones.append(drone)
|
||||
except HandledListActionError:
|
||||
fit.projectedDrones.append(drone)
|
||||
if drone not in fit.projectedDrones:
|
||||
pyfalog.warning('Failed to append to list')
|
||||
if self.commit:
|
||||
eos.db.commit()
|
||||
|
||||
@@ -2,7 +2,6 @@ import wx
|
||||
from logbook import Logger
|
||||
|
||||
import eos.db
|
||||
from eos.exception import HandledListActionError
|
||||
from service.fit import Fit
|
||||
|
||||
|
||||
@@ -41,18 +40,16 @@ class CalcAddLocalFighterCommand(wx.Command):
|
||||
fighter.active = True
|
||||
|
||||
if self.position is None:
|
||||
try:
|
||||
fit.fighters.append(fighter)
|
||||
except HandledListActionError:
|
||||
fit.fighters.append(fighter)
|
||||
if fighter not in fit.fighters:
|
||||
pyfalog.warning('Failed to append to list')
|
||||
if self.commit:
|
||||
eos.db.commit()
|
||||
return False
|
||||
self.position = fit.fighters.index(fighter)
|
||||
else:
|
||||
try:
|
||||
fit.fighters.insert(self.position, fighter)
|
||||
except HandledListActionError:
|
||||
fit.fighters.insert(self.position, fighter)
|
||||
if fighter not in fit.fighters:
|
||||
pyfalog.warning('Failed to insert to list')
|
||||
if self.commit:
|
||||
eos.db.commit()
|
||||
|
||||
@@ -2,7 +2,6 @@ import wx
|
||||
from logbook import Logger
|
||||
|
||||
import eos.db
|
||||
from eos.exception import HandledListActionError
|
||||
from service.fit import Fit
|
||||
|
||||
|
||||
@@ -25,16 +24,14 @@ class CalcAddProjectedFighterCommand(wx.Command):
|
||||
return False
|
||||
fit = Fit.getInstance().getFit(self.fitID)
|
||||
if self.position is not None:
|
||||
try:
|
||||
fit.projectedFighters.insert(self.position, fighter)
|
||||
except HandledListActionError:
|
||||
fit.projectedFighters.insert(self.position, fighter)
|
||||
if fighter not in fit.projectedFighters:
|
||||
if self.commit:
|
||||
eos.db.commit()
|
||||
return False
|
||||
else:
|
||||
try:
|
||||
fit.projectedFighters.append(fighter)
|
||||
except HandledListActionError:
|
||||
fit.projectedFighters.append(fighter)
|
||||
if fighter not in fit.projectedFighters:
|
||||
if self.commit:
|
||||
eos.db.commit()
|
||||
return False
|
||||
|
||||
@@ -2,7 +2,6 @@ import wx
|
||||
from logbook import Logger
|
||||
|
||||
import eos.db
|
||||
from eos.exception import HandledListActionError
|
||||
from service.fit import Fit
|
||||
|
||||
|
||||
@@ -35,9 +34,8 @@ class CalcAddImplantCommand(wx.Command):
|
||||
self.oldPosition, self.oldImplantInfo = fit.implants.makeRoom(newImplant)
|
||||
|
||||
if self.newPosition is not None:
|
||||
try:
|
||||
fit.implants.insert(self.newPosition, newImplant)
|
||||
except HandledListActionError:
|
||||
fit.implants.insert(self.newPosition, newImplant)
|
||||
if newImplant not in fit.implants:
|
||||
pyfalog.warning('Failed to insert to list')
|
||||
cmd = CalcAddImplantCommand(
|
||||
fitID=self.fitID,
|
||||
@@ -47,9 +45,8 @@ class CalcAddImplantCommand(wx.Command):
|
||||
cmd.Do()
|
||||
return False
|
||||
else:
|
||||
try:
|
||||
fit.implants.append(newImplant)
|
||||
except HandledListActionError:
|
||||
fit.implants.append(newImplant)
|
||||
if newImplant not in fit.implants:
|
||||
pyfalog.warning('Failed to append to list')
|
||||
cmd = CalcAddImplantCommand(
|
||||
fitID=self.fitID,
|
||||
|
||||
@@ -2,8 +2,7 @@ import wx
|
||||
from logbook import Logger
|
||||
|
||||
import eos.db
|
||||
from eos.exception import HandledListActionError
|
||||
from gui.fitCommands.helpers import restoreCheckedStates, stateLimit
|
||||
from gui.fitCommands.helpers import restoreCheckedStates, activeStateLimit
|
||||
from service.fit import Fit
|
||||
|
||||
|
||||
@@ -26,7 +25,7 @@ class CalcAddLocalModuleCommand(wx.Command):
|
||||
sFit = Fit.getInstance()
|
||||
fit = sFit.getFit(self.fitID)
|
||||
|
||||
newMod = self.newModInfo.toModule(fallbackState=stateLimit(self.newModInfo.itemID))
|
||||
newMod = self.newModInfo.toModule(fallbackState=activeStateLimit(self.newModInfo.itemID))
|
||||
if newMod is None:
|
||||
return False
|
||||
|
||||
@@ -55,9 +54,8 @@ class CalcAddLocalModuleCommand(wx.Command):
|
||||
if not newMod.fits(fit):
|
||||
pyfalog.warning('Module does not fit')
|
||||
return False
|
||||
try:
|
||||
fit.modules.append(newMod)
|
||||
except HandledListActionError:
|
||||
fit.modules.append(newMod)
|
||||
if newMod not in fit.modules:
|
||||
pyfalog.warning('Failed to append to list')
|
||||
if self.commit:
|
||||
eos.db.commit()
|
||||
|
||||
@@ -4,7 +4,6 @@ import wx
|
||||
from logbook import Logger
|
||||
|
||||
import eos.db
|
||||
from eos.exception import HandledListActionError
|
||||
from gui.fitCommands.helpers import restoreCheckedStates
|
||||
from service.fit import Fit
|
||||
|
||||
@@ -31,9 +30,8 @@ class CalcCloneLocalModuleCommand(wx.Command):
|
||||
return False
|
||||
if not fit.modules[self.dstPosition].isEmpty:
|
||||
return False
|
||||
try:
|
||||
fit.modules.replace(self.dstPosition, copyMod)
|
||||
except HandledListActionError:
|
||||
fit.modules.replace(self.dstPosition, copyMod)
|
||||
if copyMod not in fit.modules:
|
||||
pyfalog.warning('Failed to replace module')
|
||||
eos.db.commit()
|
||||
return False
|
||||
|
||||
@@ -2,8 +2,7 @@ import wx
|
||||
from logbook import Logger
|
||||
|
||||
import eos.db
|
||||
from eos.exception import HandledListActionError
|
||||
from gui.fitCommands.helpers import ModuleInfo, restoreCheckedStates, stateLimit
|
||||
from gui.fitCommands.helpers import ModuleInfo, restoreCheckedStates, activeStateLimit
|
||||
from service.fit import Fit
|
||||
|
||||
|
||||
@@ -34,7 +33,7 @@ class CalcReplaceLocalModuleCommand(wx.Command):
|
||||
self.oldModInfo = ModuleInfo.fromModule(oldMod)
|
||||
if self.newModInfo == self.oldModInfo:
|
||||
return False
|
||||
newMod = self.newModInfo.toModule(fallbackState=stateLimit(self.newModInfo.itemID))
|
||||
newMod = self.newModInfo.toModule(fallbackState=activeStateLimit(self.newModInfo.itemID))
|
||||
if newMod is None:
|
||||
return False
|
||||
if newMod.slot != oldMod.slot:
|
||||
@@ -53,9 +52,8 @@ class CalcReplaceLocalModuleCommand(wx.Command):
|
||||
pyfalog.warning('Invalid charge')
|
||||
self.Undo()
|
||||
return False
|
||||
try:
|
||||
fit.modules.replace(self.position, newMod)
|
||||
except HandledListActionError:
|
||||
fit.modules.replace(self.position, newMod)
|
||||
if newMod not in fit.modules:
|
||||
pyfalog.warning('Failed to replace in list')
|
||||
self.Undo()
|
||||
return False
|
||||
@@ -87,9 +85,8 @@ class CalcReplaceLocalModuleCommand(wx.Command):
|
||||
if oldMod is None:
|
||||
return False
|
||||
fit.modules.free(self.position)
|
||||
try:
|
||||
fit.modules.replace(self.position, oldMod)
|
||||
except HandledListActionError:
|
||||
fit.modules.replace(self.position, oldMod)
|
||||
if oldMod not in fit.modules:
|
||||
pyfalog.warning('Failed to replace in list')
|
||||
self.Do()
|
||||
return False
|
||||
|
||||
@@ -2,7 +2,6 @@ import wx
|
||||
from logbook import Logger
|
||||
|
||||
import eos.db
|
||||
from eos.exception import HandledListActionError
|
||||
from service.fit import Fit
|
||||
|
||||
|
||||
@@ -33,16 +32,14 @@ class CalcSwapLocalModuleCommand(wx.Command):
|
||||
mod2 = fit.modules[position2]
|
||||
fit.modules.free(position1)
|
||||
fit.modules.free(position2)
|
||||
try:
|
||||
fit.modules.replace(position2, mod1)
|
||||
except HandledListActionError:
|
||||
fit.modules.replace(position2, mod1)
|
||||
if len(fit.modules) <= position2 or fit.modules[position2] is not mod1:
|
||||
fit.modules.replace(position1, mod1)
|
||||
fit.modules.replace(position2, mod2)
|
||||
eos.db.commit()
|
||||
return False
|
||||
try:
|
||||
fit.modules.replace(position1, mod2)
|
||||
except HandledListActionError:
|
||||
fit.modules.replace(position1, mod2)
|
||||
if len(fit.modules) <= position1 or fit.modules[position1] is not mod2:
|
||||
fit.modules.free(position2)
|
||||
fit.modules.replace(position1, mod1)
|
||||
fit.modules.replace(position2, mod2)
|
||||
|
||||
@@ -3,7 +3,6 @@ from logbook import Logger
|
||||
|
||||
import eos.db
|
||||
from eos.const import FittingModuleState
|
||||
from eos.exception import HandledListActionError
|
||||
from gui.fitCommands.helpers import restoreCheckedStates
|
||||
from service.fit import Fit
|
||||
|
||||
@@ -39,16 +38,14 @@ class CalcAddProjectedModuleCommand(wx.Command):
|
||||
self.oldPosition, self.oldModInfo = fit.projectedModules.makeRoom(newMod)
|
||||
|
||||
if self.newPosition is not None:
|
||||
try:
|
||||
fit.projectedModules.insert(self.newPosition, newMod)
|
||||
except HandledListActionError:
|
||||
fit.projectedModules.insert(self.newPosition, newMod)
|
||||
if newMod not in fit.projectedModules:
|
||||
if self.commit:
|
||||
eos.db.commit()
|
||||
return False
|
||||
else:
|
||||
try:
|
||||
fit.projectedModules.append(newMod)
|
||||
except HandledListActionError:
|
||||
fit.projectedModules.append(newMod)
|
||||
if newMod not in fit.projectedModules:
|
||||
if self.commit:
|
||||
eos.db.commit()
|
||||
return False
|
||||
|
||||
@@ -317,9 +317,12 @@ class CargoInfo:
|
||||
return makeReprStr(self, ['itemID', 'amount'])
|
||||
|
||||
|
||||
def stateLimit(itemIdentity):
|
||||
def activeStateLimit(itemIdentity):
|
||||
item = Market.getInstance().getItem(itemIdentity)
|
||||
if {'moduleBonusAssaultDamageControl', 'moduleBonusIndustrialInvulnerability'}.intersection(item.effects):
|
||||
if {
|
||||
'moduleBonusAssaultDamageControl', 'moduleBonusIndustrialInvulnerability',
|
||||
'microJumpDrive', 'microJumpPortalDrive'
|
||||
}.intersection(item.effects):
|
||||
return FittingModuleState.ONLINE
|
||||
return FittingModuleState.ACTIVE
|
||||
|
||||
|
||||
@@ -34,6 +34,10 @@ class Graph(object):
|
||||
def getIcons(self):
|
||||
return None
|
||||
|
||||
@property
|
||||
def redrawOnEffectiveChange(self):
|
||||
return False
|
||||
|
||||
|
||||
# noinspection PyUnresolvedReferences
|
||||
from gui.builtinGraphs import fitDps # noqa: E402, F401
|
||||
from gui.builtinGraphs import *
|
||||
|
||||
@@ -18,18 +18,19 @@
|
||||
# =============================================================================
|
||||
|
||||
import os
|
||||
from logbook import Logger
|
||||
import traceback
|
||||
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
from logbook import Logger
|
||||
|
||||
from service.fit import Fit
|
||||
import gui.display
|
||||
import gui.mainFrame
|
||||
import gui.globalEvents as GE
|
||||
from gui.graph import Graph
|
||||
import gui.mainFrame
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
import traceback
|
||||
from gui.graph import Graph
|
||||
from service.fit import Fit
|
||||
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
@@ -69,6 +70,7 @@ except Exception:
|
||||
class GraphFrame(wx.Frame):
|
||||
|
||||
def __init__(self, parent, style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE | wx.FRAME_FLOAT_ON_PARENT):
|
||||
|
||||
global graphFrame_enabled
|
||||
global mplImported
|
||||
global mpl_version
|
||||
@@ -159,10 +161,19 @@ class GraphFrame(wx.Frame):
|
||||
self.mainSizer.Add(self.sl1, 0, wx.EXPAND)
|
||||
self.mainSizer.Add(self.fitList, 0, wx.EXPAND)
|
||||
|
||||
self.fitList.fitList.Bind(wx.EVT_LEFT_DCLICK, self.removeItem)
|
||||
self.fitList.fitList.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDClick)
|
||||
self.fitList.fitList.Bind(wx.EVT_CONTEXT_MENU, self.OnContextMenu)
|
||||
self.mainFrame.Bind(GE.FIT_CHANGED, self.draw)
|
||||
self.Bind(wx.EVT_CLOSE, self.closeEvent)
|
||||
self.Bind(wx.EVT_CHAR_HOOK, self.kbEvent)
|
||||
self.Bind(wx.EVT_CHOICE, self.graphChanged)
|
||||
from gui.builtinStatsViews.resistancesViewFull import EFFECTIVE_HP_TOGGLED # Grr crclar gons
|
||||
self.mainFrame.Bind(EFFECTIVE_HP_TOGGLED, self.ehpToggled)
|
||||
|
||||
self.contextMenu = wx.Menu()
|
||||
removeItem = wx.MenuItem(self.contextMenu, 1, 'Remove Fit')
|
||||
self.contextMenu.Append(removeItem)
|
||||
self.contextMenu.Bind(wx.EVT_MENU, self.ContextMenuHandler, removeItem)
|
||||
|
||||
self.Fit()
|
||||
self.SetMinSize(self.GetSize())
|
||||
@@ -177,14 +188,41 @@ class GraphFrame(wx.Frame):
|
||||
|
||||
def kbEvent(self, event):
|
||||
keycode = event.GetKeyCode()
|
||||
if keycode == wx.WXK_ESCAPE:
|
||||
mstate = wx.GetMouseState()
|
||||
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
self.closeWindow()
|
||||
return
|
||||
elif keycode == 65 and mstate.GetModifiers() == wx.MOD_CONTROL:
|
||||
self.fitList.fitList.selectAll()
|
||||
elif keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE) and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
self.removeFits(self.getSelectedFits())
|
||||
event.Skip()
|
||||
|
||||
def OnContextMenu(self, event):
|
||||
if self.getSelectedFits():
|
||||
self.PopupMenu(self.contextMenu)
|
||||
|
||||
def ContextMenuHandler(self, event):
|
||||
selectedMenuItem = event.GetId()
|
||||
if selectedMenuItem == 1: # Copy was chosen
|
||||
fits = self.getSelectedFits()
|
||||
self.removeFits(fits)
|
||||
|
||||
def ehpToggled(self, event):
|
||||
event.Skip()
|
||||
view = self.getView()
|
||||
if view.redrawOnEffectiveChange:
|
||||
self.draw()
|
||||
|
||||
def graphChanged(self, event):
|
||||
self.select(self.graphSelection.GetSelection())
|
||||
event.Skip()
|
||||
|
||||
def closeWindow(self):
|
||||
self.fitList.fitList.Unbind(wx.EVT_LEFT_DCLICK, handler=self.removeItem)
|
||||
from gui.builtinStatsViews.resistancesViewFull import EFFECTIVE_HP_TOGGLED # Grr crclar gons
|
||||
self.fitList.fitList.Unbind(wx.EVT_LEFT_DCLICK, handler=self.OnLeftDClick)
|
||||
self.mainFrame.Unbind(GE.FIT_CHANGED, handler=self.draw)
|
||||
self.mainFrame.Unbind(EFFECTIVE_HP_TOGGLED, handler=self.ehpToggled)
|
||||
self.Destroy()
|
||||
|
||||
def getView(self):
|
||||
@@ -202,6 +240,7 @@ class GraphFrame(wx.Frame):
|
||||
icons = view.getIcons()
|
||||
labels = view.getLabels()
|
||||
sizer = self.gridSizer
|
||||
sizer.Clear()
|
||||
self.gridPanel.DestroyChildren()
|
||||
self.fields.clear()
|
||||
|
||||
@@ -237,6 +276,7 @@ class GraphFrame(wx.Frame):
|
||||
imgLabelSizer.Add(wx.StaticText(self.gridPanel, wx.ID_ANY, label), 0,
|
||||
wx.LEFT | wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, 3)
|
||||
sizer.Add(imgLabelSizer, 0, wx.ALIGN_CENTER_VERTICAL)
|
||||
sizer.Layout()
|
||||
self.draw()
|
||||
|
||||
def draw(self, event=None):
|
||||
@@ -257,6 +297,8 @@ class GraphFrame(wx.Frame):
|
||||
self.subplot.grid(True)
|
||||
legend = []
|
||||
|
||||
min_y = 0
|
||||
max_y = 0
|
||||
for fit in self.fits:
|
||||
try:
|
||||
success, status = view.getPoints(fit, values)
|
||||
@@ -266,6 +308,8 @@ class GraphFrame(wx.Frame):
|
||||
return
|
||||
|
||||
x, y = success, status
|
||||
min_y = min(min_y, min(y, default=0))
|
||||
max_y = max(max_y, max(y, default=0))
|
||||
|
||||
self.subplot.plot(x, y)
|
||||
legend.append(fit.name)
|
||||
@@ -275,6 +319,14 @@ class GraphFrame(wx.Frame):
|
||||
self.canvas.draw()
|
||||
return
|
||||
|
||||
y_range = max_y - min_y
|
||||
min_y -= y_range * 0.05
|
||||
max_y += y_range * 0.05
|
||||
if min_y == max_y:
|
||||
min_y -= 5
|
||||
max_y += 5
|
||||
self.subplot.set_ylim(bottom=min_y, top=max_y)
|
||||
|
||||
if mpl_version < 2:
|
||||
if self.legendFix and len(legend) > 0:
|
||||
leg = self.subplot.legend(tuple(legend), "upper right", shadow=False)
|
||||
@@ -334,15 +386,38 @@ class GraphFrame(wx.Frame):
|
||||
self.fitList.fitList.update(self.fits)
|
||||
self.draw()
|
||||
|
||||
def removeItem(self, event):
|
||||
def OnLeftDClick(self, event):
|
||||
row, _ = self.fitList.fitList.HitTest(event.Position)
|
||||
if row != -1:
|
||||
del self.fits[row]
|
||||
self.fitList.fitList.update(self.fits)
|
||||
self.draw()
|
||||
try:
|
||||
fit = self.fits[row]
|
||||
except IndexError:
|
||||
pass
|
||||
else:
|
||||
self.removeFits([fit])
|
||||
|
||||
def removeFits(self, fits):
|
||||
toRemove = [f for f in fits if f in self.fits]
|
||||
if not toRemove:
|
||||
return
|
||||
for fit in toRemove:
|
||||
self.fits.remove(fit)
|
||||
self.fitList.fitList.update(self.fits)
|
||||
self.draw()
|
||||
|
||||
def getSelectedFits(self):
|
||||
fits = []
|
||||
for row in self.fitList.fitList.getSelectedRows():
|
||||
try:
|
||||
fit = self.fits[row]
|
||||
except IndexError:
|
||||
continue
|
||||
fits.append(fit)
|
||||
return fits
|
||||
|
||||
|
||||
class FitList(wx.Panel):
|
||||
|
||||
def __init__(self, parent):
|
||||
wx.Panel.__init__(self, parent)
|
||||
self.mainSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
|
||||
@@ -140,14 +140,15 @@ class ItemStatsDialog(wx.Dialog):
|
||||
|
||||
def kbEvent(self, event):
|
||||
keycode = event.GetKeyCode()
|
||||
if keycode == wx.WXK_ESCAPE:
|
||||
mstate = wx.GetMouseState()
|
||||
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
self.closeWindow()
|
||||
return
|
||||
event.Skip()
|
||||
|
||||
def closeEvent(self, event):
|
||||
self.closeWindow()
|
||||
self.container.onParentClose()
|
||||
self.closeWindow()
|
||||
event.Skip()
|
||||
|
||||
def closeWindow(self):
|
||||
|
||||
@@ -134,7 +134,7 @@ class MarketBrowser(wx.Panel):
|
||||
@mode.setter
|
||||
def mode(self, newMode):
|
||||
oldMode = self.__mode
|
||||
if newMode == oldMode:
|
||||
if newMode == oldMode != 'search':
|
||||
return
|
||||
# Store meta button states when switching from normal
|
||||
if oldMode == 'normal':
|
||||
|
||||
@@ -276,7 +276,8 @@ class DmgPatternEditorDlg(wx.Dialog):
|
||||
|
||||
def kbEvent(self, event):
|
||||
keycode = event.GetKeyCode()
|
||||
if keycode == wx.WXK_ESCAPE:
|
||||
mstate = wx.GetMouseState()
|
||||
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
self.closeWindow()
|
||||
return
|
||||
event.Skip()
|
||||
|
||||
@@ -82,7 +82,8 @@ class PreferenceDialog(wx.Dialog):
|
||||
|
||||
def kbEvent(self, event):
|
||||
keycode = event.GetKeyCode()
|
||||
if keycode == wx.WXK_ESCAPE:
|
||||
mstate = wx.GetMouseState()
|
||||
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
self.closeWindow()
|
||||
return
|
||||
event.Skip()
|
||||
|
||||
@@ -94,7 +94,8 @@ class AttributeEditor(wx.Frame):
|
||||
|
||||
def kbEvent(self, event):
|
||||
keycode = event.GetKeyCode()
|
||||
if keycode == wx.WXK_ESCAPE:
|
||||
mstate = wx.GetMouseState()
|
||||
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
self.closeWindow()
|
||||
return
|
||||
event.Skip()
|
||||
|
||||
@@ -296,7 +296,8 @@ class ResistsEditorDlg(wx.Dialog):
|
||||
|
||||
def kbEvent(self, event):
|
||||
keycode = event.GetKeyCode()
|
||||
if keycode == wx.WXK_ESCAPE:
|
||||
mstate = wx.GetMouseState()
|
||||
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
self.closeWindow()
|
||||
return
|
||||
event.Skip()
|
||||
|
||||
@@ -184,7 +184,8 @@ class ImplantSetEditorDlg(wx.Dialog):
|
||||
|
||||
def kbEvent(self, event):
|
||||
keycode = event.GetKeyCode()
|
||||
if keycode == wx.WXK_ESCAPE:
|
||||
mstate = wx.GetMouseState()
|
||||
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
self.closeWindow()
|
||||
return
|
||||
event.Skip()
|
||||
|
||||
BIN
imgs/renders/24469@1x.png
Normal file
BIN
imgs/renders/24469@1x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.2 KiB |
BIN
imgs/renders/24469@2x.png
Normal file
BIN
imgs/renders/24469@2x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
BIN
imgs/renders/24470@1x.png
Normal file
BIN
imgs/renders/24470@1x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.2 KiB |
BIN
imgs/renders/24470@2x.png
Normal file
BIN
imgs/renders/24470@2x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
BIN
imgs/renders/24471@1x.png
Normal file
BIN
imgs/renders/24471@1x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.2 KiB |
BIN
imgs/renders/24471@2x.png
Normal file
BIN
imgs/renders/24471@2x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
@@ -81,6 +81,14 @@ class PortEftRigSize(IntEnum):
|
||||
CAPITAL = 4
|
||||
|
||||
|
||||
@unique
|
||||
class PortDnaOptions(IntEnum):
|
||||
"""
|
||||
Contains different types of items for DNA export
|
||||
"""
|
||||
FORMATTING = 1
|
||||
|
||||
|
||||
@unique
|
||||
class GuiAttrGroup(IntEnum):
|
||||
"""
|
||||
@@ -101,4 +109,4 @@ class GuiAttrGroup(IntEnum):
|
||||
JUMP_SYSTEMS = auto()
|
||||
PROPULSIONS = auto()
|
||||
FIGHTERS = auto()
|
||||
SHIP_GROUP = auto()
|
||||
SHIP_GROUP = auto()
|
||||
|
||||
@@ -84,7 +84,7 @@ capac: 2500mm Repeating Cannon
|
||||
3500rt: 3500mm Artillery
|
||||
caprt: 3500mm Artillery
|
||||
dlp: Dual Light Pulse
|
||||
gpl: Gatling Pulse Laser
|
||||
# gpl: Gatling Pulse Laser - conflicts with giga pulse
|
||||
sfp: Small Focused Pulse
|
||||
fmp: Focused Medium Pulse
|
||||
hpl: Heavy Pulse Laser #also catches dual heavy
|
||||
@@ -110,7 +110,7 @@ hml: Heavy Missile Launcher #gets HAM + RHML launcher too?
|
||||
rhml: Rapid Heavy Missile Launcher
|
||||
cml: Cruise Missile Launcher
|
||||
tpl: Torpedo Launcher
|
||||
tl: Torpedo Launcher
|
||||
# tl: Torpedo Launcher - conflicts with tracking link entry
|
||||
rtl: Rapid Torpedo Launcher
|
||||
hawml: Rapid Torpedo Launcher
|
||||
hawtl: Rapid Torpedo Launcher
|
||||
@@ -124,6 +124,7 @@ dd: Doomsday
|
||||
rp: Reaper
|
||||
disco: Smartbomb
|
||||
sbomb: Smartbomb
|
||||
fvb: Focused Void Bomb
|
||||
|
||||
# Propulsion Modules
|
||||
ab: Afterburner
|
||||
|
||||
@@ -23,6 +23,7 @@ from collections import OrderedDict
|
||||
|
||||
from logbook import Logger
|
||||
|
||||
from eos.const import FittingModuleState, FittingSlot
|
||||
from eos.saveddata.cargo import Cargo
|
||||
from eos.saveddata.citadel import Citadel
|
||||
from eos.saveddata.drone import Drone
|
||||
@@ -30,13 +31,18 @@ from eos.saveddata.fighter import Fighter
|
||||
from eos.saveddata.fit import Fit
|
||||
from eos.saveddata.module import Module
|
||||
from eos.saveddata.ship import Ship
|
||||
from eos.const import FittingSlot, FittingModuleState
|
||||
from gui.fitCommands.helpers import activeStateLimit
|
||||
from service.const import PortDnaOptions
|
||||
from service.fit import Fit as svcFit
|
||||
from service.market import Market
|
||||
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
DNA_OPTIONS = (
|
||||
(PortDnaOptions.FORMATTING, 'Formatting Tags', 'Include formatting tags to paste fit directly into corp bulletins, MOTD, etc.', True),
|
||||
)
|
||||
|
||||
|
||||
def importDna(string, fitName=None):
|
||||
sMkt = Market.getInstance()
|
||||
@@ -111,7 +117,7 @@ def importDna(string, fitName=None):
|
||||
else:
|
||||
m.owner = f
|
||||
if m.isValidState(FittingModuleState.ACTIVE):
|
||||
m.state = FittingModuleState.ACTIVE
|
||||
m.state = activeStateLimit(m.item)
|
||||
moduleList.append(m)
|
||||
|
||||
# Recalc to get slot numbers correct for T3 cruisers
|
||||
@@ -123,18 +129,17 @@ def importDna(string, fitName=None):
|
||||
if module.fits(f):
|
||||
module.owner = f
|
||||
if module.isValidState(FittingModuleState.ACTIVE):
|
||||
module.state = FittingModuleState.ACTIVE
|
||||
module.state = activeStateLimit(module.item)
|
||||
f.modules.append(module)
|
||||
|
||||
return f
|
||||
|
||||
|
||||
def exportDna(fit, callback):
|
||||
def exportDna(fit, options, callback):
|
||||
dna = str(fit.shipID)
|
||||
subsystems = [] # EVE cares which order you put these in
|
||||
mods = OrderedDict()
|
||||
charges = OrderedDict()
|
||||
sFit = svcFit.getInstance()
|
||||
for mod in fit.modules:
|
||||
if not mod.isEmpty:
|
||||
if mod.slot == FittingSlot.SUBSYSTEM:
|
||||
@@ -162,9 +167,6 @@ def exportDna(fit, callback):
|
||||
for fighter in fit.fighters:
|
||||
dna += ":{0};{1}".format(fighter.itemID, fighter.amountActive)
|
||||
|
||||
for fighter in fit.fighters:
|
||||
dna += ":{0};{1}".format(fighter.itemID, fighter.amountActive)
|
||||
|
||||
for cargo in fit.cargo:
|
||||
# DNA format is a simple/dumb format. As CCP uses the slot information of the item itself
|
||||
# without designating slots in the DNA standard, we need to make sure we only include
|
||||
@@ -181,6 +183,9 @@ def exportDna(fit, callback):
|
||||
|
||||
text = dna + "::"
|
||||
|
||||
if options[PortDnaOptions.FORMATTING]:
|
||||
text = '<url=fitting:{}>{}</url>'.format(text, fit.name)
|
||||
|
||||
if callback:
|
||||
callback(text)
|
||||
else:
|
||||
|
||||
@@ -356,7 +356,7 @@ class EfsPort:
|
||||
"dps": stats.getDps(spoolOptions=spoolOptions).total * n, "capUse": stats.capUse * n, "falloff": stats.falloff,
|
||||
"type": typeing, "name": name, "optimal": maxRange,
|
||||
"numCharges": stats.numCharges, "numShots": stats.numShots, "reloadTime": stats.reloadTime,
|
||||
"cycleTime": stats.cycleTime, "volley": stats.getVolley(spoolOptions=spoolOptions).total * n, "tracking": tracking,
|
||||
"cycleTime": stats.getCycleParameters().averageTime, "volley": stats.getVolley(spoolOptions=spoolOptions).total * n, "tracking": tracking,
|
||||
"maxVelocity": maxVelocity, "explosionDelay": explosionDelay, "damageReductionFactor": damageReductionFactor,
|
||||
"explosionRadius": explosionRadius, "explosionVelocity": explosionVelocity, "aoeFieldRange": aoeFieldRange,
|
||||
"damageMultiplierBonusMax": stats.getModifiedItemAttr("damageMultiplierBonusMax"),
|
||||
@@ -369,7 +369,7 @@ class EfsPort:
|
||||
# Drones are using the old tracking formula for trackingSpeed. This updates it to match turrets.
|
||||
newTracking = droneAttr("trackingSpeed") / (droneAttr("optimalSigRadius") / 40000)
|
||||
statDict = {
|
||||
"dps": drone.getDps().total, "cycleTime": drone.cycleTime, "type": "Drone",
|
||||
"dps": drone.getDps().total, "cycleTime": drone.getCycleParameters().averageTime, "type": "Drone",
|
||||
"optimal": drone.maxRange, "name": drone.item.name, "falloff": drone.falloff,
|
||||
"maxSpeed": droneAttr("maxVelocity"), "tracking": newTracking,
|
||||
"volley": drone.getVolley().total
|
||||
@@ -498,11 +498,11 @@ class EfsPort:
|
||||
fitMultipliers["drones"] = list(map(getDroneMulti, tf.drones))
|
||||
|
||||
getFitTurrets = lambda f: filter(lambda mod: mod.hardpoint == FittingHardpoint.TURRET, f.modules)
|
||||
getTurretMulti = lambda mod: mod.getModifiedItemAttr("damageMultiplier") / mod.cycleTime
|
||||
getTurretMulti = lambda mod: mod.getModifiedItemAttr("damageMultiplier") / mod.getCycleParameters().averageTime
|
||||
fitMultipliers["turrets"] = list(map(getTurretMulti, getFitTurrets(tf)))
|
||||
|
||||
getFitLaunchers = lambda f: filter(lambda mod: mod.hardpoint == FittingHardpoint.MISSILE, f.modules)
|
||||
getLauncherMulti = lambda mod: sumDamage(mod.getModifiedChargeAttr) / mod.cycleTime
|
||||
getLauncherMulti = lambda mod: sumDamage(mod.getModifiedChargeAttr) / mod.getCycleParameters().averageTime
|
||||
fitMultipliers["launchers"] = list(map(getLauncherMulti, getFitLaunchers(tf)))
|
||||
return fitMultipliers
|
||||
|
||||
|
||||
@@ -22,18 +22,19 @@ import re
|
||||
|
||||
from logbook import Logger
|
||||
|
||||
from eos.const import FittingModuleState, FittingSlot
|
||||
from eos.db.gamedata.queries import getDynamicItem
|
||||
from eos.saveddata.booster import Booster
|
||||
from eos.saveddata.cargo import Cargo
|
||||
from eos.saveddata.citadel import Citadel
|
||||
from eos.saveddata.booster import Booster
|
||||
from eos.saveddata.drone import Drone
|
||||
from eos.saveddata.fighter import Fighter
|
||||
from eos.saveddata.fit import Fit
|
||||
from eos.saveddata.implant import Implant
|
||||
from eos.saveddata.module import Module
|
||||
from eos.saveddata.ship import Ship
|
||||
from eos.saveddata.fit import Fit
|
||||
from eos.const import FittingSlot, FittingModuleState
|
||||
from service.const import PortEftOptions, PortEftRigSize
|
||||
from gui.fitCommands.helpers import activeStateLimit
|
||||
from service.const import PortEftOptions
|
||||
from service.fit import Fit as svcFit
|
||||
from service.market import Market
|
||||
from service.port.muta import parseMutant, renderMutant
|
||||
@@ -443,7 +444,7 @@ def importEftCfg(shipname, lines, iportuser):
|
||||
m.owner = fitobj
|
||||
# Activate mod if it is activable
|
||||
if m.isValidState(FittingModuleState.ACTIVE):
|
||||
m.state = FittingModuleState.ACTIVE
|
||||
m.state = activeStateLimit(m.item)
|
||||
# Add charge to mod if applicable, on any errors just don't add anything
|
||||
if chargeName:
|
||||
try:
|
||||
@@ -804,7 +805,7 @@ class AbstractFit:
|
||||
if itemSpec.offline and m.isValidState(FittingModuleState.OFFLINE):
|
||||
m.state = FittingModuleState.OFFLINE
|
||||
elif m.isValidState(FittingModuleState.ACTIVE):
|
||||
m.state = FittingModuleState.ACTIVE
|
||||
m.state = activeStateLimit(m.item)
|
||||
return m
|
||||
|
||||
def addImplant(self, itemSpec):
|
||||
|
||||
@@ -23,14 +23,15 @@ import json
|
||||
|
||||
from logbook import Logger
|
||||
|
||||
from eos.const import FittingModuleState, FittingSlot
|
||||
from eos.saveddata.cargo import Cargo
|
||||
from eos.saveddata.citadel import Citadel
|
||||
from eos.saveddata.drone import Drone
|
||||
from eos.saveddata.fighter import Fighter
|
||||
from eos.saveddata.fit import Fit
|
||||
from eos.saveddata.module import Module
|
||||
from eos.const import FittingSlot, FittingModuleState
|
||||
from eos.saveddata.ship import Ship
|
||||
from gui.fitCommands.helpers import activeStateLimit
|
||||
from service.fit import Fit as svcFit
|
||||
from service.market import Market
|
||||
|
||||
@@ -196,7 +197,7 @@ def importESI(string):
|
||||
fitobj.modules.append(m)
|
||||
else:
|
||||
if m.isValidState(FittingModuleState.ACTIVE):
|
||||
m.state = FittingModuleState.ACTIVE
|
||||
m.state = activeStateLimit(m.item)
|
||||
|
||||
moduleList.append(m)
|
||||
|
||||
|
||||
@@ -272,8 +272,8 @@ class Port(object):
|
||||
return importDna(string, fitName=fitName)
|
||||
|
||||
@staticmethod
|
||||
def exportDna(fit, callback=None):
|
||||
return exportDna(fit, callback=callback)
|
||||
def exportDna(fit, options, callback=None):
|
||||
return exportDna(fit, options, callback=callback)
|
||||
|
||||
# ESI-related methods
|
||||
@staticmethod
|
||||
|
||||
@@ -23,6 +23,7 @@ import xml.parsers.expat
|
||||
|
||||
from logbook import Logger
|
||||
|
||||
from eos.const import FittingModuleState, FittingSlot
|
||||
from eos.saveddata.cargo import Cargo
|
||||
from eos.saveddata.citadel import Citadel
|
||||
from eos.saveddata.drone import Drone
|
||||
@@ -30,12 +31,11 @@ from eos.saveddata.fighter import Fighter
|
||||
from eos.saveddata.fit import Fit
|
||||
from eos.saveddata.module import Module
|
||||
from eos.saveddata.ship import Ship
|
||||
from eos.const import FittingSlot, FittingModuleState
|
||||
from gui.fitCommands.helpers import activeStateLimit
|
||||
from service.fit import Fit as svcFit
|
||||
from service.market import Market
|
||||
from utils.strfunctions import sequential_rep, replace_ltgt
|
||||
|
||||
from service.port.shared import IPortUser, processing_notify
|
||||
from utils.strfunctions import replace_ltgt, sequential_rep
|
||||
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
@@ -200,7 +200,7 @@ def importXml(text, iportuser):
|
||||
fitobj.modules.append(m)
|
||||
else:
|
||||
if m.isValidState(FittingModuleState.ACTIVE):
|
||||
m.state = FittingModuleState.ACTIVE
|
||||
m.state = activeStateLimit(m.item)
|
||||
|
||||
moduleList.append(m)
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
version: v2.9.2
|
||||
version: v2.9.4dev2
|
||||
|
||||
Reference in New Issue
Block a user