Add a jam chance ewar graph

This commit is contained in:
2026-01-20 15:48:49 +01:00
parent 1063a1ab49
commit 64a11aaa6f
2 changed files with 81 additions and 2 deletions

View File

@@ -332,3 +332,79 @@ class Distance2TpStrGetter(SmoothPointGetter):
strMult = calculateMultiplier(strMults)
strength = (strMult - 1) * 100
return strength
class Distance2JamChanceGetter(SmoothPointGetter):
_baseResolution = 50
_extraDepth = 2
ECM_ATTRS_GENERAL = ('scanGravimetricStrengthBonus', 'scanLadarStrengthBonus', 'scanMagnetometricStrengthBonus', 'scanRadarStrengthBonus')
ECM_ATTRS_FIGHTERS = ('fighterAbilityECMStrengthGravimetric', 'fighterAbilityECMStrengthLadar', 'fighterAbilityECMStrengthMagnetometric', 'fighterAbilityECMStrengthRadar')
SCAN_TYPES = ('Gravimetric', 'Ladar', 'Magnetometric', 'Radar')
def _getCommonData(self, miscParams, src, tgt):
resonance = 1 - (miscParams['resist'] or 0)
ecms = []
for mod in src.item.activeModulesIter():
for effectName in ('remoteECMFalloff', 'structureModuleEffectECM'):
if effectName in mod.item.effects:
ecms.append((
tuple(mod.getModifiedItemAttr(a) or 0 for a in self.ECM_ATTRS_GENERAL),
mod.maxRange or 0, mod.falloff or 0, True, False))
if 'doomsdayAOEECM' in mod.item.effects:
ecms.append((
tuple(mod.getModifiedItemAttr(a) or 0 for a in self.ECM_ATTRS_GENERAL),
max(0, (mod.maxRange or 0) + mod.getModifiedItemAttr('doomsdayAOERange')),
mod.falloff or 0, False, False))
for drone in src.item.activeDronesIter():
if 'entityECMFalloff' in drone.item.effects:
ecms.extend(drone.amountActive * ((
tuple(drone.getModifiedItemAttr(a) or 0 for a in self.ECM_ATTRS_GENERAL),
math.inf, 0, True, True),))
for fighter, ability in src.item.activeFighterAbilityIter():
if ability.effect.name == 'fighterAbilityECM':
ecms.append((
tuple(fighter.getModifiedItemAttr(a) or 0 for a in self.ECM_ATTRS_FIGHTERS),
math.inf, 0, True, False))
# Determine target's strongest sensor type if target is available
targetScanTypeIndex = None
if tgt is not None:
maxStr = -1
for i, scanType in enumerate(self.SCAN_TYPES):
currStr = tgt.item.ship.getModifiedItemAttr('scan%sStrength' % scanType) or 0
if currStr > maxStr:
maxStr = currStr
targetScanTypeIndex = i
return {'ecms': ecms, 'targetScanTypeIndex': targetScanTypeIndex}
def _calculatePoint(self, x, miscParams, src, tgt, commonData):
distance = x
inLockRange = checkLockRange(src=src, distance=distance)
inDroneRange = checkDroneControlRange(src=src, distance=distance)
jamStrengths = []
targetScanTypeIndex = commonData['targetScanTypeIndex']
for strengths, optimal, falloff, needsLock, needsDcr in commonData['ecms']:
if (needsLock and not inLockRange) or (needsDcr and not inDroneRange):
continue
rangeFactor = calculateRangeFactor(srcOptimalRange=optimal, srcFalloffRange=falloff, distance=distance)
# Use the strength matching the target's sensor type
if targetScanTypeIndex is not None and targetScanTypeIndex < len(strengths):
strength = strengths[targetScanTypeIndex]
effectiveStrength = strength * rangeFactor
if effectiveStrength > 0:
jamStrengths.append(effectiveStrength)
if not jamStrengths:
return 0
# Get sensor strength from target
if tgt is None:
return 0
sensorStrength = max([tgt.item.ship.getModifiedItemAttr('scan%sStrength' % scanType)
for scanType in self.SCAN_TYPES]) or 0
if sensorStrength <= 0:
return 100 # If target has no sensor strength, 100% jam chance
# Calculate jam chance: 1 - (1 - (ecmStrength / sensorStrength)) ^ numJammers
retainLockChance = 1
for jamStrength in jamStrengths:
retainLockChance *= 1 - min(1, jamStrength / sensorStrength)
return (1 - retainLockChance) * 100

View File

@@ -21,8 +21,8 @@
import wx
from graphs.data.base import FitGraph, Input, XDef, YDef
from .getter import (Distance2DampStrLockRangeGetter, Distance2EcmStrMaxGetter, Distance2GdStrRangeGetter, Distance2NeutingStrGetter, Distance2TdStrOptimalGetter,
Distance2TpStrGetter, Distance2WebbingStrGetter)
from .getter import (Distance2DampStrLockRangeGetter, Distance2EcmStrMaxGetter, Distance2GdStrRangeGetter, Distance2JamChanceGetter, Distance2NeutingStrGetter,
Distance2TdStrOptimalGetter, Distance2TpStrGetter, Distance2WebbingStrGetter)
_t = wx.GetTranslation
@@ -31,11 +31,13 @@ class FitEwarStatsGraph(FitGraph):
# UI stuff
internalName = 'ewarStatsGraph'
name = _t('Electronic Warfare Stats')
hasTargets = True
xDefs = [XDef(handle='distance', unit='km', label=_t('Distance'), mainInput=('distance', 'km'))]
yDefs = [
YDef(handle='neutStr', unit=None, label=_t('Cap neutralized per second'), selectorLabel=_t('Neuts: cap per second')),
YDef(handle='webStr', unit='%', label=_t('Speed reduction'), selectorLabel=_t('Webs: speed reduction')),
YDef(handle='ecmStrMax', unit=None, label=_t('Combined ECM strength'), selectorLabel=_t('ECM: combined strength')),
YDef(handle='jamChance', unit='%', label=_t('Jam chance'), selectorLabel=_t('ECM: jam chance')),
YDef(handle='dampStrLockRange', unit='%', label=_t('Lock range reduction'), selectorLabel=_t('Damps: lock range reduction')),
YDef(handle='tdStrOptimal', unit='%', label=_t('Turret optimal range reduction'), selectorLabel=_t('TDs: turret optimal range reduction')),
YDef(handle='gdStrRange', unit='%', label=_t('Missile flight range reduction'), selectorLabel=_t('GDs: missile flight range reduction')),
@@ -53,6 +55,7 @@ class FitEwarStatsGraph(FitGraph):
('distance', 'neutStr'): Distance2NeutingStrGetter,
('distance', 'webStr'): Distance2WebbingStrGetter,
('distance', 'ecmStrMax'): Distance2EcmStrMaxGetter,
('distance', 'jamChance'): Distance2JamChanceGetter,
('distance', 'dampStrLockRange'): Distance2DampStrLockRangeGetter,
('distance', 'tdStrOptimal'): Distance2TdStrOptimalGetter,
('distance', 'gdStrRange'): Distance2GdStrRangeGetter,