From 2fb75c8d392c3f372e348f118476768e11bf1727 Mon Sep 17 00:00:00 2001 From: Du Yifeng Date: Fri, 15 May 2026 20:02:42 +0800 Subject: [PATCH] [FIX] fixed a bug that abyssal web strength can go beyond -99% In game hard cap on web strength at -99%. Fixed pyfa to restrict this --- eos/calc.py | 5 +++++ eos/effects.py | 12 ++++++------ graphs/data/fitDamageStats/cache/projected.py | 9 +++++---- graphs/data/fitEwarStats/getter.py | 10 +++++----- gui/builtinItemStatsViews/itemAttributes.py | 15 +++++++++++++++ gui/builtinViewColumns/misc.py | 5 +++-- service/port/efs.py | 4 +++- 7 files changed, 42 insertions(+), 18 deletions(-) diff --git a/eos/calc.py b/eos/calc.py index 02cbf5d18c..e7c13a182b 100644 --- a/eos/calc.py +++ b/eos/calc.py @@ -69,3 +69,8 @@ def calculateLockTime(srcScanRes, tgtSigRadius): if not srcScanRes or not tgtSigRadius: return None return min(40000 / srcScanRes / math.asinh(tgtSigRadius) ** 2, 30 * 60) + + +def applyWebStrengthCap(speed_factor): + """Single-web speed reduction cap (EVE: at most -99%).""" + return max(speed_factor, -99.0) diff --git a/eos/effects.py b/eos/effects.py index 57a0365d81..4c65316272 100644 --- a/eos/effects.py +++ b/eos/effects.py @@ -19,7 +19,7 @@ import eos.config -from eos.calc import calculateRangeFactor +from eos.calc import calculateRangeFactor, applyWebStrengthCap from eos.const import FittingModuleState, FitSystemSecurity from eos.utils.spoolSupport import SpoolType, SpoolOptions, calculateSpoolup, resolveSpoolOptions @@ -26967,7 +26967,7 @@ def handler(fit, module, context, projectionRange, **kwargs): return if fit.ship.getModifiedItemAttr('disallowOffensiveModifiers'): return - appliedBoost = module.getModifiedItemAttr('speedFactor') * calculateRangeFactor( + appliedBoost = applyWebStrengthCap(module.getModifiedItemAttr('speedFactor')) * calculateRangeFactor( srcOptimalRange=module.getModifiedItemAttr('maxRange'), srcFalloffRange=module.getModifiedItemAttr('falloffEffectiveness'), distance=projectionRange) @@ -27099,7 +27099,7 @@ def handler(cls, fit, src, context, projectionRange, **kwargs): return if fit.ship.getModifiedItemAttr('disallowOffensiveModifiers'): return - speedBoost = src.getModifiedItemAttr('{}SpeedPenalty'.format(cls.prefix)) * src.amount + speedBoost = applyWebStrengthCap(src.getModifiedItemAttr('{}SpeedPenalty'.format(cls.prefix))) * src.amount speedBoost *= calculateRangeFactor( srcOptimalRange=src.getModifiedItemAttr('{}OptimalRange'.format(cls.prefix)), srcFalloffRange=src.getModifiedItemAttr('{}FalloffRange'.format(cls.prefix)), @@ -27445,7 +27445,7 @@ def handler(fit, module, context, projectionRange, **kwargs): return if fit.ship.getModifiedItemAttr('disallowOffensiveModifiers'): return - fit.ship.boostItemAttr('maxVelocity', module.getModifiedItemAttr('speedFactor'), + fit.ship.boostItemAttr('maxVelocity', applyWebStrengthCap(module.getModifiedItemAttr('speedFactor')), stackingPenalties=True, **kwargs) @@ -30385,7 +30385,7 @@ def handler(fit, module, context, projectionRange, **kwargs): return if fit.ship.getModifiedItemAttr('disallowOffensiveModifiers'): return - speedBoost = module.getModifiedItemAttr('speedFactor') + speedBoost = applyWebStrengthCap(module.getModifiedItemAttr('speedFactor')) speedBoost *= calculateRangeFactor( srcOptimalRange=module.getModifiedItemAttr('maxRange'), srcFalloffRange=module.getModifiedItemAttr('falloffEffectiveness'), @@ -30599,7 +30599,7 @@ def handler(fit, module, context, projectionRange, **kwargs): return if module.getModifiedItemAttr('maxRange', 0) < (projectionRange or 0): return - fit.ship.boostItemAttr('maxVelocity', module.getModifiedItemAttr('speedFactor'), + fit.ship.boostItemAttr('maxVelocity', applyWebStrengthCap(module.getModifiedItemAttr('speedFactor')), stackingPenalties=True, **kwargs) diff --git a/graphs/data/fitDamageStats/cache/projected.py b/graphs/data/fitDamageStats/cache/projected.py index 8b0dbaad0d..64fcd7b577 100644 --- a/graphs/data/fitDamageStats/cache/projected.py +++ b/graphs/data/fitDamageStats/cache/projected.py @@ -20,6 +20,7 @@ from collections import namedtuple +from eos.calc import applyWebStrengthCap from eos.modifiedAttributeDict import getResistanceAttrID from graphs.data.base import FitDataCache @@ -42,14 +43,14 @@ def getProjModData(self, src): for webEffectName in ('remoteWebifierFalloff', 'structureModuleEffectStasisWebifier'): if webEffectName in mod.item.effects: webMods.append(ModProjData( - mod.getModifiedItemAttr('speedFactor'), + applyWebStrengthCap(mod.getModifiedItemAttr('speedFactor')), mod.maxRange or 0, mod.falloff or 0, 'default', getResistanceAttrID(modifyingItem=mod, effect=mod.item.effects[webEffectName]))) if 'doomsdayAOEWeb' in mod.item.effects: webMods.append(ModProjData( - mod.getModifiedItemAttr('speedFactor'), + applyWebStrengthCap(mod.getModifiedItemAttr('speedFactor')), max(0, (mod.maxRange or 0) + mod.getModifiedItemAttr('doomsdayAOERange')), mod.falloff or 0, 'default', @@ -82,7 +83,7 @@ def getProjDroneData(self, src): for drone in src.item.activeDronesIter(): if 'remoteWebifierEntity' in drone.item.effects: webDrones.extend(drone.amountActive * (MobileProjData( - drone.getModifiedItemAttr('speedFactor'), + applyWebStrengthCap(drone.getModifiedItemAttr('speedFactor')), drone.maxRange or 0, drone.falloff or 0, 'default', @@ -111,7 +112,7 @@ def getProjFighterData(self, src): for fighter, ability in src.item.activeFighterAbilityIter(): if ability.effect.name == 'fighterAbilityStasisWebifier': webFighters.append(MobileProjData( - fighter.getModifiedItemAttr('fighterAbilityStasisWebifierSpeedPenalty') * fighter.amount, + applyWebStrengthCap(fighter.getModifiedItemAttr('fighterAbilityStasisWebifierSpeedPenalty')) * fighter.amount, fighter.getModifiedItemAttr('fighterAbilityStasisWebifierOptimalRange'), fighter.getModifiedItemAttr('fighterAbilityStasisWebifierFalloffRange'), 'default', diff --git a/graphs/data/fitEwarStats/getter.py b/graphs/data/fitEwarStats/getter.py index 1aa74d1724..a87b941c5a 100644 --- a/graphs/data/fitEwarStats/getter.py +++ b/graphs/data/fitEwarStats/getter.py @@ -20,7 +20,7 @@ import math -from eos.calc import calculateMultiplier, calculateRangeFactor +from eos.calc import calculateMultiplier, calculateRangeFactor, applyWebStrengthCap from graphs.calc import checkLockRange, checkDroneControlRange from graphs.data.base import SmoothPointGetter @@ -88,22 +88,22 @@ def _getCommonData(self, miscParams, src, tgt): for effectName in ('remoteWebifierFalloff', 'structureModuleEffectStasisWebifier'): if effectName in mod.item.effects: webs.append(( - mod.getModifiedItemAttr('speedFactor') * resonance, + applyWebStrengthCap(mod.getModifiedItemAttr('speedFactor')) * resonance, mod.maxRange or 0, mod.falloff or 0, 'default', True, False)) if 'doomsdayAOEWeb' in mod.item.effects: webs.append(( - mod.getModifiedItemAttr('speedFactor') * resonance, + applyWebStrengthCap(mod.getModifiedItemAttr('speedFactor')) * resonance, max(0, (mod.maxRange or 0) + mod.getModifiedItemAttr('doomsdayAOERange')), mod.falloff or 0, 'default', False, False)) for drone in src.item.activeDronesIter(): if 'remoteWebifierEntity' in drone.item.effects: webs.extend(drone.amountActive * (( - drone.getModifiedItemAttr('speedFactor') * resonance, + applyWebStrengthCap(drone.getModifiedItemAttr('speedFactor')) * resonance, math.inf, 0, 'default', True, True),)) for fighter, ability in src.item.activeFighterAbilityIter(): if ability.effect.name == 'fighterAbilityStasisWebifier': webs.append(( - fighter.getModifiedItemAttr('fighterAbilityStasisWebifierSpeedPenalty') * fighter.amount * resonance, + applyWebStrengthCap(fighter.getModifiedItemAttr('fighterAbilityStasisWebifierSpeedPenalty')) * fighter.amount * resonance, math.inf, 0, 'default', True, False)) return {'webs': webs} diff --git a/gui/builtinItemStatsViews/itemAttributes.py b/gui/builtinItemStatsViews/itemAttributes.py index ad0e78e1df..fb5b875682 100644 --- a/gui/builtinItemStatsViews/itemAttributes.py +++ b/gui/builtinItemStatsViews/itemAttributes.py @@ -12,6 +12,14 @@ from gui.builtinItemStatsViews.attributeGrouping import * from gui.utils.numberFormatter import formatAmount, roundDec from service.const import GuiAttrGroup +from eos.calc import applyWebStrengthCap + +_WEB_SPEED_FACTOR_EFFECTS = frozenset(( + 'remoteWebifierFalloff', + 'structureModuleEffectStasisWebifier', + 'remoteWebifierEntity', + 'doomsdayAOEWeb', +)) _t = wx.GetTranslation @@ -307,6 +315,13 @@ def GetData(self, attr, displayOveride=None): val = getattr(att, "value", None) value = val if val is not None else att + if (self.toggleView == AttributeView.NORMAL and + attr == 'speedFactor' and + self.isStuffItem and + hasattr(self.stuff, 'item') and self.stuff.item and + _WEB_SPEED_FACTOR_EFFECTS.intersection(getattr(self.stuff.item, 'effects', {}))): + value = applyWebStrengthCap(value) + if self.toggleView == AttributeView.NORMAL and ( (attr not in GroupedAttributes and not (value or valueDefault)) or info is None or not info.published or attr in RequiredSkillAttrs): return None diff --git a/gui/builtinViewColumns/misc.py b/gui/builtinViewColumns/misc.py index 7f1e73fb61..a36531e1a2 100644 --- a/gui/builtinViewColumns/misc.py +++ b/gui/builtinViewColumns/misc.py @@ -29,6 +29,7 @@ from gui.utils.listFormatter import formatList from eos.utils.float import floatUnerr from eos.utils.spoolSupport import SpoolType, SpoolOptions +from eos.calc import applyWebStrengthCap import eos.config @@ -176,7 +177,7 @@ def __getData(self, stuff): text_parts.append("{0}%".format(formatAmount(display, 3, 0, 3, forceSign=True))) tooltip_parts.append("{0} dampening".format(formatList(ttEntries)).capitalize()) if 'remoteWebifierEntity' in item.effects: - speedFactor = stuff.getModifiedItemAttr("speedFactor") + speedFactor = applyWebStrengthCap(stuff.getModifiedItemAttr("speedFactor")) if speedFactor: text_parts.append("{0}%".format(formatAmount(speedFactor, 3, 0, 3))) tooltip_parts.append("Speed reduction") @@ -282,7 +283,7 @@ def __getData(self, stuff): itemGroup in ("Stasis Web", "Stasis Grappler", "Stasis Webifying Drone", "Structure Stasis Webifier") or (itemGroup in ("Structure Burst Projector", "Burst Projectors") and "doomsdayAOEWeb" in item.effects) ): - speedFactor = stuff.getModifiedItemAttr("speedFactor") + speedFactor = applyWebStrengthCap(stuff.getModifiedItemAttr("speedFactor")) if not speedFactor: return "", None text = "{0}%".format(formatAmount(speedFactor, 3, 0, 3)) diff --git a/service/port/efs.py b/service/port/efs.py index 55358b6440..858e5fa5ba 100755 --- a/service/port/efs.py +++ b/service/port/efs.py @@ -15,6 +15,7 @@ from eos.effectHandlerHelpers import HandledList from eos.db import gamedata_session, getCategory, getAttributeInfo, getGroup from eos.gamedata import Attribute, Effect, Group, Item, ItemEffect +from eos.calc import applyWebStrengthCap from eos.utils.spoolSupport import SpoolType, SpoolOptions from gui.fitCommands.calc.module.localAdd import CalcAddLocalModuleCommand from gui.fitCommands.calc.module.localRemove import CalcRemoveLocalModulesCommand @@ -180,7 +181,8 @@ def getOutgoingProjectionData(fit): if mod.item.group.name in ["Stasis Web", "Stasis Grappler"]: stats["type"] = "Stasis Web" stats["optimal"] = mod.getModifiedItemAttr("maxRange") - EfsPort.attrDirectMap(["duration", "speedFactor"], stats, mod) + EfsPort.attrDirectMap(["duration"], stats, mod) + stats["speedFactor"] = applyWebStrengthCap(mod.getModifiedItemAttr("speedFactor")) elif mod.item.group.name == "Weapon Disruptor": stats["type"] = "Weapon Disruptor" stats["optimal"] = mod.getModifiedItemAttr("maxRange")