Permalink
Browse files

Use chance to hit when selecting targets for sentry drones, instead o…

…f distance

Autotargeter bug fixes
Factored out weapon helper chance to hit calculation into a reusable class in common.eve
  • Loading branch information...
1 parent 00ffec1 commit a1850dd88cbb85e8cfa86408a9b23eb4fa4bab6c @kg committed Dec 19, 2010
@@ -201,7 +201,7 @@ def updateTargets(self):
log("Locking %s", ", ".join(getNamesOfIDs(targetsToLock)))
for targetID in targetsToLock:
if targetID not in self.__lockedTargets:
- self.__lockedTargets.append(targetID)
+ self.__lockedTargets.add(targetID)
setItemColor(targetID, "Automatic Target")
uthread.pool(
@@ -225,7 +225,7 @@ def populateBalls(self):
targetSvc = sm.services.get('target', None)
if targetSvc:
- self.__lockedTargets = list(targetSvc.targets)
+ self.__lockedTargets = set(targetSvc.targets)
for id in self.__lockedTargets:
setItemColor(id, "Automatic Target")
@@ -252,6 +252,8 @@ def isEligible(self, cachedItem):
def _DoBallsAdded(self, lst):
for (ball, slimItem) in lst:
+ if not slimItem:
+ continue
if not slimItem.categoryID in TargetableCategories:
continue
if slimItem.itemID == eve.session.shipid:
@@ -291,8 +293,10 @@ def OnTargets(self, targets):
def OnTarget(self, what, tid=None, reason=None):
if (what == "lost"):
- if (reason == None) and (tid in self.__potentialTargets):
- self.__potentialTargets.remove(tid)
+ if (reason == None) and (tid in self.__balls):
+ ball = self.__balls[tid]
+ del self.__balls[tid]
+ self.__eligibleBalls.remove(ball)
if tid in self.__lockedTargets:
self.__lockedTargets.remove(tid)
setItemColor(tid, None)
@@ -301,7 +305,7 @@ def OnTarget(self, what, tid=None, reason=None):
for id in self.__lockedTargets:
setItemColor(id, None)
- self.__lockedTargets = []
+ self.__lockedTargets = set()
def initialize():
global serviceRunning, serviceInstance
View
@@ -75,6 +75,156 @@ def isBallTargetable(ball):
else:
return True
+class ChanceToHitCalculator(object):
+ def __init__(self, sourceID):
+ self.setSource(sourceID)
+
+ def setSource(self, sourceID):
+ from common.eve.state import getCachedItem
+ self.source = getCachedItem(sourceID)
+
+ def setDrone(self, id):
+ from common.eve.state import getCachedItem
+ self.source = getCachedItem(id)
+
+ si = self.source.slimItem
+ ball = self.source.ball
+ droneAttrs = getTypeAttributes(si.typeID, obj=ball)
+
+ self.baseDamage = float(
+ droneAttrs.get("kineticDamage", 0) +
+ droneAttrs.get("emDamage", 0) +
+ droneAttrs.get("explosiveDamage", 0) +
+ droneAttrs.get("thermalDamage", 0)
+ )
+
+ self.optimal = float(droneAttrs["maxRange"])
+ self.falloff = float(droneAttrs["falloff"])
+ self.tracking = float(droneAttrs["trackingSpeed"])
+ self.sigResolution = float(droneAttrs["optimalSigRadius"])
+
+ self.calculate = self.calculateTurret
+
+ def setModule(self, module):
+ godma = eve.LocalSvc("godma")
+
+ moduleAttrs = getModuleAttributes(module)
+ if getattr(module, "charge", None):
+ chargeObj = godma.GetItem(module.charge.itemID)
+ chargeAttrs = getTypeAttributes(module.charge.typeID, obj=chargeObj)
+ else:
+ chargeAttrs = {}
+
+ self.baseDamage = float(
+ chargeAttrs.get("kineticDamage", 0) +
+ chargeAttrs.get("emDamage", 0) +
+ chargeAttrs.get("explosiveDamage", 0) +
+ chargeAttrs.get("thermalDamage", 0)
+ )
+
+ if moduleAttrs.has_key("trackingSpeed"):
+ # Gun turret
+
+ self.optimal = float(moduleAttrs["maxRange"])
+ self.falloff = float(moduleAttrs["falloff"])
+ self.tracking = float(moduleAttrs["trackingSpeed"])
+ self.sigResolution = float(moduleAttrs["optimalSigRadius"])
+
+ self.calculate = self.calculateTurret
+
+ elif chargeAttrs.has_key("maxVelocity"):
+ # Missile launcher
+
+ self.maxVelocity = float(chargeAttrs["maxVelocity"])
+ self.flightTime = float(chargeAttrs["explosionDelay"]) / 1000.0
+ self.explosionRadius = max(float(chargeAttrs["aoeCloudSize"]), 0.00001)
+ self.explosionVelocity = float(chargeAttrs["aoeVelocity"])
+ self.damageReductionFactor = float(chargeAttrs["aoeDamageReductionFactor"])
+ self.maxRange = flightTime * maxVelocity
+
+ self.calculate = self.calculateLauncher
+
+ def calculateNone(self, targetID, **kwargs):
+ return 0.0
+
+ def calculateTurret(self, targetID, velocityModifier=1.0, radiusModifier=1.0):
+ import blue, foo
+ from common.eve.state import getCachedItem
+ target = getCachedItem(targetID)
+
+ ballpark = sm.services["michelle"].GetBallpark()
+ now = blue.os.GetTime()
+
+ shipVelocity = self.source.ball.GetVectorDotAt(now)
+ shipPos = foo.Vector3(
+ self.source.ball.x, self.source.ball.y, self.source.ball.z
+ )
+
+ distance = max(ballpark.DistanceBetween(eve.session.shipid, targetID), 0.00001)
+ distanceFactor = max(0.0, distance - self.optimal) / self.falloff
+
+ targetBall = target.ball
+ targetItem = target.slimItem
+ targetVelocity = targetBall.GetVectorDotAt(now) * velocityModifier
+ targetPos = foo.Vector3(targetBall.x, targetBall.y, targetBall.z)
+ targetRadius = target.radius * radiusModifier
+
+ combinedVelocity = foo.Vector3(
+ targetVelocity.x - shipVelocity.x,
+ targetVelocity.y - shipVelocity.y,
+ targetVelocity.z - shipVelocity.z
+ )
+
+ radialVector = targetPos - shipPos
+ if ((radialVector.x, radialVector.y, radialVector.z) != (0.0, 0.0, 0.0)):
+ radialVector = radialVector.Normalize()
+
+ radialVelocity = combinedVelocity * radialVector
+ transversalVelocity = (combinedVelocity - (radialVelocity * radialVector)).Length()
+
+ trackingFactor = transversalVelocity / distance * self.tracking
+
+ resolutionFactor = self.sigResolution / targetRadius
+
+ result = 0.5 ** (((trackingFactor * resolutionFactor) ** 2) + (distanceFactor ** 2))
+
+ return result
+
+ def calculateLauncher(self, targetID, velocityModifier=1.0, radiusModifier=1.0):
+ import blue, foo
+ from common.eve.state import getCachedItem
+ target = getCachedItem(targetID)
+ targetBall = target.ball
+ targetItem = target.slimItem
+
+ ballpark = sm.services["michelle"].GetBallpark()
+ now = blue.os.GetTime()
+
+ distance = max(ballpark.DistanceBetween(self.source.id, targetID), 0.00001)
+
+ targetVelocity = max(
+ targetBall.GetVectorDotAt(now).Length() * velocityModifier,
+ 0.00001
+ )
+ targetRadius = target.radius * radiusModifier
+
+ estimatedDamage = self.baseDamage * min(
+ min(
+ targetRadius / self.explosionRadius, 1
+ ), (
+ (self.explosionVelocity / self.explosionRadius * targetRadius / targetVelocity) **
+ (math.log(self.damageReductionFactor) / math.log(5.5))
+ )
+ )
+
+ if distance > self.maxRange:
+ return 0
+ else:
+ return estimatedDamage / self.baseDamage
+
+ def calculateSmartBomb(self, targetID, **kwargs):
+ return 0.0
+
def findModule(groupNames=None, groupIDs=None, typeNames=None, typeIDs=None):
modules = findModules(
count=1,
@@ -38,6 +38,18 @@ def slimItem(self):
return self._slimItem
+ @property
+ def radius(self):
+ r = None
+
+ if self.slimItem:
+ r = getattr(self.slimItem, "signatureRadius", None)
+
+ if ((r is None) or (r <= 0.0)) and (self.ball):
+ r = self.ball.radius
+
+ return r
+
def removedFromBallpark(self):
self._slimItem = None
self._ball = None
@@ -124,7 +136,7 @@ def OnStandingSet(self, fromID, toID, standing):
if ce:
ce.clearFlag()
- def OnStandingsModified(self):
+ def OnStandingsModified(self, modifications):
for obj in self.objectCache.values():
obj.clearFlag()
@@ -1,6 +1,7 @@
import shootblues
from shootblues.common import log
-from shootblues.common.eve import SafeTimer, getFlagName, getNamesOfIDs
+from shootblues.common.eve import SafeTimer, getFlagName, getNamesOfIDs, ChanceToHitCalculator, getTypeAttributes
+from shootblues.common.eve.state import getCachedItem
from shootblues.common.service import forceStart, forceStop
import service
import uix
@@ -35,10 +36,16 @@ def notifyPrefsChanged(newPrefsJson):
class DroneInfo(object):
def __init__(self, droneID):
self.id = droneID
+ ci = getCachedItem(droneID)
+ self.ball = ci.ball
+ self.slimItem = ci.slimItem
self.target = None
self.actionTimestamp = self.timestamp = 0
self.shield = self.armor = self.structure = 1.0
self.state = None
+
+ attributes = getTypeAttributes(ci.slimItem.typeID, obj=ci.ball)
+ self.isSentry = float(attributes.get("entityCruiseSpeed", 0.0)) <= 0.0
def setState(self, newState, timestamp):
if timestamp > self.timestamp:
@@ -76,6 +83,7 @@ def __init__(self):
self.__lastAttackOrder = None
self.__recalledDrones = {}
self.__numFighters = 0
+ self.__numSentries = 0
self.disabled = False
self.checkUpdateTimer()
@@ -160,6 +168,39 @@ def getTargetSorter(self):
gp = Memoized(getPriority)
gd = Memoized(self.getDistance)
+ numDrones = len(self.__drones)
+ useChanceToHit = (self.__numSentries >= (numDrones / 2)) and (self.__numSentries > 0)
+
+ if useChanceToHit:
+ def getCalcForDrone(id):
+ cthc = ChanceToHitCalculator(id)
+ cthc.setDrone(id)
+ return Memoized(cthc.calculate)
+
+ calcs = [
+ getCalcForDrone(id) for id in self.__drones.iterkeys()
+ if self.__drones[id].isSentry
+ ]
+
+ def toHitSorter(lhs, rhs):
+ cthLhs = 0
+ cthRhs = 0
+
+ for calc in calcs:
+ cthLhs += calc(lhs)
+ cthRhs += calc(rhs)
+
+ cthLhs /= len(calcs)
+ cthRhs /= len(calcs)
+
+ return cmp(cthRhs, cthLhs)
+
+ else:
+ def toHitSorter(lhs, rhs):
+ distLhs = gd(lhs)
+ distRhs = gd(rhs)
+ return cmp(distLhs, distRhs)
+
def targetSorter(lhs, rhs):
# Highest priority first
priLhs = gp(lhs)
@@ -173,10 +214,8 @@ def targetSorter(lhs, rhs):
lhs is self.__lastAttackOrder
)
- if result == 0:
- distLhs = gd(lhs)
- distRhs = gd(rhs)
- result = cmp(distLhs, distRhs)
+ if result == 0:
+ result = toHitSorter(lhs, rhs)
return result
@@ -353,6 +392,11 @@ def updateDrones(self):
)):
self.__numFighters += 1
+ self.__numSentries = 0
+ for droneObj in self.__drones.itervalues():
+ if droneObj.isSentry:
+ self.__numSentries += 1
+
for (pendingID, psc) in list(self.__pendingStateChanges.items()):
if self.__pendingStateChanges.has_key(pendingID):
del self.__pendingStateChanges[pendingID]
@@ -387,7 +431,7 @@ def updateDrones(self):
commonTarget = self.getCommonTarget(filtered=False)
if commonTarget == eve.session.shipid:
return
-
+
oldPriority = getPriority(commonTarget)
newTarget = self.selectTarget()
newPriority = getPriority(newTarget)
@@ -20,6 +20,9 @@ def adjustPriority(targetID, delta=1):
def getPriority(id):
global priorities, priorityBoosts
+ if not id:
+ return -1
+
ci = getCachedItem(id)
if not ci.slimItem:
Oops, something went wrong.

0 comments on commit a1850dd

Please sign in to comment.