Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
eve.db
#Python specific
*.pyc

Expand Down
8 changes: 8 additions & 0 deletions _development/helpers_fits.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,41 @@
# noinspection PyShadowingNames
@pytest.fixture
def RifterFit(DB, Gamedata, Saveddata):
from eos.const import ImplantLocation
print("Creating Rifter")
item = DB['gamedata_session'].query(Gamedata['Item']).filter(Gamedata['Item'].name == "Rifter").first()
ship = Saveddata['Ship'](item)
# setup fit
fit = Saveddata['Fit'](ship, "My Rifter Fit")
fit.implantLocation = ImplantLocation.FIT

return fit


# noinspection PyShadowingNames
@pytest.fixture
def KeepstarFit(DB, Gamedata, Saveddata):
from eos.const import ImplantLocation
print("Creating Keepstar")
item = DB['gamedata_session'].query(Gamedata['Item']).filter(Gamedata['Item'].name == "Keepstar").first()
ship = Saveddata['Structure'](item)
# setup fit
fit = Saveddata['Fit'](ship, "Keepstar Fit")
fit.implantLocation = ImplantLocation.FIT

return fit


# noinspection PyShadowingNames
@pytest.fixture
def CurseFit(DB, Gamedata, Saveddata):
from eos.const import ImplantLocation
print("Creating Curse - With Neuts")
item = DB['gamedata_session'].query(Gamedata['Item']).filter(Gamedata['Item'].name == "Curse").first()
ship = Saveddata['Ship'](item)
# setup fit
fit = Saveddata['Fit'](ship, "Curse - With Neuts")
fit.implantLocation = ImplantLocation.FIT

mod = Saveddata['Module'](DB['db'].getItem("Medium Energy Neutralizer II"))
mod.state = Saveddata['State'].ONLINE
Expand All @@ -49,11 +55,13 @@ def CurseFit(DB, Gamedata, Saveddata):
# noinspection PyShadowingNames
@pytest.fixture
def HeronFit(DB, Gamedata, Saveddata):
from eos.const import ImplantLocation
print("Creating Heron - RemoteSebo")
item = DB['gamedata_session'].query(Gamedata['Item']).filter(Gamedata['Item'].name == "Heron").first()
ship = Saveddata['Ship'](item)
# setup fit
fit = Saveddata['Fit'](ship, "Heron - RemoteSebo")
fit.implantLocation = ImplantLocation.FIT

mod = Saveddata['Module'](DB['db'].getItem("Remote Sensor Booster II"))
mod.state = Saveddata['State'].ONLINE
Expand Down
3 changes: 2 additions & 1 deletion _development/helpers_locale.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ def GetPath(root, file=None, codec=None):
path = os.path.join(path, file)

if codec:
path = path.decode(codec)
# path = path.decode(codec)
pass

return path

Expand Down
14 changes: 14 additions & 0 deletions eos/db/migrations/upgrade50.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
"""
Migration 50

- add pulseInterval column to modules table
"""

import sqlalchemy


def upgrade(saveddata_engine):
try:
saveddata_engine.execute("SELECT pulseInterval FROM modules LIMIT 1;")
except sqlalchemy.exc.DatabaseError:
saveddata_engine.execute("ALTER TABLE modules ADD COLUMN pulseInterval FLOAT;")
1 change: 1 addition & 0 deletions eos/db/saveddata/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
Column("spoolType", Integer, nullable=True),
Column("spoolAmount", Float, nullable=True),
Column("projectionRange", Float, nullable=True),
Column("pulseInterval", Float, nullable=True),
CheckConstraint('("dummySlot" = NULL OR "itemID" = NULL) AND "dummySlot" != "itemID"'))


Expand Down
60 changes: 46 additions & 14 deletions eos/effects.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,34 @@ class DummyEffect(BaseEffect):
pass


def _get_effect_cycle_time_ms(container, reloadOverride=None):
try:
cycle_params = container.getCycleParameters(reloadOverride=reloadOverride)
except AttributeError:
cycle_params = None
if cycle_params is not None:
return cycle_params.averageTime
return getattr(container, 'pulseAdjustedCycleTime', 0)


def _get_projected_rr_cycle_time_s(container, fit):
reload_override = None
try:
reload_override = container.owner.factorReload
except AttributeError:
reload_override = None
if reload_override is None:
reload_override = getattr(fit, "factorReload", None)
cycle_ms = _get_effect_cycle_time_ms(container, reloadOverride=reload_override)
if reload_override and getattr(container, "charge", None) and getattr(container, "reloadTime", 0):
shots = getattr(container, "numShots", 0) or 0
if shots == 0:
base_ms = getattr(container, "pulseAdjustedCycleTime", 0) or getattr(container, "rawCycleTime", 0)
if base_ms:
cycle_ms = base_ms + container.reloadTime
return cycle_ms / 1000.0


class Effect100000(BaseEffect):
"""
pyfaCustomSuppressionTackleRange
Expand Down Expand Up @@ -89,7 +117,7 @@ class Effect4(BaseEffect):
@staticmethod
def handler(fit, module, context, projectionRange, **kwargs):
amount = module.getModifiedItemAttr('shieldBonus')
speed = module.getModifiedItemAttr('duration') / 1000.0
speed = _get_effect_cycle_time_ms(module) / 1000.0
fit.extraAttributes.increase('shieldRepair', amount / speed, **kwargs)


Expand Down Expand Up @@ -181,7 +209,7 @@ class Effect26(BaseEffect):
@staticmethod
def handler(fit, module, context, projectionRange, **kwargs):
amount = module.getModifiedItemAttr('structureDamageAmount')
speed = module.getModifiedItemAttr('duration') / 1000.0
speed = _get_effect_cycle_time_ms(module) / 1000.0
fit.extraAttributes.increase('hullRepair', amount / speed, **kwargs)


Expand All @@ -199,7 +227,7 @@ class Effect27(BaseEffect):
@staticmethod
def handler(fit, module, context, projectionRange, **kwargs):
amount = module.getModifiedItemAttr('armorDamageAmount')
speed = module.getModifiedItemAttr('duration') / 1000.0
speed = _get_effect_cycle_time_ms(module) / 1000.0
rps = amount / speed
fit.extraAttributes.increase('armorRepair', rps, **kwargs)
fit.extraAttributes.increase('armorRepairPreSpool', rps, **kwargs)
Expand Down Expand Up @@ -16586,7 +16614,7 @@ class Effect4936(BaseEffect):
@staticmethod
def handler(fit, module, context, projectionRange, **kwargs):
amount = module.getModifiedItemAttr('shieldBonus')
speed = module.getModifiedItemAttr('duration') / 1000.0
speed = _get_effect_cycle_time_ms(module) / 1000.0
fit.extraAttributes.increase('shieldRepair', amount / speed, **kwargs)


Expand Down Expand Up @@ -18792,7 +18820,7 @@ def handler(fit, module, context, projectionRange, **kwargs):
multiplier = 1

amount = module.getModifiedItemAttr('armorDamageAmount') * multiplier
speed = module.getModifiedItemAttr('duration') / 1000.0
speed = _get_effect_cycle_time_ms(module) / 1000.0
rps = amount / speed
fit.extraAttributes.increase('armorRepair', rps, **kwargs)
fit.extraAttributes.increase('armorRepairPreSpool', rps, **kwargs)
Expand Down Expand Up @@ -24762,7 +24790,7 @@ def handler(fit, src, context, projectionRange, **kwargs):
if src.getModifiedItemAttr('maxRange', 0) < (projectionRange or 0):
return
amount = src.getModifiedItemAttr('powerTransferAmount')
duration = src.getModifiedItemAttr('duration')
duration = _get_effect_cycle_time_ms(src)
if 'effect' in kwargs:
from eos.modifiedAttributeDict import ModifiedAttributeDict
amount *= ModifiedAttributeDict.getResistance(fit, kwargs['effect'])
Expand Down Expand Up @@ -24791,7 +24819,7 @@ def handler(fit, module, context, projectionRange, **kwargs):
srcOptimalRange=module.getModifiedItemAttr('maxRange'),
srcFalloffRange=module.getModifiedItemAttr('falloffEffectiveness'),
distance=projectionRange)
duration = module.getModifiedItemAttr('duration') / 1000.0
duration = _get_projected_rr_cycle_time_s(module, fit)
fit._hullRr.append((bonus, duration))


Expand All @@ -24816,7 +24844,7 @@ def handler(fit, container, context, projectionRange, **kwargs):
srcOptimalRange=container.getModifiedItemAttr('maxRange'),
srcFalloffRange=container.getModifiedItemAttr('falloffEffectiveness'),
distance=projectionRange)
duration = container.getModifiedItemAttr('duration') / 1000.0
duration = _get_projected_rr_cycle_time_s(container, fit)
fit._shieldRr.append((bonus, duration))


Expand Down Expand Up @@ -24844,7 +24872,7 @@ def handler(fit, src, context, projectionRange, **kwargs):
if 'effect' in kwargs:
from eos.modifiedAttributeDict import ModifiedAttributeDict
amount *= ModifiedAttributeDict.getResistance(fit, kwargs['effect'])
time = src.getModifiedItemAttr('duration')
time = _get_effect_cycle_time_ms(src)
fit.addDrain(src, time, amount, 0)


Expand All @@ -24870,7 +24898,7 @@ def handler(fit, container, context, projectionRange, **kwargs):
srcOptimalRange=container.getModifiedItemAttr('maxRange'),
srcFalloffRange=container.getModifiedItemAttr('falloffEffectiveness'),
distance=projectionRange)
duration = container.getModifiedItemAttr('duration') / 1000.0
duration = _get_projected_rr_cycle_time_s(container, fit)
fit._armorRr.append((bonus, duration))
fit._armorRrPreSpool.append((bonus, duration))
fit._armorRrFullSpool.append((bonus, duration))
Expand Down Expand Up @@ -24908,7 +24936,7 @@ class Effect6197(BaseEffect):
@staticmethod
def handler(fit, src, context, projectionRange, **kwargs):
amount = src.getModifiedItemAttr('powerTransferAmount')
time = src.getModifiedItemAttr('duration')
time = _get_effect_cycle_time_ms(src)
if 'projected' in context:
if 'effect' in kwargs:
from eos.modifiedAttributeDict import ModifiedAttributeDict
Expand Down Expand Up @@ -24996,7 +25024,7 @@ def handler(fit, src, context, projectionRange, **kwargs):
if 'effect' in kwargs:
from eos.modifiedAttributeDict import ModifiedAttributeDict
amount *= ModifiedAttributeDict.getResistance(fit, kwargs['effect'])
time = src.getModifiedItemAttr('duration')
time = src.pulseAdjustedCycleTime
fit.addDrain(src, time, amount, 0)


Expand Down Expand Up @@ -29865,8 +29893,8 @@ def handler(fit, module, context, projectionRange, **kwargs):
srcOptimalRange=module.getModifiedItemAttr('maxRange'),
srcFalloffRange=module.getModifiedItemAttr('falloffEffectiveness'),
distance=projectionRange)
speed = module.getModifiedItemAttr('duration') / 1000.0
fit._shieldRr.append((amount, speed))
duration = _get_projected_rr_cycle_time_s(module, fit)
fit._shieldRr.append((amount, duration))


class Effect6653(BaseEffect):
Expand Down Expand Up @@ -35103,6 +35131,10 @@ def handler(fit, container, context, projectionRange, **kwargs):
repSpoolPerCycle = container.getModifiedItemAttr('repairMultiplierBonusPerCycle')
defaultSpoolValue = eos.config.settings['globalDefaultSpoolupPercentage']
spoolType, spoolAmount = resolveSpoolOptions(SpoolOptions(SpoolType.SPOOL_SCALE, defaultSpoolValue, False), container)

if container.pulseInterval is not None and container.pulseInterval > cycleTime + 0.001:
spoolAmount = 0

amount = repAmountBase * (1 + calculateSpoolup(repSpoolMax, repSpoolPerCycle, cycleTime, spoolType, spoolAmount)[0])
amountPreSpool = repAmountBase * (1 + calculateSpoolup(repSpoolMax, repSpoolPerCycle, cycleTime, SpoolType.SPOOL_SCALE, 0)[0])
amountFullSpool = repAmountBase * (1 + calculateSpoolup(repSpoolMax, repSpoolPerCycle, cycleTime, SpoolType.SPOOL_SCALE, 1)[0])
Expand Down
112 changes: 110 additions & 2 deletions eos/modifiedAttributeDict.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
cappingAttrKeyCache = {}
resistanceCache = {}

_debug_max_velocity_count = 0


def getAttrDefault(key, fallback=None):
try:
Expand Down Expand Up @@ -217,19 +219,125 @@ def getExtended(self, key, extraMultipliers=None, ignoreAfflictors=None, default
multiplierAdjustment = 1
ignorePenalizedMultipliers = {}
postIncreaseAdjustment = 0
for fit, afflictors in self.getAfflictions(key).items():
afflictions = self.getAfflictions(key)
ignored_zero_multiplier = False
global _debug_max_velocity_count
if key == 'maxVelocity' and _debug_max_velocity_count < 8:
ignore_names = []
if ignoreAfflictors:
ignore_names = [getattr(getattr(a, 'item', None), 'name', str(a)) for a in ignoreAfflictors]
affliction_names = []
for fit, afflictors in afflictions.items():
for afflictor, operator, stackingGroup, preResAmount, postResAmount, used in afflictors:
name = getattr(getattr(afflictor, 'item', None), 'name', str(afflictor))
affliction_names.append((name, operator, stackingGroup, preResAmount, postResAmount, used))
print(
"[AttrDebug] key=maxVelocity ignores={} afflictions={}".format(ignore_names, affliction_names),
flush=True)
_debug_max_velocity_count += 1

for fit, afflictors in afflictions.items():
for afflictor, operator, stackingGroup, preResAmount, postResAmount, used in afflictors:
if afflictor in ignoreAfflictors:
if operator == Operator.MULTIPLY:
if stackingGroup is None:
multiplierAdjustment /= postResAmount
if postResAmount == 0:
ignored_zero_multiplier = True
else:
multiplierAdjustment /= postResAmount
else:
ignorePenalizedMultipliers.setdefault(stackingGroup, []).append(postResAmount)
elif operator == Operator.PREINCREASE:
preIncreaseAdjustment -= postResAmount
elif operator == Operator.POSTINCREASE:
postIncreaseAdjustment -= postResAmount

if ignored_zero_multiplier and self.__multipliers.get(key, 1) == 0:
recomputed_multiplier = 1
for fit, afflictors in afflictions.items():
for afflictor, operator, stackingGroup, preResAmount, postResAmount, used in afflictors:
if operator != Operator.MULTIPLY or stackingGroup is not None:
continue
if afflictor in ignoreAfflictors:
continue
recomputed_multiplier *= postResAmount
# Recalculate value without applying stored zero multiplier
try:
cappingKey = cappingAttrKeyCache[key]
except KeyError:
attrInfo = getAttributeInfo(key)
if attrInfo is None:
cappingId = cappingAttrKeyCache[key] = None
else:
cappingId = attrInfo.maxAttributeID
if cappingId is None:
cappingKey = None
else:
cappingAttrInfo = getAttributeInfo(cappingId)
cappingKey = None if cappingAttrInfo is None else cappingAttrInfo.name
cappingAttrKeyCache[key] = cappingKey
if cappingKey:
cappingValue = self[cappingKey]
cappingValue = cappingValue.value if hasattr(cappingValue, "value") else cappingValue
else:
cappingValue = None

preIncrease = self.__preIncreases.get(key, 0)
postIncrease = self.__postIncreases.get(key, 0)
penalizedMultiplierGroups = self.__penalizedMultipliers.get(key, {})
if extraMultipliers is not None:
penalizedMultiplierGroups = copy(penalizedMultiplierGroups)
for stackGroup, operationsData in extraMultipliers.items():
multipliers = []
for mult, resAttrID in operationsData:
if not resAttrID:
multipliers.append(mult)
continue
resAttrInfo = getAttributeInfo(resAttrID)
if not resAttrInfo:
multipliers.append(mult)
continue
resMult = self.fit.ship.itemModifiedAttributes[resAttrInfo.attributeName]
if resMult is None or resMult == 1:
multipliers.append(mult)
continue
mult = (mult - 1) * resMult + 1
multipliers.append(mult)
penalizedMultiplierGroups[stackGroup] = penalizedMultiplierGroups.get(stackGroup, []) + multipliers

default = getAttrDefault(key, fallback=0.0)
val = self.__intermediary.get(key, self.__preAssigns.get(key, self.getOriginal(key, default)))
val += preIncrease
if preIncreaseAdjustment is not None:
val += preIncreaseAdjustment
val *= recomputed_multiplier
for penaltyGroup, penalizedMultipliers in penalizedMultiplierGroups.items():
if ignorePenalizedMultipliers is not None and penaltyGroup in ignorePenalizedMultipliers:
penalizedMultipliers = penalizedMultipliers[:]
for ignoreMult in ignorePenalizedMultipliers[penaltyGroup]:
try:
penalizedMultipliers.remove(ignoreMult)
except ValueError:
pass
l1 = [_val for _val in penalizedMultipliers if _val > 1]
l2 = [_val for _val in penalizedMultipliers if _val < 1]
abssort = lambda _val: -abs(_val - 1)
l1.sort(key=abssort)
l2.sort(key=abssort)
for l in (l1, l2):
for i in range(len(l)):
bonus = l[i]
val *= 1 + (bonus - 1) * exp(- i ** 2 / 7.1289)
val += postIncrease
if postIncreaseAdjustment is not None:
val += postIncreaseAdjustment

if cappingValue is not None:
val = min(val, cappingValue)
if key in ("cpu", "power", "cpuOutput", "powerOutput"):
val = round(val, 2)
return val

# If we apply no customizations - use regular getter
if (
not extraMultipliers and
Expand Down
Loading