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 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 getVortonMult(mod, distance, tgtSpeed, tgtSigRadius): rangeFactor = calculateRangeFactor(mod.getModifiedItemAttr('maxRange'), 0, distance) applicationFactor = _calcMissileFactor( atkEr=mod.getModifiedItemAttr('aoeCloudSize'), atkEv=mod.getModifiedItemAttr('aoeVelocity'), atkDrf=mod.getModifiedItemAttr('aoeDamageReductionFactor'), tgtSpeed=tgtSpeed, tgtSigRadius=tgtSigRadius) return rangeFactor * applicationFactor
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 _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) # Turrets can be activated regardless of range to target rangeFactor = calculateRangeFactor(atkOptimalRange, atkFalloffRange, distance, restrictedRange=False) trackingFactor = _calcTrackingFactor(atkTracking, atkOptimalSigRadius, angularSpeed, tgtSigRadius) cth = rangeFactor * trackingFactor return cth
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 getFighterAbilityMult(fighter, ability, src, tgt, distance, tgtSpeed, tgtSigRadius): fighterSpeed = fighter.getModifiedItemAttr('maxVelocity') attrPrefix = ability.attrPrefix # It's bomb attack if attrPrefix == 'fighterAbilityLaunchBomb': # Just assume we can land bomb anywhere return _calcBombFactor( atkEr=fighter.getModifiedChargeAttr('aoeCloudSize'), tgtSigRadius=tgtSigRadius) droneOpt = GraphSettings.getInstance().get('mobileDroneMode') # It's regular missile-based attack if (droneOpt == GraphDpsDroneMode.auto and fighterSpeed >= tgtSpeed ) or droneOpt == GraphDpsDroneMode.followTarget: rangeFactor = 1 # Same as with drones, if fighters are slower - put them to center of # the ship and see how they apply else: if distance is None: rangeFactorDistance = None else: rangeFactorDistance = distance + src.getRadius( ) - fighter.getModifiedItemAttr('radius') rangeFactor = calculateRangeFactor( srcOptimalRange=fighter.getModifiedItemAttr( '{}RangeOptimal'.format(attrPrefix)) or fighter.getModifiedItemAttr('{}Range'.format(attrPrefix)), srcFalloffRange=fighter.getModifiedItemAttr( '{}RangeFalloff'.format(attrPrefix)), distance=rangeFactorDistance) 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) resistMult = 1 if tgt.isFit: resistAttrID = fighter.getModifiedItemAttr( '{}ResistanceID'.format(attrPrefix)) if resistAttrID: resistAttrInfo = Attribute.getInstance().getAttributeInfo( resistAttrID) if resistAttrInfo is not None: resistMult = tgt.item.ship.getModifiedItemAttr( resistAttrInfo.name, 1) mult = rangeFactor * missileFactor * resistMult return mult