From d1be0bb6807abcab0dd3915e197b78980dde7291 Mon Sep 17 00:00:00 2001 From: DarkPhoenix Date: Thu, 24 Oct 2019 13:52:34 +0300 Subject: [PATCH] Add scanres vs locktime on active fit graph as experimental feature --- config.py | 6 ++ eos/calc.py | 38 ++++++++++++ eos/saveddata/fit.py | 23 +++++-- graphs/calc.py | 31 ---------- graphs/data/__init__.py | 8 ++- graphs/data/fitEwarStats/getter.py | 4 +- .../__init__.py | 4 +- graphs/data/fitLockTimeIncoming/getter.py | 39 ++++++++++++ graphs/data/fitLockTimeIncoming/graph.py | 40 +++++++++++++ graphs/data/fitLockTimeOutgoing/__init__.py | 24 ++++++++ .../getter.py | 0 .../graph.py | 6 +- graphs/wrapper.py | 2 +- gui/builtinViewColumns/dampScanRes.py | 60 +++++++++++++++++++ gui/viewColumn.py | 1 + 15 files changed, 242 insertions(+), 44 deletions(-) rename graphs/data/{fitLockTime => fitLockTimeIncoming}/__init__.py (91%) create mode 100644 graphs/data/fitLockTimeIncoming/getter.py create mode 100644 graphs/data/fitLockTimeIncoming/graph.py create mode 100644 graphs/data/fitLockTimeOutgoing/__init__.py rename graphs/data/{fitLockTime => fitLockTimeOutgoing}/getter.py (100%) rename graphs/data/{fitLockTime => fitLockTimeOutgoing}/graph.py (91%) create mode 100644 gui/builtinViewColumns/dampScanRes.py diff --git a/config.py b/config.py index dddd59edb..b515ae909 100644 --- a/config.py +++ b/config.py @@ -39,6 +39,7 @@ loggingLevel = None logging_setup = None cipher = None clientHash = None +experimentalFeatures = None ESI_CACHE = 'esi_cache' @@ -103,6 +104,7 @@ def defPaths(customSavePath=None): global cipher global clientHash global version + global experimentalFeatures pyfalog.debug("Configuring Pyfa") @@ -168,6 +170,10 @@ def defPaths(customSavePath=None): logPath = os.path.join(savePath, logFile) + experimentalFeatures = getattr(configforced, "experimentalFeatures", experimentalFeatures) + if experimentalFeatures is None: + experimentalFeatures = False + # DON'T MODIFY ANYTHING BELOW import eos.config diff --git a/eos/calc.py b/eos/calc.py index 7896f65d4..02cbf5d18 100644 --- a/eos/calc.py +++ b/eos/calc.py @@ -18,6 +18,38 @@ # ============================================================================= +import math + + +# Just copy-paste penalization chain calculation code (with some modifications, +# as multipliers arrive in different form) in here to not make actual attribute +# calculations slower than they already are due to extra function calls +def calculateMultiplier(multipliers): + """ + multipliers: dictionary in format: + {stacking group name: [(mult, resist attr ID), (mult, resist attr ID)]} + """ + val = 1 + for penalizedMultipliers in multipliers.values(): + # A quick explanation of how this works: + # 1: Bonuses and penalties are calculated seperately, so we'll have to filter each of them + l1 = [v[0] for v in penalizedMultipliers if v[0] > 1] + l2 = [v[0] for v in penalizedMultipliers if v[0] < 1] + # 2: The most significant bonuses take the smallest penalty, + # This means we'll have to sort + abssort = lambda _val: -abs(_val - 1) + l1.sort(key=abssort) + l2.sort(key=abssort) + # 3: The first module doesn't get penalized at all + # Any module after the first takes penalties according to: + # 1 + (multiplier - 1) * math.exp(- math.pow(i, 2) / 7.1289) + for l in (l1, l2): + for i in range(len(l)): + bonus = l[i] + val *= 1 + (bonus - 1) * math.exp(- i ** 2 / 7.1289) + return val + + def calculateRangeFactor(srcOptimalRange, srcFalloffRange, distance, restrictedRange=True): """Range strength/chance factor, applicable to guns, ewar, RRs, etc.""" if distance is None: @@ -31,3 +63,9 @@ def calculateRangeFactor(srcOptimalRange, srcFalloffRange, distance, restrictedR return 1 else: return 0 + + +def calculateLockTime(srcScanRes, tgtSigRadius): + if not srcScanRes or not tgtSigRadius: + return None + return min(40000 / srcScanRes / math.asinh(tgtSigRadius) ** 2, 30 * 60) diff --git a/eos/saveddata/fit.py b/eos/saveddata/fit.py index 4ca3e8294..66b43541f 100644 --- a/eos/saveddata/fit.py +++ b/eos/saveddata/fit.py @@ -21,13 +21,14 @@ import datetime import time from copy import deepcopy from itertools import chain -from math import asinh, log, sqrt +from math import log, sqrt from logbook import Logger from sqlalchemy.orm import reconstructor, validates import eos.db from eos import capSim +from eos.calc import calculateMultiplier, calculateLockTime from eos.const import CalcType, FitSystemSecurity, FittingHardpoint, FittingModuleState, FittingSlot, ImplantLocation from eos.effectHandlerHelpers import ( HandledBoosterList, HandledDroneCargoList, HandledImplantList, @@ -1529,9 +1530,7 @@ class Fit: def calculateLockTime(self, radius): scanRes = self.ship.getModifiedItemAttr("scanResolution") if scanRes is not None and scanRes > 0: - # Yes, this function returns time in seconds, not miliseconds. - # 40,000 is indeed the correct constant here. - return min(40000 / scanRes / asinh(radius) ** 2, 30 * 60) + return calculateLockTime(srcScanRes=scanRes, tgtSigRadius=radius) else: return self.ship.getModifiedItemAttr("scanSpeed") / 1000.0 @@ -1626,6 +1625,22 @@ class Fit: if ability.active: yield fighter, ability + def getDampMultScanRes(self): + damps = [] + for mod in self.activeModulesIter(): + for effectName in ('remoteSensorDampFalloff', 'structureModuleEffectRemoteSensorDampener'): + if effectName in mod.item.effects: + damps.append((mod.getModifiedItemAttr('scanResolutionBonus'), 'default')) + if 'doomsdayAOEDamp' in mod.item.effects: + damps.append((mod.getModifiedItemAttr('scanResolutionBonus'), 'default')) + for drone in self.activeDronesIter(): + if 'remoteSensorDampEntity' in drone.item.effects: + damps.extend(drone.amountActive * ((drone.getModifiedItemAttr('scanResolutionBonus'), 'default'),)) + mults = {} + for strength, stackingGroup in damps: + mults.setdefault(stackingGroup, []).append((1 + strength / 100, None)) + return calculateMultiplier(mults) + def __deepcopy__(self, memo=None): fitCopy = Fit() # Character and owner are not copied diff --git a/graphs/calc.py b/graphs/calc.py index 27f40ce1b..9cb868eba 100644 --- a/graphs/calc.py +++ b/graphs/calc.py @@ -18,40 +18,9 @@ # ============================================================================= -import math - from service.settings import GraphSettings -# Just copy-paste penalization chain calculation code (with some modifications, -# as multipliers arrive in different form) in here to not make actual attribute -# calculations slower than they already are due to extra function calls -def calculateMultiplier(multipliers): - """ - multipliers: dictionary in format: - {stacking group name: [(mult, resist attr ID), (mult, resist attr ID)]} - """ - val = 1 - for penalizedMultipliers in multipliers.values(): - # A quick explanation of how this works: - # 1: Bonuses and penalties are calculated seperately, so we'll have to filter each of them - l1 = [v[0] for v in penalizedMultipliers if v[0] > 1] - l2 = [v[0] for v in penalizedMultipliers if v[0] < 1] - # 2: The most significant bonuses take the smallest penalty, - # This means we'll have to sort - abssort = lambda _val: -abs(_val - 1) - l1.sort(key=abssort) - l2.sort(key=abssort) - # 3: The first module doesn't get penalized at all - # Any module after the first takes penalties according to: - # 1 + (multiplier - 1) * math.exp(- math.pow(i, 2) / 7.1289) - for l in (l1, l2): - for i in range(len(l)): - bonus = l[i] - val *= 1 + (bonus - 1) * math.exp(- i ** 2 / 7.1289) - return val - - def checkLockRange(src, distance): if distance is None: return True diff --git a/graphs/data/__init__.py b/graphs/data/__init__.py index e4207f1cf..abd740ea0 100644 --- a/graphs/data/__init__.py +++ b/graphs/data/__init__.py @@ -18,6 +18,9 @@ # ============================================================================= +import config as _config + + from . import fitDamageStats from . import fitEwarStats from . import fitRemoteReps @@ -25,4 +28,7 @@ from . import fitShieldRegen from . import fitCapacitor from . import fitMobility from . import fitWarpTime -from . import fitLockTime +from . import fitLockTimeOutgoing + +if _config.experimentalFeatures: + from . import fitLockTimeIncoming diff --git a/graphs/data/fitEwarStats/getter.py b/graphs/data/fitEwarStats/getter.py index c1121d656..2bad90f3f 100644 --- a/graphs/data/fitEwarStats/getter.py +++ b/graphs/data/fitEwarStats/getter.py @@ -20,8 +20,8 @@ import math -from eos.calc import calculateRangeFactor -from graphs.calc import calculateMultiplier, checkLockRange, checkDroneControlRange +from eos.calc import calculateMultiplier, calculateRangeFactor +from graphs.calc import checkLockRange, checkDroneControlRange from graphs.data.base import SmoothPointGetter diff --git a/graphs/data/fitLockTime/__init__.py b/graphs/data/fitLockTimeIncoming/__init__.py similarity index 91% rename from graphs/data/fitLockTime/__init__.py rename to graphs/data/fitLockTimeIncoming/__init__.py index 13f22a3ae..4d33df5ad 100644 --- a/graphs/data/fitLockTime/__init__.py +++ b/graphs/data/fitLockTimeIncoming/__init__.py @@ -18,7 +18,7 @@ # ============================================================================= -from .graph import FitLockTimeGraph +from .graph import FitLockTimeIncomingGraph -FitLockTimeGraph.register() +FitLockTimeIncomingGraph.register() diff --git a/graphs/data/fitLockTimeIncoming/getter.py b/graphs/data/fitLockTimeIncoming/getter.py new file mode 100644 index 000000000..b0decbb39 --- /dev/null +++ b/graphs/data/fitLockTimeIncoming/getter.py @@ -0,0 +1,39 @@ +# ============================================================================= +# Copyright (C) 2010 Diego Duclos +# +# This file is part of pyfa. +# +# pyfa is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# pyfa is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with pyfa. If not, see . +# ============================================================================= + + +from eos.calc import calculateLockTime +from graphs.data.base import SmoothPointGetter + + +class ScanRes2LockTimeGetter(SmoothPointGetter): + + def _getCommonData(self, miscParams, src, tgt): + if miscParams['applyDamps']: + scanResMult = src.item.getDampMultScanRes() + else: + scanResMult = 1 + return {'scanResMult': scanResMult} + + def _calculatePoint(self, x, miscParams, src, tgt, commonData): + scanRes = x + time = calculateLockTime( + srcScanRes=scanRes * commonData['scanResMult'], + tgtSigRadius=src.item.ship.getModifiedItemAttr('signatureRadius')) + return time diff --git a/graphs/data/fitLockTimeIncoming/graph.py b/graphs/data/fitLockTimeIncoming/graph.py new file mode 100644 index 000000000..f177e8bf5 --- /dev/null +++ b/graphs/data/fitLockTimeIncoming/graph.py @@ -0,0 +1,40 @@ +# ============================================================================= +# Copyright (C) 2010 Diego Duclos +# +# This file is part of pyfa. +# +# pyfa is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# pyfa is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with pyfa. If not, see . +# ============================================================================= + + +import math + +from graphs.data.base import FitGraph, XDef, YDef, Input, InputCheckbox +from .getter import ScanRes2LockTimeGetter + + +class FitLockTimeIncomingGraph(FitGraph): + + # UI stuff + internalName = 'lockTimeIncomingGraph' + name = 'Lock Time (Incoming)' + xDefs = [XDef(handle='scanRes', unit='mm', label='Scan Resolution', mainInput=('scanRes', 'mm'))] + yDefs = [YDef(handle='time', unit='s', label='Lock time')] + inputs = [Input(handle='scanRes', unit='mm', label='Scan Resolution', iconID=74, defaultValue=None, defaultRange=(100, 1000))] + checkboxes = [InputCheckbox(handle='applyDamps', label='Apply sensor dampeners', defaultValue=True)] + srcExtraCols = ('SigRadius', 'Damp ScanRes') + + # Calculation stuff + _limiters = {'scanRes': lambda src, tgt: (1, math.inf)} + _getters = {('scanRes', 'time'): ScanRes2LockTimeGetter} diff --git a/graphs/data/fitLockTimeOutgoing/__init__.py b/graphs/data/fitLockTimeOutgoing/__init__.py new file mode 100644 index 000000000..8713af700 --- /dev/null +++ b/graphs/data/fitLockTimeOutgoing/__init__.py @@ -0,0 +1,24 @@ +# ============================================================================= +# Copyright (C) 2010 Diego Duclos +# +# This file is part of pyfa. +# +# pyfa is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# pyfa is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with pyfa. If not, see . +# ============================================================================= + + +from .graph import FitLockTimeOutgoingGraph + + +FitLockTimeOutgoingGraph.register() diff --git a/graphs/data/fitLockTime/getter.py b/graphs/data/fitLockTimeOutgoing/getter.py similarity index 100% rename from graphs/data/fitLockTime/getter.py rename to graphs/data/fitLockTimeOutgoing/getter.py diff --git a/graphs/data/fitLockTime/graph.py b/graphs/data/fitLockTimeOutgoing/graph.py similarity index 91% rename from graphs/data/fitLockTime/graph.py rename to graphs/data/fitLockTimeOutgoing/graph.py index 66199fc89..aa5732a98 100644 --- a/graphs/data/fitLockTime/graph.py +++ b/graphs/data/fitLockTimeOutgoing/graph.py @@ -20,14 +20,14 @@ import math -from graphs.data.base import FitGraph, Input, XDef, YDef +from graphs.data.base import FitGraph, XDef, YDef, Input from .getter import TgtSigRadius2LockTimeGetter -class FitLockTimeGraph(FitGraph): +class FitLockTimeOutgoingGraph(FitGraph): # UI stuff - internalName = 'lockTimeGraph' + internalName = 'lockTimeOutgoingGraph' name = 'Lock Time' xDefs = [XDef(handle='tgtSigRad', unit='m', label='Target signature radius', mainInput=('tgtSigRad', 'm'))] yDefs = [YDef(handle='time', unit='s', label='Lock time')] diff --git a/graphs/wrapper.py b/graphs/wrapper.py index 129522792..a24998369 100644 --- a/graphs/wrapper.py +++ b/graphs/wrapper.py @@ -18,11 +18,11 @@ # ============================================================================= +from eos.calc import calculateMultiplier from eos.saveddata.damagePattern import DamagePattern from eos.saveddata.fit import Fit from eos.saveddata.targetProfile import TargetProfile from service.const import TargetResistMode -from .calc import calculateMultiplier class BaseWrapper: diff --git a/gui/builtinViewColumns/dampScanRes.py b/gui/builtinViewColumns/dampScanRes.py new file mode 100644 index 000000000..0d6280d57 --- /dev/null +++ b/gui/builtinViewColumns/dampScanRes.py @@ -0,0 +1,60 @@ +# ============================================================================= +# Copyright (C) 2010 Diego Duclos +# +# This file is part of pyfa. +# +# pyfa is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# pyfa is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with pyfa. If not, see . +# ============================================================================= + +# noinspection PyPackageRequirements +import wx + +from eos.saveddata.fit import Fit +from graphs.wrapper import BaseWrapper +from gui.bitmap_loader import BitmapLoader +from eos.utils.float import floatUnerr +from gui.utils.numberFormatter import formatAmount +from gui.viewColumn import ViewColumn + + +class DampScanResColumn(ViewColumn): + + name = 'Damp ScanRes' + + def __init__(self, fittingView, params): + ViewColumn.__init__(self, fittingView) + self.imageId = fittingView.imageList.GetImageIndex(74, 'icons') + self.bitmap = BitmapLoader.getBitmap(74, 'icons') + self.mask = wx.LIST_MASK_IMAGE + + def getText(self, stuff): + if isinstance(stuff, BaseWrapper): + stuff = stuff.item + mult = 1 + if isinstance(stuff, Fit): + mult = floatUnerr(stuff.getDampMultScanRes()) + if mult == 1: + text = '' + else: + text = '{}%'.format(formatAmount((mult - 1) * 100, 3, 0, 0, forceSign=True)) + return text + + def getImageId(self, stuff): + return -1 + + def getToolTip(self, stuff): + return 'Scan resolution dampening' + + +DampScanResColumn.register() diff --git a/gui/viewColumn.py b/gui/viewColumn.py index 28c39bb3d..43bed258c 100644 --- a/gui/viewColumn.py +++ b/gui/viewColumn.py @@ -77,6 +77,7 @@ from gui.builtinViewColumns import ( # noqa: E402, F401 baseIcon, baseName, capacitorUse, + dampScanRes, graphColor, graphLightness, graphLineStyle,