Merge branch 'origin_master' into effect_rollup
This commit is contained in:
@@ -21,13 +21,14 @@ if istravis is True or hasattr(sys, '_called_from_test'):
|
||||
# Running in Travis. Run saveddata database in memory.
|
||||
saveddata_connectionstring = 'sqlite:///:memory:'
|
||||
else:
|
||||
saveddata_connectionstring = 'sqlite:///' + realpath(join(dirname(abspath(__file__)), "..", "saveddata", "saveddata-py3-db.db"))
|
||||
saveddata_connectionstring = 'sqlite:///' + realpath(join(dirname(abspath(__file__)), "..", "saveddata", "saveddata.db"))
|
||||
|
||||
pyfalog.debug("Saveddata connection string: {0}", saveddata_connectionstring)
|
||||
|
||||
settings = {
|
||||
"useStaticAdaptiveArmorHardener": False,
|
||||
"strictSkillLevels": True,
|
||||
"globalDefaultSpoolupPercentage": 1.0
|
||||
}
|
||||
|
||||
# Autodetect path, only change if the autodetection bugs out.
|
||||
|
||||
75
eos/const.py
75
eos/const.py
@@ -1,8 +1,30 @@
|
||||
from eos.enum import Enum
|
||||
# =============================================================================
|
||||
# Copyright (C) 2019 Ryan Holmes
|
||||
#
|
||||
# This file is part of pyfa.
|
||||
#
|
||||
# pyfa is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# pyfa is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with pyfa. If not, see <http://www.gnu.org/licenses/>.
|
||||
# =============================================================================
|
||||
|
||||
from enum import IntEnum,unique
|
||||
|
||||
|
||||
|
||||
class Slot(Enum):
|
||||
@unique
|
||||
class FittingSlot(IntEnum):
|
||||
"""
|
||||
Contains slots for ship fittings
|
||||
"""
|
||||
# These are self-explanatory
|
||||
LOW = 1
|
||||
MED = 2
|
||||
@@ -24,3 +46,50 @@ class Slot(Enum):
|
||||
FS_LIGHT = 13
|
||||
FS_SUPPORT = 14
|
||||
FS_HEAVY = 15
|
||||
|
||||
|
||||
@unique
|
||||
class ImplantLocation(IntEnum):
|
||||
"""
|
||||
Contains location of the implant
|
||||
"""
|
||||
FIT = 0
|
||||
CHARACTER = 1
|
||||
|
||||
|
||||
@unique
|
||||
class CalcType(IntEnum):
|
||||
"""
|
||||
Contains location of the calculation
|
||||
"""
|
||||
LOCAL = 0
|
||||
PROJECTED = 1
|
||||
COMMAND = 2
|
||||
|
||||
|
||||
@unique
|
||||
class FittingModuleState(IntEnum):
|
||||
"""
|
||||
Contains the state of a fitting module
|
||||
"""
|
||||
OFFLINE = -1
|
||||
ONLINE = 0
|
||||
ACTIVE = 1
|
||||
OVERHEATED = 2
|
||||
|
||||
|
||||
@unique
|
||||
class FittingHardpoint(IntEnum):
|
||||
"""
|
||||
Contains the types of a fitting hardpoint
|
||||
"""
|
||||
NONE = 0
|
||||
MISSILE = 1
|
||||
TURRET = 2
|
||||
|
||||
|
||||
@unique
|
||||
class SpoolType(IntEnum):
|
||||
SCALE = 0 # [0..1]
|
||||
TIME = 1 # Expressed via time in seconds since spool up started
|
||||
CYCLES = 2 # Expressed in amount of cycles since spool up started
|
||||
|
||||
@@ -41,8 +41,7 @@ items_table = Table("invtypes", gamedata_meta,
|
||||
Column("iconID", Integer),
|
||||
Column("graphicID", Integer),
|
||||
Column("groupID", Integer, ForeignKey("invgroups.groupID"), index=True),
|
||||
Column("replaceSame", String),
|
||||
Column("replaceBetter", String))
|
||||
Column("replacements", String))
|
||||
|
||||
from .metaGroup import metatypes_table # noqa
|
||||
from .traits import traits_table # noqa
|
||||
|
||||
@@ -32,6 +32,4 @@ prices_table = Table("prices", saveddata_meta,
|
||||
Column("status", Integer, nullable=False))
|
||||
|
||||
|
||||
mapper(Price, prices_table, properties={
|
||||
"_Price__price": prices_table.c.price,
|
||||
})
|
||||
mapper(Price, prices_table)
|
||||
|
||||
@@ -21,6 +21,7 @@ import sys
|
||||
|
||||
from sqlalchemy.sql import and_
|
||||
from sqlalchemy import desc, select
|
||||
from sqlalchemy import func
|
||||
|
||||
from eos.db import saveddata_session, sd_lock
|
||||
from eos.db.saveddata.fit import projectedFits_table
|
||||
@@ -283,6 +284,12 @@ def countAllFits():
|
||||
return count
|
||||
|
||||
|
||||
def countFitGroupedByShip():
|
||||
with sd_lock:
|
||||
count = eos.db.saveddata_session.query(Fit.shipID, func.count(Fit.shipID)).group_by(Fit.shipID).all()
|
||||
return count
|
||||
|
||||
|
||||
def countFitsWithShip(lookfor, ownerID=None, where=None, eager=None):
|
||||
"""
|
||||
Get all the fits using a certain ship.
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#
|
||||
# Used by:
|
||||
# Modules from group: Warp Disrupt Field Generator (7 of 7)
|
||||
from eos.saveddata.module import State
|
||||
from eos.const import FittingModuleState
|
||||
|
||||
type = "projected", "active"
|
||||
runTime = "early"
|
||||
@@ -19,10 +19,10 @@ def handler(fit, module, context):
|
||||
fit.ship.increaseItemAttr("warpScrambleStatus", module.getModifiedItemAttr("warpScrambleStrength"))
|
||||
if module.charge is not None and module.charge.ID == 45010:
|
||||
for mod in fit.modules:
|
||||
if not mod.isEmpty and mod.item.requiresSkill("High Speed Maneuvering") and mod.state > State.ONLINE:
|
||||
mod.state = State.ONLINE
|
||||
if not mod.isEmpty and mod.item.requiresSkill("Micro Jump Drive Operation") and mod.state > State.ONLINE:
|
||||
mod.state = State.ONLINE
|
||||
if not mod.isEmpty and mod.item.requiresSkill("High Speed Maneuvering") and mod.state > FittingModuleState.ONLINE:
|
||||
mod.state = FittingModuleState.ONLINE
|
||||
if not mod.isEmpty and mod.item.requiresSkill("Micro Jump Drive Operation") and mod.state > FittingModuleState.ONLINE:
|
||||
mod.state = FittingModuleState.ONLINE
|
||||
else:
|
||||
if module.charge is None:
|
||||
fit.ship.boostItemAttr("mass", module.getModifiedItemAttr("massBonusPercentage"))
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# Used by:
|
||||
# Modules named like: Warp Scrambler (27 of 27)
|
||||
from eos.saveddata.module import State
|
||||
from eos.const import FittingModuleState
|
||||
|
||||
runTime = "early"
|
||||
type = "projected", "active"
|
||||
@@ -16,10 +16,10 @@ def handler(fit, module, context):
|
||||
|
||||
# this is such a dirty hack
|
||||
for mod in fit.modules:
|
||||
if not mod.isEmpty and mod.state > State.ONLINE and (
|
||||
if not mod.isEmpty and mod.state > FittingModuleState.ONLINE and (
|
||||
mod.item.requiresSkill("Micro Jump Drive Operation") or
|
||||
mod.item.requiresSkill("High Speed Maneuvering")
|
||||
):
|
||||
mod.state = State.ONLINE
|
||||
if not mod.isEmpty and mod.item.requiresSkill("Micro Jump Drive Operation") and mod.state > State.ONLINE:
|
||||
mod.state = State.ONLINE
|
||||
mod.state = FittingModuleState.ONLINE
|
||||
if not mod.isEmpty and mod.item.requiresSkill("Micro Jump Drive Operation") and mod.state > FittingModuleState.ONLINE:
|
||||
mod.state = FittingModuleState.ONLINE
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
#
|
||||
# Used by:
|
||||
# Modules from group: Energy Neutralizer (54 of 54)
|
||||
from eos.saveddata.module import State
|
||||
from eos.const import FittingModuleState
|
||||
from eos.modifiedAttributeDict import ModifiedAttributeDict
|
||||
|
||||
type = "active", "projected"
|
||||
|
||||
|
||||
def handler(fit, src, context, **kwargs):
|
||||
if "projected" in context and ((hasattr(src, "state") and src.state >= State.ACTIVE) or
|
||||
hasattr(src, "amountActive")):
|
||||
if "projected" in context and ((hasattr(src, "state") and src.state >= FittingModuleState.ACTIVE) or
|
||||
hasattr(src, "amountActive")):
|
||||
amount = src.getModifiedItemAttr("energyNeutralizerAmount")
|
||||
|
||||
if 'effect' in kwargs:
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# Used by:
|
||||
# Structure Modules from group: Structure Energy Neutralizer (5 of 5)
|
||||
from eos.saveddata.module import State
|
||||
from eos.const import FittingModuleState
|
||||
from eos.modifiedAttributeDict import ModifiedAttributeDict
|
||||
|
||||
type = "active", "projected"
|
||||
@@ -11,7 +11,7 @@ type = "active", "projected"
|
||||
def handler(fit, src, context, **kwargs):
|
||||
amount = 0
|
||||
if "projected" in context:
|
||||
if (hasattr(src, "state") and src.state >= State.ACTIVE) or hasattr(src, "amountActive"):
|
||||
if (hasattr(src, "state") and src.state >= FittingModuleState.ACTIVE) or hasattr(src, "amountActive"):
|
||||
amount = src.getModifiedItemAttr("energyNeutralizerAmount")
|
||||
|
||||
if 'effect' in kwargs:
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# Used by:
|
||||
# Structure Modules from group: Structure Warp Scrambler (2 of 2)
|
||||
from eos.saveddata.module import State
|
||||
from eos.const import FittingModuleState
|
||||
|
||||
# Not used by any item
|
||||
runTime = "early"
|
||||
@@ -14,7 +14,7 @@ def handler(fit, module, context):
|
||||
fit.ship.increaseItemAttr("warpScrambleStatus", module.getModifiedItemAttr("warpScrambleStrength"))
|
||||
if module.charge is not None and module.charge.ID == 47336:
|
||||
for mod in fit.modules:
|
||||
if not mod.isEmpty and mod.item.requiresSkill("High Speed Maneuvering") and mod.state > State.ONLINE:
|
||||
mod.state = State.ONLINE
|
||||
if not mod.isEmpty and mod.item.requiresSkill("Micro Jump Drive Operation") and mod.state > State.ONLINE:
|
||||
mod.state = State.ONLINE
|
||||
if not mod.isEmpty and mod.item.requiresSkill("High Speed Maneuvering") and mod.state > FittingModuleState.ONLINE:
|
||||
mod.state = FittingModuleState.ONLINE
|
||||
if not mod.isEmpty and mod.item.requiresSkill("Micro Jump Drive Operation") and mod.state > FittingModuleState.ONLINE:
|
||||
mod.state = FittingModuleState.ONLINE
|
||||
|
||||
@@ -3,15 +3,15 @@
|
||||
# Used by:
|
||||
# Module: Energy Neutralization Burst Projector
|
||||
# Structure Module: Standup Energy Neutralization Burst Projector
|
||||
from eos.saveddata.module import State
|
||||
from eos.const import FittingModuleState
|
||||
from eos.modifiedAttributeDict import ModifiedAttributeDict
|
||||
|
||||
type = "active", "projected"
|
||||
|
||||
|
||||
def handler(fit, src, context, **kwargs):
|
||||
if "projected" in context and ((hasattr(src, "state") and src.state >= State.ACTIVE) or
|
||||
hasattr(src, "amountActive")):
|
||||
if "projected" in context and ((hasattr(src, "state") and src.state >= FittingModuleState.ACTIVE) or
|
||||
hasattr(src, "amountActive")):
|
||||
amount = src.getModifiedItemAttr("energyNeutralizerAmount")
|
||||
|
||||
if 'effect' in kwargs:
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
#
|
||||
# Used by:
|
||||
# Drones from group: Energy Neutralizer Drone (3 of 3)
|
||||
from eos.saveddata.module import State
|
||||
from eos.const import FittingModuleState
|
||||
from eos.modifiedAttributeDict import ModifiedAttributeDict
|
||||
|
||||
type = "active", "projected"
|
||||
|
||||
|
||||
def handler(fit, src, context, **kwargs):
|
||||
if "projected" in context and ((hasattr(src, "state") and src.state >= State.ACTIVE) or
|
||||
hasattr(src, "amountActive")):
|
||||
if "projected" in context and ((hasattr(src, "state") and src.state >= FittingModuleState.ACTIVE) or
|
||||
hasattr(src, "amountActive")):
|
||||
amount = src.getModifiedItemAttr("energyNeutralizerAmount")
|
||||
time = src.getModifiedItemAttr("energyNeutralizerDuration")
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
# Modules from group: Mutadaptive Remote Armor Repairer (5 of 5)
|
||||
|
||||
|
||||
import eos.config
|
||||
from eos.utils.spoolSupport import SpoolType, SpoolOptions, calculateSpoolup, resolveSpoolOptions
|
||||
|
||||
|
||||
@@ -17,8 +18,7 @@ def handler(fit, container, context, **kwargs):
|
||||
cycleTime = container.getModifiedItemAttr("duration") / 1000.0
|
||||
repSpoolMax = container.getModifiedItemAttr("repairMultiplierBonusMax")
|
||||
repSpoolPerCycle = container.getModifiedItemAttr("repairMultiplierBonusPerCycle")
|
||||
# TODO: fetch spoolup option
|
||||
defaultSpoolValue = 1
|
||||
defaultSpoolValue = eos.config.settings['globalDefaultSpoolupPercentage']
|
||||
spoolType, spoolAmount = resolveSpoolOptions(SpoolOptions(SpoolType.SCALE, defaultSpoolValue, False), container)
|
||||
rps = repAmountBase * (1 + calculateSpoolup(repSpoolMax, repSpoolPerCycle, cycleTime, spoolType, spoolAmount)[0]) / cycleTime
|
||||
rpsPreSpool = repAmountBase * (1 + calculateSpoolup(repSpoolMax, repSpoolPerCycle, cycleTime, SpoolType.SCALE, 0)[0]) / cycleTime
|
||||
|
||||
17
eos/effects/structurecombatrigsecuritymodification.py
Normal file
17
eos/effects/structurecombatrigsecuritymodification.py
Normal file
@@ -0,0 +1,17 @@
|
||||
runTime = "early"
|
||||
type = "passive"
|
||||
|
||||
|
||||
def handler(fit, module, context):
|
||||
secModifier = module.getModifiedItemAttr("securityModifier")
|
||||
module.multiplyItemAttr("structureRigDoomsdayDamageLossTargetBonus", secModifier)
|
||||
module.multiplyItemAttr("structureRigScanResBonus", secModifier)
|
||||
module.multiplyItemAttr("structureRigPDRangeBonus", secModifier)
|
||||
module.multiplyItemAttr("structureRigPDCapUseBonus", secModifier)
|
||||
module.multiplyItemAttr("structureRigMissileExploVeloBonus", secModifier)
|
||||
module.multiplyItemAttr("structureRigMissileVelocityBonus", secModifier)
|
||||
module.multiplyItemAttr("structureRigEwarOptimalBonus", secModifier)
|
||||
module.multiplyItemAttr("structureRigEwarFalloffBonus", secModifier)
|
||||
module.multiplyItemAttr("structureRigEwarCapUseBonus", secModifier)
|
||||
module.multiplyItemAttr("structureRigMissileExplosionRadiusBonus", secModifier)
|
||||
module.multiplyItemAttr("structureRigMaxTargetRangeBonus", secModifier)
|
||||
23
eos/enum.py
23
eos/enum.py
@@ -1,23 +0,0 @@
|
||||
class Enum(object):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def getTypes(cls):
|
||||
for stuff in cls.__dict__:
|
||||
if stuff.upper() == stuff:
|
||||
yield stuff
|
||||
|
||||
@classmethod
|
||||
def getName(cls, v):
|
||||
map = getattr(cls, "_map", None)
|
||||
if map is None:
|
||||
map = cls._map = {}
|
||||
for type in cls.getTypes():
|
||||
map[cls.getValue(type)] = type
|
||||
|
||||
return map.get(v)
|
||||
|
||||
@classmethod
|
||||
def getValue(cls, type):
|
||||
return cls.__dict__[type]
|
||||
@@ -20,7 +20,7 @@
|
||||
from math import log, sin, radians, exp
|
||||
|
||||
from eos.graph import Graph
|
||||
from eos.saveddata.module import State, Hardpoint
|
||||
from eos.const import FittingModuleState, FittingHardpoint
|
||||
from logbook import Logger
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
@@ -46,7 +46,7 @@ class FitDpsGraph(Graph):
|
||||
abssort = lambda _val: -abs(_val - 1)
|
||||
|
||||
for mod in fit.modules:
|
||||
if not mod.isEmpty and mod.state >= State.ACTIVE:
|
||||
if not mod.isEmpty and mod.state >= FittingModuleState.ACTIVE:
|
||||
if "remoteTargetPaintFalloff" in mod.item.effects or "structureModuleEffectTargetPainter" in mod.item.effects:
|
||||
ew['signatureRadius'].append(
|
||||
1 + (mod.getModifiedItemAttr("signatureRadiusBonus") / 100) * self.calculateModuleMultiplier(
|
||||
@@ -76,12 +76,12 @@ class FitDpsGraph(Graph):
|
||||
|
||||
for mod in fit.modules:
|
||||
dps = mod.getDps(targetResists=fit.targetResists).total
|
||||
if mod.hardpoint == Hardpoint.TURRET:
|
||||
if mod.state >= State.ACTIVE:
|
||||
if mod.hardpoint == FittingHardpoint.TURRET:
|
||||
if mod.state >= FittingModuleState.ACTIVE:
|
||||
total += dps * self.calculateTurretMultiplier(mod, data)
|
||||
|
||||
elif mod.hardpoint == Hardpoint.MISSILE:
|
||||
if mod.state >= State.ACTIVE and mod.maxRange is not None and mod.maxRange >= distance:
|
||||
elif mod.hardpoint == FittingHardpoint.MISSILE:
|
||||
if mod.state >= FittingModuleState.ACTIVE and mod.maxRange is not None and mod.maxRange >= distance:
|
||||
total += dps * self.calculateMissileMultiplier(mod, data)
|
||||
|
||||
if distance <= fit.extraAttributes["droneControlRange"]:
|
||||
|
||||
@@ -30,6 +30,7 @@ pyfalog = Logger(__name__)
|
||||
|
||||
|
||||
class Booster(HandledItem, ItemAttrShortcut):
|
||||
|
||||
def __init__(self, item):
|
||||
self.__item = item
|
||||
|
||||
@@ -147,3 +148,17 @@ class Booster(HandledItem, ItemAttrShortcut):
|
||||
copyEffect.active = sideEffect.active
|
||||
|
||||
return copy
|
||||
|
||||
def rebase(self, item):
|
||||
active = self.active
|
||||
sideEffectStates = {se.effectID: se.active for se in self.sideEffects}
|
||||
Booster.__init__(self, item)
|
||||
self.active = active
|
||||
for sideEffect in self.sideEffects:
|
||||
if sideEffect.effectID in sideEffectStates:
|
||||
sideEffect.active = sideEffectStates[sideEffect.effectID]
|
||||
|
||||
def __repr__(self):
|
||||
return "Booster(ID={}, name={}) at {}".format(
|
||||
self.item.ID, self.item.name, hex(id(self))
|
||||
)
|
||||
|
||||
@@ -89,3 +89,13 @@ class Cargo(HandledItem, ItemAttrShortcut):
|
||||
copy = Cargo(self.item)
|
||||
copy.amount = self.amount
|
||||
return copy
|
||||
|
||||
def rebase(self, item):
|
||||
amount = self.amount
|
||||
Cargo.__init__(self, item)
|
||||
self.amount = amount
|
||||
|
||||
def __repr__(self):
|
||||
return "Cargo(ID={}, name={}) at {}".format(
|
||||
self.item.ID, self.item.name, hex(id(self))
|
||||
)
|
||||
|
||||
@@ -296,6 +296,13 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
copy.amountActive = self.amountActive
|
||||
return copy
|
||||
|
||||
def rebase(self, item):
|
||||
amount = self.amount
|
||||
amountActive = self.amountActive
|
||||
Drone.__init__(self, item)
|
||||
self.amount = amount
|
||||
self.amountActive = amountActive
|
||||
|
||||
def fits(self, fit):
|
||||
fitDroneGroupLimits = set()
|
||||
for i in range(1, 3):
|
||||
|
||||
@@ -25,8 +25,8 @@ import eos.db
|
||||
from eos.effectHandlerHelpers import HandledItem, HandledCharge
|
||||
from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut, ChargeAttrShortcut
|
||||
from eos.saveddata.fighterAbility import FighterAbility
|
||||
from eos.saveddata.module import Slot
|
||||
from eos.utils.stats import DmgTypes
|
||||
from eos.const import FittingSlot
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
@@ -116,12 +116,12 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
|
||||
def __calculateSlot(self, item):
|
||||
types = {
|
||||
"Light" : Slot.F_LIGHT,
|
||||
"Support": Slot.F_SUPPORT,
|
||||
"Heavy" : Slot.F_HEAVY,
|
||||
"StandupLight": Slot.FS_LIGHT,
|
||||
"StandupSupport": Slot.FS_SUPPORT,
|
||||
"StandupHeavy": Slot.FS_HEAVY
|
||||
"Light" : FittingSlot.F_LIGHT,
|
||||
"Support": FittingSlot.F_SUPPORT,
|
||||
"Heavy" : FittingSlot.F_HEAVY,
|
||||
"StandupLight": FittingSlot.FS_LIGHT,
|
||||
"StandupSupport": FittingSlot.FS_SUPPORT,
|
||||
"StandupHeavy": FittingSlot.FS_HEAVY
|
||||
}
|
||||
|
||||
for t, slot in types.items():
|
||||
@@ -355,6 +355,17 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
copyAbility.active = ability.active
|
||||
return copy
|
||||
|
||||
def rebase(self, item):
|
||||
amount = self.amount
|
||||
active = self.active
|
||||
abilityEffectStates = {a.effectID: a.active for a in self.abilities}
|
||||
Fighter.__init__(self, item)
|
||||
self.amount = amount
|
||||
self.active = active
|
||||
for ability in self.abilities:
|
||||
if ability.effectID in abilityEffectStates:
|
||||
ability.active = abilityEffectStates[ability.effectID]
|
||||
|
||||
def fits(self, fit):
|
||||
# If ships doesn't support this type of fighter, don't add it
|
||||
if fit.getNumSlots(self.slot) == 0:
|
||||
|
||||
@@ -28,28 +28,19 @@ from sqlalchemy.orm import validates, reconstructor
|
||||
import eos.db
|
||||
from eos import capSim
|
||||
from eos.effectHandlerHelpers import HandledModuleList, HandledDroneCargoList, HandledImplantBoosterList, HandledProjectedDroneList, HandledProjectedModList
|
||||
from eos.enum import Enum
|
||||
from eos.const import ImplantLocation, CalcType, FittingSlot
|
||||
from eos.saveddata.ship import Ship
|
||||
from eos.saveddata.drone import Drone
|
||||
from eos.saveddata.character import Character
|
||||
from eos.saveddata.citadel import Citadel
|
||||
from eos.saveddata.module import Module, State, Slot, Hardpoint
|
||||
from eos.const import FittingModuleState, FittingHardpoint
|
||||
from eos.saveddata.module import Module
|
||||
from eos.utils.stats import DmgTypes
|
||||
from logbook import Logger
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
|
||||
class ImplantLocation(Enum):
|
||||
FIT = 0
|
||||
CHARACTER = 1
|
||||
|
||||
|
||||
class CalcType(Enum):
|
||||
LOCAL = 0
|
||||
PROJECTED = 1
|
||||
COMMAND = 2
|
||||
|
||||
|
||||
class Fit(object):
|
||||
"""Represents a fitting, with modules, ship, implants, etc."""
|
||||
|
||||
@@ -393,6 +384,32 @@ class Fit(object):
|
||||
else:
|
||||
return val
|
||||
|
||||
def canFit(self, item):
|
||||
# Whereas Module.fits() deals with current state of the fit in order to determine if somethign fits (for example maxGroupFitted which can be modified by effects),
|
||||
# this function should be used against Items to see if the item is even allowed on the fit with rules that don't change
|
||||
|
||||
fitsOnType = set()
|
||||
fitsOnGroup = set()
|
||||
|
||||
shipType = item.attributes.get("fitsToShipType", None)
|
||||
if shipType is not None:
|
||||
fitsOnType.add(shipType.value)
|
||||
|
||||
fitsOnType.update([item.attributes[attr].value for attr in item.attributes if attr.startswith("canFitShipType")])
|
||||
fitsOnGroup.update([item.attributes[attr].value for attr in item.attributes if attr.startswith("canFitShipGroup")])
|
||||
|
||||
if (len(fitsOnGroup) > 0 or len(fitsOnType) > 0) \
|
||||
and self.ship.item.group.ID not in fitsOnGroup \
|
||||
and self.ship.item.ID not in fitsOnType:
|
||||
return False
|
||||
|
||||
# Citadel modules are now under a new category, so we can check this to ensure only structure modules can fit on a citadel
|
||||
if isinstance(self.ship, Citadel) and item.category.name != "Structure Module" or \
|
||||
not isinstance(self.ship, Citadel) and item.category.name == "Structure Module":
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def clear(self, projected=False, command=False):
|
||||
self.__effectiveTank = None
|
||||
self.__weaponDpsMap = {}
|
||||
@@ -741,7 +758,7 @@ class Fit(object):
|
||||
The type of calculation our current iteration is in. This helps us determine the interactions between
|
||||
fits that rely on others for proper calculations
|
||||
"""
|
||||
pyfalog.info("Starting fit calculation on: {0}, calc: {1}", repr(self), CalcType.getName(type))
|
||||
pyfalog.info("Starting fit calculation on: {0}, calc: {1}", repr(self), CalcType(type).name)
|
||||
|
||||
# If we are projecting this fit onto another one, collect the projection info for later use
|
||||
|
||||
@@ -901,7 +918,7 @@ class Fit(object):
|
||||
if self.ship is None:
|
||||
return
|
||||
|
||||
for slotType in (Slot.LOW, Slot.MED, Slot.HIGH, Slot.RIG, Slot.SUBSYSTEM, Slot.SERVICE):
|
||||
for slotType in (FittingSlot.LOW.value, FittingSlot.MED.value, FittingSlot.HIGH.value, FittingSlot.RIG.value, FittingSlot.SUBSYSTEM.value, FittingSlot.SERVICE.value):
|
||||
amount = self.getSlotsFree(slotType, True)
|
||||
if amount > 0:
|
||||
for _ in range(int(amount)):
|
||||
@@ -948,7 +965,7 @@ class Fit(object):
|
||||
def getItemAttrOnlineSum(dict, attr):
|
||||
amount = 0
|
||||
for mod in dict:
|
||||
add = mod.getModifiedItemAttr(attr) if mod.state >= State.ONLINE else None
|
||||
add = mod.getModifiedItemAttr(attr) if mod.state >= FittingModuleState.ONLINE else None
|
||||
if add is not None:
|
||||
amount += add
|
||||
|
||||
@@ -967,29 +984,29 @@ class Fit(object):
|
||||
|
||||
for mod in chain(self.modules, self.fighters):
|
||||
if mod.slot is type and (not getattr(mod, "isEmpty", False) or countDummies):
|
||||
if type in (Slot.F_HEAVY, Slot.F_SUPPORT, Slot.F_LIGHT, Slot.FS_HEAVY, Slot.FS_LIGHT, Slot.FS_SUPPORT) and not mod.active:
|
||||
if type in (FittingSlot.F_HEAVY, FittingSlot.F_SUPPORT, FittingSlot.F_LIGHT, FittingSlot.FS_HEAVY, FittingSlot.FS_LIGHT, FittingSlot.FS_SUPPORT) and not mod.active:
|
||||
continue
|
||||
amount += 1
|
||||
|
||||
return amount
|
||||
|
||||
slots = {
|
||||
Slot.LOW : "lowSlots",
|
||||
Slot.MED : "medSlots",
|
||||
Slot.HIGH : "hiSlots",
|
||||
Slot.RIG : "rigSlots",
|
||||
Slot.SUBSYSTEM: "maxSubSystems",
|
||||
Slot.SERVICE : "serviceSlots",
|
||||
Slot.F_LIGHT : "fighterLightSlots",
|
||||
Slot.F_SUPPORT: "fighterSupportSlots",
|
||||
Slot.F_HEAVY : "fighterHeavySlots",
|
||||
Slot.FS_LIGHT: "fighterStandupLightSlots",
|
||||
Slot.FS_SUPPORT: "fighterStandupSupportSlots",
|
||||
Slot.FS_HEAVY: "fighterStandupHeavySlots",
|
||||
FittingSlot.LOW : "lowSlots",
|
||||
FittingSlot.MED : "medSlots",
|
||||
FittingSlot.HIGH : "hiSlots",
|
||||
FittingSlot.RIG : "rigSlots",
|
||||
FittingSlot.SUBSYSTEM: "maxSubSystems",
|
||||
FittingSlot.SERVICE : "serviceSlots",
|
||||
FittingSlot.F_LIGHT : "fighterLightSlots",
|
||||
FittingSlot.F_SUPPORT: "fighterSupportSlots",
|
||||
FittingSlot.F_HEAVY : "fighterHeavySlots",
|
||||
FittingSlot.FS_LIGHT: "fighterStandupLightSlots",
|
||||
FittingSlot.FS_SUPPORT: "fighterStandupSupportSlots",
|
||||
FittingSlot.FS_HEAVY: "fighterStandupHeavySlots",
|
||||
}
|
||||
|
||||
def getSlotsFree(self, type, countDummies=False):
|
||||
if type in (Slot.MODE, Slot.SYSTEM):
|
||||
if type in (FittingSlot.MODE, FittingSlot.SYSTEM):
|
||||
# These slots don't really exist, return default 0
|
||||
return 0
|
||||
|
||||
@@ -1001,12 +1018,12 @@ class Fit(object):
|
||||
return self.ship.getModifiedItemAttr(self.slots[type]) or 0
|
||||
|
||||
def getHardpointsFree(self, type):
|
||||
if type == Hardpoint.NONE:
|
||||
if type == FittingHardpoint.NONE:
|
||||
return 1
|
||||
elif type == Hardpoint.TURRET:
|
||||
return self.ship.getModifiedItemAttr('turretSlotsLeft') - self.getHardpointsUsed(Hardpoint.TURRET)
|
||||
elif type == Hardpoint.MISSILE:
|
||||
return self.ship.getModifiedItemAttr('launcherSlotsLeft') - self.getHardpointsUsed(Hardpoint.MISSILE)
|
||||
elif type == FittingHardpoint.TURRET:
|
||||
return self.ship.getModifiedItemAttr('turretSlotsLeft') - self.getHardpointsUsed(FittingHardpoint.TURRET)
|
||||
elif type == FittingHardpoint.MISSILE:
|
||||
return self.ship.getModifiedItemAttr('launcherSlotsLeft') - self.getHardpointsUsed(FittingHardpoint.MISSILE)
|
||||
else:
|
||||
raise ValueError("%d is not a valid value for Hardpoint Enum", type)
|
||||
|
||||
@@ -1168,7 +1185,7 @@ class Fit(object):
|
||||
capUsed = 0
|
||||
capAdded = 0
|
||||
for mod in self.modules:
|
||||
if mod.state >= State.ACTIVE:
|
||||
if mod.state >= FittingModuleState.ACTIVE:
|
||||
if (mod.getModifiedItemAttr("capacitorNeed") or 0) != 0:
|
||||
cycleTime = mod.rawCycleTime or 0
|
||||
reactivationTime = mod.getModifiedItemAttr("moduleReactivationDelay") or 0
|
||||
@@ -1182,7 +1199,7 @@ class Fit(object):
|
||||
capAdded -= capNeed
|
||||
|
||||
# If this is a turret, don't stagger activations
|
||||
disableStagger = mod.hardpoint == Hardpoint.TURRET
|
||||
disableStagger = mod.hardpoint == FittingHardpoint.TURRET
|
||||
|
||||
drains.append((int(fullCycleTime), mod.getModifiedItemAttr("capacitorNeed") or 0,
|
||||
mod.numShots or 0, disableStagger, reloadTime))
|
||||
|
||||
@@ -115,6 +115,11 @@ class Implant(HandledItem, ItemAttrShortcut):
|
||||
copy.active = self.active
|
||||
return copy
|
||||
|
||||
def rebase(self, item):
|
||||
active = self.active
|
||||
Implant.__init__(self, item)
|
||||
self.active = active
|
||||
|
||||
def __repr__(self):
|
||||
return "Implant(ID={}, name={}) at {}".format(
|
||||
self.item.ID, self.item.name, hex(id(self))
|
||||
|
||||
@@ -23,9 +23,8 @@ from logbook import Logger
|
||||
from sqlalchemy.orm import reconstructor, validates
|
||||
|
||||
import eos.db
|
||||
from eos.const import Slot
|
||||
from eos.const import FittingModuleState, FittingHardpoint, FittingSlot
|
||||
from eos.effectHandlerHelpers import HandledCharge, HandledItem
|
||||
from eos.enum import Enum
|
||||
from eos.modifiedAttributeDict import ChargeAttrShortcut, ItemAttrShortcut, ModifiedAttributeDict
|
||||
from eos.saveddata.citadel import Citadel
|
||||
from eos.saveddata.mutator import Mutator
|
||||
@@ -35,44 +34,30 @@ from eos.utils.stats import DmgTypes
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
|
||||
class State(Enum):
|
||||
OFFLINE = -1
|
||||
ONLINE = 0
|
||||
ACTIVE = 1
|
||||
OVERHEATED = 2
|
||||
|
||||
|
||||
ProjectedMap = {
|
||||
State.OVERHEATED: State.ACTIVE,
|
||||
State.ACTIVE: State.OFFLINE,
|
||||
State.OFFLINE: State.ACTIVE,
|
||||
State.ONLINE: State.ACTIVE # Just in case
|
||||
FittingModuleState.OVERHEATED: FittingModuleState.ACTIVE,
|
||||
FittingModuleState.ACTIVE: FittingModuleState.OFFLINE,
|
||||
FittingModuleState.OFFLINE: FittingModuleState.ACTIVE,
|
||||
FittingModuleState.ONLINE: FittingModuleState.ACTIVE # Just in case
|
||||
}
|
||||
|
||||
|
||||
# Old state : New State
|
||||
LocalMap = {
|
||||
State.OVERHEATED: State.ACTIVE,
|
||||
State.ACTIVE: State.ONLINE,
|
||||
State.OFFLINE: State.ONLINE,
|
||||
State.ONLINE: State.ACTIVE
|
||||
FittingModuleState.OVERHEATED: FittingModuleState.ACTIVE,
|
||||
FittingModuleState.ACTIVE: FittingModuleState.ONLINE,
|
||||
FittingModuleState.OFFLINE: FittingModuleState.ONLINE,
|
||||
FittingModuleState.ONLINE: FittingModuleState.ACTIVE
|
||||
}
|
||||
|
||||
|
||||
# For system effects. They should only ever be online or offline
|
||||
ProjectedSystem = {
|
||||
State.OFFLINE: State.ONLINE,
|
||||
State.ONLINE: State.OFFLINE
|
||||
FittingModuleState.OFFLINE: FittingModuleState.ONLINE,
|
||||
FittingModuleState.ONLINE: FittingModuleState.OFFLINE
|
||||
}
|
||||
|
||||
|
||||
class Hardpoint(Enum):
|
||||
NONE = 0
|
||||
MISSILE = 1
|
||||
TURRET = 2
|
||||
|
||||
|
||||
class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
"""An instance of this class represents a module together with its charge and modified attributes"""
|
||||
MINING_ATTRIBUTES = ("miningAmount",)
|
||||
@@ -104,7 +89,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
self.__charge = None
|
||||
|
||||
self.projected = False
|
||||
self.state = State.ONLINE
|
||||
self.state = FittingModuleState.ONLINE
|
||||
self.build()
|
||||
|
||||
@reconstructor
|
||||
@@ -153,7 +138,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
self.__reloadTime = None
|
||||
self.__reloadForce = None
|
||||
self.__chargeCycles = None
|
||||
self.__hardpoint = Hardpoint.NONE
|
||||
self.__hardpoint = FittingHardpoint.NONE
|
||||
self.__itemModifiedAttributes = ModifiedAttributeDict(parent=self)
|
||||
self.__chargeModifiedAttributes = ModifiedAttributeDict(parent=self)
|
||||
self.__slot = self.dummySlot # defaults to None
|
||||
@@ -396,7 +381,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
if self.isEmpty:
|
||||
self.__miningyield = 0
|
||||
else:
|
||||
if self.state >= State.ACTIVE:
|
||||
if self.state >= FittingModuleState.ACTIVE:
|
||||
volley = self.getModifiedItemAttr("specialtyMiningAmount") or self.getModifiedItemAttr(
|
||||
"miningAmount") or 0
|
||||
if volley:
|
||||
@@ -410,7 +395,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
return self.__miningyield
|
||||
|
||||
def getVolley(self, spoolOptions=None, targetResists=None, ignoreState=False):
|
||||
if self.isEmpty or (self.state < State.ACTIVE and not ignoreState):
|
||||
if self.isEmpty or (self.state < FittingModuleState.ACTIVE and not ignoreState):
|
||||
return DmgTypes(0, 0, 0, 0)
|
||||
if self.__baseVolley is None:
|
||||
dmgGetter = self.getModifiedChargeAttr if self.charge else self.getModifiedItemAttr
|
||||
@@ -448,7 +433,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
return dps
|
||||
|
||||
def getRemoteReps(self, spoolOptions=None, ignoreState=False):
|
||||
if self.isEmpty or (self.state < State.ACTIVE and not ignoreState):
|
||||
if self.isEmpty or (self.state < FittingModuleState.ACTIVE and not ignoreState):
|
||||
return None, 0
|
||||
|
||||
def getBaseRemoteReps(module):
|
||||
@@ -554,34 +539,8 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
return fits
|
||||
|
||||
def __fitRestrictions(self, fit, hardpointLimit=True):
|
||||
# Check ship type restrictions
|
||||
fitsOnType = set()
|
||||
fitsOnGroup = set()
|
||||
|
||||
shipType = self.getModifiedItemAttr("fitsToShipType", None)
|
||||
if shipType is not None:
|
||||
fitsOnType.add(shipType)
|
||||
|
||||
for attr in list(self.itemModifiedAttributes.keys()):
|
||||
if attr.startswith("canFitShipType"):
|
||||
shipType = self.getModifiedItemAttr(attr, None)
|
||||
if shipType is not None:
|
||||
fitsOnType.add(shipType)
|
||||
|
||||
for attr in list(self.itemModifiedAttributes.keys()):
|
||||
if attr.startswith("canFitShipGroup"):
|
||||
shipGroup = self.getModifiedItemAttr(attr, None)
|
||||
if shipGroup is not None:
|
||||
fitsOnGroup.add(shipGroup)
|
||||
|
||||
if (len(fitsOnGroup) > 0 or len(fitsOnType) > 0) \
|
||||
and fit.ship.item.group.ID not in fitsOnGroup \
|
||||
and fit.ship.item.ID not in fitsOnType:
|
||||
return False
|
||||
|
||||
# Citadel modules are now under a new category, so we can check this to ensure only structure modules can fit on a citadel
|
||||
if isinstance(fit.ship, Citadel) and self.item.category.name != "Structure Module" or \
|
||||
not isinstance(fit.ship, Citadel) and self.item.category.name == "Structure Module":
|
||||
if not fit.canFit(self.item):
|
||||
return False
|
||||
|
||||
# EVE doesn't let capital modules be fit onto subcapital hulls. Confirmed by CCP Larrikin that this is dictated
|
||||
@@ -590,14 +549,14 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
return False
|
||||
|
||||
# If the mod is a subsystem, don't let two subs in the same slot fit
|
||||
if self.slot == Slot.SUBSYSTEM:
|
||||
if self.slot == FittingSlot.SUBSYSTEM:
|
||||
subSlot = self.getModifiedItemAttr("subSystemSlot")
|
||||
for mod in fit.modules:
|
||||
if mod.getModifiedItemAttr("subSystemSlot") == subSlot:
|
||||
return False
|
||||
|
||||
# Check rig sizes
|
||||
if self.slot == Slot.RIG:
|
||||
if self.slot == FittingSlot.RIG:
|
||||
if self.getModifiedItemAttr("rigSize") != fit.ship.getModifiedItemAttr("rigSize"):
|
||||
return False
|
||||
|
||||
@@ -627,9 +586,9 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
# Check if we're within bounds
|
||||
if state < -1 or state > 2:
|
||||
return False
|
||||
elif state >= State.ACTIVE and not self.item.isType("active"):
|
||||
elif state >= FittingModuleState.ACTIVE and not self.item.isType("active"):
|
||||
return False
|
||||
elif state == State.OVERHEATED and not self.item.isType("overheat"):
|
||||
elif state == FittingModuleState.OVERHEATED and not self.item.isType("overheat"):
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
@@ -641,7 +600,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
# If we're going to set module to offline or online for local modules or offline for projected,
|
||||
# it should be fine for all cases
|
||||
item = self.item
|
||||
if (state <= State.ONLINE and projectedOnto is None) or (state <= State.OFFLINE):
|
||||
if (state <= FittingModuleState.ONLINE and projectedOnto is None) or (state <= FittingModuleState.OFFLINE):
|
||||
return True
|
||||
|
||||
# Check if the local module is over it's max limit; if it's not, we're fine
|
||||
@@ -655,7 +614,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
group = item.group.name
|
||||
for mod in self.owner.modules:
|
||||
currItem = getattr(mod, "item", None)
|
||||
if mod.state >= State.ACTIVE and currItem is not None and currItem.group.name == group:
|
||||
if mod.state >= FittingModuleState.ACTIVE and currItem is not None and currItem.group.name == group:
|
||||
currActive += 1
|
||||
if currActive > maxGroupActive:
|
||||
break
|
||||
@@ -718,28 +677,28 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
@staticmethod
|
||||
def __calculateHardpoint(item):
|
||||
effectHardpointMap = {
|
||||
"turretFitted" : Hardpoint.TURRET,
|
||||
"launcherFitted": Hardpoint.MISSILE
|
||||
"turretFitted" : FittingHardpoint.TURRET,
|
||||
"launcherFitted": FittingHardpoint.MISSILE
|
||||
}
|
||||
|
||||
if item is None:
|
||||
return Hardpoint.NONE
|
||||
return FittingHardpoint.NONE
|
||||
|
||||
for effectName, slot in effectHardpointMap.items():
|
||||
if effectName in item.effects:
|
||||
return slot
|
||||
|
||||
return Hardpoint.NONE
|
||||
return FittingHardpoint.NONE
|
||||
|
||||
@staticmethod
|
||||
def calculateSlot(item):
|
||||
effectSlotMap = {
|
||||
"rigSlot" : Slot.RIG,
|
||||
"loPower" : Slot.LOW,
|
||||
"medPower" : Slot.MED,
|
||||
"hiPower" : Slot.HIGH,
|
||||
"subSystem" : Slot.SUBSYSTEM,
|
||||
"serviceSlot": Slot.SERVICE
|
||||
"rigSlot" : FittingSlot.RIG.value,
|
||||
"loPower" : FittingSlot.LOW.value,
|
||||
"medPower" : FittingSlot.MED.value,
|
||||
"hiPower" : FittingSlot.HIGH.value,
|
||||
"subSystem" : FittingSlot.SUBSYSTEM.value,
|
||||
"serviceSlot": FittingSlot.SERVICE.value
|
||||
}
|
||||
if item is None:
|
||||
return None
|
||||
@@ -747,7 +706,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
if effectName in item.effects:
|
||||
return slot
|
||||
if item.group.name in Module.SYSTEM_GROUPS:
|
||||
return Slot.SYSTEM
|
||||
return FittingSlot.SYSTEM
|
||||
|
||||
return None
|
||||
|
||||
@@ -801,8 +760,8 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
if effect.runTime == runTime and \
|
||||
effect.activeByDefault and \
|
||||
(effect.isType("offline") or
|
||||
(effect.isType("passive") and self.state >= State.ONLINE) or
|
||||
(effect.isType("active") and self.state >= State.ACTIVE)) and \
|
||||
(effect.isType("passive") and self.state >= FittingModuleState.ONLINE) or
|
||||
(effect.isType("active") and self.state >= FittingModuleState.ACTIVE)) and \
|
||||
(not gang or (gang and effect.isType("gang"))):
|
||||
|
||||
chargeContext = ("moduleCharge",)
|
||||
@@ -815,7 +774,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
effect.handler(fit, self, chargeContext)
|
||||
|
||||
if self.item:
|
||||
if self.state >= State.OVERHEATED:
|
||||
if self.state >= FittingModuleState.OVERHEATED:
|
||||
for effect in self.item.effects.values():
|
||||
if effect.runTime == runTime and \
|
||||
effect.isType("overheat") \
|
||||
@@ -828,8 +787,8 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
if effect.runTime == runTime and \
|
||||
effect.activeByDefault and \
|
||||
(effect.isType("offline") or
|
||||
(effect.isType("passive") and self.state >= State.ONLINE) or
|
||||
(effect.isType("active") and self.state >= State.ACTIVE)) \
|
||||
(effect.isType("passive") and self.state >= FittingModuleState.ONLINE) or
|
||||
(effect.isType("active") and self.state >= FittingModuleState.ACTIVE)) \
|
||||
and ((projected and effect.isType("projected")) or not projected) \
|
||||
and ((gang and effect.isType("gang")) or not gang):
|
||||
try:
|
||||
@@ -904,7 +863,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
@property
|
||||
def capUse(self):
|
||||
capNeed = self.getModifiedItemAttr("capacitorNeed")
|
||||
if capNeed and self.state >= State.ACTIVE:
|
||||
if capNeed and self.state >= FittingModuleState.ACTIVE:
|
||||
cycleTime = self.cycleTime
|
||||
if cycleTime > 0:
|
||||
capUsed = capNeed / (cycleTime / 1000.0)
|
||||
@@ -916,10 +875,10 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
def getProposedState(mod, click, proposedState=None):
|
||||
# todo: instead of passing in module, make this a instanced function.
|
||||
pyfalog.debug("Get proposed state for module.")
|
||||
if mod.slot == Slot.SUBSYSTEM or mod.isEmpty:
|
||||
return State.ONLINE
|
||||
if mod.slot == FittingSlot.SUBSYSTEM or mod.isEmpty:
|
||||
return FittingModuleState.ONLINE
|
||||
|
||||
if mod.slot == Slot.SYSTEM:
|
||||
if mod.slot == FittingSlot.SYSTEM:
|
||||
transitionMap = ProjectedSystem
|
||||
else:
|
||||
transitionMap = ProjectedMap if mod.projected else LocalMap
|
||||
@@ -929,9 +888,9 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
if proposedState is not None:
|
||||
state = proposedState
|
||||
elif click == "right":
|
||||
state = State.OVERHEATED
|
||||
state = FittingModuleState.OVERHEATED
|
||||
elif click == "ctrl":
|
||||
state = State.OFFLINE
|
||||
state = FittingModuleState.OFFLINE
|
||||
else:
|
||||
state = transitionMap[currState]
|
||||
if not mod.isValidState(state):
|
||||
@@ -956,6 +915,16 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
|
||||
return copy
|
||||
|
||||
def rebase(self, item):
|
||||
state = self.state
|
||||
charge = self.charge
|
||||
Module.__init__(self, item, self.baseItem, self.mutaplasmid)
|
||||
self.state = state
|
||||
if self.isValidCharge(charge):
|
||||
self.charge = charge
|
||||
for x in self.mutators.values():
|
||||
Mutator(self, x.attribute, x.value)
|
||||
|
||||
def __repr__(self):
|
||||
if self.item:
|
||||
return "Module(ID={}, name={}) at {}".format(
|
||||
|
||||
@@ -19,41 +19,56 @@
|
||||
# ===============================================================================
|
||||
|
||||
|
||||
import time
|
||||
from enum import IntEnum, unique
|
||||
from time import time
|
||||
|
||||
from logbook import Logger
|
||||
|
||||
|
||||
VALIDITY = 24 * 60 * 60 # Price validity period, 24 hours
|
||||
REREQUEST = 4 * 60 * 60 # Re-request delay for failed fetches, 4 hours
|
||||
TIMEOUT = 15 * 60 # Network timeout delay for connection issues, 15 minutes
|
||||
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
|
||||
@unique
|
||||
class PriceStatus(IntEnum):
|
||||
notFetched = 0
|
||||
success = 1
|
||||
fail = 2
|
||||
notSupported = 3
|
||||
initialized = 0
|
||||
notSupported = 1
|
||||
fetchSuccess = 2
|
||||
fetchFail = 3
|
||||
fetchTimeout = 4
|
||||
|
||||
|
||||
class Price(object):
|
||||
def __init__(self, typeID):
|
||||
self.typeID = typeID
|
||||
self.time = 0
|
||||
self.__price = 0
|
||||
self.status = PriceStatus.notFetched
|
||||
self.price = 0
|
||||
self.status = PriceStatus.initialized
|
||||
|
||||
@property
|
||||
def isValid(self):
|
||||
return self.time >= time.time()
|
||||
|
||||
@property
|
||||
def price(self):
|
||||
if self.status != PriceStatus.success:
|
||||
return 0
|
||||
def isValid(self, validityOverride=None):
|
||||
# Always attempt to update prices which were just initialized, and prices
|
||||
# of unsupported items (maybe we start supporting them at some point)
|
||||
if self.status in (PriceStatus.initialized, PriceStatus.notSupported):
|
||||
return False
|
||||
elif self.status == PriceStatus.fetchSuccess:
|
||||
return time() <= self.time + (validityOverride if validityOverride is not None else VALIDITY)
|
||||
elif self.status == PriceStatus.fetchFail:
|
||||
return time() <= self.time + REREQUEST
|
||||
elif self.status == PriceStatus.fetchTimeout:
|
||||
return time() <= self.time + TIMEOUT
|
||||
else:
|
||||
return self.__price or 0
|
||||
return False
|
||||
|
||||
@price.setter
|
||||
def price(self, price):
|
||||
self.__price = price
|
||||
def update(self, status, price=0):
|
||||
# Keep old price if we failed to fetch new one
|
||||
if status in (PriceStatus.fetchFail, PriceStatus.fetchTimeout):
|
||||
price = self.price
|
||||
elif status != PriceStatus.fetchSuccess:
|
||||
price = 0
|
||||
self.time = time()
|
||||
self.price = price
|
||||
self.status = status
|
||||
|
||||
@@ -19,21 +19,14 @@
|
||||
|
||||
|
||||
from collections import namedtuple
|
||||
from enum import IntEnum, unique
|
||||
|
||||
from eos.const import SpoolType
|
||||
from eos.utils.float import floatUnerr
|
||||
|
||||
|
||||
SpoolOptions = namedtuple('SpoolOptions', ('spoolType', 'spoolAmount', 'force'))
|
||||
|
||||
|
||||
@unique
|
||||
class SpoolType(IntEnum):
|
||||
SCALE = 0 # [0..1]
|
||||
TIME = 1 # Expressed via time in seconds since spool up started
|
||||
CYCLES = 2 # Expressed in amount of cycles since spool up started
|
||||
|
||||
|
||||
def calculateSpoolup(modMaxValue, modStepValue, modCycleTime, spoolType, spoolAmount):
|
||||
"""
|
||||
Calculate damage multiplier increment based on passed parameters. Module cycle time
|
||||
|
||||
Reference in New Issue
Block a user