Apply all webs including drones to target
This commit is contained in:
@@ -38,8 +38,8 @@ class GraphDmgDroneModeMenu(ContextMenuUnconditional):
|
|||||||
self.idOptionMap = {}
|
self.idOptionMap = {}
|
||||||
optionMap = OrderedDict([
|
optionMap = OrderedDict([
|
||||||
(GraphDpsDroneMode.auto, 'Auto'),
|
(GraphDpsDroneMode.auto, 'Auto'),
|
||||||
(GraphDpsDroneMode.followAttacker, 'Stick to Attacker'),
|
(GraphDpsDroneMode.followTarget, 'Stick to Target'),
|
||||||
(GraphDpsDroneMode.followTarget, 'Stick to Target')])
|
(GraphDpsDroneMode.followAttacker, 'Stick to Attacker')])
|
||||||
for option, label in optionMap.items():
|
for option, label in optionMap.items():
|
||||||
menuId = ContextMenuUnconditional.nextID()
|
menuId = ContextMenuUnconditional.nextID()
|
||||||
item = wx.MenuItem(m, menuId, label, kind=wx.ITEM_CHECK)
|
item = wx.MenuItem(m, menuId, label, kind=wx.ITEM_CHECK)
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ def getFighterAbilityMult(fighter, ability, fit, distance, tgtSpeed, tgtSigRadiu
|
|||||||
return mult
|
return mult
|
||||||
|
|
||||||
|
|
||||||
def applyWebs(tgt, currentUnwebbedSpeed, webMods, distance):
|
def applyWebs(fit, tgt, currentUnwebbedSpeed, webMods, webDrones, webFighters, distance):
|
||||||
if tgt.ship.getModifiedItemAttr('disallowOffensiveModifiers'):
|
if tgt.ship.getModifiedItemAttr('disallowOffensiveModifiers'):
|
||||||
return currentUnwebbedSpeed
|
return currentUnwebbedSpeed
|
||||||
unwebbedSpeed = tgt.ship.getModifiedItemAttr('maxVelocity')
|
unwebbedSpeed = tgt.ship.getModifiedItemAttr('maxVelocity')
|
||||||
@@ -181,11 +181,46 @@ def applyWebs(tgt, currentUnwebbedSpeed, webMods, distance):
|
|||||||
currentWebbedSpeed = 0
|
currentWebbedSpeed = 0
|
||||||
else:
|
else:
|
||||||
appliedMultipliers = {}
|
appliedMultipliers = {}
|
||||||
|
# Modules first, they are applied always the same way
|
||||||
for boost, optimal, falloff, stackingChain, resistanceAttrID in webMods:
|
for boost, optimal, falloff, stackingChain, resistanceAttrID in webMods:
|
||||||
appliedBoost = boost * _calcRangeFactor(atkOptimalRange=optimal, atkFalloffRange=falloff, distance=distance)
|
appliedBoost = boost * _calcRangeFactor(atkOptimalRange=optimal, atkFalloffRange=falloff, distance=distance)
|
||||||
if appliedBoost:
|
if appliedBoost:
|
||||||
appliedMultipliers.setdefault(stackingChain, []).append((1 + appliedBoost / 100, resistanceAttrID))
|
appliedMultipliers.setdefault(stackingChain, []).append((1 + appliedBoost / 100, resistanceAttrID))
|
||||||
webbedSpeed = tgt.ship.getModifiedItemAttrWithExtraMods('maxVelocity', extraMultipliers=appliedMultipliers)
|
webbedSpeed = tgt.ship.getModifiedItemAttrWithExtraMods('maxVelocity', extraMultipliers=appliedMultipliers)
|
||||||
|
# Drones and fighters
|
||||||
|
mobileWebs = []
|
||||||
|
mobileWebs.extend(webFighters)
|
||||||
|
# Drones have range limit
|
||||||
|
if distance <= fit.extraAttributes['droneControlRange']:
|
||||||
|
mobileWebs.extend(webDrones)
|
||||||
|
atkRadius = fit.ship.getModifiedItemAttr('radius')
|
||||||
|
# As mobile webs either follow the target or stick to the attacking ship,
|
||||||
|
# if target is within mobile web optimal - it can be applied unconditionally
|
||||||
|
longEnoughMws = [mw for mw in mobileWebs if distance <= mw.optimal - atkRadius + mw.radius]
|
||||||
|
if longEnoughMws:
|
||||||
|
for mwData in longEnoughMws:
|
||||||
|
appliedMultipliers.setdefault(mwData.stackingGroup, []).append((1 + mwData.boost / 100, mwData.resAttrID))
|
||||||
|
mobileWebs.remove(mwData)
|
||||||
|
webbedSpeed = tgt.ship.getModifiedItemAttrWithExtraMods('maxVelocity', extraMultipliers=appliedMultipliers)
|
||||||
|
# Apply remaining webs, from fastest to slowest
|
||||||
|
droneOpt = GraphSettings.getInstance().get('mobileDroneMode')
|
||||||
|
while mobileWebs:
|
||||||
|
# Process in batches unified by speed to save up resources
|
||||||
|
fastestMwSpeed = max(mobileWebs, key=lambda mw: mw.speed).speed
|
||||||
|
fastestMws = [mw for mw in mobileWebs if mw.speed == fastestMwSpeed]
|
||||||
|
for mwData in fastestMws:
|
||||||
|
# Faster than target or set to follow it - apply full slowdown
|
||||||
|
if (droneOpt == GraphDpsDroneMode.auto and mwData.speed >= webbedSpeed) or droneOpt == GraphDpsDroneMode.followTarget:
|
||||||
|
appliedMwBoost = mwData.boost
|
||||||
|
# Otherwise project from the center of the ship
|
||||||
|
else:
|
||||||
|
appliedMwBoost = mwData.boost * _calcRangeFactor(
|
||||||
|
atkOptimalRange=mwData.optimal,
|
||||||
|
atkFalloffRange=mwData.falloff,
|
||||||
|
distance=distance + fit.ship.getModifiedItemAttr('radius') - mwData.radius)
|
||||||
|
appliedMultipliers.setdefault(mwData.stackingGroup, []).append((1 + appliedMwBoost / 100, mwData.resAttrID))
|
||||||
|
mobileWebs.remove(mwData)
|
||||||
|
webbedSpeed = tgt.ship.getModifiedItemAttrWithExtraMods('maxVelocity', extraMultipliers=appliedMultipliers)
|
||||||
currentWebbedSpeed = webbedSpeed * speedRatio
|
currentWebbedSpeed = webbedSpeed * speedRatio
|
||||||
return currentWebbedSpeed
|
return currentWebbedSpeed
|
||||||
|
|
||||||
|
|||||||
@@ -182,9 +182,12 @@ class FitDamageStatsGraph(FitGraph):
|
|||||||
webDrones, tpDrones = self._projectedCache.getProjDroneData(fit)
|
webDrones, tpDrones = self._projectedCache.getProjDroneData(fit)
|
||||||
webFighters, tpFighters = self._projectedCache.getProjFighterData(fit)
|
webFighters, tpFighters = self._projectedCache.getProjFighterData(fit)
|
||||||
tgtSpeed = applyWebs(
|
tgtSpeed = applyWebs(
|
||||||
|
fit=fit,
|
||||||
tgt=tgt,
|
tgt=tgt,
|
||||||
currentUnwebbedSpeed=miscInputMap['tgtSpeed'],
|
currentUnwebbedSpeed=miscInputMap['tgtSpeed'],
|
||||||
webMods=webMods,
|
webMods=webMods,
|
||||||
|
webDrones=webDrones,
|
||||||
|
webFighters=webFighters,
|
||||||
distance=distance)
|
distance=distance)
|
||||||
tgtSigRadius = tgt.ship.getModifiedItemAttr('signatureRadius') * applyTps(
|
tgtSigRadius = tgt.ship.getModifiedItemAttr('signatureRadius') * applyTps(
|
||||||
tgt=tgt,
|
tgt=tgt,
|
||||||
|
|||||||
@@ -18,11 +18,17 @@
|
|||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|
||||||
|
|
||||||
|
from collections import namedtuple
|
||||||
|
|
||||||
from gui.builtinGraphs.base import FitDataCache
|
from gui.builtinGraphs.base import FitDataCache
|
||||||
from eos.const import FittingModuleState
|
from eos.const import FittingModuleState
|
||||||
from eos.modifiedAttributeDict import getResistanceAttrID
|
from eos.modifiedAttributeDict import getResistanceAttrID
|
||||||
|
|
||||||
|
|
||||||
|
ModProjData = namedtuple('ModProjData', ('boost', 'optimal', 'falloff', 'stackingGroup', 'resAttrID'))
|
||||||
|
MobileProjData = namedtuple('MobileProjData', ('boost', 'optimal', 'falloff', 'stackingGroup', 'resAttrID', 'speed', 'radius'))
|
||||||
|
|
||||||
|
|
||||||
class ProjectedDataCache(FitDataCache):
|
class ProjectedDataCache(FitDataCache):
|
||||||
|
|
||||||
def getProjModData(self, fit):
|
def getProjModData(self, fit):
|
||||||
@@ -38,14 +44,14 @@ class ProjectedDataCache(FitDataCache):
|
|||||||
continue
|
continue
|
||||||
for webEffectName in ('remoteWebifierFalloff', 'structureModuleEffectStasisWebifier'):
|
for webEffectName in ('remoteWebifierFalloff', 'structureModuleEffectStasisWebifier'):
|
||||||
if webEffectName in mod.item.effects:
|
if webEffectName in mod.item.effects:
|
||||||
webMods.append((
|
webMods.append(ModProjData(
|
||||||
mod.getModifiedItemAttr('speedFactor'),
|
mod.getModifiedItemAttr('speedFactor'),
|
||||||
mod.maxRange or 0,
|
mod.maxRange or 0,
|
||||||
mod.falloff or 0,
|
mod.falloff or 0,
|
||||||
'default',
|
'default',
|
||||||
getResistanceAttrID(modifyingItem=mod, effect=mod.item.effects[webEffectName])))
|
getResistanceAttrID(modifyingItem=mod, effect=mod.item.effects[webEffectName])))
|
||||||
if 'doomsdayAOEWeb' in mod.item.effects:
|
if 'doomsdayAOEWeb' in mod.item.effects:
|
||||||
webMods.append((
|
webMods.append(ModProjData(
|
||||||
mod.getModifiedItemAttr('speedFactor'),
|
mod.getModifiedItemAttr('speedFactor'),
|
||||||
max(0, (mod.maxRange or 0) + mod.getModifiedItemAttr('doomsdayAOERange') - fit.ship.getModifiedItemAttr('radius')),
|
max(0, (mod.maxRange or 0) + mod.getModifiedItemAttr('doomsdayAOERange') - fit.ship.getModifiedItemAttr('radius')),
|
||||||
mod.falloff or 0,
|
mod.falloff or 0,
|
||||||
@@ -53,14 +59,14 @@ class ProjectedDataCache(FitDataCache):
|
|||||||
getResistanceAttrID(modifyingItem=mod, effect=mod.item.effects['doomsdayAOEWeb'])))
|
getResistanceAttrID(modifyingItem=mod, effect=mod.item.effects['doomsdayAOEWeb'])))
|
||||||
for tpEffectName in ('remoteTargetPaintFalloff', 'structureModuleEffectTargetPainter'):
|
for tpEffectName in ('remoteTargetPaintFalloff', 'structureModuleEffectTargetPainter'):
|
||||||
if tpEffectName in mod.item.effects:
|
if tpEffectName in mod.item.effects:
|
||||||
tpMods.append((
|
tpMods.append(ModProjData(
|
||||||
mod.getModifiedItemAttr('signatureRadiusBonus'),
|
mod.getModifiedItemAttr('signatureRadiusBonus'),
|
||||||
mod.maxRange or 0,
|
mod.maxRange or 0,
|
||||||
mod.falloff or 0,
|
mod.falloff or 0,
|
||||||
'default',
|
'default',
|
||||||
getResistanceAttrID(modifyingItem=mod, effect=mod.item.effects[tpEffectName])))
|
getResistanceAttrID(modifyingItem=mod, effect=mod.item.effects[tpEffectName])))
|
||||||
if 'doomsdayAOEPaint' in mod.item.effects:
|
if 'doomsdayAOEPaint' in mod.item.effects:
|
||||||
tpMods.append((
|
tpMods.append(ModProjData(
|
||||||
mod.getModifiedItemAttr('signatureRadiusBonus'),
|
mod.getModifiedItemAttr('signatureRadiusBonus'),
|
||||||
max(0, (mod.maxRange or 0) + mod.getModifiedItemAttr('doomsdayAOERange') - fit.ship.getModifiedItemAttr('radius')),
|
max(0, (mod.maxRange or 0) + mod.getModifiedItemAttr('doomsdayAOERange') - fit.ship.getModifiedItemAttr('radius')),
|
||||||
mod.falloff or 0,
|
mod.falloff or 0,
|
||||||
@@ -80,7 +86,7 @@ class ProjectedDataCache(FitDataCache):
|
|||||||
if drone.amountActive <= 0:
|
if drone.amountActive <= 0:
|
||||||
continue
|
continue
|
||||||
if 'remoteWebifierEntity' in drone.item.effects:
|
if 'remoteWebifierEntity' in drone.item.effects:
|
||||||
webDrones.extend(drone.amountActive * ((
|
webDrones.extend(drone.amountActive * (MobileProjData(
|
||||||
drone.getModifiedItemAttr('speedFactor'),
|
drone.getModifiedItemAttr('speedFactor'),
|
||||||
drone.maxRange or 0,
|
drone.maxRange or 0,
|
||||||
drone.falloff or 0,
|
drone.falloff or 0,
|
||||||
@@ -89,7 +95,7 @@ class ProjectedDataCache(FitDataCache):
|
|||||||
drone.getModifiedItemAttr('maxVelocity'),
|
drone.getModifiedItemAttr('maxVelocity'),
|
||||||
drone.getModifiedItemAttr('radius')),))
|
drone.getModifiedItemAttr('radius')),))
|
||||||
if 'remoteTargetPaintEntity' in drone.item.effects:
|
if 'remoteTargetPaintEntity' in drone.item.effects:
|
||||||
tpDrones.extend(drone.amountActive * ((
|
tpDrones.extend(drone.amountActive * (MobileProjData(
|
||||||
drone.getModifiedItemAttr('signatureRadiusBonus'),
|
drone.getModifiedItemAttr('signatureRadiusBonus'),
|
||||||
drone.maxRange or 0,
|
drone.maxRange or 0,
|
||||||
drone.falloff or 0,
|
drone.falloff or 0,
|
||||||
@@ -103,7 +109,7 @@ class ProjectedDataCache(FitDataCache):
|
|||||||
try:
|
try:
|
||||||
projectedData = self._data[fit.ID]['fighters']
|
projectedData = self._data[fit.ID]['fighters']
|
||||||
except KeyError:
|
except KeyError:
|
||||||
# Format of items for both: (boost strength, optimal, falloff, stacking group, resistance attr ID, drone speed, drone radius)
|
# Format of items for both: (boost strength, optimal, falloff, stacking group, resistance attr ID, fighter speed, fighter radius)
|
||||||
webFighters = []
|
webFighters = []
|
||||||
tpFighters = []
|
tpFighters = []
|
||||||
projectedData = self._data.setdefault(fit.ID, {})['fighters'] = (webFighters, tpFighters)
|
projectedData = self._data.setdefault(fit.ID, {})['fighters'] = (webFighters, tpFighters)
|
||||||
@@ -114,7 +120,7 @@ class ProjectedDataCache(FitDataCache):
|
|||||||
if not ability.active:
|
if not ability.active:
|
||||||
continue
|
continue
|
||||||
if ability.effect.name == 'fighterAbilityStasisWebifier':
|
if ability.effect.name == 'fighterAbilityStasisWebifier':
|
||||||
webFighters.extend((
|
webFighters.append(MobileProjData(
|
||||||
fighter.getModifiedItemAttr('fighterAbilityStasisWebifierSpeedPenalty') * fighter.amountActive,
|
fighter.getModifiedItemAttr('fighterAbilityStasisWebifierSpeedPenalty') * fighter.amountActive,
|
||||||
fighter.getModifiedItemAttr('fighterAbilityStasisWebifierOptimalRange'),
|
fighter.getModifiedItemAttr('fighterAbilityStasisWebifierOptimalRange'),
|
||||||
fighter.getModifiedItemAttr('fighterAbilityStasisWebifierFalloffRange'),
|
fighter.getModifiedItemAttr('fighterAbilityStasisWebifierFalloffRange'),
|
||||||
|
|||||||
Reference in New Issue
Block a user