Merge branch 'development' into MorePreferences
This commit is contained in:
@@ -1,15 +1,29 @@
|
||||
import sys
|
||||
from os.path import realpath, join, dirname, abspath
|
||||
|
||||
from logbook import Logger
|
||||
import os
|
||||
istravis = os.environ.get('TRAVIS') == 'true'
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
debug = False
|
||||
gamedataCache = True
|
||||
saveddataCache = True
|
||||
gamedata_version = ""
|
||||
gamedata_connectionstring = 'sqlite:///' + unicode(realpath(join(dirname(abspath(__file__)), "..", "eve.db")),
|
||||
sys.getfilesystemencoding())
|
||||
saveddata_connectionstring = 'sqlite:///' + unicode(
|
||||
realpath(join(dirname(abspath(__file__)), "..", "saveddata", "saveddata.db")), sys.getfilesystemencoding())
|
||||
gamedata_connectionstring = 'sqlite:///' + unicode(realpath(join(dirname(abspath(__file__)), "..", "eve.db")), sys.getfilesystemencoding())
|
||||
pyfalog.debug("Gamedata connection string: {0}", gamedata_connectionstring)
|
||||
|
||||
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:///' + unicode(realpath(join(dirname(abspath(__file__)), "..", "saveddata", "saveddata.db")), sys.getfilesystemencoding())
|
||||
|
||||
pyfalog.debug("Saveddata connection string: {0}", saveddata_connectionstring)
|
||||
|
||||
settings = {
|
||||
"setting1": True
|
||||
}
|
||||
|
||||
# Autodetect path, only change if the autodetection bugs out.
|
||||
path = dirname(unicode(__file__, sys.getfilesystemencoding()))
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
#
|
||||
# Used by:
|
||||
# Modules from group: Tractor Beam (4 of 4)
|
||||
from eos.config import settings
|
||||
type = "active"
|
||||
|
||||
|
||||
def handler(fit, module, context):
|
||||
print settings['setting1']
|
||||
pass
|
||||
|
||||
105
eos/gamedata.py
105
eos/gamedata.py
@@ -30,7 +30,10 @@ except ImportError:
|
||||
from utils.compat import OrderedDict
|
||||
|
||||
from logbook import Logger
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
# Keep a list of handlers that fail to import so we don't keep trying repeatedly.
|
||||
badHandlers = []
|
||||
|
||||
|
||||
class Effect(EqBase):
|
||||
@@ -158,43 +161,51 @@ class Effect(EqBase):
|
||||
Grab the handler, type and runTime from the effect code if it exists,
|
||||
if it doesn't, set dummy values and add a dummy handler
|
||||
"""
|
||||
try:
|
||||
self.__effectModule = effectModule = __import__('eos.effects.' + self.handlerName, fromlist=True)
|
||||
try:
|
||||
self.__handler = getattr(effectModule, "handler")
|
||||
except AttributeError:
|
||||
print "effect {} exists, but no handler".format(self.handlerName)
|
||||
raise
|
||||
global badHandlers
|
||||
|
||||
# Skip if we've tried to import before and failed
|
||||
if self.handlerName not in badHandlers:
|
||||
try:
|
||||
self.__runTime = getattr(effectModule, "runTime") or "normal"
|
||||
except AttributeError:
|
||||
self.__effectModule = effectModule = __import__('eos.effects.' + self.handlerName, fromlist=True)
|
||||
self.__handler = getattr(effectModule, "handler", effectDummy)
|
||||
self.__runTime = getattr(effectModule, "runTime", "normal")
|
||||
self.__activeByDefault = getattr(effectModule, "activeByDefault", True)
|
||||
t = getattr(effectModule, "type", None)
|
||||
|
||||
t = t if isinstance(t, tuple) or t is None else (t,)
|
||||
self.__type = t
|
||||
except (ImportError) as e:
|
||||
# Effect probably doesn't exist, so create a dummy effect and flag it with a warning.
|
||||
self.__handler = effectDummy
|
||||
self.__runTime = "normal"
|
||||
|
||||
try:
|
||||
self.__activeByDefault = getattr(effectModule, "activeByDefault")
|
||||
except AttributeError:
|
||||
self.__activeByDefault = True
|
||||
self.__type = None
|
||||
pyfalog.debug("ImportError generating handler: {0}", e)
|
||||
badHandlers.append(self.handlerName)
|
||||
except (AttributeError) as e:
|
||||
# Effect probably exists but there is an issue with it. Turn it into a dummy effect so we can continue, but flag it with an error.
|
||||
self.__handler = effectDummy
|
||||
self.__runTime = "normal"
|
||||
self.__activeByDefault = True
|
||||
self.__type = None
|
||||
pyfalog.error("AttributeError generating handler: {0}", e)
|
||||
badHandlers.append(self.handlerName)
|
||||
except Exception as e:
|
||||
self.__handler = effectDummy
|
||||
self.__runTime = "normal"
|
||||
self.__activeByDefault = True
|
||||
self.__type = None
|
||||
pyfalog.critical("Exception generating handler:")
|
||||
pyfalog.critical(e)
|
||||
badHandlers.append(self.handlerName)
|
||||
|
||||
try:
|
||||
t = getattr(effectModule, "type")
|
||||
except AttributeError:
|
||||
t = None
|
||||
|
||||
t = t if isinstance(t, tuple) or t is None else (t,)
|
||||
self.__type = t
|
||||
except (ImportError, AttributeError) as e:
|
||||
self.__generated = True
|
||||
else:
|
||||
# We've already failed on this one, just pass a dummy effect back
|
||||
self.__handler = effectDummy
|
||||
self.__runTime = "normal"
|
||||
self.__activeByDefault = True
|
||||
self.__type = None
|
||||
pyfalog.debug("ImportError or AttributeError generating handler:")
|
||||
pyfalog.debug(e)
|
||||
except Exception as e:
|
||||
pyfalog.critical("Exception generating handler:")
|
||||
pyfalog.critical(e)
|
||||
|
||||
self.__generated = True
|
||||
|
||||
def getattr(self, key):
|
||||
if not self.__generated:
|
||||
@@ -292,8 +303,6 @@ class Item(EqBase):
|
||||
@property
|
||||
def requiredSkills(self):
|
||||
if self.__requiredSkills is None:
|
||||
# This import should be here to make sure it's fully initialized
|
||||
from eos import db
|
||||
requiredSkills = OrderedDict()
|
||||
self.__requiredSkills = requiredSkills
|
||||
# Map containing attribute IDs we may need for required skills
|
||||
@@ -304,7 +313,7 @@ class Item(EqBase):
|
||||
# { attributeID : attributeValue }
|
||||
skillAttrs = {}
|
||||
# Get relevant attribute values from db (required skill IDs and levels) for our item
|
||||
for attrInfo in db.directAttributeRequest((self.ID,), tuple(combinedAttrIDs)):
|
||||
for attrInfo in eos.db.directAttributeRequest((self.ID,), tuple(combinedAttrIDs)):
|
||||
attrID = attrInfo[1]
|
||||
attrVal = attrInfo[2]
|
||||
skillAttrs[attrID] = attrVal
|
||||
@@ -315,7 +324,7 @@ class Item(EqBase):
|
||||
skillID = int(skillAttrs[srqIDAtrr])
|
||||
skillLvl = skillAttrs[srqLvlAttr]
|
||||
# Fetch item from database and fill map
|
||||
item = db.getItem(skillID)
|
||||
item = eos.db.getItem(skillID)
|
||||
requiredSkills[item] = skillLvl
|
||||
return self.__requiredSkills
|
||||
|
||||
@@ -348,18 +357,20 @@ class Item(EqBase):
|
||||
# thus keep old mechanism for now
|
||||
except KeyError:
|
||||
# Define race map
|
||||
map = {1: "caldari",
|
||||
2: "minmatar",
|
||||
4: "amarr",
|
||||
5: "sansha", # Caldari + Amarr
|
||||
6: "blood", # Minmatar + Amarr
|
||||
8: "gallente",
|
||||
9: "guristas", # Caldari + Gallente
|
||||
10: "angelserp", # Minmatar + Gallente, final race depends on the order of skills
|
||||
12: "sisters", # Amarr + Gallente
|
||||
16: "jove",
|
||||
32: "sansha", # Incrusion Sansha
|
||||
128: "ore"}
|
||||
map = {
|
||||
1 : "caldari",
|
||||
2 : "minmatar",
|
||||
4 : "amarr",
|
||||
5 : "sansha", # Caldari + Amarr
|
||||
6 : "blood", # Minmatar + Amarr
|
||||
8 : "gallente",
|
||||
9 : "guristas", # Caldari + Gallente
|
||||
10 : "angelserp", # Minmatar + Gallente, final race depends on the order of skills
|
||||
12 : "sisters", # Amarr + Gallente
|
||||
16 : "jove",
|
||||
32 : "sansha", # Incrusion Sansha
|
||||
128: "ore"
|
||||
}
|
||||
# Race is None by default
|
||||
race = None
|
||||
# Check primary and secondary required skills' races
|
||||
@@ -429,7 +440,7 @@ class Item(EqBase):
|
||||
|
||||
def __repr__(self):
|
||||
return "Item(ID={}, name={}) at {}".format(
|
||||
self.ID, self.name, hex(id(self))
|
||||
self.ID, self.name, hex(id(self))
|
||||
)
|
||||
|
||||
|
||||
@@ -454,7 +465,6 @@ class Category(EqBase):
|
||||
|
||||
|
||||
class AlphaClone(EqBase):
|
||||
|
||||
@reconstructor
|
||||
def init(self):
|
||||
self.skillCache = {}
|
||||
@@ -482,10 +492,9 @@ class Icon(EqBase):
|
||||
|
||||
|
||||
class MarketGroup(EqBase):
|
||||
|
||||
def __repr__(self):
|
||||
return u"MarketGroup(ID={}, name={}, parent={}) at {}".format(
|
||||
self.ID, self.name, getattr(self.parent, "name", None), self.name, hex(id(self))
|
||||
self.ID, self.name, getattr(self.parent, "name", None), self.name, hex(id(self))
|
||||
).encode('utf8')
|
||||
|
||||
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
|
||||
import collections
|
||||
from math import exp
|
||||
# TODO: This needs to be moved out, we shouldn't have *ANY* dependencies back to other modules/methods inside eos.
|
||||
# This also breaks writing any tests. :(
|
||||
from eos.db.gamedata.queries import getAttributeInfo
|
||||
|
||||
defaultValuesCache = {}
|
||||
cappingAttrKeyCache = {}
|
||||
@@ -26,22 +29,26 @@ cappingAttrKeyCache = {}
|
||||
|
||||
class ItemAttrShortcut(object):
|
||||
def getModifiedItemAttr(self, key, default=None):
|
||||
if key in self.itemModifiedAttributes:
|
||||
return self.itemModifiedAttributes[key]
|
||||
else:
|
||||
return default
|
||||
return_value = self.itemModifiedAttributes.get(key)
|
||||
|
||||
if return_value is None and default is not None:
|
||||
return_value = default
|
||||
|
||||
return return_value
|
||||
|
||||
|
||||
class ChargeAttrShortcut(object):
|
||||
def getModifiedChargeAttr(self, key, default=None):
|
||||
if key in self.chargeModifiedAttributes:
|
||||
return self.chargeModifiedAttributes[key]
|
||||
else:
|
||||
return default
|
||||
return_value = self.chargeModifiedAttributes.get(key)
|
||||
|
||||
if return_value is None and default is not None:
|
||||
return_value = default
|
||||
|
||||
return return_value
|
||||
|
||||
|
||||
class ModifiedAttributeDict(collections.MutableMapping):
|
||||
OVERRIDES = False
|
||||
overrides_enabled = False
|
||||
|
||||
class CalculationPlaceholder(object):
|
||||
def __init__(self):
|
||||
@@ -98,15 +105,23 @@ class ModifiedAttributeDict(collections.MutableMapping):
|
||||
|
||||
def __getitem__(self, key):
|
||||
# Check if we have final calculated value
|
||||
if key in self.__modified:
|
||||
if self.__modified[key] == self.CalculationPlaceholder:
|
||||
self.__modified[key] = self.__calculateValue(key)
|
||||
return self.__modified[key]
|
||||
key_value = self.__modified.get(key)
|
||||
if key_value is self.CalculationPlaceholder:
|
||||
key_value = self.__modified[key] = self.__calculateValue(key)
|
||||
|
||||
if key_value is not None:
|
||||
return key_value
|
||||
|
||||
# Then in values which are not yet calculated
|
||||
elif key in self.__intermediary:
|
||||
return self.__intermediary[key]
|
||||
# Original value is the least priority
|
||||
if self.__intermediary:
|
||||
val = self.__intermediary.get(key)
|
||||
else:
|
||||
val = None
|
||||
|
||||
if val is not None:
|
||||
return val
|
||||
else:
|
||||
# Original value is the least priority
|
||||
return self.getOriginal(key)
|
||||
|
||||
def __delitem__(self, key):
|
||||
@@ -115,12 +130,18 @@ class ModifiedAttributeDict(collections.MutableMapping):
|
||||
if key in self.__intermediary:
|
||||
del self.__intermediary[key]
|
||||
|
||||
def getOriginal(self, key):
|
||||
if self.OVERRIDES and key in self.__overrides:
|
||||
return self.__overrides.get(key).value
|
||||
val = self.__original.get(key)
|
||||
def getOriginal(self, key, default=None):
|
||||
if self.overrides_enabled and self.overrides:
|
||||
val = self.overrides.get(key, None)
|
||||
else:
|
||||
val = None
|
||||
|
||||
if val is None:
|
||||
return None
|
||||
if self.original:
|
||||
val = self.original.get(key, None)
|
||||
|
||||
if val is None and val != default:
|
||||
val = default
|
||||
|
||||
return val.value if hasattr(val, "value") else val
|
||||
|
||||
@@ -128,12 +149,12 @@ class ModifiedAttributeDict(collections.MutableMapping):
|
||||
self.__intermediary[key] = val
|
||||
|
||||
def __iter__(self):
|
||||
all = dict(self.__original, **self.__modified)
|
||||
return (key for key in all)
|
||||
all_dict = dict(self.original, **self.__modified)
|
||||
return (key for key in all_dict)
|
||||
|
||||
def __contains__(self, key):
|
||||
return (self.__original is not None and key in self.__original) or \
|
||||
key in self.__modified or key in self.__intermediary
|
||||
return (self.original is not None and key in self.original) or \
|
||||
key in self.__modified or key in self.__intermediary
|
||||
|
||||
def __placehold(self, key):
|
||||
"""Create calculation placeholder in item's modified attribute dict"""
|
||||
@@ -141,7 +162,7 @@ class ModifiedAttributeDict(collections.MutableMapping):
|
||||
|
||||
def __len__(self):
|
||||
keys = set()
|
||||
keys.update(self.__original.iterkeys())
|
||||
keys.update(self.original.iterkeys())
|
||||
keys.update(self.__modified.iterkeys())
|
||||
keys.update(self.__intermediary.iterkeys())
|
||||
return len(keys)
|
||||
@@ -152,7 +173,6 @@ class ModifiedAttributeDict(collections.MutableMapping):
|
||||
try:
|
||||
cappingKey = cappingAttrKeyCache[key]
|
||||
except KeyError:
|
||||
from eos.db.gamedata.queries import getAttributeInfo
|
||||
attrInfo = getAttributeInfo(key)
|
||||
if attrInfo is None:
|
||||
cappingId = cappingAttrKeyCache[key] = None
|
||||
@@ -166,12 +186,8 @@ class ModifiedAttributeDict(collections.MutableMapping):
|
||||
cappingKey = None if cappingAttrInfo is None else cappingAttrInfo.name
|
||||
|
||||
if cappingKey:
|
||||
if cappingKey in self.original:
|
||||
# some items come with their own caps (ie: carriers). If they do, use this
|
||||
cappingValue = self.original.get(cappingKey).value
|
||||
else:
|
||||
# If not, get info about the default value
|
||||
cappingValue = self.__calculateValue(cappingKey)
|
||||
cappingValue = self.original.get(cappingKey, self.__calculateValue(cappingKey))
|
||||
cappingValue = cappingValue.value if hasattr(cappingValue, "value") else cappingValue
|
||||
else:
|
||||
cappingValue = None
|
||||
|
||||
@@ -183,25 +199,28 @@ class ModifiedAttributeDict(collections.MutableMapping):
|
||||
force = min(force, cappingValue)
|
||||
return force
|
||||
# Grab our values if they're there, otherwise we'll take default values
|
||||
preIncrease = self.__preIncreases[key] if key in self.__preIncreases else 0
|
||||
multiplier = self.__multipliers[key] if key in self.__multipliers else 1
|
||||
penalizedMultiplierGroups = self.__penalizedMultipliers[key] if key in self.__penalizedMultipliers else {}
|
||||
postIncrease = self.__postIncreases[key] if key in self.__postIncreases else 0
|
||||
preIncrease = self.__preIncreases.get(key, 0)
|
||||
multiplier = self.__multipliers.get(key, 1)
|
||||
penalizedMultiplierGroups = self.__penalizedMultipliers.get(key, {})
|
||||
postIncrease = self.__postIncreases.get(key, 0)
|
||||
|
||||
# Grab initial value, priorities are:
|
||||
# Results of ongoing calculation > preAssign > original > 0
|
||||
try:
|
||||
default = defaultValuesCache[key]
|
||||
except KeyError:
|
||||
from eos.db.gamedata.queries import getAttributeInfo
|
||||
attrInfo = getAttributeInfo(key)
|
||||
if attrInfo is None:
|
||||
default = defaultValuesCache[key] = 0.0
|
||||
else:
|
||||
dv = attrInfo.defaultValue
|
||||
default = defaultValuesCache[key] = dv if dv is not None else 0.0
|
||||
val = self.__intermediary[key] if key in self.__intermediary else self.__preAssigns[
|
||||
key] if key in self.__preAssigns else self.getOriginal(key) if key in self.__original else default
|
||||
|
||||
val = self.__intermediary.get(key,
|
||||
self.__preAssigns.get(key,
|
||||
self.getOriginal(key, default)
|
||||
)
|
||||
)
|
||||
|
||||
# We'll do stuff in the following order:
|
||||
# preIncrease > multiplier > stacking penalized multipliers > postIncrease
|
||||
@@ -254,7 +273,7 @@ class ModifiedAttributeDict(collections.MutableMapping):
|
||||
return skill.level
|
||||
|
||||
def getAfflictions(self, key):
|
||||
return self.__affectedBy[key] if key in self.__affectedBy else {}
|
||||
return self.__affectedBy.get(key, {})
|
||||
|
||||
def iterAfflictions(self):
|
||||
return self.__affectedBy.__iter__()
|
||||
@@ -360,6 +379,6 @@ class ModifiedAttributeDict(collections.MutableMapping):
|
||||
|
||||
|
||||
class Affliction(object):
|
||||
def __init__(self, type, amount):
|
||||
self.type = type
|
||||
def __init__(self, affliction_type, amount):
|
||||
self.type = affliction_type
|
||||
self.amount = amount
|
||||
|
||||
@@ -128,9 +128,9 @@ class Fit(object):
|
||||
self.__capRecharge = None
|
||||
self.__calculatedTargets = []
|
||||
self.__remoteReps = {
|
||||
"Armor": None,
|
||||
"Shield": None,
|
||||
"Hull": None,
|
||||
"Armor" : None,
|
||||
"Shield" : None,
|
||||
"Hull" : None,
|
||||
"Capacitor": None,
|
||||
}
|
||||
self.factorReload = False
|
||||
@@ -370,9 +370,11 @@ class Fit(object):
|
||||
|
||||
@validates("ID", "ownerID", "shipID")
|
||||
def validator(self, key, val):
|
||||
map = {"ID": lambda _val: isinstance(_val, int),
|
||||
"ownerID": lambda _val: isinstance(_val, int) or _val is None,
|
||||
"shipID": lambda _val: isinstance(_val, int) or _val is None}
|
||||
map = {
|
||||
"ID" : lambda _val: isinstance(_val, int),
|
||||
"ownerID": lambda _val: isinstance(_val, int) or _val is None,
|
||||
"shipID" : lambda _val: isinstance(_val, int) or _val is None
|
||||
}
|
||||
|
||||
if not map[key](val):
|
||||
raise ValueError(str(val) + " is not a valid value for " + key)
|
||||
@@ -408,15 +410,15 @@ class Fit(object):
|
||||
self.ship.clear()
|
||||
|
||||
c = chain(
|
||||
self.modules,
|
||||
self.drones,
|
||||
self.fighters,
|
||||
self.boosters,
|
||||
self.implants,
|
||||
self.projectedDrones,
|
||||
self.projectedModules,
|
||||
self.projectedFighters,
|
||||
(self.character, self.extraAttributes),
|
||||
self.modules,
|
||||
self.drones,
|
||||
self.fighters,
|
||||
self.boosters,
|
||||
self.implants,
|
||||
self.projectedDrones,
|
||||
self.projectedModules,
|
||||
self.projectedFighters,
|
||||
(self.character, self.extraAttributes),
|
||||
)
|
||||
|
||||
for stuff in c:
|
||||
@@ -476,11 +478,11 @@ class Fit(object):
|
||||
|
||||
if warfareBuffID == 11: # Shield Burst: Active Shielding: Repair Duration/Capacitor
|
||||
self.modules.filteredItemBoost(
|
||||
lambda mod: mod.item.requiresSkill("Shield Operation") or mod.item.requiresSkill(
|
||||
"Shield Emission Systems"), "capacitorNeed", value)
|
||||
lambda mod: mod.item.requiresSkill("Shield Operation") or mod.item.requiresSkill(
|
||||
"Shield Emission Systems"), "capacitorNeed", value)
|
||||
self.modules.filteredItemBoost(
|
||||
lambda mod: mod.item.requiresSkill("Shield Operation") or mod.item.requiresSkill(
|
||||
"Shield Emission Systems"), "duration", value)
|
||||
lambda mod: mod.item.requiresSkill("Shield Operation") or mod.item.requiresSkill(
|
||||
"Shield Emission Systems"), "duration", value)
|
||||
|
||||
if warfareBuffID == 12: # Shield Burst: Shield Extension: Shield HP
|
||||
self.ship.boostItemAttr("shieldCapacity", value, stackingPenalties=True)
|
||||
@@ -506,26 +508,26 @@ class Fit(object):
|
||||
if warfareBuffID == 17: # Information Burst: Electronic Superiority: EWAR Range and Strength
|
||||
groups = ("ECM", "Sensor Dampener", "Weapon Disruptor", "Target Painter")
|
||||
self.modules.filteredItemBoost(lambda mod: mod.item.group.name in groups, "maxRange", value,
|
||||
stackingPenalties=True)
|
||||
stackingPenalties=True)
|
||||
self.modules.filteredItemBoost(lambda mod: mod.item.group.name in groups,
|
||||
"falloffEffectiveness", value, stackingPenalties=True)
|
||||
"falloffEffectiveness", value, stackingPenalties=True)
|
||||
|
||||
for scanType in ("Magnetometric", "Radar", "Ladar", "Gravimetric"):
|
||||
self.modules.filteredItemBoost(lambda mod: mod.item.group.name == "ECM",
|
||||
"scan%sStrengthBonus" % scanType, value,
|
||||
stackingPenalties=True)
|
||||
"scan%sStrengthBonus" % scanType, value,
|
||||
stackingPenalties=True)
|
||||
|
||||
for attr in ("missileVelocityBonus", "explosionDelayBonus", "aoeVelocityBonus", "falloffBonus",
|
||||
"maxRangeBonus", "aoeCloudSizeBonus", "trackingSpeedBonus"):
|
||||
self.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Weapon Disruptor",
|
||||
attr, value)
|
||||
attr, value)
|
||||
|
||||
for attr in ("maxTargetRangeBonus", "scanResolutionBonus"):
|
||||
self.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Sensor Dampener",
|
||||
attr, value)
|
||||
attr, value)
|
||||
|
||||
self.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Target Painter",
|
||||
"signatureRadiusBonus", value, stackingPenalties=True)
|
||||
"signatureRadiusBonus", value, stackingPenalties=True)
|
||||
|
||||
if warfareBuffID == 18: # Information Burst: Electronic Hardening: Scan Strength
|
||||
for scanType in ("Gravimetric", "Radar", "Ladar", "Magnetometric"):
|
||||
@@ -543,35 +545,33 @@ class Fit(object):
|
||||
|
||||
if warfareBuffID == 21: # Skirmish Burst: Interdiction Maneuvers: Tackle Range
|
||||
groups = ("Stasis Web", "Warp Scrambler")
|
||||
self.modules.filteredItemBoost(lambda mod: mod.item.group.name in groups, "maxRange", value,
|
||||
stackingPenalties=True)
|
||||
self.modules.filteredItemBoost(lambda mod: mod.item.group.name in groups, "maxRange", value, stackingPenalties=True)
|
||||
|
||||
if warfareBuffID == 22: # Skirmish Burst: Rapid Deployment: AB/MWD Speed Increase
|
||||
self.modules.filteredItemBoost(
|
||||
lambda mod: mod.item.requiresSkill("Afterburner") or mod.item.requiresSkill(
|
||||
"High Speed Maneuvering"), "speedFactor", value, stackingPenalties=True)
|
||||
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Afterburner") or mod.item.requiresSkill("High Speed Maneuvering"),
|
||||
"speedFactor", value, stackingPenalties=True)
|
||||
|
||||
if warfareBuffID == 23: # Mining Burst: Mining Laser Field Enhancement: Mining/Survey Range
|
||||
self.modules.filteredItemBoost(
|
||||
lambda mod: mod.item.requiresSkill("Mining") or mod.item.requiresSkill(
|
||||
"Ice Harvesting") or mod.item.requiresSkill("Gas Cloud Harvesting"), "maxRange",
|
||||
value, stackingPenalties=True)
|
||||
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("CPU Management"),
|
||||
"surveyScanRange", value, stackingPenalties=True)
|
||||
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining") or
|
||||
mod.item.requiresSkill("Ice Harvesting") or
|
||||
mod.item.requiresSkill("Gas Cloud Harvesting"),
|
||||
"maxRange", value, stackingPenalties=True)
|
||||
|
||||
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("CPU Management"), "surveyScanRange", value, stackingPenalties=True)
|
||||
|
||||
if warfareBuffID == 24: # Mining Burst: Mining Laser Optimization: Mining Capacitor/Duration
|
||||
self.modules.filteredItemBoost(
|
||||
lambda mod: mod.item.requiresSkill("Mining") or mod.item.requiresSkill(
|
||||
"Ice Harvesting") or mod.item.requiresSkill("Gas Cloud Harvesting"),
|
||||
"capacitorNeed", value, stackingPenalties=True)
|
||||
self.modules.filteredItemBoost(
|
||||
lambda mod: mod.item.requiresSkill("Mining") or mod.item.requiresSkill(
|
||||
"Ice Harvesting") or mod.item.requiresSkill("Gas Cloud Harvesting"), "duration",
|
||||
value, stackingPenalties=True)
|
||||
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining") or
|
||||
mod.item.requiresSkill("Ice Harvesting") or
|
||||
mod.item.requiresSkill("Gas Cloud Harvesting"),
|
||||
"capacitorNeed", value, stackingPenalties=True)
|
||||
|
||||
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining") or
|
||||
mod.item.requiresSkill("Ice Harvesting") or
|
||||
mod.item.requiresSkill("Gas Cloud Harvesting"),
|
||||
"duration", value, stackingPenalties=True)
|
||||
|
||||
if warfareBuffID == 25: # Mining Burst: Mining Equipment Preservation: Crystal Volatility
|
||||
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining"),
|
||||
"crystalVolatilityChance", value, stackingPenalties=True)
|
||||
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining"), "crystalVolatilityChance", value, stackingPenalties=True)
|
||||
|
||||
if warfareBuffID == 60: # Skirmish Burst: Evasive Maneuvers: Agility
|
||||
self.ship.boostItemAttr("agility", value, stackingPenalties=True)
|
||||
@@ -844,15 +844,17 @@ class Fit(object):
|
||||
|
||||
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"}
|
||||
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"
|
||||
}
|
||||
|
||||
def getSlotsFree(self, type, countDummies=False):
|
||||
if type in (Slot.MODE, Slot.SYSTEM):
|
||||
@@ -927,20 +929,19 @@ class Fit(object):
|
||||
|
||||
return amount
|
||||
|
||||
# Expresses how difficult a target is to probe down with scan probes
|
||||
# If this is <1.08, the ship is unproabeable
|
||||
@property
|
||||
def probeSize(self):
|
||||
"""
|
||||
Expresses how difficult a target is to probe down with scan probes
|
||||
"""
|
||||
|
||||
sigRad = self.ship.getModifiedItemAttr("signatureRadius")
|
||||
sensorStr = float(self.scanStrength)
|
||||
probeSize = sigRad / sensorStr if sensorStr != 0 else None
|
||||
# http://www.eveonline.com/ingameboard.asp?a=topic&threadID=1532170&page=2#42
|
||||
if probeSize is not None:
|
||||
# http://forum.eve-ru.com/index.php?showtopic=74195&view=findpost&p=1333691
|
||||
# http://forum.eve-ru.com/index.php?showtopic=74195&view=findpost&p=1333763
|
||||
# Tests by tester128 and several conclusions by me, prove that cap is in range
|
||||
# from 1.1 to 1.12, we're picking average value
|
||||
probeSize = max(probeSize, 1.11)
|
||||
# Probe size is capped at 1.08
|
||||
probeSize = max(probeSize, 1.08)
|
||||
return probeSize
|
||||
|
||||
@property
|
||||
@@ -1002,29 +1003,35 @@ class Fit(object):
|
||||
def calculateSustainableTank(self, effective=True):
|
||||
if self.__sustainableTank is None:
|
||||
if self.capStable:
|
||||
sustainable = {"armorRepair": self.extraAttributes["armorRepair"],
|
||||
"shieldRepair": self.extraAttributes["shieldRepair"],
|
||||
"hullRepair": self.extraAttributes["hullRepair"]}
|
||||
sustainable = {
|
||||
"armorRepair" : self.extraAttributes["armorRepair"],
|
||||
"shieldRepair": self.extraAttributes["shieldRepair"],
|
||||
"hullRepair" : self.extraAttributes["hullRepair"]
|
||||
}
|
||||
else:
|
||||
sustainable = {}
|
||||
|
||||
repairers = []
|
||||
# Map a repairer type to the attribute it uses
|
||||
groupAttrMap = {"Armor Repair Unit": "armorDamageAmount",
|
||||
"Ancillary Armor Repairer": "armorDamageAmount",
|
||||
"Hull Repair Unit": "structureDamageAmount",
|
||||
"Shield Booster": "shieldBonus",
|
||||
"Ancillary Shield Booster": "shieldBonus",
|
||||
"Remote Armor Repairer": "armorDamageAmount",
|
||||
"Remote Shield Booster": "shieldBonus"}
|
||||
groupAttrMap = {
|
||||
"Armor Repair Unit" : "armorDamageAmount",
|
||||
"Ancillary Armor Repairer": "armorDamageAmount",
|
||||
"Hull Repair Unit" : "structureDamageAmount",
|
||||
"Shield Booster" : "shieldBonus",
|
||||
"Ancillary Shield Booster": "shieldBonus",
|
||||
"Remote Armor Repairer" : "armorDamageAmount",
|
||||
"Remote Shield Booster" : "shieldBonus"
|
||||
}
|
||||
# Map repairer type to attribute
|
||||
groupStoreMap = {"Armor Repair Unit": "armorRepair",
|
||||
"Hull Repair Unit": "hullRepair",
|
||||
"Shield Booster": "shieldRepair",
|
||||
"Ancillary Shield Booster": "shieldRepair",
|
||||
"Remote Armor Repairer": "armorRepair",
|
||||
"Remote Shield Booster": "shieldRepair",
|
||||
"Ancillary Armor Repairer": "armorRepair", }
|
||||
groupStoreMap = {
|
||||
"Armor Repair Unit" : "armorRepair",
|
||||
"Hull Repair Unit" : "hullRepair",
|
||||
"Shield Booster" : "shieldRepair",
|
||||
"Ancillary Shield Booster": "shieldRepair",
|
||||
"Remote Armor Repairer" : "armorRepair",
|
||||
"Remote Shield Booster" : "shieldRepair",
|
||||
"Ancillary Armor Repairer": "armorRepair",
|
||||
}
|
||||
|
||||
capUsed = self.capUsed
|
||||
for attr in ("shieldRepair", "armorRepair", "hullRepair"):
|
||||
@@ -1052,7 +1059,7 @@ class Fit(object):
|
||||
|
||||
# Sort repairers by efficiency. We want to use the most efficient repairers first
|
||||
repairers.sort(key=lambda _mod: _mod.getModifiedItemAttr(
|
||||
groupAttrMap[_mod.item.group.name]) / _mod.getModifiedItemAttr("capacitorNeed"), reverse=True)
|
||||
groupAttrMap[_mod.item.group.name]) / _mod.getModifiedItemAttr("capacitorNeed"), reverse=True)
|
||||
|
||||
# Loop through every module until we're above peak recharge
|
||||
# Most efficient first, as we sorted earlier.
|
||||
@@ -1365,10 +1372,10 @@ class Fit(object):
|
||||
|
||||
def __repr__(self):
|
||||
return u"Fit(ID={}, ship={}, name={}) at {}".format(
|
||||
self.ID, self.ship.item.name, self.name, hex(id(self))
|
||||
self.ID, self.ship.item.name, self.name, hex(id(self))
|
||||
).encode('utf8')
|
||||
|
||||
def __str__(self):
|
||||
return u"{} ({})".format(
|
||||
self.name, self.ship.item.name
|
||||
self.name, self.ship.item.name
|
||||
).encode('utf8')
|
||||
|
||||
@@ -173,7 +173,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
charges = 0
|
||||
else:
|
||||
charges = floor(containerCapacity / chargeVolume)
|
||||
return charges
|
||||
return int(charges)
|
||||
|
||||
@property
|
||||
def numShots(self):
|
||||
@@ -689,7 +689,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
|
||||
Currently would apply to bomb launchers and defender missiles
|
||||
"""
|
||||
effective_reload_time = ((self.reactivationDelay * numShots) + raw_reload_time) / numShots
|
||||
effective_reload_time = ((self.reactivationDelay * (numShots - 1)) + max(raw_reload_time, self.reactivationDelay, 0)) / numShots
|
||||
else:
|
||||
"""
|
||||
Applies to MJD/MJFG
|
||||
|
||||
Reference in New Issue
Block a user