(src, tgt, currentUntackledSpeed, srcScramRange, tgtScrammables, webMods, webDrones, webFighters, distance)
| 69 | |
| 70 | |
| 71 | def getTackledSpeed(src, tgt, currentUntackledSpeed, srcScramRange, tgtScrammables, webMods, webDrones, webFighters, distance): |
| 72 | # Can slow down non-immune ships and target profiles |
| 73 | if tgt.isFit and tgt.item.ship.getModifiedItemAttr('disallowOffensiveModifiers'): |
| 74 | return currentUntackledSpeed |
| 75 | maxUntackledSpeed = tgt.getMaxVelocity() |
| 76 | # What's immobile cannot be slowed |
| 77 | if maxUntackledSpeed == 0: |
| 78 | return maxUntackledSpeed |
| 79 | inLockRange = checkLockRange(src=src, distance=distance) |
| 80 | inDroneRange = checkDroneControlRange(src=src, distance=distance) |
| 81 | speedRatio = currentUntackledSpeed / maxUntackledSpeed |
| 82 | # No scrams or distance is longer than longest scram - nullify scrammables list |
| 83 | if not inLockRange or srcScramRange is None or (distance is not None and distance > srcScramRange): |
| 84 | tgtScrammables = () |
| 85 | appliedMultipliers = {} |
| 86 | # Modules first, they are always applied the same way |
| 87 | if inLockRange: |
| 88 | for wData in webMods: |
| 89 | appliedBoost = wData.boost * calculateRangeFactor( |
| 90 | srcOptimalRange=wData.optimal, |
| 91 | srcFalloffRange=wData.falloff, |
| 92 | distance=distance) |
| 93 | if appliedBoost: |
| 94 | appliedMultipliers.setdefault(wData.stackingGroup, []).append((1 + appliedBoost / 100, wData.resAttrID)) |
| 95 | maxTackledSpeed = tgt.getMaxVelocity(extraMultipliers=appliedMultipliers, ignoreAfflictors=tgtScrammables) |
| 96 | currentTackledSpeed = maxTackledSpeed * speedRatio |
| 97 | # Drones and fighters |
| 98 | mobileWebs = [] |
| 99 | if inLockRange: |
| 100 | mobileWebs.extend(webFighters) |
| 101 | if inLockRange and inDroneRange: |
| 102 | mobileWebs.extend(webDrones) |
| 103 | atkRadius = src.getRadius() |
| 104 | # As mobile webs either follow the target or stick to the attacking ship, |
| 105 | # if target is within mobile web optimal - it can be applied unconditionally |
| 106 | longEnoughMws = [mw for mw in mobileWebs if distance is None or distance <= mw.optimal - atkRadius + mw.radius] |
| 107 | if longEnoughMws: |
| 108 | for mwData in longEnoughMws: |
| 109 | appliedMultipliers.setdefault(mwData.stackingGroup, []).append((1 + mwData.boost / 100, mwData.resAttrID)) |
| 110 | mobileWebs.remove(mwData) |
| 111 | maxTackledSpeed = tgt.getMaxVelocity(extraMultipliers=appliedMultipliers, ignoreAfflictors=tgtScrammables) |
| 112 | currentTackledSpeed = maxTackledSpeed * speedRatio |
| 113 | # Apply remaining webs, from fastest to slowest |
| 114 | droneOpt = GraphSettings.getInstance().get('mobileDroneMode') |
| 115 | while mobileWebs: |
| 116 | # Process in batches unified by speed to save up resources |
| 117 | fastestMwSpeed = max(mobileWebs, key=lambda mw: mw.speed).speed |
| 118 | fastestMws = [mw for mw in mobileWebs if mw.speed == fastestMwSpeed] |
| 119 | for mwData in fastestMws: |
| 120 | # Faster than target or set to follow it - apply full slowdown |
| 121 | if (droneOpt == GraphDpsDroneMode.auto and mwData.speed >= currentTackledSpeed) or droneOpt == GraphDpsDroneMode.followTarget: |
| 122 | appliedMwBoost = mwData.boost |
| 123 | # Otherwise project from the center of the ship |
| 124 | else: |
| 125 | if distance is None: |
| 126 | rangeFactorDistance = None |
| 127 | else: |
| 128 | rangeFactorDistance = distance + atkRadius - mwData.radius |
no test coverage detected