Split up dps graph file a little

This commit is contained in:
DarkPhoenix
2019-07-04 09:11:55 +03:00
parent 5857413285
commit ae110371fe
4 changed files with 235 additions and 185 deletions

View File

@@ -0,0 +1 @@
import gui.builtinGraphs.fitDamageStats.graph # noqa: E402,F401

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/>.
# =============================================================================
class TimeCache:
def __init__(self):
self.data = {}

View File

@@ -0,0 +1,207 @@
# =============================================================================
# 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
def getTurretMult(mod, fit, tgt, atkSpeed, atkAngle, distance, tgtSpeed, tgtAngle, tgtSigRadius):
cth = _calcTurretChanceToHit(
atkSpeed=atkSpeed,
atkAngle=atkAngle,
atkRadius=fit.ship.getModifiedItemAttr('radius'),
atkOptimalRange=mod.maxRange,
atkFalloffRange=mod.falloff,
atkTracking=mod.getModifiedItemAttr('trackingSpeed'),
atkOptimalSigRadius=mod.getModifiedItemAttr('optimalSigRadius'),
distance=distance,
tgtSpeed=tgtSpeed,
tgtAngle=tgtAngle,
tgtRadius=tgt.ship.getModifiedItemAttr('radius'),
tgtSigRadius=tgtSigRadius)
mult = _calcTurretMult(cth)
return mult
def getLauncherMult(mod, fit, distance, tgtSpeed, tgtSigRadius):
modRange = mod.maxRange
if modRange is None:
return 0
mult = _calcMissileMult(
atkRadius=fit.ship.getModifiedItemAttr('radius'),
atkRange=modRange,
atkEr=mod.getModifiedChargeAttr('aoeCloudSize'),
atkEv=mod.getModifiedChargeAttr('aoeVelocity'),
atkDrf=mod.getModifiedChargeAttr('aoeDamageReductionFactor'),
distance=distance,
tgtSpeed=tgtSpeed,
tgtSigRadius=tgtSigRadius)
return mult
def getDroneMult(drone, fit, tgt, atkSpeed, atkAngle, distance, tgtSpeed, tgtAngle, tgtSigRadius):
if distance > fit.extraAttributes['droneControlRange']:
return 0
droneSpeed = drone.getModifiedItemAttr('maxVelocity')
# Hard to simulate drone behavior, so assume chance to hit is 1
# when drone is not sentry and is faster than its target
if droneSpeed > 1 and droneSpeed >= tgtSpeed:
cth = 1
# Otherwise put the drone into center of the ship, move it at its max speed or ship's speed
# (whichever is lower) towards direction of attacking ship and see how well it projects
else:
droneRadius = drone.getModifiedItemAttr('radius')
cth = _calcTurretChanceToHit(
atkSpeed=min(atkSpeed, droneSpeed),
atkAngle=atkAngle,
atkRadius=droneRadius,
atkOptimalRange=drone.maxRange,
atkFalloffRange=drone.falloff,
atkTracking=drone.getModifiedItemAttr('trackingSpeed'),
atkOptimalSigRadius=drone.getModifiedItemAttr('optimalSigRadius'),
# As distance is ship surface to ship surface, we adjust it according
# to attacker fit's radiuses to have drone surface to ship surface distance
distance=distance + fit.ship.getModifiedItemAttr('radius') - droneRadius,
tgtSpeed=tgtSpeed,
tgtAngle=tgtAngle,
tgtRadius=tgt.ship.getModifiedItemAttr('radius'),
tgtSigRadius=tgtSigRadius)
mult = _calcTurretMult(cth)
return mult
def getFighterAbilityMult(fighter, ability, fit, distance, tgtSpeed, tgtSigRadius):
fighterSpeed = fighter.getModifiedItemAttr('maxVelocity')
attrPrefix = ability.attrPrefix
if fighterSpeed >= tgtSpeed:
rangeFactor = 1
# Same as with drones, if fighters are slower - put them to center of
# the ship and see how they apply
else:
rangeFactor = _calcRangeFactor(
atkOptimalRange=fighter.getModifiedItemAttr('{}RangeOptimal'.format(attrPrefix)),
atkFalloffRange=fighter.getModifiedItemAttr('{}RangeFalloff'.format(attrPrefix)),
distance=distance + fit.ship.getModifiedItemAttr('radius') - fighter.getModifiedItemAttr('radius'))
drf = fighter.getModifiedItemAttr('{}ReductionFactor'.format(attrPrefix), None)
if drf is None:
drf = fighter.getModifiedItemAttr('{}DamageReductionFactor'.format(attrPrefix))
drs = fighter.getModifiedItemAttr('{}ReductionSensitivity'.format(attrPrefix), None)
if drs is None:
drs = fighter.getModifiedItemAttr('{}DamageReductionSensitivity'.format(attrPrefix))
missileFactor = _calcMissileFactor(
atkEr=fighter.getModifiedItemAttr('{}ExplosionRadius'.format(attrPrefix)),
atkEv=fighter.getModifiedItemAttr('{}ExplosionVelocity'.format(attrPrefix)),
atkDrf=_calcAggregatedDrf(reductionFactor=drf, reductionSensitivity=drs),
tgtSpeed=tgtSpeed,
tgtSigRadius=tgtSigRadius)
mult = rangeFactor * missileFactor
return mult
# Turret-specific
def _calcTurretMult(chanceToHit):
"""Calculate damage multiplier for turret-based weapons."""
# https://wiki.eveuniversity.org/Turret_mechanics#Damage
wreckingChance = min(chanceToHit, 0.01)
wreckingPart = wreckingChance * 3
normalChance = chanceToHit - wreckingChance
if normalChance > 0:
avgDamageMult = (0.01 + chanceToHit) / 2 + 0.49
normalPart = normalChance * avgDamageMult
else:
normalPart = 0
totalMult = normalPart + wreckingPart
return totalMult
def _calcTurretChanceToHit(
atkSpeed, atkAngle, atkRadius, atkOptimalRange, atkFalloffRange, atkTracking, atkOptimalSigRadius,
distance, tgtSpeed, tgtAngle, tgtRadius, tgtSigRadius
):
"""Calculate chance to hit for turret-based weapons."""
# https://wiki.eveuniversity.org/Turret_mechanics#Hit_Math
angularSpeed = _calcAngularSpeed(atkSpeed, atkAngle, atkRadius, distance, tgtSpeed, tgtAngle, tgtRadius)
rangeFactor = _calcRangeFactor(atkOptimalRange, atkFalloffRange, distance)
trackingFactor = _calcTrackingFactor(atkTracking, atkOptimalSigRadius, angularSpeed, tgtSigRadius)
cth = rangeFactor * trackingFactor
return cth
def _calcAngularSpeed(atkSpeed, atkAngle, atkRadius, distance, tgtSpeed, tgtAngle, tgtRadius):
"""Calculate angular speed based on mobility parameters of two ships."""
atkAngle = atkAngle * math.pi / 180
tgtAngle = tgtAngle * math.pi / 180
ctcDistance = atkRadius + distance + tgtRadius
# Target is to the right of the attacker, so transversal is projection onto Y axis
transSpeed = abs(atkSpeed * math.sin(atkAngle) - tgtSpeed * math.sin(tgtAngle))
if ctcDistance == 0:
angularSpeed = 0 if transSpeed == 0 else math.inf
else:
angularSpeed = transSpeed / ctcDistance
return angularSpeed
def _calcTrackingFactor(atkTracking, atkOptimalSigRadius, angularSpeed, tgtSigRadius):
"""Calculate tracking chance to hit component."""
return 0.5 ** (((angularSpeed * atkOptimalSigRadius) / (atkTracking * tgtSigRadius)) ** 2)
# Missile-specific
def _calcMissileMult(atkRadius, atkRange, atkEr, atkEv, atkDrf, distance, tgtSpeed, tgtSigRadius):
"""Calculate damage multiplier for missile launcher."""
# Missiles spawn in the center of the attacking ship
if distance + atkRadius > atkRange:
mult = 0
else:
mult = _calcMissileFactor(atkEr, atkEv, atkDrf, tgtSpeed, tgtSigRadius)
return mult
def _calcFighterMult(atkOptimalRange, atkFalloffRange, atkEr, atkEv, atkDrf, distance, tgtSpeed, tgtSigRadius):
"""Calculate damage multiplier for separate fighter ability,"""
rangeFactor = _calcRangeFactor(atkOptimalRange, atkFalloffRange, distance)
missileFactor = _calcMissileFactor(atkEr, atkEv, atkDrf, tgtSpeed, tgtSigRadius)
mult = rangeFactor * missileFactor
return mult
def _calcMissileFactor(atkEr, atkEv, atkDrf, tgtSpeed, tgtSigRadius):
"""Missile application."""
factors = [1]
# "Slow" part
factors.append(tgtSigRadius / atkEr)
# "Fast" part
if tgtSpeed > 0:
factors.append(((atkEv * tgtSigRadius) / (atkEr * tgtSpeed)) ** atkDrf)
totalMult = min(factors)
return totalMult
def _calcAggregatedDrf(reductionFactor, reductionSensitivity):
"""
Sometimes DRF is specified as 2 separate numbers,
here we combine them into generic form.
"""
return math.log(reductionFactor) / math.log(reductionSensitivity)
# Generic
def _calcRangeFactor(atkOptimalRange, atkFalloffRange, distance):
"""Range strength/chance factor, applicable to guns, ewar, RRs, etc."""
return 0.5 ** ((max(0, distance - atkOptimalRange) / atkFalloffRange) ** 2)

View File

@@ -18,7 +18,6 @@
# =============================================================================
import math
from copy import copy
from itertools import chain
@@ -27,7 +26,8 @@ from eos.const import FittingHardpoint, FittingModuleState
from eos.utils.float import floatUnerr
from eos.utils.spoolSupport import SpoolType, SpoolOptions
from eos.utils.stats import DmgTypes
from .base import FitGraph, XDef, YDef, Input, VectorDef
from gui.builtinGraphs.base import FitGraph, XDef, YDef, Input, VectorDef
from .calc import getTurretMult, getLauncherMult, getDroneMult, getFighterAbilityMult
class FitDamageStatsGraph(FitGraph):
@@ -96,7 +96,7 @@ class FitDamageStatsGraph(FitGraph):
totalDps += modDps * getLauncherMult(
mod=mod,
fit=fit,
distance=miscInputMap['distance'],
distance=distance,
tgtSpeed=miscInputMap['tgtSpeed'],
tgtSigRadius=tgtSigRad)
xs.append(distance)
@@ -459,186 +459,4 @@ class FitDamageStatsGraph(FitGraph):
return xs, ys
def getTurretMult(mod, fit, tgt, atkSpeed, atkAngle, distance, tgtSpeed, tgtAngle, tgtSigRadius):
cth = _calcTurretChanceToHit(
atkSpeed=atkSpeed,
atkAngle=atkAngle,
atkRadius=fit.ship.getModifiedItemAttr('radius'),
atkOptimalRange=mod.maxRange,
atkFalloffRange=mod.falloff,
atkTracking=mod.getModifiedItemAttr('trackingSpeed'),
atkOptimalSigRadius=mod.getModifiedItemAttr('optimalSigRadius'),
distance=distance,
tgtSpeed=tgtSpeed,
tgtAngle=tgtAngle,
tgtRadius=tgt.ship.getModifiedItemAttr('radius'),
tgtSigRadius=tgtSigRadius)
mult = _calcTurretMult(cth)
return mult
def getLauncherMult(mod, fit, distance, tgtSpeed, tgtSigRadius):
modRange = mod.maxRange
if modRange is None:
return 0
mult = _calcMissileMult(
atkRadius=fit.ship.getModifiedItemAttr('radius'),
atkRange=modRange,
atkEr=mod.getModifiedChargeAttr('aoeCloudSize'),
atkEv=mod.getModifiedChargeAttr('aoeVelocity'),
atkDrf=mod.getModifiedChargeAttr('aoeDamageReductionFactor'),
distance=distance,
tgtSpeed=tgtSpeed,
tgtSigRadius=tgtSigRadius)
return mult
def getDroneMult(drone, fit, tgt, atkSpeed, atkAngle, distance, tgtSpeed, tgtAngle, tgtSigRadius):
if distance > fit.extraAttributes['droneControlRange']:
return 0
droneSpeed = drone.getModifiedItemAttr('maxVelocity')
# Hard to simulate drone behavior, so assume chance to hit is 1
# when drone is not sentry and is faster than its target
if droneSpeed > 1 and droneSpeed >= tgtSpeed:
cth = 1
# Otherwise put the drone into center of the ship, move it at its max speed or ship's speed
# (whichever is lower) towards direction of attacking ship and see how well it projects
else:
droneRadius = drone.getModifiedItemAttr('radius')
cth = _calcTurretChanceToHit(
atkSpeed=min(atkSpeed, droneSpeed),
atkAngle=atkAngle,
atkRadius=droneRadius,
atkOptimalRange=drone.maxRange,
atkFalloffRange=drone.falloff,
atkTracking=drone.getModifiedItemAttr('trackingSpeed'),
atkOptimalSigRadius=drone.getModifiedItemAttr('optimalSigRadius'),
# As distance is ship surface to ship surface, we adjust it according
# to attacker fit's radiuses to have drone surface to ship surface distance
distance=distance + fit.ship.getModifiedItemAttr('radius') - droneRadius,
tgtSpeed=tgtSpeed,
tgtAngle=tgtAngle,
tgtRadius=tgt.ship.getModifiedItemAttr('radius'),
tgtSigRadius=tgtSigRadius)
mult = _calcTurretMult(cth)
return mult
def getFighterAbilityMult(fighter, ability, fit, distance, tgtSpeed, tgtSigRadius):
fighterSpeed = fighter.getModifiedItemAttr('maxVelocity')
attrPrefix = ability.attrPrefix
if fighterSpeed >= tgtSpeed:
rangeFactor = 1
# Same as with drones, if fighters are slower - put them to center of
# the ship and see how they apply
else:
rangeFactor = _calcRangeFactor(
atkOptimalRange=fighter.getModifiedItemAttr('{}RangeOptimal'.format(attrPrefix)),
atkFalloffRange=fighter.getModifiedItemAttr('{}RangeFalloff'.format(attrPrefix)),
distance=distance + fit.ship.getModifiedItemAttr('radius') - fighter.getModifiedItemAttr('radius'))
drf = fighter.getModifiedItemAttr('{}ReductionFactor'.format(attrPrefix), None)
if drf is None:
drf = fighter.getModifiedItemAttr('{}DamageReductionFactor'.format(attrPrefix))
drs = fighter.getModifiedItemAttr('{}ReductionSensitivity'.format(attrPrefix), None)
if drs is None:
drs = fighter.getModifiedItemAttr('{}DamageReductionSensitivity'.format(attrPrefix))
missileFactor = _calcMissileFactor(
atkEr=fighter.getModifiedItemAttr('{}ExplosionRadius'.format(attrPrefix)),
atkEv=fighter.getModifiedItemAttr('{}ExplosionVelocity'.format(attrPrefix)),
atkDrf=_calcAggregatedDrf(reductionFactor=drf, reductionSensitivity=drs),
tgtSpeed=tgtSpeed,
tgtSigRadius=tgtSigRadius)
mult = rangeFactor * missileFactor
return mult
# Turret-specific
def _calcTurretMult(chanceToHit):
"""Calculate damage multiplier for turret-based weapons."""
# https://wiki.eveuniversity.org/Turret_mechanics#Damage
wreckingChance = min(chanceToHit, 0.01)
wreckingPart = wreckingChance * 3
normalChance = chanceToHit - wreckingChance
if normalChance > 0:
avgDamageMult = (0.01 + chanceToHit) / 2 + 0.49
normalPart = normalChance * avgDamageMult
else:
normalPart = 0
totalMult = normalPart + wreckingPart
return totalMult
def _calcTurretChanceToHit(
atkSpeed, atkAngle, atkRadius, atkOptimalRange, atkFalloffRange, atkTracking, atkOptimalSigRadius,
distance, tgtSpeed, tgtAngle, tgtRadius, tgtSigRadius
):
"""Calculate chance to hit for turret-based weapons."""
# https://wiki.eveuniversity.org/Turret_mechanics#Hit_Math
angularSpeed = _calcAngularSpeed(atkSpeed, atkAngle, atkRadius, distance, tgtSpeed, tgtAngle, tgtRadius)
rangeFactor = _calcRangeFactor(atkOptimalRange, atkFalloffRange, distance)
trackingFactor = _calcTrackingFactor(atkTracking, atkOptimalSigRadius, angularSpeed, tgtSigRadius)
cth = rangeFactor * trackingFactor
return cth
def _calcAngularSpeed(atkSpeed, atkAngle, atkRadius, distance, tgtSpeed, tgtAngle, tgtRadius):
"""Calculate angular speed based on mobility parameters of two ships."""
atkAngle = atkAngle * math.pi / 180
tgtAngle = tgtAngle * math.pi / 180
ctcDistance = atkRadius + distance + tgtRadius
# Target is to the right of the attacker, so transversal is projection onto Y axis
transSpeed = abs(atkSpeed * math.sin(atkAngle) - tgtSpeed * math.sin(tgtAngle))
if ctcDistance == 0:
angularSpeed = 0 if transSpeed == 0 else math.inf
else:
angularSpeed = transSpeed / ctcDistance
return angularSpeed
def _calcTrackingFactor(atkTracking, atkOptimalSigRadius, angularSpeed, tgtSigRadius):
"""Calculate tracking chance to hit component."""
return 0.5 ** (((angularSpeed * atkOptimalSigRadius) / (atkTracking * tgtSigRadius)) ** 2)
# Missile-specific
def _calcMissileMult(atkRadius, atkRange, atkEr, atkEv, atkDrf, distance, tgtSpeed, tgtSigRadius):
"""Calculate damage multiplier for missile launcher."""
# Missiles spawn in the center of the attacking ship
if distance + atkRadius > atkRange:
mult = 0
else:
mult = _calcMissileFactor(atkEr, atkEv, atkDrf, tgtSpeed, tgtSigRadius)
return mult
def _calcFighterMult(atkOptimalRange, atkFalloffRange, atkEr, atkEv, atkDrf, distance, tgtSpeed, tgtSigRadius):
"""Calculate damage multiplier for separate fighter ability,"""
rangeFactor = _calcRangeFactor(atkOptimalRange, atkFalloffRange, distance)
missileFactor = _calcMissileFactor(atkEr, atkEv, atkDrf, tgtSpeed, tgtSigRadius)
mult = rangeFactor * missileFactor
return mult
def _calcMissileFactor(atkEr, atkEv, atkDrf, tgtSpeed, tgtSigRadius):
"""Missile application."""
slowPart = tgtSigRadius / atkEr
fastPart = ((atkEv * tgtSigRadius) / (atkEr * tgtSpeed)) ** atkDrf
totalMult = min(1, slowPart, fastPart)
return totalMult
def _calcAggregatedDrf(reductionFactor, reductionSensitivity):
"""
Sometimes DRF is specified as 2 separate numbers,
here we combine them into generic form.
"""
return math.log(reductionFactor) / math.log(reductionSensitivity)
# Generic
def _calcRangeFactor(atkOptimalRange, atkFalloffRange, distance):
"""Range strength/chance factor, applicable to guns, ewar, RRs, etc."""
return 0.5 ** ((max(0, distance - atkOptimalRange) / atkFalloffRange) ** 2)
FitDamageStatsGraph.register()