NovaBryan/EVE_Dump
Log | Files | 📦 下载最新源码 (.zip) | Refs | README

targetMgr.py (55808B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\parklife\targetMgr.py
      2 import sys
      3 import blue
      4 import localization
      5 import telemetry
      6 import uthread
      7 from carbon.common.script.sys.service import Service
      8 from carbonui import uiconst
      9 from carbonui.primitives.container import Container
     10 from carbonui.primitives.line import Line
     11 from carbonui.uicore import uicore
     12 from carbonui.util.various_unsorted import SortListOfTuples
     13 from eve.client.script.parklife import states as state
     14 from eve.client.script.parklife.targetUtils import get_closest_entity_target
     15 from eve.client.script.ui.control.eveIcon import Icon
     16 from eve.client.script.ui.inflight.activeitem import ActiveItem
     17 from eve.client.script.ui.inflight.bracketsAndTargets.targetInBar import TargetInBar
     18 from eve.client.script.ui.inflight.bracketsAndTargets.targetInBarFighter import TargetInBarFighter
     19 from eve.client.script.ui.inflight.selectedItemWnd import SelectedItemWnd
     20 from eve.client.script.ui.shared.pointerTool import pointerToolConst as pConst
     21 from eve.client.script.ui.util.uix import GetBallparkRecord
     22 from eve.common.script.sys import eveSessions, idCheckers
     23 from eve.client.script.ui.view import overlaySettings
     24 from eve.client.script.ui.view.viewStateConst import ViewOverlay
     25 from eveexceptions import UserError
     26 from menu import MenuLabel
     27 import eve.common.lib.appConst as const
     28 from player_messaging.client.ui_message import prompt_player_fsd_dialog
     29 MAX_TARGET_WIDTH = 110
     30 AVG_TARGET_HEIGHT = 200
     31 MIN_TARGET_HEIHT = 170
     32 TARGET_PADDING = 10
     33 
     34 class TargetMgr(Service):
     35     __guid__ = 'svc.target'
     36     __exportedcalls__ = {'LockTarget': [],
     37      'UnlockTarget': [],
     38      'ClearTargets': [],
     39      'GetActiveTargetID': [],
     40      'GetTargets': [],
     41      'GetTargeting': [],
     42      'StartLockTarget': [],
     43      'FailLockTarget': [],
     44      'CancelAddTarget': []}
     45     __notifyevents__ = ['OnTarget',
     46      'OnTargets',
     47      'OnTargetClear',
     48      'ProcessSessionChange',
     49      'OnSpecialFX',
     50      'DoBallsAdded',
     51      'DoBallRemove',
     52      'DoBallClear',
     53      'OnStateChange',
     54      'DoBallsRemove',
     55      'OnFleetStateChange']
     56     __dependencies__ = ['michelle',
     57      'godma',
     58      'settings',
     59      'stateSvc']
     60     __update_on_reload__ = 0
     61 
     62     def Run(self, memStream = None):
     63         super(TargetMgr, self).Run(memStream)
     64         self.viewMode = 'normal'
     65         self.preViewMode = None
     66         self.Reset()
     67         if session.shipid and not session.stationid:
     68             self.godma.GetStateManager().RefreshTargets()
     69         self.ownerToShipIDCache = {}
     70         self.deadShipsBeingRemoved = set()
     71         self.disableSpinnyReticule = False
     72         self.overrideBarHeight = None
     73 
     74     def Stop(self, stream):
     75         super(TargetMgr, self).Stop(stream)
     76         self.viewMode = 'normal'
     77         self.CheckViewMode()
     78         self.Reset()
     79 
     80     def PlaceOrigin(self):
     81         if self.origin is not None:
     82             return
     83         self.CreateOrigin()
     84         self.PositionOriginWithAnchors()
     85         self.UpdateOriginDirection()
     86 
     87     def CreateOrigin(self, *args):
     88         if not len(self.targetsByID):
     89             self.origin = None
     90             return
     91         par = uicore.layer.target
     92         origin = Container(name='targetOrigin', parent=par, align=uiconst.RELATIVE, width=16, height=16, state=uiconst.UI_NORMAL, uniqueUiName=pConst.UNIQUE_NAME_TARGET_ANCHOR)
     93         origin.OnMouseDown = (self.OnOriginMD, origin)
     94         origin.OnMouseUp = (self.OnOriginMU, origin)
     95         origin.GetMenu = self.GetOriginMenu
     96         origin.hint = localization.GetByLabel('UI/Inflight/TargetListAnchor/TargetListAnchorHint')
     97         origin.leftline = Line(parent=origin, align=uiconst.RELATIVE, width=4, height=1, left=-4, top=7, color=(1.0, 1.0, 1.0, 1.0))
     98         origin.topline = Line(parent=origin, align=uiconst.RELATIVE, width=1, height=4, left=7, top=-4, color=(1.0, 1.0, 1.0, 1.0))
     99         origin.rightline = Line(parent=origin, align=uiconst.RELATIVE, width=4, height=1, left=15, top=7, color=(1.0, 1.0, 1.0, 1.0))
    100         origin.bottomline = Line(parent=origin, align=uiconst.RELATIVE, width=1, height=4, left=7, top=15, color=(1.0, 1.0, 1.0, 1.0))
    101         origin.opacity = 0.75
    102         Icon(icon='ui_38_16_72', parent=origin, state=uiconst.UI_DISABLED)
    103         self.origin = origin
    104         if getattr(self, 'targetPar', None) is None or self.targetPar.destroyed:
    105             targetPar = Container(parent=uicore.layer.target, align=uiconst.TOALL)
    106             self.targetPar = targetPar
    107             self.targetPar.rows = []
    108 
    109     def PositionOriginWithAnchors(self):
    110         if not self.origin or self.origin.destroyed:
    111             self.CreateOrigin()
    112         if not self.origin:
    113             return
    114         origin = self.origin
    115         (cX, cY), (toLeft, toTop) = self.GetOriginPosition(getDirection=1)
    116         pl, pt, pw, ph = origin.parent.GetAbsolute()
    117         originAlign = self.GetOriginAlign(toLeft, toTop)
    118         origin.SetAlign(originAlign)
    119         if toLeft:
    120             origin.left = pw - cX - 8
    121         else:
    122             origin.left = cX - 8
    123         if toTop:
    124             origin.top = ph - cY - 8
    125         else:
    126             origin.top = cY - 8
    127 
    128     def GetOriginMenu(self):
    129         if settings.user.ui.Get('targetOriginLocked', 0):
    130             m = [(MenuLabel('UI/Inflight/TargetListAnchor/UnlockAnchor'), self.UnlockOrigin)]
    131         else:
    132             m = [(MenuLabel('UI/Inflight/TargetListAnchor/LockAnchor'), self.LockOrigin)]
    133         if settings.user.ui.Get('targetPositionLocked', 0):
    134             m += [(MenuLabel('UI/Inflight/TargetListAnchor/UnlockTargets'), self.ToggleLockTargetPosition)]
    135         else:
    136             m += [(MenuLabel('UI/Inflight/TargetListAnchor/LockTargets'), self.ToggleLockTargetPosition)]
    137         if settings.user.ui.Get('alignHorizontally', 1):
    138             m += [(MenuLabel('UI/Inflight/TargetListAnchor/ArrangeTargetsVertically'), self.ToggleAlignment)]
    139         else:
    140             m += [(MenuLabel('UI/Inflight/TargetListAnchor/ArrangeTargetsHorizontally'), self.ToggleAlignment)]
    141         if overlaySettings.IsFullscreenOverlayModeEnabled(ViewOverlay.Target):
    142             m += [(MenuLabel('/Carbon/UI/Controls/Window/DisableOverlayMode'), lambda : overlaySettings.DisableFullscreenOverlayMode(ViewOverlay.Target))]
    143         else:
    144             m += [(MenuLabel('/Carbon/UI/Controls/Window/EnableOverlayMode'), lambda : overlaySettings.EnableFullscreenOverlayMode(ViewOverlay.Target))]
    145         m += [(MenuLabel('UI/Commands/CmdUnlockTargetItems'), sm.GetService('cmd').CmdUnlockTargetItems)]
    146         return m
    147 
    148     def UnlockOrigin(self, *args):
    149         settings.user.ui.Set('targetOriginLocked', 0)
    150 
    151     def LockOrigin(self, *args):
    152         settings.user.ui.Set('targetOriginLocked', 1)
    153 
    154     def ToggleLockTargetPosition(self, *args):
    155         current = settings.user.ui.Get('targetPositionLocked', 0)
    156         settings.user.ui.Set('targetPositionLocked', not current)
    157 
    158     def ToggleAlignment(self, *args):
    159         current = settings.user.ui.Get('alignHorizontally', 1)
    160         settings.user.ui.Set('alignHorizontally', not current)
    161         self.ArrangeTargets()
    162 
    163     def GetTargetLayerAbsolutes(self):
    164         d = uicore.desktop
    165         wnd = uicore.layer.target
    166         if wnd and not wnd.state == uiconst.UI_HIDDEN:
    167             pl, pt, pw, ph = wnd.GetAbsolute()
    168         else:
    169             pl, pt, pw, ph = d.GetAbsolute()
    170         return (wnd,
    171          pl,
    172          pt,
    173          pw,
    174          ph)
    175 
    176     def GetOriginPosition(self, inPixels = 1, getDirection = 0):
    177         d = uicore.desktop
    178         wnd, pl, pt, pw, ph = self.GetTargetLayerAbsolutes()
    179         myPrefs = settings.user.ui.Get('targetOrigin', None)
    180         if myPrefs is None:
    181             selecteditem = SelectedItemWnd.GetIfOpen()
    182             if selecteditem and selecteditem.display and not selecteditem.IsMinimized():
    183                 stack = getattr(selecteditem.sr, 'stack', None)
    184                 if stack:
    185                     selecteditem = stack
    186                 topAlignedWindows = selecteditem.FindConnectingWindows('top')
    187                 left, top, width, height = selecteditem.GetGroupAbsolute(topAlignedWindows)
    188                 dw = d.width
    189                 dh = d.height
    190             else:
    191                 left, top, width, height, dw, dh = SelectedItemWnd.GetRegisteredPositionAndSizeByClass()
    192             left -= 15
    193             portionalCY = top / float(dh)
    194             if left + width < d.width / 2:
    195                 portionalCX = (left + width - pl) / float(pw)
    196             elif left >= d.width / 2:
    197                 portionalCX = (left - pl) / float(pw)
    198             else:
    199                 portionalCX = (d.width - 300) / float(dw)
    200                 portionalCY = 16 / float(d.height)
    201         else:
    202             portionalCX, portionalCY = myPrefs
    203         if inPixels:
    204             ret = (int(portionalCX * pw), int(portionalCY * ph))
    205         else:
    206             ret = (portionalCX, portionalCY)
    207         if getDirection:
    208             return (ret, (bool(portionalCX > 0.5), bool(portionalCY > 0.5)))
    209         return ret
    210 
    211     def OnOriginMU(self, origin, btn, *args):
    212         if btn != 0 or settings.user.ui.Get('targetOriginLocked', 0):
    213             return
    214         if uicore.uilib.mouseOver == self.origin:
    215             par, tl, tt, tw, th = self.GetTargetLayerAbsolutes()
    216             origin.left = uicore.uilib.x - origin.grab[0]
    217             origin.top = uicore.uilib.y - origin.grab[1]
    218             origin.opacity = 0.75
    219             d = uicore.desktop
    220             l, t, w, h = origin.GetAbsolute()
    221             cX = l - tl + w / 2
    222             cY = t - tt + h / 2
    223             pcX = cX / float(tw)
    224             pcY = cY / float(th)
    225             settings.user.ui.Set('targetOrigin', (pcX, pcY))
    226         self.ArrangeTargets()
    227         sm.GetService('ui').ForceCursorUpdate()
    228         origin.dragging = 0
    229         uicore.uilib.UnclipCursor()
    230         origin.hint = localization.GetByLabel('UI/Inflight/TargetListAnchor/TargetListAnchorHint')
    231 
    232     def OnOriginMD(self, origin, btn, *args):
    233         if btn != 0 or settings.user.ui.Get('targetOriginLocked', 0):
    234             return
    235         origin.opacity = 1.0
    236         l, t, w, h = origin.GetAbsolute()
    237         pl, pt, pw, ph = origin.parent.GetAbsolute()
    238         origin.grab = [uicore.uilib.x - l, uicore.uilib.y - t]
    239         origin.dragging = 1
    240         origin.SetAlign(uiconst.ABSOLUTE)
    241         origin.left = l
    242         origin.top = t
    243         uthread.new(self.BeginDrag, origin)
    244         d = uicore.desktop
    245         gx, gy = origin.grab
    246         uicore.uilib.ClipCursor(gx, gy, d.width - (origin.width - gx), d.height - (origin.height - gy))
    247 
    248     def GetBaseLeftAndTop(self, *args):
    249         (cX, cY), (toLeft, toTop) = self.GetOriginPosition(getDirection=1)
    250         par, pl, pt, pw, ph = self.GetTargetLayerAbsolutes()
    251         if toLeft:
    252             baseleft = pw - cX
    253         else:
    254             baseleft = cX
    255         if toTop:
    256             basetop = ph - cY
    257         else:
    258             basetop = cY
    259         return (baseleft, basetop)
    260 
    261     def UpdateOriginDirection(self):
    262         if not self.origin:
    263             return
    264         origin = self.origin
    265         d = uicore.desktop
    266         l, t, w, h = origin.GetAbsolute()
    267         cX = l + w / 2
    268         cY = t + h / 2
    269         if cX > d.width / 2:
    270             origin.leftline.state = uiconst.UI_NORMAL
    271             origin.rightline.state = uiconst.UI_HIDDEN
    272         else:
    273             origin.rightline.state = uiconst.UI_NORMAL
    274             origin.leftline.state = uiconst.UI_HIDDEN
    275         if cY > d.height / 2:
    276             origin.topline.state = uiconst.UI_NORMAL
    277             origin.bottomline.state = uiconst.UI_HIDDEN
    278         else:
    279             origin.bottomline.state = uiconst.UI_NORMAL
    280             origin.topline.state = uiconst.UI_HIDDEN
    281 
    282     def BeginDrag(self, origin):
    283         while self.origin and not origin.destroyed and getattr(origin, 'dragging', 0):
    284             uicore.uilib.SetCursor(uiconst.UICURSOR_NONE)
    285             origin.left = uicore.uilib.x - origin.grab[0]
    286             origin.top = uicore.uilib.y - origin.grab[1]
    287             self.UpdateOriginDirection()
    288             if uicore.uilib.mouseOver == self.origin:
    289                 self.origin.hint = localization.GetByLabel('UI/Inflight/TargetListAnchor/AnchorIsMoving')
    290             else:
    291                 self.origin.hint = ''
    292             blue.pyos.synchro.SleepWallclock(1)
    293 
    294     def Reset(self):
    295         self.dogmaLM = None
    296         self.allTargets = []
    297         self.rowDict = {}
    298         self.ClearingTargets()
    299         self.pendingTargets = []
    300         self.pendingTargeters = []
    301         self.targeting = {}
    302         self.autoTargeting = []
    303         self.weaponsOnMe = {}
    304         self.needtarget = []
    305         self.teams = [[], []]
    306         self.origin = None
    307         self.ownerToShipIDCache = {}
    308         self.ClearTargetedBy()
    309         uicore.layer.target.Flush()
    310 
    311     def CheckViewMode(self):
    312         toggleWnds = {'main': [ child for child in uicore.layer.main.children ],
    313          'inflight': [ child for child in uicore.layer.inflight.children if child.name is not 'l_target' ]}
    314         if self.viewMode == 'normal':
    315             if self.origin:
    316                 self.origin.state = uiconst.UI_NORMAL
    317             if self.preViewMode:
    318                 for wnd in toggleWnds:
    319                     layer = uicore.layer.Get(wnd)
    320                     for wndState, name in self.preViewMode:
    321                         wnd = layer.FindChild(name)
    322                         if wnd:
    323                             wnd.state = wndState
    324 
    325             self.preViewMode = None
    326         else:
    327             if self.origin:
    328                 self.origin.state = uiconst.UI_HIDDEN
    329             self.preViewMode = []
    330             for layer, children in toggleWnds.iteritems():
    331                 for wnd in children:
    332                     if wnd and wnd.name not in 'l_bracket':
    333                         self.preViewMode.append((wnd.state, wnd.name))
    334                         wnd.state = uiconst.UI_HIDDEN
    335 
    336     def IsObserving(self):
    337         return bool(self.viewMode == 'observe')
    338 
    339     def ToggleTeam(self, itemID):
    340         current = self.GetTeam(itemID)
    341         if itemID in self.teams[current]:
    342             self.teams[current].remove(itemID)
    343         new = not current
    344         if itemID not in self.teams[new]:
    345             self.teams[new].append(itemID)
    346         settings.user.ui.Set('targetTeamsII', self.teams[:])
    347         self.ArrangeTargets()
    348 
    349     def RemoveFromTeam(self, itemID, reset = 0):
    350         obs = self.IsObserving()
    351         if obs:
    352             if reset:
    353                 settings.user.ui.Set('targetTeamsII', [[], []])
    354             current = self.GetTeam(itemID)
    355             if itemID in self.teams[current]:
    356                 self.teams[current].remove(itemID)
    357 
    358     def MoveUp(self, itemID):
    359         current = self.GetTeam(itemID)
    360         teamOrder = self.teams[current]
    361         idx = 0
    362         if itemID in teamOrder:
    363             idx = teamOrder.index(itemID) - 1
    364         self.SetTeamOrder(itemID, idx)
    365         self.ArrangeTargets()
    366 
    367     def MoveDown(self, itemID):
    368         current = self.GetTeam(itemID)
    369         teamOrder = self.teams[current]
    370         idx = 0
    371         if itemID in teamOrder:
    372             idx = teamOrder.index(itemID) + 1
    373         self.SetTeamOrder(itemID, idx)
    374         self.ArrangeTargets()
    375 
    376     def GetTeam(self, itemID):
    377         return itemID not in self.teams[0]
    378 
    379     def GetTeamOrder(self, itemID):
    380         current = self.GetTeam(itemID)
    381         teamOrder = self.teams[current]
    382         if itemID in teamOrder:
    383             return teamOrder.index(itemID)
    384         return -1
    385 
    386     def SetTeamOrder(self, itemID, idx):
    387         idx = max(0, idx)
    388         current = self.GetTeam(itemID)
    389         if itemID in self.teams[current]:
    390             self.teams[current].remove(itemID)
    391         self.teams[current].insert(idx, itemID)
    392 
    393     def ToggleViewMode(self):
    394         self.viewMode = ['normal', 'observe'][self.viewMode == 'normal']
    395         self.Reset()
    396         if session.shipid and not session.stationid:
    397             self.godma.GetStateManager().RefreshTargets()
    398         self.CheckViewMode()
    399         if self.viewMode == 'observe':
    400             sm.GetService('infoPanel').ShowHideSidePanel(hide=True)
    401         else:
    402             sm.GetService('infoPanel').ShowHideSidePanel(hide=False)
    403 
    404     def Show(self):
    405         wnd = uicore.layer.target
    406         if wnd:
    407             for each in wnd.children:
    408                 each.state = uiconst.UI_NORMAL
    409 
    410     def GetDogmaLM(self):
    411         if self.dogmaLM is None:
    412             self.dogmaLM = self.godma.GetDogmaLM()
    413         return self.dogmaLM
    414 
    415     def OnSpecialFX(self, shipID, moduleID, moduleTypeID, targetID, otherTypeID, guid, isOffensive, start, active, duration = -1, repeat = None, startTime = None, timeFromStart = 0, graphicInfo = None):
    416         if isOffensive and targetID == session.shipid:
    417             self.weaponsOnMe[shipID] = self.weaponsOnMe.get(shipID, 0) + (-1, +1)[start]
    418 
    419     def OnStateChange(self, itemID, flag, true, *args):
    420         if flag == state.selected and true and self.needtarget and len(self.needtarget) > 0:
    421             if not self.IsTarget(itemID):
    422                 self.HideTargetingCursor()
    423                 target = self.needtarget[0]
    424                 if hasattr(target, 'GetDefaultEffect'):
    425                     effect = self.needtarget[0].GetDefaultEffect()
    426                     if effect is not None and effect.effectName == 'targetPassively':
    427                         uthread.pool('TargetManager::OnStateChange-->ActivateTargetPassively', self.LockTargetPassively, itemID, self.needtarget[0])
    428                     else:
    429                         uthread.pool('TargetManager::OnStateChange-->LockTarget', self.TryLockTarget, itemID)
    430                 elif target is None:
    431                     self.LogError("target doesn't have a GetDefaultEffect because it None")
    432                 elif hasattr(target, '__class__'):
    433                     self.LogError(target.__class__.__name__, "doesn't have GetDefaultEffect attr")
    434                 else:
    435                     self.LogError(target, "doesn't have GetDefaultEffect attr")
    436                 self.needtarget = []
    437 
    438     def ProcessSessionChange(self, isRemote, session, change):
    439         if not isRemote:
    440             return
    441         spaceShipChange = 'ship' in change and 'solarsystemid' not in change
    442         if not sm.GetService('connection').IsConnected() or session.stationid is not None or session.charid is None:
    443             self.CleanUp()
    444         elif spaceShipChange and change['shipid'][0] is not None:
    445             self.OnTargetClear()
    446             self.pendingTargeters = []
    447         if 'solarsystemid' in change:
    448             self.deadShipsBeingRemoved = set()
    449         if 'fleetid' in change and change['fleetid'][1] is None:
    450             self.UpdateFleetTags()
    451         changingShipsInSpace = spaceShipChange and session.solarsystemid and session.shipid
    452         loggingDirectlyIntoSpace = change.get('solarsystemid', (1, 1))[0] is None and not session.stationid
    453         undocking = eveSessions.IsUndockingSessionChange(session, change)
    454         if changingShipsInSpace or loggingDirectlyIntoSpace or undocking:
    455             for otherID in self.targetedBy:
    456                 self.stateSvc.SetState(otherID, state.threatTargetsMe, 0)
    457 
    458             if session.shipid:
    459                 uthread.new(self.godma.GetStateManager().RefreshTargets)
    460         self.dogmaLM = None
    461 
    462     def CleanUp(self):
    463         self.viewMode = 'normal'
    464         self.CheckViewMode()
    465         self.ClearingTargets()
    466         self.dogmaLM = None
    467         self.ClearTargetedBy()
    468 
    469     def ClearingTargets(self, *args):
    470         if getattr(self, 'targetsByID', None) is not None:
    471             for target in self.targetsByID.values():
    472                 target.Close()
    473 
    474         self.targetsByID = {}
    475         self.groupIdByTarget = {}
    476         self.deadShipsBeingRemoved = set()
    477 
    478     def DoBallsAdded(self, *args, **kw):
    479         import stackless
    480         import blue
    481         t = stackless.getcurrent()
    482         timer = t.PushTimer(blue.pyos.taskletTimer.GetCurrent() + '::targetMgr')
    483         try:
    484             return self.DoBallsAdded_(*args, **kw)
    485         finally:
    486             t.PopTimer(timer)
    487 
    488     def DoBallsAdded_(self, lst):
    489         for ball, slimItem in lst:
    490             if ball.id in self.pendingTargets:
    491                 self.OnTargetAdded(ball.id)
    492             if ball.id in self.pendingTargeters:
    493                 self.OnTargetByOther(ball.id)
    494 
    495     @telemetry.ZONE_METHOD
    496     def DoBallsRemove(self, pythonBalls, isRelease):
    497         if isRelease:
    498             if len(self.pendingTargets) or len(self.autoTargeting) or len(self.targeting):
    499                 sm.GetService('audio').StopSoundLoop('TargetLocking', 'msg_TargetLocked_play')
    500             for targetID, target in self.targetsByID.iteritems():
    501                 target.Close()
    502 
    503             if self.origin is not None and not self.origin.destroyed:
    504                 self.origin.Close()
    505                 self.origin = None
    506             self.targetsByID = {}
    507             self.groupIdByTarget = {}
    508             self.pendingTargets = []
    509             self.rowDict = {}
    510             self.deadShipsBeingRemoved = set()
    511             return
    512         for ball, slimItem, terminal in pythonBalls:
    513             self.DoBallRemove(ball, slimItem, terminal)
    514 
    515     def DoBallRemove(self, ball, slimItem, terminal):
    516         if ball is None:
    517             return
    518         tid = ball.id
    519         if tid in self.pendingTargets or tid in self.autoTargeting or tid in self.targeting:
    520             sm.GetService('audio').StopSoundLoop('TargetLocking', 'msg_TargetLocked_play')
    521         if tid not in self.deadShipsBeingRemoved:
    522             self.ClearTarget(tid)
    523 
    524     def DoBallClear(self, solitem):
    525         self.CleanUp()
    526 
    527     def OnTargets(self, targets):
    528         for each in targets:
    529             self.OnTarget(*each[1:])
    530 
    531     def OnTarget(self, what, tid = None, reason = None):
    532         if what == 'add':
    533             self.OnTargetAdded(tid)
    534         elif what == 'clear':
    535             self.OnTargetClear()
    536         elif what == 'lost':
    537             uthread.new(self.OnTargetLost, tid, reason)
    538         elif what == 'otheradd':
    539             self.OnTargetByOther(tid)
    540         elif what == 'otherlost':
    541             self.OnTargetByOtherLost(tid, reason)
    542         sm.ChainEvent('ProcessTargetChanged', what, tid, reason)
    543 
    544     def OnTargetAdded(self, tid):
    545         if self.origin is None:
    546             self.PlaceOrigin()
    547         if tid in self.targeting:
    548             del self.targeting[tid]
    549         if tid in self.autoTargeting:
    550             self.autoTargeting.remove(tid)
    551         sm.GetService('audio').StopSoundLoop('TargetLocking', 'msg_TargetLocked_play')
    552         slimItem = None
    553         bp = sm.GetService('michelle').GetBallpark()
    554         if bp is not None:
    555             slimItem = bp.GetInvItem(tid)
    556         if bp is None or slimItem is None:
    557             if tid not in self.pendingTargets:
    558                 self.pendingTargets.append(tid)
    559             return
    560         self.stateSvc.SetState(tid, state.targeting, 0)
    561         self.stateSvc.SetState(tid, state.targeted, 1)
    562         if tid in self.pendingTargets:
    563             self.pendingTargets.remove(tid)
    564         sm.ScatterEvent('OnClientEvent_LockItem', slimItem)
    565         if tid in self.targetsByID:
    566             self.ClearTarget(tid)
    567         is_observing = self.IsObserving()
    568         if is_observing and not (tid in self.teams[0] or tid in self.teams[1]):
    569             self.teams[1].append(tid)
    570         if settings.user.ui.Get('alignHorizontally', 1):
    571             padBottom = 0
    572         else:
    573             padBottom = TARGET_PADDING
    574         targetClass = self.GetTargetClassForSlimItem(slimItem)
    575         target = targetClass(name='target', parent=uicore.layer.target, align=uiconst.TOPRIGHT, width=[110, 160][is_observing], height=[150, 80][is_observing], state=uiconst.UI_PICKCHILDREN, padBottom=padBottom)
    576         target.Startup(slimItem)
    577         targetID = slimItem.itemID
    578         self.targetsByID[targetID] = target
    579         self.groupIdByTarget[targetID] = slimItem.groupID
    580         bracket = sm.GetService('bracket').GetBracket(tid)
    581         if bracket and bracket.targeted_indicator:
    582             bracket.targeted_indicator.state = [uiconst.UI_DISABLED, uiconst.UI_HIDDEN][is_observing]
    583         self.ArrangeTargets()
    584         if not self.GetActiveTargetID():
    585             self.stateSvc.SetState(tid, state.activeTarget, 1)
    586             if self.stateSvc.GetExclState(state.selected) is None:
    587                 self.stateSvc.SetState(tid, state.selected, 1)
    588 
    589     def GetTargetClassForSlimItem(self, slimItem):
    590         if slimItem.categoryID == const.categoryFighter:
    591             return TargetInBarFighter
    592         else:
    593             return TargetInBar
    594 
    595     def OnFleetStateChange(self, fleetState):
    596         if not fleetState:
    597             return
    598         for itemID, tag in fleetState.targetTags.iteritems():
    599             target = self.GetTarget(itemID)
    600             if target:
    601                 target.SetFleetTag(tag)
    602 
    603     def UpdateFleetTags(self):
    604         fleetSvc = sm.GetService('fleet')
    605         for eachID, eachTarget in self.GetTargets().iteritems():
    606             tag = fleetSvc.GetTargetTag(eachID)
    607             eachTarget.SetFleetTag(tag)
    608 
    609     def OrderTarget(self, who):
    610         if who not in self.needtarget[:]:
    611             self.needtarget.append(who)
    612         self.ShowTargetingCursor()
    613 
    614     def IsSomeModuleWaitingForTargetClick(self):
    615         return bool(self.needtarget)
    616 
    617     def CancelTargetOrder(self, who = None):
    618         if not who and len(self.needtarget):
    619             for each in self.needtarget:
    620                 if each and not each.destroyed:
    621                     each.waitingForActiveTarget = 0
    622 
    623             self.needtarget = []
    624         elif who in self.needtarget:
    625             self.needtarget.remove(who)
    626             who.waitingForActiveTarget = 0
    627         if not len(self.needtarget):
    628             self.HideTargetingCursor()
    629 
    630     def ShowTargetingCursor(self):
    631         uicore.layer.inflight.sr.tcursor.state = uiconst.UI_DISABLED
    632 
    633     def HideTargetingCursor(self):
    634         uicore.layer.inflight.sr.tcursor.state = uiconst.UI_HIDDEN
    635 
    636     def GetOriginAlign(self, toLeft, toTop):
    637         if toLeft:
    638             if toTop:
    639                 align = uiconst.BOTTOMRIGHT
    640             else:
    641                 align = uiconst.TOPRIGHT
    642         elif toTop:
    643             align = uiconst.BOTTOMLEFT
    644         else:
    645             align = uiconst.TOPLEFT
    646         return align
    647 
    648     def PrepareRowsOrColumns(self, originAlign, baseleft, basetop, *args):
    649         targetPar = self.targetPar
    650         alignmentDict = {(0, uiconst.TOPLEFT): (uiconst.TOLEFT, uiconst.TOTOP, (baseleft,
    651                                  basetop,
    652                                  0,
    653                                  0)),
    654          (0, uiconst.TOPRIGHT): (uiconst.TORIGHT, uiconst.TOTOP, (0,
    655                                   basetop,
    656                                   baseleft,
    657                                   0)),
    658          (0, uiconst.BOTTOMLEFT): (uiconst.TOLEFT, uiconst.TOBOTTOM, (baseleft,
    659                                     0,
    660                                     0,
    661                                     basetop)),
    662          (0, uiconst.BOTTOMRIGHT): (uiconst.TORIGHT, uiconst.TOBOTTOM, (0,
    663                                      0,
    664                                      baseleft,
    665                                      basetop)),
    666          (1, uiconst.TOPLEFT): (uiconst.TOTOP, uiconst.TOLEFT, (baseleft,
    667                                  basetop,
    668                                  0,
    669                                  0)),
    670          (1, uiconst.TOPRIGHT): (uiconst.TOTOP, uiconst.TORIGHT, (0,
    671                                   basetop,
    672                                   baseleft,
    673                                   0)),
    674          (1, uiconst.BOTTOMLEFT): (uiconst.TOBOTTOM, uiconst.TOLEFT, (baseleft,
    675                                     0,
    676                                     0,
    677                                     basetop)),
    678          (1, uiconst.BOTTOMRIGHT): (uiconst.TOBOTTOM, uiconst.TORIGHT, (0,
    679                                      0,
    680                                      baseleft,
    681                                      basetop))}
    682         horizontally = settings.user.ui.Get('alignHorizontally', 1)
    683         rowAlignment, targetAlignment, targetParPadding = alignmentDict[horizontally, originAlign]
    684         widthAvailable = uicore.desktop.width - baseleft
    685         heightAvailable = uicore.desktop.height - basetop
    686         if horizontally:
    687             availableSpace = heightAvailable
    688             maxHeight = AVG_TARGET_HEIGHT
    689             maxWidth = 0
    690             maxRowsPossible = availableSpace / maxHeight
    691         else:
    692             availableSpace = widthAvailable
    693             maxHeight = 0
    694             maxWidth = MAX_TARGET_WIDTH
    695             maxRowsPossible = availableSpace / maxWidth
    696         counter = 0
    697         for eachRow in targetPar.children:
    698             if not isinstance(eachRow, Container):
    699                 continue
    700             if counter >= maxRowsPossible:
    701                 eachRow.display = False
    702                 counter += 1
    703                 continue
    704             eachRow.display = True
    705             if eachRow.align != rowAlignment:
    706                 eachRow.SetAlign(rowAlignment)
    707                 eachRow.height = maxHeight
    708                 eachRow.width = maxWidth
    709             for eachTarget in eachRow.children:
    710                 if not isinstance(eachTarget, TargetInBar):
    711                     continue
    712                 eachTarget.SetAlign(targetAlignment)
    713                 if horizontally:
    714                     eachTarget.padBottom = 0
    715                 else:
    716                     eachTarget.padBottom = max(TARGET_PADDING, eachTarget.padBottom)
    717 
    718             counter += 1
    719 
    720         newRowList = []
    721         for i in xrange(maxRowsPossible):
    722             aRow = getattr(targetPar, 'aRow_%s' % i, None)
    723             if not aRow or aRow.destroyed:
    724                 aRow = Container(parent=targetPar, align=rowAlignment, height=maxHeight, width=maxWidth, name='aRowx_%s' % i, state=uiconst.UI_PICKCHILDREN)
    725                 aRow.numRow = i
    726                 setattr(targetPar, 'aRow_%s' % i, aRow)
    727             newRowList.append(aRow)
    728 
    729         targetPar.rows = newRowList
    730         targetPar.padding = targetParPadding
    731         return targetAlignment
    732 
    733     def ArrangeTargets(self):
    734         self.PositionOriginWithAnchors()
    735         self.UpdateOriginDirection()
    736         if self.origin:
    737             self.origin.state = uiconst.UI_NORMAL
    738         targetPar = getattr(self, 'targetPar', None)
    739         if targetPar is None or getattr(self, 'origin', None) is None:
    740             return
    741         (cX, cY), (toLeft, toTop) = self.GetOriginPosition(getDirection=1)
    742         originAlign = self.GetOriginAlign(toLeft, toTop)
    743         baseleft, basetop = self.GetBaseLeftAndTop()
    744         targetAlignment = self.PrepareRowsOrColumns(originAlign, baseleft, basetop)
    745         targetsThatDontFit = []
    746         targetsAdded = []
    747         rows = targetPar.rows
    748         for key, targetIDs in self.rowDict.iteritems():
    749             if len(rows) >= key + 1:
    750                 cont = rows[key]
    751             else:
    752                 targetsThatDontFit += targetIDs
    753                 self.rowDict[key] = []
    754                 continue
    755             newTargetList = []
    756             counter = 0
    757             for eachTargetID in targetIDs:
    758                 eachTarget = self.GetTarget(eachTargetID)
    759                 if getattr(eachTarget, 'isDragging', False):
    760                     continue
    761                 if eachTarget is None or eachTarget.destroyed:
    762                     continue
    763                 if not self.DoesTargetFit(cont, baseleft, basetop, counter + 1, excludedTargetID=eachTargetID):
    764                     targetsThatDontFit.append(eachTargetID)
    765                     counter += 1
    766                     continue
    767                 eachTarget.SetAlign(targetAlignment)
    768                 eachTarget.SetParent(cont)
    769                 targetsAdded.append(eachTarget.id)
    770                 newTargetList.append(eachTargetID)
    771                 counter += 1
    772 
    773             self.rowDict[key] = newTargetList
    774 
    775         horizontally = settings.user.ui.Get('alignHorizontally', 1)
    776         wnd, pl, pt, pw, ph = self.GetTargetLayerAbsolutes()
    777         for targetID, target in self.targetsByID.iteritems():
    778             if not isinstance(target, TargetInBar):
    779                 continue
    780             if targetID in targetsAdded:
    781                 continue
    782             if getattr(target, 'isDragging', False):
    783                 continue
    784             myCont, numTargets = self.FindRow(rows, baseleft, basetop)
    785             target.state = uiconst.UI_PICKCHILDREN
    786             target.SetAlign(targetAlignment)
    787             if myCont:
    788                 target.SetParent(myCont)
    789                 targetList = self.rowDict.get(myCont.numRow, [])
    790                 targetList.append(targetID)
    791                 self.rowDict[myCont.numRow] = targetList
    792                 if targetID in targetsThatDontFit:
    793                     targetsThatDontFit.remove(targetID)
    794 
    795         if len(targetsThatDontFit) > 0:
    796             self.LogError('did not add the following targets: ', targetsThatDontFit)
    797         uthread.new(self.AdjustRowSize)
    798         sm.ScatterEvent('OnTargetsArranged')
    799 
    800     def FindRow(self, possibleContainers, baseLeft, baseTop, *args):
    801         for cont in possibleContainers:
    802             if self.DoesTargetFit(cont, baseLeft, baseTop):
    803                 numTargets = len([ child for child in cont.children if isinstance(child, TargetInBar) ])
    804                 return (cont, numTargets)
    805 
    806         return (None, 0)
    807 
    808     def AdjustRowSize(self):
    809         if self.IsObserving():
    810             return
    811         targetPar = getattr(self, 'targetPar', None)
    812         if targetPar is None:
    813             return
    814         blue.pyos.synchro.Yield()
    815         horizontally = settings.user.ui.Get('alignHorizontally', 1)
    816         if horizontally:
    817             for eachRow in targetPar.rows:
    818                 if not eachRow.display:
    819                     continue
    820                 if self.overrideBarHeight is None:
    821                     maxHeight = self.GetRowMaxHeight(eachRow, MIN_TARGET_HEIHT)
    822                     uicore.animations.MorphScalar(eachRow, 'height', eachRow.height, maxHeight + TARGET_PADDING, duration=0.3)
    823                 else:
    824                     eachRow.height = self.overrideBarHeight
    825 
    826         else:
    827             maxLineHeights = self.GetLinesMaxHeights()
    828             for lineIdx, values in maxLineHeights.iteritems():
    829                 maxH = max([ h for h, targetID in values ])
    830                 maxH = maxH
    831                 for h, targetID in values:
    832                     target = self.GetTarget(targetID)
    833                     if target:
    834                         padBottom = maxH - target.height
    835                         uicore.animations.MorphScalar(target, 'padBottom', target.padBottom, padBottom, duration=0.5)
    836 
    837     def GetRowMaxHeight(self, targetRow, minSize, *args):
    838         horizontally = settings.user.ui.Get('alignHorizontally', 1)
    839         if horizontally:
    840             maxHeight = max([minSize] + [ target.height for target in targetRow.children if isinstance(target, TargetInBar) ])
    841             return maxHeight
    842 
    843     def GetLinesMaxHeights(self, *args):
    844         heightByLineIdx = {}
    845         for rowKey, targetIDs in self.rowDict.iteritems():
    846             if not targetIDs:
    847                 continue
    848             for lineIdx, targetID in enumerate(targetIDs):
    849                 target = self.GetTarget(targetID)
    850                 if targetID:
    851                     heightList = heightByLineIdx.get(lineIdx, [])
    852                     heightList.append((target.height + TARGET_PADDING, targetID))
    853                     heightByLineIdx[lineIdx] = heightList
    854 
    855         return heightByLineIdx
    856 
    857     def DoesTargetFit(self, cont, baseLeft, baseTop, idx = None, excludedTargetID = None, *args):
    858         horizontally = settings.user.ui.Get('alignHorizontally', 1)
    859         if horizontally:
    860             parSize = uicore.desktop.width - baseLeft
    861             commonTargetSize = MAX_TARGET_WIDTH
    862         else:
    863             parSize = uicore.desktop.height - baseTop
    864             commonTargetSize = AVG_TARGET_HEIGHT
    865         numTargets = 0
    866         for child in cont.children[:idx]:
    867             if not isinstance(child, TargetInBar):
    868                 continue
    869             if child.id == excludedTargetID:
    870                 continue
    871             numTargets += 1
    872 
    873         totalSizeOfTargets = numTargets * commonTargetSize
    874         return totalSizeOfTargets + commonTargetSize < parSize
    875 
    876     def OnMoveTarget(self, target, *args):
    877         if settings.user.ui.Get('targetPositionLocked', 0):
    878             return
    879         targetPar = getattr(self, 'targetPar', None)
    880         if targetPar is None or targetPar.destroyed:
    881             return targetPar
    882         wnd, pl, pt, pw, ph = self.GetTargetLayerAbsolutes()
    883         tl, tt, tw, th = targetPar.GetAbsolute()
    884         clipper = (tl - 10,
    885          tt - 5,
    886          tl + tw + 20,
    887          tt + th + 10)
    888         uthread.new(self.DoRepositionDrag, target, clipper)
    889 
    890     def DoRepositionDrag(self, myTarget, cursorClipper):
    891         if uicore.uilib.leftbtn and uicore.uilib.mouseOver == myTarget.barAndImageCont:
    892             uicore.uilib.ClipCursor(*cursorClipper)
    893         else:
    894             return
    895         horizontalAlign = settings.user.ui.Get('alignHorizontally', True)
    896         repositionLine = Line(align=uiconst.TORIGHT_NOPUSH, weight=2, color=(1, 1, 1, 0.5))
    897         oldParentRowOrColumn = getattr(myTarget.parent, 'numRow', None)
    898         if oldParentRowOrColumn is None:
    899             for rowKey, row in self.rowDict.iteritems():
    900                 if myTarget.id in row:
    901                     oldParentRowOrColumn = rowKey
    902                     break
    903 
    904         myTarget.opacity = 0.3
    905         myTarget.isDragging = True
    906         targetSvc = sm.GetService('target')
    907         targetPar = getattr(self, 'targetPar', None)
    908         if not targetPar:
    909             return
    910         myRow = None
    911         posIndex = 0
    912         while uicore.uilib.leftbtn:
    913             (originX, originY), (toLeft, toTop) = targetSvc.GetOriginPosition(getDirection=1)
    914             lessThanAll = True
    915             for row in targetPar.children:
    916                 if not isinstance(row, Container):
    917                     continue
    918                 tl, tt, tw, th = row.GetAbsolute()
    919                 if tl - 2 <= uicore.uilib.x <= tl + tw + 2 and tt - 2 <= uicore.uilib.y <= tt + th + 2:
    920                     myRow = row
    921                     break
    922 
    923             if not myRow:
    924                 blue.pyos.synchro.Yield()
    925                 continue
    926             counter = 0
    927             mouseX = uicore.uilib.x
    928             mouseY = uicore.uilib.y
    929             for target in myRow.children:
    930                 if not isinstance(target, TargetInBar):
    931                     continue
    932                 counter += 1
    933                 tl, tt, tw, th = target.GetAbsolute()
    934                 isOverTarget = tl <= mouseX < tl + tw and tt <= mouseY < tt + th
    935                 if isOverTarget:
    936                     if horizontalAlign:
    937                         if toLeft:
    938                             repositionLine.SetAlign(uiconst.TORIGHT_NOPUSH)
    939                             posIndex = counter
    940                         else:
    941                             posIndex = counter - 1
    942                             repositionLine.SetAlign(uiconst.TOLEFT_NOPUSH)
    943                     elif toTop:
    944                         posIndex = counter
    945                         repositionLine.SetAlign(uiconst.TOBOTTOM_NOPUSH)
    946                     else:
    947                         posIndex = counter - 1
    948                         repositionLine.SetAlign(uiconst.TOTOP_NOPUSH)
    949                     repositionLine.SetParent(myRow)
    950                     repositionLine.SetOrder(posIndex)
    951                     lessThanAll = False
    952                     break
    953 
    954             if lessThanAll:
    955                 posIndex = -1
    956                 if horizontalAlign:
    957                     if toLeft:
    958                         if originX < mouseX:
    959                             posIndex = 0
    960                         repositionLine.SetAlign(uiconst.TORIGHT_NOPUSH)
    961                     else:
    962                         if originX > mouseX:
    963                             posIndex = 0
    964                         repositionLine.SetAlign(uiconst.TOLEFT_NOPUSH)
    965                 elif toTop:
    966                     if originY < mouseY:
    967                         posIndex = 0
    968                     repositionLine.SetAlign(uiconst.TOBOTTOM_NOPUSH)
    969                 else:
    970                     if originY > mouseY:
    971                         posIndex = 0
    972                     repositionLine.SetAlign(uiconst.TOTOP_NOPUSH)
    973                 repositionLine.SetParent(myRow)
    974                 repositionLine.SetOrder(posIndex)
    975             self.ColorRepositionLine(myRow, myTarget.id, repositionLine)
    976             blue.pyos.synchro.Yield()
    977 
    978         uicore.uilib.UnclipCursor()
    979         if myRow:
    980             doAddTarget = True
    981             if oldParentRowOrColumn is not None:
    982                 old = self.rowDict.get(oldParentRowOrColumn, [])
    983                 if myTarget.id in old:
    984                     targetsCurrentPos = (posIndex, posIndex - 1)
    985                     if oldParentRowOrColumn == myRow.numRow:
    986                         oldIndex = old.index(myTarget.id)
    987                         if oldIndex in targetsCurrentPos:
    988                             doAddTarget = False
    989                         elif oldIndex < posIndex:
    990                             posIndex -= 1
    991                     if doAddTarget:
    992                         old.remove(myTarget.id)
    993                 self.rowDict[oldParentRowOrColumn] = old
    994             if doAddTarget:
    995                 posIndex = max(-1, posIndex)
    996                 newRow = self.rowDict.get(myRow.numRow, [])
    997                 if posIndex < 0:
    998                     newRow.append(myTarget.id)
    999                 else:
   1000                     newRow.insert(posIndex, myTarget.id)
   1001                 self.rowDict[myRow.numRow] = newRow
   1002         myTarget.isDragging = False
   1003         repositionLine.Close()
   1004         myTarget.opacity = 1.0
   1005         targetSvc.ArrangeTargets()
   1006 
   1007     def ColorRepositionLine(self, myRow, excludedTargetID, repositionLine, *args):
   1008         baseLeft, baseTop = self.GetBaseLeftAndTop()
   1009         if self.DoesTargetFit(myRow, baseLeft, baseTop, excludedTargetID=excludedTargetID):
   1010             repositionLine.SetRGBA(1, 1, 1, 0.5)
   1011         else:
   1012             repositionLine.SetRGBA(1, 0.5, 0, 0.5)
   1013 
   1014     def SelectNextTarget(self):
   1015         activeID = self.GetActiveTargetID()
   1016         if activeID is None:
   1017             return
   1018         nextTarget = self.FindNextTarget(activeID, direction=1)
   1019         if nextTarget:
   1020             self._SetSelected(nextTarget)
   1021 
   1022     def SelectPrevTarget(self):
   1023         activeID = self.GetActiveTargetID()
   1024         if activeID is None:
   1025             return
   1026         prevTarget = self.FindNextTarget(activeID, direction=-1)
   1027         if prevTarget:
   1028             self._SetSelected(prevTarget)
   1029 
   1030     def FindNextTarget(self, activeID, direction = 1):
   1031         allTargetIDsInOrder = self.GetSortedTargetList()
   1032         if not allTargetIDsInOrder:
   1033             return None
   1034         elif activeID not in allTargetIDsInOrder:
   1035             return allTargetIDsInOrder[0]
   1036         activeIndex = allTargetIDsInOrder.index(activeID)
   1037         if direction == 1:
   1038             if activeIndex == len(allTargetIDsInOrder) - 1:
   1039                 return allTargetIDsInOrder[0]
   1040             return allTargetIDsInOrder[activeIndex + 1]
   1041         else:
   1042             return allTargetIDsInOrder[activeIndex - 1]
   1043 
   1044     def GetSortedTargetList(self, *args):
   1045         sortedLists = []
   1046         for rowKey, targetIDs in self.rowDict.iteritems():
   1047             validTargetIDs = filter(lambda x: x not in self.deadShipsBeingRemoved, targetIDs)
   1048             sortedLists.append((rowKey, validTargetIDs))
   1049 
   1050         sortedLists = SortListOfTuples(sortedLists)
   1051         allTargetIDsInOrder = []
   1052         for eachList in sortedLists:
   1053             allTargetIDsInOrder += eachList
   1054 
   1055         return allTargetIDsInOrder
   1056 
   1057     def _SetSelected(self, targetID):
   1058         self.stateSvc.SetState(targetID, state.selected, 1)
   1059         self.stateSvc.SetState(targetID, state.activeTarget, 1)
   1060 
   1061     def OnTargetLost(self, tid, reason):
   1062         (self.stateSvc.SetState(tid, state.targeted, 0), self.stateSvc.GetExclState(state.activeTarget))
   1063         if self.stateSvc.GetExclState(state.activeTarget) == tid:
   1064             self.stateSvc.SetState(tid, state.activeTarget, 0)
   1065         if tid in self.pendingTargets:
   1066             self.pendingTargets.remove(tid)
   1067             sm.GetService('audio').StopSoundLoop('TargetLocking', 'msg_TargetLocked_play')
   1068         self.LogInfo('OnTargetLost ', tid)
   1069         if reason == 'Exploding':
   1070             self.ClearDeadTarget(targetID=tid, reason=reason)
   1071         else:
   1072             self.ClearTarget(tid, reason=reason)
   1073 
   1074     def ClearDeadTarget(self, targetID, reason = None, *args):
   1075         if targetID in self.targetsByID and targetID not in self.deadShipsBeingRemoved:
   1076             try:
   1077                 self.deadShipsBeingRemoved.add(targetID)
   1078                 target_in_bar = self.targetsByID[targetID]
   1079                 self.SetFirstTargetActiveIfNoActive()
   1080                 target_in_bar.anim_disappear()
   1081             except:
   1082                 self.LogException()
   1083             finally:
   1084                 self.ClearTarget(targetID, reason=reason)
   1085 
   1086     def OnTargetByOther(self, otherID):
   1087         self.stateSvc.SetState(otherID, state.threatTargetsMe, 1)
   1088         if otherID in self.targetedBy:
   1089             self.LogError('already targeted by', otherID)
   1090         else:
   1091             self.AddTargetedBy(otherID)
   1092         bp = sm.GetService('michelle').GetBallpark()
   1093         if bp is not None:
   1094             slimItem = bp.GetInvItem(otherID)
   1095             if slimItem is not None:
   1096                 if slimItem.ownerID:
   1097                     self.ownerToShipIDCache[slimItem.ownerID] = otherID
   1098         if bp is None or slimItem is None:
   1099             if otherID not in self.pendingTargeters:
   1100                 self.pendingTargeters.append(otherID)
   1101             return
   1102         if otherID in self.pendingTargeters:
   1103             self.pendingTargeters.remove(otherID)
   1104         tgts = self.GetTargets().keys() + self.targeting.keys()
   1105         if otherID != session.shipid and otherID not in tgts and otherID not in self.autoTargeting and min(settings.user.ui.Get('autoTargetBack', 0), sm.GetService('godma').GetItem(session.charid).maxLockedTargets, sm.GetService('godma').GetItem(session.shipid).maxLockedTargets) > len(tgts):
   1106             if len(self.autoTargeting) < settings.user.ui.Get('autoTargetBack', 0):
   1107                 self.autoTargeting.append(otherID)
   1108                 uthread.pool('TargetManages::OnTargetByOther-->LockTarget', self.LockTarget, otherID, autotargeting=1)
   1109 
   1110     def GetTarget(self, targetID):
   1111         return self.targetsByID.get(targetID, None)
   1112 
   1113     def OnTargetByOtherLost(self, otherID, reason):
   1114         self.stateSvc.SetState(otherID, state.threatTargetsMe, 0)
   1115         if otherID in self.pendingTargeters:
   1116             self.pendingTargeters.remove(otherID)
   1117         try:
   1118             self.RemoveTargetedBy(otherID)
   1119         except ValueError:
   1120             self.LogInfo('was not targeted by', otherID)
   1121             sys.exc_clear()
   1122 
   1123         bp = sm.GetService('michelle').GetBallpark()
   1124         if bp is not None:
   1125             slimItem = bp.GetInvItem(otherID)
   1126             if slimItem is not None:
   1127                 if slimItem.ownerID and slimItem.ownerID in self.ownerToShipIDCache:
   1128                     del self.ownerToShipIDCache[slimItem.ownerID]
   1129 
   1130     def OnTargetClear(self, targetsToSkip = None):
   1131         for tid in self.targetsByID.keys():
   1132             if targetsToSkip and tid in targetsToSkip:
   1133                 continue
   1134             self.stateSvc.SetState(tid, state.activeTarget, 0)
   1135             self.stateSvc.SetState(tid, state.targeting, 0)
   1136             self.stateSvc.SetState(tid, state.targeted, 0)
   1137             self.CancelTargetOrder()
   1138             self.ClearTarget(tid)
   1139 
   1140     def GetTargetedBy(self):
   1141         return self.targetedBy
   1142 
   1143     def IsTargeted(self):
   1144         return len(self.targetedBy) > 0
   1145 
   1146     def GetTargeting(self):
   1147         return self.targeting.keys()
   1148 
   1149     def ClearTarget(self, tid, reason = None):
   1150         removedTarget = False
   1151         if tid in self.targetsByID:
   1152             t = self.targetsByID[tid]
   1153             del self.targetsByID[tid]
   1154             groupID = self.groupIdByTarget[tid]
   1155             del self.groupIdByTarget[tid]
   1156             self.deadShipsBeingRemoved.discard(tid)
   1157             t.Close()
   1158             self.ArrangeTargets()
   1159             removedTarget = True
   1160             sm.ScatterEvent('OnClientEvent_UnlockItem', tid)
   1161         if tid in self.pendingTargets:
   1162             self.pendingTargets.remove(tid)
   1163         if removedTarget and not self.targetsByID:
   1164             if self.origin is not None and not self.origin.destroyed:
   1165                 self.origin.Close()
   1166                 self.origin = None
   1167             self.stateSvc.SetState(tid, state.activeTarget, 0)
   1168         for rowKey, row in self.rowDict.iteritems():
   1169             if tid in row:
   1170                 row.remove(tid)
   1171                 self.rowDict[rowKey] = row
   1172                 break
   1173 
   1174         if self.targetsByID:
   1175             self.SetFirstTargetActiveIfNoActive()
   1176 
   1177     def SetFirstTargetActiveIfNoActive(self):
   1178         active = self.stateSvc.GetExclState(state.activeTarget)
   1179         if active is None and self.targetsByID:
   1180             allTargetIDsInOrder = self.GetSortedTargetList()
   1181             if allTargetIDsInOrder:
   1182                 targetToSelect = allTargetIDsInOrder[0]
   1183                 self.stateSvc.SetState(targetToSelect, state.activeTarget, 1)
   1184 
   1185     def GetTargetingStartTime(self, targetID):
   1186         return self.targeting.get(targetID, None)
   1187 
   1188     def BeingTargeted(self, targetID):
   1189         return targetID in self.targeting
   1190 
   1191     def StartLockTarget(self, tid, autotargeting = 0):
   1192         if tid in self.GetTargets():
   1193             self.ClearTarget(tid)
   1194             self.stateSvc.SetState(tid, state.targeting, 0)
   1195             sm.GetService('audio').StopSoundLoop('TargetLocking')
   1196         if tid in self.targeting:
   1197             return
   1198         self.LogInfo('targetMgr: Locking Target for ', tid)
   1199         sm.GetService('audio').StartSoundLoop('TargetLocking')
   1200         sm.ScatterEvent('OnTargetLockStarted', GetBallparkRecord(tid))
   1201         self.stateSvc.SetState(tid, state.targeting, 1)
   1202         self.targeting[tid] = blue.os.GetSimTime()
   1203 
   1204     def FailLockTarget(self, tid):
   1205         if tid in self.targeting:
   1206             del self.targeting[tid]
   1207         if tid in self.autoTargeting:
   1208             self.autoTargeting.remove(tid)
   1209         self.stateSvc.SetState(tid, state.targeted, 0)
   1210         self.stateSvc.SetState(tid, state.targeting, 0)
   1211         sm.GetService('audio').StopSoundLoop('TargetLocking')
   1212         sm.ScatterEvent('OnFailLockTarget', tid)
   1213 
   1214     def CancelAddTarget(self, tid):
   1215         self.stateSvc.SetState(tid, state.targeted, 0)
   1216         self.stateSvc.SetState(tid, state.targeting, 0)
   1217         sm.GetService('audio').StopSoundLoop('TargetLocking')
   1218         sm.ScatterEvent('OnFailLockTarget', tid)
   1219         self.GetDogmaLM().CancelAddTarget(tid)
   1220 
   1221     def TryLockTarget(self, itemID):
   1222         if itemID in self.GetTargeting() or itemID in self.targetsByID:
   1223             return
   1224         slimItem = GetBallparkRecord(itemID)
   1225         if not slimItem:
   1226             return
   1227         if slimItem.groupID in const.nonTargetableGroups:
   1228             return
   1229         if itemID == session.shipid and session.shipid == session.structureid:
   1230             return
   1231         self.LockTarget(itemID)
   1232 
   1233     def GetDistanceToTarget(self, itemID):
   1234         if session.shipid is None or session.solarsystemid is None:
   1235             raise RuntimeError('Failed to get distance to target. Must be in ship in space (shipID: %s, solarSystemID: %s).' % (session.shipid, session.solarsystemid))
   1236         if itemID is None or itemID == session.shipid or idCheckers.IsUniverseCelestial(itemID):
   1237             raise ValueError('Failed to get distance to target. Target item is not valid (itemID: %s).' % itemID)
   1238         bp = sm.GetService('michelle').GetBallpark()
   1239         if bp is None:
   1240             return RuntimeError('Failed to get distance to target. Ballpark is not available.')
   1241         otherBall = bp and bp.GetBall(itemID) or None
   1242         if otherBall is None:
   1243             return RuntimeError('Failed to get distance to target. Target ball is not available (itemID: %s).' % itemID)
   1244         distanceToTarget = max(0, otherBall.surfaceDist)
   1245         return distanceToTarget
   1246 
   1247     def GetMaxTargetRange(self):
   1248         shipItem = sm.GetService('godma').GetStateManager().GetItem(session.shipid)
   1249         if shipItem is None:
   1250             return
   1251         return shipItem.maxTargetRange
   1252 
   1253     def IsInTargetingRange(self, itemID):
   1254         try:
   1255             distanceToTarget = self.GetDistanceToTarget(itemID)
   1256         except (RuntimeError, ValueError):
   1257             return False
   1258 
   1259         maxTargetRange = self.GetMaxTargetRange()
   1260         if maxTargetRange is None:
   1261             return False
   1262         return distanceToTarget < maxTargetRange
   1263 
   1264     def LockTarget(self, tid, autotargeting = 0):
   1265         try:
   1266             self._LockTarget(tid, autotargeting)
   1267         except Exception:
   1268             self.FailLockTarget(tid)
   1269             sys.exc_clear()
   1270 
   1271     def _LockTarget(self, tid, autotargeting = 0):
   1272         self.StartLockTarget(tid, autotargeting)
   1273         try:
   1274             flag, targetList = self.GetDogmaLM().AddTarget(tid)
   1275             if not flag:
   1276                 self.OnTargetAdded(tid)
   1277         except UserError as e:
   1278             self.FailLockTarget(tid)
   1279             if autotargeting:
   1280                 sys.exc_clear()
   1281                 return
   1282             if e.msg in ('DeniedShipChanged', 'TargetingAttemptCancelled'):
   1283                 sys.exc_clear()
   1284                 return
   1285             prompt_player_fsd_dialog(e.msg, e.dict)
   1286             raise
   1287 
   1288         self.LogInfo('targetMgr: Locking Target for ', tid, ' done')
   1289 
   1290     def UnlockTarget(self, tid):
   1291         if tid in self.targetsByID:
   1292             self.GetDogmaLM().RemoveTarget(tid)
   1293             self.RemoveFromTeam(tid)
   1294 
   1295     def UnlockTargets(self):
   1296         targetsToUnlock = self.targetsByID.keys()
   1297         if targetsToUnlock:
   1298             self.GetDogmaLM().RemoveTargets(targetsToUnlock)
   1299             for tID in targetsToUnlock:
   1300                 self.RemoveFromTeam(tID)
   1301 
   1302     def ToggleLockTarget(self, targetID):
   1303         if self.IsTarget(targetID):
   1304             self.UnlockTarget(targetID)
   1305         else:
   1306             self.TryLockTarget(targetID)
   1307 
   1308     def LockTargetPassively(self, tid, smb):
   1309         self.StartLockTarget(tid, 0)
   1310         try:
   1311             smb.OnStateChange(tid, state.activeTarget, True)
   1312         except UserError as e:
   1313             self.FailLockTarget(tid)
   1314             if e.msg == 'DeniedShipChanged':
   1315                 sys.exc_clear()
   1316                 return
   1317             raise
   1318 
   1319         self.LogInfo('targetMgr: PassiveLocking Target for ', tid, ' done')
   1320 
   1321     def ClearTargets(self):
   1322         self.GetDogmaLM().ClearTargets()
   1323 
   1324     def GetActiveTargetID(self):
   1325         selected = self.stateSvc.GetExclState(state.activeTarget)
   1326         if selected and selected in self.targetsByID:
   1327             return selected
   1328 
   1329     def GetTargets(self):
   1330         return self.targetsByID
   1331 
   1332     def IsTarget(self, targetID):
   1333         return targetID in self.targetsByID
   1334 
   1335     def GetNumAdditionalTargetsAllowed(self):
   1336         maxTargets = sm.GetService('godma').GetItem(session.shipid).maxLockedTargets
   1337         currTargets = len(self.GetTargets())
   1338         return maxTargets - currTargets
   1339 
   1340     def FindClosestEntity(self, targetPriority = None):
   1341         return get_closest_entity_target(targetPriority)
   1342 
   1343     def AddTargetedBy(self, enemyID):
   1344         self.targetedBy.append(enemyID)
   1345         sm.ScatterEvent('OnClientEvent_TargetedByChanged', enemyID)
   1346 
   1347     def RemoveTargetedBy(self, enemyID):
   1348         self.targetedBy.remove(enemyID)
   1349         sm.ScatterEvent('OnClientEvent_TargetedByChanged', enemyID)
   1350 
   1351     def ClearTargetedBy(self):
   1352         self.targetedBy = []
   1353         sm.ScatterEvent('OnClientEvent_TargetedByChanged', None)