def getApplicationPerKey(src, distance): inLockRange = checkLockRange(src=src, distance=distance) inDroneRange = checkDroneControlRange(src=src, distance=distance) applicationMap = {} for mod in src.item.activeModulesIter(): if not mod.isRemoteRepping(): continue if not inLockRange: applicationMap[mod] = 0 else: applicationMap[mod] = calculateRangeFactor( srcOptimalRange=mod.maxRange or 0, srcFalloffRange=mod.falloff or 0, distance=distance) for drone in src.item.activeDronesIter(): if not drone.isRemoteRepping(): continue if not inLockRange or not inDroneRange: applicationMap[drone] = 0 else: applicationMap[drone] = 1 # Ensure consistent results - round off a little to avoid float errors for k, v in applicationMap.items(): applicationMap[k] = floatUnerr(v) return applicationMap
def getSigRadiusMult(src, tgt, tgtSpeed, srcScramRange, tgtScrammables, tpMods, tpDrones, tpFighters, distance): # Can blow non-immune ships and target profiles if tgt.isFit and tgt.item.ship.getModifiedItemAttr( 'disallowOffensiveModifiers'): return 1 inLockRange = checkLockRange(src=src, distance=distance) inDroneRange = checkDroneControlRange(src=src, distance=distance) initSig = tgt.getSigRadius() # No scrams or distance is longer than longest scram - nullify scrammables list if not inLockRange or srcScramRange is None or (distance is not None and distance > srcScramRange): tgtScrammables = () # TPing modules appliedMultipliers = {} if inLockRange: for tpData in tpMods: appliedBoost = tpData.boost * calculateRangeFactor( srcOptimalRange=tpData.optimal, srcFalloffRange=tpData.falloff, distance=distance) if appliedBoost: appliedMultipliers.setdefault(tpData.stackingGroup, []).append( (1 + appliedBoost / 100, tpData.resAttrID)) # TPing drones mobileTps = [] if inLockRange: mobileTps.extend(tpFighters) if inLockRange and inDroneRange: mobileTps.extend(tpDrones) droneOpt = GraphSettings.getInstance().get('mobileDroneMode') atkRadius = src.getRadius() for mtpData in mobileTps: # Faster than target or set to follow it - apply full TP if (droneOpt == GraphDpsDroneMode.auto and mtpData.speed >= tgtSpeed ) or droneOpt == GraphDpsDroneMode.followTarget: appliedMtpBoost = mtpData.boost # Otherwise project from the center of the ship else: if distance is None: rangeFactorDistance = None else: rangeFactorDistance = distance + atkRadius - mtpData.radius appliedMtpBoost = mtpData.boost * calculateRangeFactor( srcOptimalRange=mtpData.optimal, srcFalloffRange=mtpData.falloff, distance=rangeFactorDistance) appliedMultipliers.setdefault(mtpData.stackingGroup, []).append( (1 + appliedMtpBoost / 100, mtpData.resAttrID)) modifiedSig = tgt.getSigRadius(extraMultipliers=appliedMultipliers, ignoreAfflictors=tgtScrammables) if modifiedSig == math.inf and initSig == math.inf: return 1 mult = modifiedSig / initSig # Ensure consistent results - round off a little to avoid float errors return floatUnerr(mult)
def _calculatePoint(self, x, miscParams, src, tgt, commonData): distance = x inLockRange = checkLockRange(src=src, distance=distance) inDroneRange = checkDroneControlRange(src=src, distance=distance) combinedStr = 0 for strength, optimal, falloff, needsLock, needsDcr in commonData['neuts']: if (needsLock and not inLockRange) or (needsDcr and not inDroneRange): continue combinedStr += strength * calculateRangeFactor(srcOptimalRange=optimal, srcFalloffRange=falloff, distance=distance) return combinedStr
def _calculatePoint(self, x, miscParams, src, tgt, commonData): distance = x inLockRange = checkLockRange(src=src, distance=distance) inDroneRange = checkDroneControlRange(src=src, distance=distance) strMults = {} for strength, optimal, falloff, stackingGroup, needsLock, needsDcr in commonData['tps']: if (needsLock and not inLockRange) or (needsDcr and not inDroneRange): continue strength *= calculateRangeFactor(srcOptimalRange=optimal, srcFalloffRange=falloff, distance=distance) strMults.setdefault(stackingGroup, []).append((1 + strength / 100, None)) strMult = calculateMultiplier(strMults) strength = (strMult - 1) * 100 return strength
def _calculatePoint(self, x, miscParams, src, tgt, commonData): distance = x inLockRange = checkLockRange(src=src, distance=distance) inDroneRange = checkDroneControlRange(src=src, distance=distance) velocityStrMults = {} timeStrMults = {} for velocityStr, timeStr, optimal, falloff, stackingGroup, needsLock, needsDcr in commonData['gds']: if (needsLock and not inLockRange) or (needsDcr and not inDroneRange): continue rangeFactor = calculateRangeFactor(srcOptimalRange=optimal, srcFalloffRange=falloff, distance=distance) velocityStr *= rangeFactor timeStr *= rangeFactor velocityStrMults.setdefault(stackingGroup, []).append((1 + velocityStr / 100, None)) timeStrMults.setdefault(stackingGroup, []).append((1 + timeStr / 100, None)) velocityStrMult = calculateMultiplier(velocityStrMults) timeStrMult = calculateMultiplier(timeStrMults) strength = (1 - velocityStrMult * timeStrMult) * 100 return strength
def getTackledSpeed(src, tgt, currentUntackledSpeed, srcScramRange, tgtScrammables, webMods, webDrones, webFighters, distance): # Can slow down non-immune ships and target profiles if tgt.isFit and tgt.item.ship.getModifiedItemAttr( 'disallowOffensiveModifiers'): return currentUntackledSpeed maxUntackledSpeed = tgt.getMaxVelocity() # What's immobile cannot be slowed if maxUntackledSpeed == 0: return maxUntackledSpeed inLockRange = checkLockRange(src=src, distance=distance) inDroneRange = checkDroneControlRange(src=src, distance=distance) speedRatio = currentUntackledSpeed / maxUntackledSpeed # No scrams or distance is longer than longest scram - nullify scrammables list if not inLockRange or srcScramRange is None or (distance is not None and distance > srcScramRange): tgtScrammables = () appliedMultipliers = {} # Modules first, they are always applied the same way if inLockRange: for wData in webMods: appliedBoost = wData.boost * calculateRangeFactor( srcOptimalRange=wData.optimal, srcFalloffRange=wData.falloff, distance=distance) if appliedBoost: appliedMultipliers.setdefault(wData.stackingGroup, []).append( (1 + appliedBoost / 100, wData.resAttrID)) maxTackledSpeed = tgt.getMaxVelocity(extraMultipliers=appliedMultipliers, ignoreAfflictors=tgtScrammables) currentTackledSpeed = maxTackledSpeed * speedRatio # Drones and fighters mobileWebs = [] if inLockRange: mobileWebs.extend(webFighters) if inLockRange and inDroneRange: mobileWebs.extend(webDrones) atkRadius = src.getRadius() # 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 is None or 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) maxTackledSpeed = tgt.getMaxVelocity( extraMultipliers=appliedMultipliers, ignoreAfflictors=tgtScrammables) currentTackledSpeed = maxTackledSpeed * speedRatio # 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 >= currentTackledSpeed ) or droneOpt == GraphDpsDroneMode.followTarget: appliedMwBoost = mwData.boost # Otherwise project from the center of the ship else: if distance is None: rangeFactorDistance = None else: rangeFactorDistance = distance + atkRadius - mwData.radius appliedMwBoost = mwData.boost * calculateRangeFactor( srcOptimalRange=mwData.optimal, srcFalloffRange=mwData.falloff, distance=rangeFactorDistance) appliedMultipliers.setdefault(mwData.stackingGroup, []).append( (1 + appliedMwBoost / 100, mwData.resAttrID)) mobileWebs.remove(mwData) maxTackledSpeed = tgt.getMaxVelocity( extraMultipliers=appliedMultipliers, ignoreAfflictors=tgtScrammables) currentTackledSpeed = maxTackledSpeed * speedRatio # Ensure consistent results - round off a little to avoid float errors return floatUnerr(currentTackledSpeed)
def getApplicationPerKey(src, tgt, atkSpeed, atkAngle, distance, tgtSpeed, tgtAngle, tgtSigRadius): inLockRange = checkLockRange(src=src, distance=distance) inDroneRange = checkDroneControlRange(src=src, distance=distance) applicationMap = {} for mod in src.item.activeModulesIter(): if not mod.isDealingDamage(): continue if "ChainLightning" in mod.item.effects: if inLockRange: applicationMap[mod] = getVortonMult(mod=mod, distance=distance, tgtSpeed=tgtSpeed, tgtSigRadius=tgtSigRadius) elif mod.hardpoint == FittingHardpoint.TURRET: if inLockRange: applicationMap[mod] = getTurretMult(mod=mod, src=src, tgt=tgt, atkSpeed=atkSpeed, atkAngle=atkAngle, distance=distance, tgtSpeed=tgtSpeed, tgtAngle=tgtAngle, tgtSigRadius=tgtSigRadius) else: applicationMap[mod] = 0 elif mod.hardpoint == FittingHardpoint.MISSILE: # FoF missiles can shoot beyond lock range if inLockRange or (mod.charge is not None and 'fofMissileLaunching' in mod.charge.effects): applicationMap[mod] = getLauncherMult( mod=mod, distance=distance, tgtSpeed=tgtSpeed, tgtSigRadius=tgtSigRadius) else: applicationMap[mod] = 0 elif mod.item.group.name in ('Smart Bomb', 'Structure Area Denial Module'): applicationMap[mod] = getSmartbombMult(mod=mod, distance=distance) elif mod.item.group.name == 'Missile Launcher Bomb': applicationMap[mod] = getBombMult(mod=mod, src=src, tgt=tgt, distance=distance, tgtSigRadius=tgtSigRadius) elif mod.item.group.name == 'Structure Guided Bomb Launcher': if inLockRange: applicationMap[mod] = getGuidedBombMult( mod=mod, src=src, distance=distance, tgtSigRadius=tgtSigRadius) else: applicationMap[mod] = 0 elif mod.item.group.name in ('Super Weapon', 'Structure Doomsday Weapon'): # Only single-target DDs need locks if not inLockRange and { 'superWeaponAmarr', 'superWeaponCaldari', 'superWeaponGallente', 'superWeaponMinmatar', 'lightningWeapon' }.intersection(mod.item.effects): applicationMap[mod] = 0 else: applicationMap[mod] = getDoomsdayMult( mod=mod, tgt=tgt, distance=distance, tgtSigRadius=tgtSigRadius) for drone in src.item.activeDronesIter(): if not drone.isDealingDamage(): continue if inLockRange and inDroneRange: applicationMap[drone] = getDroneMult(drone=drone, src=src, tgt=tgt, atkSpeed=atkSpeed, atkAngle=atkAngle, distance=distance, tgtSpeed=tgtSpeed, tgtAngle=tgtAngle, tgtSigRadius=tgtSigRadius) else: applicationMap[drone] = 0 for fighter in src.item.activeFightersIter(): if not fighter.isDealingDamage(): continue for ability in fighter.abilities: if not ability.dealsDamage or not ability.active: continue # Bomb launching doesn't need locks if inLockRange or ability.effect.name == 'fighterAbilityLaunchBomb': applicationMap[(fighter, ability.effectID)] = getFighterAbilityMult( fighter=fighter, ability=ability, src=src, tgt=tgt, distance=distance, tgtSpeed=tgtSpeed, tgtSigRadius=tgtSigRadius) else: applicationMap[(fighter, ability.effectID)] = 0 # Ensure consistent results - round off a little to avoid float errors for k, v in applicationMap.items(): applicationMap[k] = floatUnerr(v) return applicationMap