Add scanres vs locktime on active fit graph as experimental feature

This commit is contained in:
DarkPhoenix
2019-10-24 13:52:34 +03:00
parent 536eb1efa5
commit d1be0bb680
15 changed files with 242 additions and 44 deletions

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -18,7 +18,7 @@
# =============================================================================
from .graph import FitLockTimeGraph
from .graph import FitLockTimeIncomingGraph
FitLockTimeGraph.register()
FitLockTimeIncomingGraph.register()

View File

@@ -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 <http://www.gnu.org/licenses/>.
# =============================================================================
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

View File

@@ -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 <http://www.gnu.org/licenses/>.
# =============================================================================
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}

View File

@@ -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 <http://www.gnu.org/licenses/>.
# =============================================================================
from .graph import FitLockTimeOutgoingGraph
FitLockTimeOutgoingGraph.register()

View File

@@ -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')]

View File

@@ -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:

View File

@@ -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 <http://www.gnu.org/licenses/>.
# =============================================================================
# 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()

View File

@@ -77,6 +77,7 @@ from gui.builtinViewColumns import ( # noqa: E402, F401
baseIcon,
baseName,
capacitorUse,
dampScanRes,
graphColor,
graphLightness,
graphLineStyle,