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

shipUI.py (48744B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\inflight\shipHud\shipUI.py
      2 import blue
      3 import dogma.const as dogma_const
      4 import eve.client.script.ui.shared.pointerTool.pointerToolConst as pConst
      5 import log
      6 import telemetry
      7 import threadutils
      8 import trinity
      9 import uthread
     10 from carbon.client.script.environment.AudioUtil import PlaySound
     11 from carbonui import uiconst, Align, PickState
     12 from carbonui.control.buttonIcon import ButtonIcon
     13 from carbonui.control.layer import LayerCore
     14 from carbonui.primitives.container import Container
     15 from carbonui.primitives.sprite import Sprite
     16 from carbonui.uianimations import animations
     17 from carbonui.uicore import uicore
     18 from carbonui.util import colorblind
     19 from carbonui.loggers.buttonLogger import log_button_clicked
     20 from eve.client.script.parklife import states
     21 from eve.client.script.ui.control.utilMenu import UtilMenu
     22 from eve.client.script.ui.inflight.notifySettingsWindow import NotifySettingsWindow
     23 from eve.client.script.ui.inflight.radialMenuScanner import RadialMenuScanner
     24 from eve.client.script.ui.inflight.shipAlert import ShipAlertContainer
     25 from eve.client.script.ui.inflight.shipHud.action_indication.hud_action_indication_container import HudActionIndicationContainer
     26 from eve.client.script.ui.inflight.shipHud.activeShipController import ActiveShipController
     27 from eve.client.script.ui.inflight.shipHud.buffBarContainer import BuffBarContainer
     28 from eve.client.script.ui.inflight.shipHud.capacitorContainer import CapacitorContainer
     29 from eve.client.script.ui.inflight.shipHud.heatGauges import HeatGauges
     30 from eve.client.script.ui.inflight.shipHud.hpGauges import HPGauges
     31 from eve.client.script.ui.inflight.shipHud.hudButtonsCont import HudButtonsCont
     32 from eve.client.script.ui.inflight.shipHud.hudReadout import HudReadout
     33 from eve.client.script.ui.inflight.shipHud.hudShape import HUDShape, StructureHUDShape
     34 from eve.client.script.ui.inflight.shipHud.releaseControlBtn import ReleaseControlBtn
     35 from eve.client.script.ui.inflight.shipHud.shipHudConst import SHIP_UI_HEIGHT, SHIP_UI_WIDTH
     36 from eve.client.script.ui.inflight.shipHud.slotsContainer import SlotsContainer
     37 from eve.client.script.ui.inflight.shipHud.speedGauge import SpeedGauge
     38 from eve.client.script.ui.inflight.shipSafetyButton import SafetyButton
     39 from eve.client.script.ui.inflight.shipscan import CargoScan, ShipScan
     40 from eve.client.script.ui.inflight.squadrons.shipFighterState import GetShipFighterState
     41 from eve.client.script.ui.inflight.squadrons.squadronsUI import SquadronsUI
     42 from eve.client.script.ui.inflight.stanceButtons import StanceButtons
     43 from eve.client.script.ui.moonmining.moonminingExtraHudBtns import MoonminingExtraHudBtns
     44 from eve.client.script.ui.oremining.mining_scan_buttons import MiningScanButtons
     45 from eve.client.script.ui.services.menuSvcExtras import movementFunctions
     46 from eve.client.script.ui.util.uix import GetSlimItemName
     47 from eve.client.script.ui.view import overlaySettings
     48 from eve.client.script.ui.view.viewStateConst import ViewOverlay
     49 from eve.client.script.util.settings import IsShipHudTopAligned, SetShipHudTopAligned
     50 from eve.common.script.sys.eveCfg import IsControllingStructure
     51 from gametime import GetDurationInClient
     52 from localization import GetByLabel
     53 from menu import MenuLabel
     54 from mining.client.mining_util import has_current_ship_integrated_mining_scanner
     55 from player_messaging.client.ui_message import prompt_player_fsd_dialog
     56 from sensorsuite.overlay.sitecompass import Compass
     57 from structures import IsDrillingPlatform
     58 from structures.types import IsFlexStructure
     59 from uihider import UiHiderMixin
     60 SLOTS_CONTAINER_LEFT = SHIP_UI_WIDTH / 2.0 + 70
     61 SLOTS_CONTAINER_TOP = -1
     62 SLOTS_CONTAINER_WIDTH = 512
     63 SLOTS_CONTAINER_HEIGHT = 152
     64 EWAR_CONTAINER_WIDTH = 480
     65 EWAR_CONTAINER_HEIGHT = 44
     66 
     67 class ShipUI(LayerCore):
     68     __notifyevents__ = ['OnShipScanCompleted',
     69      'OnJamStart',
     70      'OnJamEnd',
     71      'OnCargoScanComplete',
     72      'DoBallRemove',
     73      'OnSetDevice',
     74      'OnAssumeStructureControl',
     75      'OnRelinquishStructureControl',
     76      'OnUIRefresh',
     77      'ProcessPendingOverloadUpdate',
     78      'DoBallsRemove',
     79      'OnSetCameraOffset',
     80      'ProcessShipEffect',
     81      'OnStateChange',
     82      'OnDogmaAttributeChanged']
     83 
     84     def ApplyAttributes(self, attributes):
     85         self.shipuiReady = False
     86         self.fighterHudBinding = None
     87         LayerCore.ApplyAttributes(self, attributes)
     88         self.setupShipTasklet = None
     89         self.updateTasklet = None
     90         self.controller = ActiveShipController()
     91         self.controller.on_new_itemID.connect(self.OnShipChanged)
     92         self.compass = None
     93         self.stanceButtons = None
     94         self.moon_mining_buttons = None
     95         self.mining_overlay_buttons = None
     96         self.ResetSelf()
     97         colorblind.on_colorblind_mode_changed.connect(self.OnColorBlindModeChanged)
     98 
     99     def Close(self):
    100         LayerCore.Close(self)
    101         self.controller.Close()
    102 
    103     def OnSetCameraOffset(self, cameraOffset):
    104         if self.shipuiReady:
    105             self.UpdatePosition()
    106 
    107     def OnSetDevice(self):
    108         if self.shipuiReady:
    109             self.UpdatePosition()
    110 
    111     def UpdateUIScaling(self, value, oldValue):
    112         super(ShipUI, self).UpdateUIScaling(value, oldValue)
    113         self.OnUIRefresh()
    114 
    115     def OnUIRefresh(self):
    116         self.CloseView(recreate=False)
    117         self.OpenView()
    118 
    119     def OnColorBlindModeChanged(self):
    120         self.OnUIRefresh()
    121 
    122     @telemetry.ZONE_METHOD
    123     def ResetSelf(self):
    124         self.RemoveFighterHudBinding()
    125         self.safetyButton = None
    126         self.fighterHudBinding = None
    127         self.sr.selectedcateg = 0
    128         self.sr.pendingreloads = []
    129         self.sr.rampTimers = {}
    130         self.shipuiReady = False
    131         self.initing = None
    132         self.jammers = {}
    133         self.assumingdelay = None
    134         if self.updateTasklet is not None:
    135             self.updateTasklet.kill()
    136         self.updateTasklet = None
    137         if self.setupShipTasklet is not None:
    138             self.setupShipTasklet.kill()
    139         self.setupShipTasklet = None
    140         self.fighterCont = None
    141         self.Flush()
    142 
    143     def CheckSession(self, change):
    144         if sm.GetService('autoPilot').GetState():
    145             self.OnAutoPilotOn()
    146         else:
    147             self.OnAutoPilotOff()
    148 
    149     @telemetry.ZONE_METHOD
    150     def UpdatePosition(self):
    151         cameraOffset = sm.GetService('sceneManager').GetCameraOffset()
    152         halfWidth = uicore.desktop.width / 2
    153         baseOffset = -cameraOffset * halfWidth
    154         wndLeft = settings.char.windows.Get('shipuialignleftoffset', 0)
    155         maxRight, minLeft = self.GetShipuiOffsetMinMax()
    156         self.hudContainer.left = min(maxRight, max(minLeft, baseOffset + wndLeft))
    157         self.buffBarContainer.left = self.hudContainer.left
    158         if IsShipHudTopAligned():
    159             self.hudContainer.SetAlign(uiconst.CENTERTOP)
    160             self.buffBarContainer.SetAlign(uiconst.CENTERTOP)
    161         else:
    162             self.hudContainer.SetAlign(uiconst.CENTERBOTTOM)
    163             self.buffBarContainer.SetAlign(uiconst.CENTERBOTTOM)
    164         self._update_indicator_cont_top()
    165         if self.IsFightersDetached() and self.fighterCont:
    166             left, top = GetFightersDetachedPos()
    167             buttonWidth = 32
    168             left = min(left, uicore.desktop.width - buttonWidth)
    169             top = min(top, uicore.desktop.height - self.fighterCont.height)
    170             self.fighterCont.left = left
    171             self.fighterCont.top = top
    172             settings.char.ui.Set('fightersDetachedPosition', (left, top))
    173         self.AlignFighters()
    174         self.sr.shipAlertContainer.UpdatePosition()
    175 
    176     def _update_indicator_cont_top(self):
    177         if IsShipHudTopAligned():
    178             self.indicationContainer.align = Align.CENTERTOP
    179         else:
    180             self.indicationContainer.align = Align.CENTERBOTTOM
    181         self.indicationContainer.top = self.hudContainer.height + self.buffBarContainer.height + 8
    182 
    183     def MakeFighterHudBinding(self):
    184         self.RemoveFighterHudBinding()
    185         self.fighterHudBinding = trinity.CreatePythonBinding(uicore.uilib.bracketCurveSet, self, 'fighterContLeft', self.fighterCont, 'left')
    186 
    187     def RemoveFighterHudBinding(self):
    188         if self.fighterHudBinding:
    189             cs = uicore.uilib.bracketCurveSet
    190             cs.bindings.fremove(self.fighterHudBinding)
    191             self.fighterHudBinding = None
    192 
    193     def OnShipMouseDown(self, wnd, btn, *args):
    194         if btn != 0:
    195             return
    196         self.dragging = True
    197         if not self.hudContainer:
    198             return
    199         self.grab = [uicore.uilib.x, self.hudContainer.left]
    200         uthread.new(self.BeginDrag)
    201 
    202     def GetShipuiOffsetMinMax(self, *args):
    203         magicNumber = 275
    204         if self.CheckShipHasFighterBay():
    205             magicNumber = 300
    206         sidePanelsLeft, sidePanelsRight = uicore.layer.sidepanels.GetSideOffset()
    207         maxRight = uicore.desktop.width / 2 - self.slotsContainer.width / 2 - magicNumber - sidePanelsRight
    208         minLeft = -(uicore.desktop.width / 2 - 180) + sidePanelsLeft
    209         return (maxRight, minLeft)
    210 
    211     def OnToggleShipSelected(self, *args):
    212         if not self.CheckShipHasFighterBay():
    213             return
    214         if not self.IsFightersShown():
    215             self.DeselectShip()
    216             return
    217         x, y = self.grab
    218         currentX, currentY = uicore.uilib.x, self.hudContainer.left
    219         if x != currentX or y != currentY:
    220             return
    221         if uicore.uilib.Key(uiconst.VK_CONTROL):
    222             if movementFunctions.IsSelectedForNavigation(session.shipid):
    223                 self.DeselectShip()
    224             else:
    225                 self.SelectShip()
    226         else:
    227             self.SelectShip()
    228             self.fighterCont.ClearSelection()
    229 
    230     def SelectShip(self):
    231         movementFunctions.SelectForNavigation(session.shipid)
    232 
    233     def DeselectShip(self):
    234         movementFunctions.DeselectForNavigation(session.shipid)
    235 
    236     def OnStateChange(self, itemID, flag, flagState, *args):
    237         if not self.CheckShipHasFighterBay():
    238             return
    239         if flag == states.selectedForNavigation and itemID == session.shipid:
    240             if flagState:
    241                 self.ShowSelectionHilite()
    242             else:
    243                 self.HideSelectionHilite()
    244 
    245     def ShowSelectionHilite(self):
    246         if self.shipSelectHilight.display:
    247             return
    248         self.shipSelectHilight.display = True
    249         self.ringSprite.opacity = 0.2
    250         self.bracketSprite.opacity = 3.0
    251         uicore.animations.FadeTo(self.ringSprite, self.ringSprite.opacity, 1.0, duration=0.2)
    252         uicore.animations.FadeTo(self.bracketSprite, self.bracketSprite.opacity, 1.0, duration=0.2)
    253 
    254     def HideSelectionHilite(self):
    255         self.shipSelectHilight.display = False
    256 
    257     def OnShipMouseUp(self, wnd, btn, *args):
    258         if btn != 0:
    259             return
    260         if wnd is self.capacitorContainer:
    261             ui_name = getattr(self.capacitorContainer, 'uniqueUiName', None)
    262             if ui_name:
    263                 self.capacitorContainer.analyticID = ui_name
    264                 log_button_clicked(self.capacitorContainer)
    265         sm.StartService('ui').ForceCursorUpdate()
    266         self.dragging = False
    267 
    268     def BeginDrag(self, *args):
    269         cameraOffset = sm.GetService('sceneManager').GetCameraOffset()
    270         halfWidth = uicore.desktop.width / 2
    271         baseOffset = -cameraOffset * halfWidth
    272         while not self.hudContainer.destroyed and getattr(self, 'dragging', 0):
    273             uicore.uilib.SetCursor(uiconst.UICURSOR_DIVIDERADJUST)
    274             maxRight, minLeft = self.GetShipuiOffsetMinMax()
    275             grabMouseDiff = uicore.uilib.x - self.grab[0]
    276             combinedOffset = min(maxRight, max(minLeft, self.grab[1] + grabMouseDiff))
    277             dragOffset = combinedOffset - baseOffset
    278             if -8 <= dragOffset <= 8:
    279                 settings.char.windows.Set('shipuialignleftoffset', 0)
    280                 self.hudContainer.left = baseOffset
    281             else:
    282                 self.hudContainer.left = combinedOffset
    283                 settings.char.windows.Set('shipuialignleftoffset', dragOffset)
    284             self.buffBarContainer.left = self.hudContainer.left
    285             blue.pyos.synchro.SleepWallclock(1)
    286 
    287     def ConstructOverlayContainer(self):
    288         self.optionsCont = Container(parent=self.overlayContainer, name='optionsCont', pos=(190, 194, 16, 16), align=uiconst.TOPLEFT, state=uiconst.UI_PICKCHILDREN)
    289         self.moduleToggleCont = Container(parent=self.overlayContainer, name='moduleToggleCont', pos=(self.moduleToggleContLeft,
    290          170,
    291          24,
    292          24), align=uiconst.TOPLEFT, state=uiconst.UI_PICKCHILDREN)
    293         if not IsControllingStructure():
    294             self.stopButton = StopButton(parent=self.overlayContainer, align=uiconst.TOPLEFT, controller=self.controller, left=75, top=155)
    295             self.maxspeedButton = MaxSpeedButton(parent=self.overlayContainer, align=uiconst.TOPLEFT, controller=self.controller, left=168, top=155, uniqueUiName=pConst.UNIQUE_NAME_FULL_SPEED_BTN)
    296 
    297     @telemetry.ZONE_METHOD
    298     def OnOpenView(self, **kwargs):
    299         self.ResetSelf()
    300         self.state = uiconst.UI_HIDDEN
    301         self.hudContainer = HudContainer(name='hudContainer', parent=self, controller=self.controller, align=uiconst.CENTERBOTTOM, width=SHIP_UI_WIDTH, height=SHIP_UI_HEIGHT)
    302         self.centerParent = CenterHudContainer(name='centerParent', parent=self.hudContainer, align=uiconst.CENTERBOTTOM, pos=(0, 0, 185, 200))
    303         self.overlayContainer = Container(parent=self.centerParent, name='overlayContainer', pos=(0, 0, 256, 256), align=uiconst.CENTER, state=uiconst.UI_PICKCHILDREN, idx=0)
    304         self.ConstructOverlayContainer()
    305         if IsControllingStructure():
    306             shipShape = StructureHUDShape(parent=self.centerParent, align=uiconst.CENTER)
    307         else:
    308             shipShape = HUDShape(parent=self.centerParent, align=uiconst.CENTER)
    309         self.shipuiMainShape = shipShape.shipuiMainShape
    310         self.capacitorContainer = CapacitorContainer(parent=self.centerParent, align=uiconst.CENTER, top=-1, controller=self.controller, uniqueUiName=pConst.UNIQUE_NAME_CAPACITOR)
    311         self.capacitorContainer.OnMouseDown = (self.OnShipMouseDown, self.capacitorContainer)
    312         self.capacitorContainer.OnMouseUp = (self.OnShipMouseUp, self.capacitorContainer)
    313         self.capacitorContainer.OnClick = self.OnToggleShipSelected
    314         heatPicker = Container(name='heatPicker', parent=self.centerParent, align=uiconst.CENTER, width=160, height=160, pickRadius=43, state=uiconst.UI_NORMAL)
    315         self.heatGauges = HeatGauges(parent=heatPicker, align=uiconst.CENTERTOP, controller=self.controller)
    316         self.hpGauges = HPGauges(name='healthGauges', parent=self.centerParent, align=uiconst.CENTER, pos=(0, -37, 148, 74), controller=self.controller, uniqueUiName=pConst.UNIQUE_NAME_SHIP_HEALTH)
    317         if IsControllingStructure():
    318             ReleaseControlBtn(parent=self.centerParent, top=29, align=uiconst.CENTERBOTTOM, func=self.ReleaseStructureControl, structureTypeID=self.controller.GetTypeID(), uniqueUiName=pConst.UNIQUE_NAME_RELEASE_CONTROL)
    319         else:
    320             self.speedGauge = SpeedGauge(parent=self.centerParent, top=29, align=uiconst.CENTERBOTTOM, controller=self.controller)
    321         if is_compass_enabled():
    322             self.compass = self.CreateCompass()
    323         self.shipSelectHilight = Container(name='navSelectHilight', parent=self.centerParent, align=uiconst.CENTER, state=uiconst.UI_DISABLED, width=self.shipSelectHilightSize, height=self.shipSelectHilightSize)
    324         self.ringSprite = Sprite(bgParent=self.shipSelectHilight, texturePath='res:/UI/Texture/classes/ShipUI/Fighters/selectionRingLarge.png')
    325         self.bracketSprite = Sprite(bgParent=self.shipSelectHilight, texturePath='res:/UI/Texture/classes/ShipUI/Fighters/selectionBracketLarge.png')
    326         self.shipSelectHilight.display = False
    327         self.slotsContainer = SlotsContainer(parent=self.hudContainer, pos=(self.slotsContLeft,
    328          SLOTS_CONTAINER_TOP,
    329          SLOTS_CONTAINER_WIDTH,
    330          SLOTS_CONTAINER_HEIGHT), align=uiconst.CENTERLEFT, state=uiconst.UI_PICKCHILDREN, controller=self.controller)
    331         self.stanceButtons = StanceButtons(parent=self.hudContainer, pos=(self.stanceButtonsLeft,
    332          1,
    333          40,
    334          120), name='stanceButtons', align=uiconst.CENTERLEFT, state=uiconst.UI_PICKCHILDREN, buttonSize=36)
    335         self.stanceButtons.Hide()
    336         self.moon_mining_buttons = MoonminingExtraHudBtns(parent=self.hudContainer, pos=(self.stanceButtonsLeft,
    337          -14,
    338          60,
    339          120), align=uiconst.CENTERLEFT, state=uiconst.UI_PICKCHILDREN)
    340         self.moon_mining_buttons.Hide()
    341         self.mining_overlay_buttons = MiningScanButtons(parent=self.hudContainer, left=self.stanceButtonsLeft, align=Align.CENTERLEFT, display=False, pickState=PickState.ON)
    342         self.hudButtons = HudButtonsCont(name='hudButtons', parent=self.hudContainer, align=uiconst.CENTERRIGHT, left=self.hudButtonsLeft, top=15)
    343         self.buffBarContainer = BuffBarContainer(parent=self, align=uiconst.CENTERBOTTOM, top=SHIP_UI_HEIGHT, height=EWAR_CONTAINER_HEIGHT, width=EWAR_CONTAINER_WIDTH)
    344         self.sr.shipAlertContainer = ShipAlertContainer(parent=self.hudContainer)
    345         self.indicationContainer = HudActionIndicationContainer(parent=self.hudContainer, name='indicationContainer', align=uiconst.CENTERTOP)
    346         self.safetyButton = SafetyButton(parent=self.overlayContainer, left=self.safetyButtonLeft, top=self.safetyButtonTop, uniqueUiName=pConst.UNIQUE_NAME_SAFETY_BUTTON)
    347         self.ConstructReadoutCont()
    348         self.settingsMenu = UtilMenu(name='hudSettingsMenu', uniqueUiName=pConst.UNIQUE_NAME_HUD_SETTINGS, menuAlign=uiconst.BOTTOMLEFT, parent=self.optionsCont, align=uiconst.TOPLEFT, GetUtilMenu=self.GetHUDOptionMenu, pos=(0, 0, 16, 16), texturePath='res:/UI/Texture/Icons/73_16_50.png', hint=GetByLabel('UI/Inflight/Options'))
    349         _oldMouseUp = self.settingsMenu.OnMouseUp
    350         menu = self.settingsMenu
    351 
    352         def _SettingsMenuOnMouseUp(*args):
    353             ui_name = getattr(menu, 'uniqueUiName', None)
    354             if ui_name:
    355                 menu.analyticID = ui_name
    356                 try:
    357                     log_button_clicked(menu)
    358                 except Exception:
    359                     pass
    360 
    361             if _oldMouseUp:
    362                 return _oldMouseUp(*args)
    363 
    364         menu.OnMouseUp = _SettingsMenuOnMouseUp
    365         self.moduleToggleBtn = ButtonIcon(name='moduleToggleBtn', parent=self.moduleToggleCont, align=uiconst.TOPLEFT, width=24, height=24, iconSize=24, texturePath='res:/UI/Texture/classes/ShipUI/Fighters/toggleModules_Up.png', func=self.OnToggleHudModules, hint=GetByLabel('UI/Inflight/HUDOptions/ClickToToggle'), uniqueUiName=pConst.UNIQUE_NAME_FIGHTERS_TOGGLE)
    366         self.moduleToggleBtn.display = False
    367         self.moduleToggleBtn.GetMenu = self.GetModuleToggleBtnMenu
    368         self.DrawFighters()
    369         self.hudContainer.state = uiconst.UI_PICKCHILDREN
    370         self.UpdatePosition()
    371         self.UpdateButtonPositions()
    372         self.shipuiReady = True
    373         self.SetupShip()
    374         Sprite(parent=self.centerParent, name='bgCircle', texturePath='res:/UI/Texture/classes/ShipUI/mainBGCircle.png', color=(0, 0, 0, 0.3), pos=(0, 0, 160, 160), align=uiconst.CENTER, state=uiconst.UI_DISABLED)
    375 
    376     def ReleaseStructureControl(self, structureTypeID):
    377         structureControlSvc = sm.GetService('structureControl')
    378         if IsFlexStructure(structureTypeID):
    379             structureControlSvc.BoardPreviousShip()
    380         else:
    381             return structureControlSvc.ReleaseControl()
    382 
    383     def DrawFighters(self):
    384         if self.fighterCont and not self.fighterCont.destroyed:
    385             self.fighterCont.Close()
    386         self.fighterCont = SquadronsUI(name='fighters', parent=self, state=uiconst.UI_PICKCHILDREN, parentFunc=self.OnToggleFightersDetached)
    387         self.AlignFighters()
    388         if self.IsFightersDetached():
    389             left, top = GetFightersDetachedPos()
    390             self.fighterCont.left = left
    391             self.fighterCont.top = top
    392             self.slotsContainer.display = True
    393         else:
    394             self.fighterCont.left = self.slotsContLeft
    395         self.SetFighterButtonsHint()
    396         self.fighterCont.KeepSelection()
    397 
    398     def SetFighterButtonsHint(self):
    399         isDetached = self.IsFightersDetached()
    400         if isDetached:
    401             self.fighterCont.fighterToggleBtn.hint = GetByLabel('UI/Inflight/HUDOptions/ClickToAttach')
    402         else:
    403             self.fighterCont.fighterToggleBtn.hint = GetByLabel('UI/Inflight/HUDOptions/DragToDetach')
    404         self.fighterCont.SetToggleBtnTexture(isDetached)
    405 
    406     def CheckShipHasFighterBay(self):
    407         if not session.shipid:
    408             return False
    409         godmaSM = sm.GetService('godma').GetStateManager()
    410         shipTypeID = self.controller.GetTypeID()
    411         if shipTypeID is None:
    412             return False
    413         fighterCapacity = godmaSM.GetType(shipTypeID).fighterCapacity
    414         return fighterCapacity > 0
    415 
    416     def OptionsBtnMouseEnter(self, *args):
    417         self.options.SetAlpha(1.0)
    418 
    419     def OptionsBtnMouseExit(self, *args):
    420         self.options.SetAlpha(0.8)
    421 
    422     def CheckControl(self):
    423         control = sm.GetService('pwn').GetCurrentControl()
    424         if control:
    425             self.OnAssumeStructureControl()
    426 
    427     def SetButtonState(self):
    428         if settings.user.ui.Get('hudButtonsExpanded', 1):
    429             self.hudButtons.state = uiconst.UI_PICKCHILDREN
    430             if IsControllingStructure():
    431                 self.hudButtons.autopilotBtn.Disable()
    432             else:
    433                 self.hudButtons.autopilotBtn.Enable()
    434         else:
    435             self.hudButtons.state = uiconst.UI_HIDDEN
    436 
    437     @telemetry.ZONE_METHOD
    438     def ConstructReadoutCont(self):
    439         self.readoutCont = HudReadout(parent=self.hudContainer, pos=(278, 22, 200, 0), align=uiconst.TOPLEFT, state=uiconst.UI_HIDDEN, controller=self.controller)
    440 
    441     def OnAssumeStructureControl(self, *args):
    442         now = blue.os.GetSimTime()
    443         self.assumingdelay = now
    444         uthread.new(self.DelayedOnAssumeStructureControl, now)
    445 
    446     def DelayedOnAssumeStructureControl(self, issueTime):
    447         blue.pyos.synchro.SleepSim(250)
    448         if self.assumingdelay is None:
    449             return
    450         issuedAt = self.assumingdelay
    451         if issuedAt != issueTime:
    452             return
    453         self.assumingdelay = None
    454         self.ShowStructureControl()
    455 
    456     def ShowStructureControl(self, *args):
    457         if self.controller.IsControllingTurret():
    458             self.initing = 1
    459             self.slotsContainer.InitSlots()
    460             self.hudButtons.construct_buttons()
    461             self.initing = 0
    462 
    463     def OnRelinquishStructureControl(self, *args):
    464         self.SetupShip()
    465 
    466     @property
    467     def slotsContLeft(self):
    468         offset = 16 if is_compass_enabled() else 0
    469         if self.stanceButtons and self.stanceButtons.display or self.moon_mining_buttons and self.moon_mining_buttons.display or self.mining_overlay_buttons and self.mining_overlay_buttons.display:
    470             offset += 32
    471         return SLOTS_CONTAINER_LEFT + offset - 8
    472 
    473     @property
    474     def stanceButtonsLeft(self):
    475         offset = 16 if is_compass_enabled() else 0
    476         return SLOTS_CONTAINER_LEFT + 4 + offset - 8
    477 
    478     @property
    479     def fighterContLeft(self):
    480         offset = 8 if is_compass_enabled() else -8
    481         left = self.centerParent.GetAbsoluteRight()
    482         return left + offset
    483 
    484     @property
    485     def moduleToggleContLeft(self):
    486         if is_compass_enabled():
    487             return 202
    488         return 186
    489 
    490     @property
    491     def safetyButtonLeft(self):
    492         if is_compass_enabled():
    493             return 40
    494         return 50
    495 
    496     @property
    497     def safetyButtonTop(self):
    498         if is_compass_enabled():
    499             return 28
    500         return 38
    501 
    502     @property
    503     def hudButtonsLeft(self):
    504         if is_compass_enabled():
    505             return 690
    506         return 680
    507 
    508     @property
    509     def shipSelectHilightSize(self):
    510         if is_compass_enabled():
    511             return 206
    512         return 172
    513 
    514     def UpdateButtonsForShip(self):
    515         itemID = self.controller.GetItemID()
    516         typeID = self.controller.GetTypeID()
    517         self.moon_mining_buttons.Hide()
    518         if self.stanceButtons.HasStances():
    519             self.stanceButtons.Hide()
    520         self.stanceButtons.UpdateButtonsForShip(itemID, typeID)
    521         if self.stanceButtons.HasStances():
    522             self.stanceButtons.Show()
    523         elif self.should_show_moon_mining_buttons(typeID):
    524             self.moon_mining_buttons.UpdateButtonsForHull()
    525             self.moon_mining_buttons.Show()
    526         self.mining_overlay_buttons.display = has_current_ship_integrated_mining_scanner()
    527         self.UpdateButtonPositions()
    528 
    529     def should_show_moon_mining_buttons(self, typeID):
    530         return IsDrillingPlatform(typeID)
    531 
    532     def ToggleCompass(self):
    533         settings.user.ui.Set('showSensorOverlay', not is_compass_enabled())
    534         if is_compass_enabled():
    535             self.compass = self.CreateCompass()
    536         else:
    537             self.compass.Close()
    538             self.compass = None
    539         self.UpdateButtonPositions()
    540 
    541     def CreateCompass(self):
    542         return Compass(parent=self.centerParent, uniqueUiName=pConst.UNIQUE_NAME_SENSOR_OVERLAY)
    543 
    544     def UpdateButtonPositions(self):
    545         self.slotsContainer.left = self.slotsContLeft
    546         self.stanceButtons.left = self.stanceButtonsLeft
    547         self.moon_mining_buttons.left = self.stanceButtonsLeft
    548         self.mining_overlay_buttons.left = self.stanceButtonsLeft
    549         self.moduleToggleCont.left = self.moduleToggleContLeft
    550         self.safetyButton.left = self.safetyButtonLeft
    551         self.safetyButton.top = self.safetyButtonTop
    552         self.hudButtons.left = self.hudButtonsLeft
    553         self.shipSelectHilight.width = self.shipSelectHilightSize
    554         self.shipSelectHilight.height = self.shipSelectHilightSize
    555 
    556     def OnDogmaAttributeChanged(self, shipID, itemID, attributeID, value):
    557         if shipID == session.shipid and attributeID in (dogma_const.attributeMiningScannerUpgrade, dogma_const.attributeIntegratedMiningScanner):
    558             self.UpdateButtonsForShip()
    559 
    560     def GetHUDOptionMenu(self, menuParent):
    561         showPassive = settings.user.ui.Get('showPassiveModules', 1)
    562         text = GetByLabel('UI/Inflight/HUDOptions/DisplayPassiveModules')
    563         menuParent.AddCheckBox(text=text, checked=showPassive, callback=self.ToggleShowPassive)
    564         showEmpty = settings.user.ui.Get('showEmptySlots', 0)
    565         text = GetByLabel('UI/Inflight/HUDOptions/DisplayEmptySlots')
    566         menuParent.AddCheckBox(text=text, checked=showEmpty, callback=self.ToggleShowEmpty)
    567         showReadout = settings.user.ui.Get('showReadout', 0)
    568         text = GetByLabel('UI/Inflight/HUDOptions/DisplayReadout')
    569         menuParent.AddCheckBox(text=text, checked=showReadout, callback=self.ToggleReadout)
    570         readoutType = settings.user.ui.Get('readoutType', 1)
    571         text = GetByLabel('UI/Inflight/HUDOptions/DisplayReadoutAsPercentage')
    572         if showReadout:
    573             callback = self.ToggleReadoutType
    574         else:
    575             callback = None
    576         menuParent.AddCheckBox(text=text, checked=readoutType, callback=callback)
    577         showHudButtons = settings.user.ui.Get('hudButtonsExpanded', 1)
    578         menuParent.AddCheckBox(text=GetByLabel('UI/Inflight/HUDOptions/DisplayUtilityButtons'), checked=showHudButtons, callback=self.ToggleHudButtons)
    579         menuParent.AddCheckBox(text=GetByLabel('UI/Inflight/HUDOptions/DisplayZoomButtons'), checked=settings.user.ui.Get('showZoomBtns', 0), callback=self.ToggleShowZoomBtns if showHudButtons else None)
    580         showTooltips = settings.user.ui.Get('showModuleTooltips', 1)
    581         text = GetByLabel('UI/Inflight/HUDOptions/DisplayModuleTooltips')
    582         menuParent.AddCheckBox(text=text, checked=showTooltips, callback=self.ToggleShowModuleTooltips)
    583         text = GetByLabel('UI/Inflight/HUDOptions/DisplaySensorOverlay')
    584         menuParent.AddCheckBox(text=text, checked=is_compass_enabled(), callback=self.ToggleCompass, uniqueUiName=pConst.UNIQUE_NAME_SENSOR_OVERLAY_SETTING)
    585         menuParent.AddDivider()
    586         lockModules = settings.user.ui.Get('lockModules', 0)
    587         text = GetByLabel('UI/Inflight/HUDOptions/LockModulesInPlace')
    588         menuParent.AddCheckBox(text=text, checked=lockModules, callback=self.ToggleLockModules)
    589         menuParent.AddCheckBox(text=GetByLabel('UI/Inflight/HUDOptions/BlinkCargo'), checked=self.GetCargoBlinkValue(), callback=self.ToggleBlinkCargo)
    590         lockOverload = settings.user.ui.Get('lockOverload', 0)
    591         text = GetByLabel('UI/Inflight/HUDOptions/LockOverloadState')
    592         menuParent.AddCheckBox(text=text, checked=lockOverload, callback=self.ToggleOverloadLock)
    593         text = GetByLabel('UI/Inflight/HUDOptions/AlignHUDToTop')
    594         cb = menuParent.AddCheckBox(text=text, checked=IsShipHudTopAligned(), callback=self.ToggleAlign)
    595         cb.isToggleEntry = False
    596         text = GetByLabel('/Carbon/UI/Controls/Window/AlwaysAboveFullScreenViews')
    597         menuParent.AddCheckBox(text=text, checked=overlaySettings.IsFullscreenOverlayModeEnabled(ViewOverlay.ShipUI), callback=lambda : overlaySettings.ToggleFullscreenOverlayMode(ViewOverlay.ShipUI))
    598         menuParent.AddDivider()
    599         text = GetByLabel('UI/Inflight/NotifySettingsWindow/DamageAlertSettings')
    600         iconPath = 'res:/UI/Texture/classes/UtilMenu/BulletIcon.png'
    601         menuParent.AddIconEntry(icon=iconPath, text=text, callback=self.ShowNotifySettingsWindow)
    602         if sm.GetService('logger').IsInDragMode():
    603             text = GetByLabel('UI/Accessories/Log/ExitMessageMovingMode')
    604             enterArgs = False
    605         else:
    606             text = GetByLabel('UI/Accessories/Log/EnterMessageMovingMode')
    607             enterArgs = True
    608         menuParent.AddIconEntry(icon='res:/UI/Texture/classes/UtilMenu/BulletIcon.png', text=text, callback=(sm.GetService('logger').MoveNotifications, enterArgs))
    609 
    610     def ShowNotifySettingsWindow(self):
    611         NotifySettingsWindow.Open()
    612 
    613     def ToggleAlign(self):
    614         SetShipHudTopAligned(not IsShipHudTopAligned())
    615         self.UpdatePosition()
    616         for each in uicore.layer.abovemain.children[:]:
    617             if each.name == 'message':
    618                 each.Close()
    619                 break
    620 
    621         msg = getattr(uicore.layer.target, 'message', None)
    622         if msg:
    623             msg.Close()
    624 
    625     def CheckShowReadoutCont(self):
    626         if settings.user.ui.Get('showReadout', 0):
    627             self.readoutCont.state = uiconst.UI_DISABLED
    628             self.hudButtons.top = 30
    629         else:
    630             self.readoutCont.state = uiconst.UI_HIDDEN
    631             self.hudButtons.top = 15
    632 
    633     def ToggleReadout(self):
    634         current = not settings.user.ui.Get('showReadout', 0)
    635         settings.user.ui.Set('showReadout', current)
    636         self.CheckShowReadoutCont()
    637 
    638     def GetCargoBlinkValue(self):
    639         return settings.user.ui.Get('BlinkCargoHudIcon', True)
    640 
    641     def ToggleBlinkCargo(self):
    642         settings.user.ui.Set('BlinkCargoHudIcon', not self.GetCargoBlinkValue())
    643 
    644     def ToggleReadoutType(self):
    645         current = settings.user.ui.Get('readoutType', 1)
    646         settings.user.ui.Set('readoutType', not current)
    647 
    648     def ToggleShowZoomBtns(self):
    649         settings.user.ui.Set('showZoomBtns', not settings.user.ui.Get('showZoomBtns', 0))
    650         self.hudButtons.construct_buttons()
    651 
    652     def ToggleLockModules(self):
    653         settings.user.ui.Set('lockModules', not settings.user.ui.Get('lockModules', 0))
    654         self.slotsContainer.CheckGroupAllButton()
    655 
    656     def ToggleOverloadLock(self):
    657         settings.user.ui.Set('lockOverload', not settings.user.ui.Get('lockOverload', 0))
    658 
    659     def ToggleShowModuleTooltips(self):
    660         settings.user.ui.Set('showModuleTooltips', not settings.user.ui.Get('showModuleTooltips', 1))
    661 
    662     def ToggleHudButtons(self):
    663         isExpanded = self.hudButtons.state == uiconst.UI_PICKCHILDREN
    664         if isExpanded:
    665             PlaySound(uiconst.SOUND_COLLAPSE)
    666             self.hudButtons.state = uiconst.UI_HIDDEN
    667         else:
    668             PlaySound(uiconst.SOUND_EXPAND)
    669             self.hudButtons.state = uiconst.UI_PICKCHILDREN
    670         settings.user.ui.Set('hudButtonsExpanded', not isExpanded)
    671 
    672     def GetModuleToggleBtnMenu(self, *args):
    673         if self.IsFightersDetached():
    674             menuLabel = MenuLabel('UI/Inflight/ResetFighterPosition')
    675             return [[menuLabel, self.ResetSetFightersPos, ()]]
    676         return []
    677 
    678     def ResetSetFightersPos(self):
    679         ResetSetFightersDetachedPos()
    680         self.DrawFighters()
    681 
    682     def OnToggleHudModules(self, *args):
    683         if self.IsFightersDetached():
    684             self.OnToggleFightersDetached()
    685         else:
    686             settings.user.ui.Set('displayFighterUI', not settings.user.ui.Get('displayFighterUI', False))
    687             self.ShowHideFighters()
    688 
    689     def OnToggleFightersDetached(self, *args):
    690         isDetached = self.IsFightersDetached()
    691         settings.user.ui.Set('detachFighterUI', not isDetached)
    692         if isDetached:
    693             self.AttachHudModules()
    694         else:
    695             self.DetachHudModules()
    696 
    697     def AttachHudModules(self):
    698         self.AlignFighters()
    699         self.fighterCont.left = 0
    700         self.fighterCont.top = 10
    701         self.MakeFighterHudBinding()
    702         self.ShowHideFighters()
    703         self.SetFighterButtonsHint()
    704 
    705     def DetachHudModules(self):
    706         self.slotsContainer.display = True
    707         self.RemoveFighterHudBinding()
    708         self.DrawFighters()
    709 
    710     def InitFighters(self):
    711         if not self.IsFightersDetached():
    712             self.MakeFighterHudBinding()
    713             self.ShowHideFighters()
    714         else:
    715             self.DetachHudModules()
    716             self.slotsContainer.display = True
    717 
    718     def ShowHideFighters(self):
    719         displayFighters = self.IsFightersShown()
    720         isDetached = self.IsFightersDetached()
    721         if self.CheckShipHasFighterBay():
    722             if isDetached:
    723                 self.fighterCont.display = True
    724                 self.slotsContainer.display = True
    725                 return
    726             if displayFighters == True:
    727                 self.fighterCont.display = True
    728                 self.slotsContainer.display = False
    729                 return
    730         self.fighterCont.ClearSelection()
    731         self.fighterCont.display = False
    732         self.slotsContainer.display = True
    733         self.DeselectShip()
    734 
    735     def AlignFighters(self):
    736         if not self.fighterCont or self.fighterCont.destroyed:
    737             return
    738         if self.IsFightersDetached():
    739             self.fighterCont.SetAlign(uiconst.TOPLEFT)
    740         elif IsShipHudTopAligned():
    741             self.fighterCont.SetAlign(uiconst.TOTOP)
    742         else:
    743             self.fighterCont.SetAlign(uiconst.TOBOTTOM)
    744             self.fighterCont.top = 10
    745 
    746     def IsFightersDetached(self):
    747         return settings.user.ui.Get('detachFighterUI', False)
    748 
    749     def IsFightersShown(self):
    750         return settings.user.ui.Get('displayFighterUI', False)
    751 
    752     def Scanner(self, button):
    753         self.expandTimer = None
    754         uicore.layer.menu.Flush()
    755         radialMenu = RadialMenuScanner(name='radialMenu', parent=uicore.layer.menu, state=uiconst.UI_NORMAL, align=uiconst.TOPLEFT, anchorObject=button)
    756         uicore.layer.menu.radialMenu = radialMenu
    757         uicore.uilib.SetMouseCapture(radialMenu)
    758 
    759     def BlinkButton(self, key):
    760         self.slotsContainer.BlinkButton(key)
    761 
    762     def ChangeOpacityForRange(self, currentRange, *args):
    763         if getattr(self, 'slotContainer', None):
    764             self.slotsContainer.ChangeOpacityForRange(self, currentRange)
    765 
    766     def ResetModuleButtonOpacity(self, *args):
    767         if getattr(self, 'slotContainer', None):
    768             self.slotsContainer.ResetModuleButtonOpacity()
    769 
    770     def ToggleRackOverload(self, slotName):
    771         self.slotsContainer.ToggleRackOverload(slotName)
    772 
    773     def ProcessPendingOverloadUpdate(self, moduleIDs):
    774         self.slotsContainer.ProcessPendingOverloadUpdate(moduleIDs)
    775 
    776     def ResetSwapMode(self):
    777         self.slotsContainer.ResetSwapMode()
    778 
    779     def StartDragMode(self, itemID, typeID):
    780         self.slotsContainer.StartDragMode(itemID, typeID)
    781 
    782     def GetPosFromFlag(self, slotFlag):
    783         return self.slotsContainer.GetPosFromFlag(slotFlag)
    784 
    785     def GetSlotByName(self, name):
    786         return self.slotsContainer.FindChild(name)
    787 
    788     def ChangeSlots(self, toFlag, fromFlag):
    789         self.slotsContainer.ChangeSlots(toFlag, fromFlag)
    790 
    791     def SwapSlots(self, slotFlag1, slotFlag2):
    792         self.slotsContainer.SwapSlots(slotFlag1, slotFlag2)
    793 
    794     def LinkWeapons(self, master, slave, slotFlag1, slotFlag2, merge = False):
    795         self.slotsContainer.LinkWeapons(master, slave, slotFlag1, slotFlag2, merge)
    796 
    797     def GetModuleType(self, flag):
    798         return self.slotsContainer.GetModuleType(flag)
    799 
    800     def GetModuleFromID(self, moduleID):
    801         return self.slotsContainer.GetModuleFromID(moduleID)
    802 
    803     def ToggleShowEmpty(self):
    804         self.slotsContainer.ToggleShowEmpty()
    805 
    806     def ToggleShowPassive(self):
    807         self.slotsContainer.ToggleShowPassive()
    808 
    809     def GetModuleForFKey(self, key):
    810         return self.slotsContainer.GetModuleForFKey(key)
    811 
    812     def GetModule(self, moduleID):
    813         return self.slotsContainer.GetModule(moduleID)
    814 
    815     def OnF(self, sidx, gidx):
    816         if not self.CheckShipHasFighterBay():
    817             self.slotsContainer.OnF(sidx, gidx)
    818             return
    819         shipIsSelected, fightersSelected = movementFunctions.GetSelectedShipAndFighters()
    820         if self.IsFightersDetached():
    821             moduleIsActive = self.IsModuleActiveForFKey(sidx, gidx)
    822             isAllAbilitiesActiveOrInCooldown = GetShipFighterState().IsAllAbilitiesInSlotActiveOrInCooldown(sidx)
    823             if shipIsSelected:
    824                 if isAllAbilitiesActiveOrInCooldown:
    825                     if moduleIsActive is not None:
    826                         self.slotsContainer.OnF(sidx, gidx)
    827                     if moduleIsActive is None or moduleIsActive == True:
    828                         self.TryActivateFighters(sidx, gidx)
    829                 else:
    830                     self.TryActivateFighters(sidx, gidx)
    831                     if moduleIsActive == False:
    832                         self.slotsContainer.OnF(sidx, gidx)
    833             else:
    834                 self.TryActivateFighters(sidx, gidx)
    835         elif self.IsFightersShown():
    836             self.TryActivateFighters(sidx, gidx)
    837         else:
    838             self.slotsContainer.OnF(sidx, gidx)
    839 
    840     def ActivateModuleByItemID(self, itemID):
    841         moduleButton = self.GetModule(itemID)
    842         if moduleButton:
    843             moduleButton.ActivateModule()
    844 
    845     def DeactivateModuleByItemID(self, itemID):
    846         moduleButton = self.GetModule(itemID)
    847         if moduleButton:
    848             moduleButton.DeactivateModule()
    849 
    850     def TryActivateFighters(self, sidx, gidx):
    851         if gidx != 0:
    852             return
    853         self.fighterCont.OnF(sidx)
    854 
    855     def GetModuleDefaultEffect(self, sidx, gidx):
    856         slot = self.slotsContainer.slotsByOrder.get((gidx, sidx), None)
    857         if not slot:
    858             return
    859         if not slot.sr.module:
    860             return
    861         if slot.sr.module.state != uiconst.UI_NORMAL:
    862             return
    863         if slot.sr.module.def_effect is None:
    864             return
    865         return slot.sr.module.def_effect
    866 
    867     def IsModuleActiveForFKey(self, sidx, gidx):
    868         defaultEffect = self.GetModuleDefaultEffect(sidx, gidx)
    869         if defaultEffect:
    870             return defaultEffect.isActive
    871 
    872     def OnFKeyOverload(self, sidx, gidx):
    873         self.slotsContainer.OnFKeyOverload(sidx, gidx)
    874 
    875     def OnReloadAmmo(self):
    876         self.slotsContainer.OnReloadAmmo()
    877 
    878     def OnCloseView(self):
    879         self.ResetSelf()
    880         settings.user.ui.Set('selected_shipuicateg', self.sr.selectedcateg)
    881 
    882     @telemetry.ZONE_METHOD
    883     def DoBallsRemove(self, pythonBalls, isRelease):
    884         if isRelease:
    885             self.UnhookBall()
    886             self.jammers = {}
    887             return
    888         controllerBall = self.controller.GetBall()
    889         if controllerBall is not None:
    890             for ball, slimItem, terminal in pythonBalls:
    891                 if ball and ball.id == controllerBall.id:
    892                     self.UnhookBall()
    893                     controllerBall = self.controller.GetBall()
    894 
    895         if self.jammers:
    896             uthread.new(self.UpdateJammersAfterBallRemoval, [ ball.id for ball, _, _ in pythonBalls if ball ])
    897         if isRelease and self.compass:
    898             self.compass.RemoveAll()
    899 
    900     def DoBallRemove(self, ball, slimItem, terminal):
    901         if ball is None:
    902             return
    903         log.LogInfo('DoBallRemove::shipui', ball.id)
    904         if self.controller.GetBall() is not None and ball.id == self.controller.GetBall().id:
    905             self.UnhookBall()
    906         uthread.new(self.UpdateJammersAfterBallRemoval, (ball.id,))
    907 
    908     def UpdateJammersAfterBallRemoval(self, ballIDs):
    909         jams = self.jammers.keys()
    910         for jammingType in jams:
    911             jam = self.jammers[jammingType]
    912             for id in jam.keys():
    913                 sourceBallID, moduleID, targetBallID = id
    914                 if sourceBallID in ballIDs:
    915                     del self.jammers[jammingType][id]
    916 
    917     def ProcessShipEffect(self, godmaStm, effectState):
    918         if effectState.error is not None:
    919             uthread.new(prompt_player_fsd_dialog, effectState.error[0], effectState.error[1])
    920 
    921     def OnJamStart(self, sourceBallID, moduleID, targetBallID, jammingType, startTime, duration):
    922         durationInClient = GetDurationInClient(startTime, duration)
    923         if durationInClient < 0.0:
    924             return
    925         if jammingType not in self.jammers:
    926             self.jammers[jammingType] = {}
    927         jammerID = (sourceBallID, moduleID, targetBallID)
    928         self.jammers[jammingType][jammerID] = (blue.os.GetSimTime(), durationInClient)
    929         if self.buffBarContainer and targetBallID == session.shipid:
    930             self.buffBarContainer.StartTimer(jammingType, jammerID, durationInClient)
    931 
    932     def OnJamEnd(self, sourceBallID, moduleID, targetBallID, jammingType):
    933         if jammingType in self.jammers:
    934             jammerID = (sourceBallID, moduleID, targetBallID)
    935             if jammerID in self.jammers[jammingType]:
    936                 del self.jammers[jammingType][jammerID]
    937 
    938     def OnShipScanCompleted(self, shipID, capacitorCharge, capacitorCapacity, hardwareList):
    939         bp = sm.GetService('michelle').GetBallpark()
    940         if not bp:
    941             return
    942         slimItem = bp.slimItems[shipID]
    943         wndName = GetByLabel('UI/Inflight/ScanWindowName', itemName=GetSlimItemName(slimItem), title=GetByLabel('UI/Inflight/ScanResult'))
    944         ShipScan.CloseIfOpen(windowID='shipscan', windowInstanceID=shipID)
    945         ShipScan.Open(windowID='shipscan', windowInstanceID=shipID, caption=wndName, shipID=shipID, results=(capacitorCharge, capacitorCapacity, hardwareList))
    946 
    947     def OnCargoScanComplete(self, shipID, cargoList):
    948         bp = sm.GetService('michelle').GetBallpark()
    949         if not bp:
    950             return
    951         wnd = CargoScan.Open(windowID='cargoscanner', windowInstanceID=shipID, shipID=shipID, cargoList=cargoList)
    952         if wnd:
    953             wnd.LoadResult(cargoList)
    954 
    955     def UnhookBall(self):
    956         self.controller.InvalidateBall()
    957 
    958     def OnShipChanged(self, *args):
    959         self.SetupShip(animate=True)
    960 
    961     @threadutils.Throttled(1.5)
    962     def SetupShip(self, animate = False):
    963         if self.setupShipTasklet is not None:
    964             self.setupShipTasklet.kill()
    965         self.setupShipTasklet = uthread.new(self._SetupShip, animate)
    966 
    967     @telemetry.ZONE_METHOD
    968     def _SetupShip(self, animate = False):
    969         if self.destroyed or self.initing or not self.shipuiReady:
    970             return
    971         self.initing = True
    972         try:
    973             while not self.controller.IsLoaded():
    974                 blue.synchro.Yield()
    975 
    976             if not sm.GetService('viewState').IsViewActive('planet') and sm.GetService('ui').IsUiVisible():
    977                 self.state = uiconst.UI_PICKCHILDREN
    978             self.ResetUpdateTasklet()
    979             self.sr.rampTimers = {}
    980             self.slotsContainer.InitSlots(animate)
    981             self.hudButtons.construct_buttons()
    982             self.SetButtonState()
    983             self.CheckControl()
    984             self.UpdateButtonsForShip()
    985             self.capacitorContainer.InitCapacitor()
    986             self.DrawFighters()
    987             self.InitFighters()
    988             self.ShowHideFighters()
    989             if self.CheckShipHasFighterBay():
    990                 self.moduleToggleBtn.display = True
    991             else:
    992                 self.moduleToggleBtn.display = False
    993                 if self.shipSelectHilight.display:
    994                     self.shipSelectHilight.display = False
    995             blue.pyos.synchro.SleepWallclock(200)
    996         except Exception as e:
    997             log.LogException(e)
    998         finally:
    999             self.initing = False
   1000             if self.display:
   1001                 sm.GetService('michelle').DetectAndFixMissingHudModules()
   1002 
   1003     def ResetUpdateTasklet(self):
   1004         if self.updateTasklet:
   1005             self.updateTasklet.kill()
   1006         self.updateTasklet = uthread.new(self.UpdateGauges)
   1007 
   1008     def SetSpeed(self, speedRatio):
   1009         self.controller.SetSpeed(speedRatio)
   1010 
   1011     def Hide(self):
   1012         self.state = uiconst.UI_HIDDEN
   1013 
   1014     def Show(self):
   1015         self.state = uiconst.UI_PICKCHILDREN
   1016 
   1017     def OnMouseEnter(self, *args):
   1018         uicore.layer.inflight.HideTargetingCursor()
   1019 
   1020     def GetMenu(self):
   1021         return self.controller.GetMenu()
   1022 
   1023     @telemetry.ZONE_FUNCTION
   1024     def UpdateGauges(self):
   1025         while not self.destroyed:
   1026             try:
   1027                 if self.controller.IsLoaded():
   1028                     self.heatGauges.Update()
   1029                     self.hpGauges.Update()
   1030                     self.UpdateReadouts()
   1031             except Exception as e:
   1032                 log.LogException(e)
   1033 
   1034             blue.synchro.SleepWallclock(500)
   1035 
   1036     def UpdateReadouts(self):
   1037         self.CheckShowReadoutCont()
   1038         if self.readoutCont.state != uiconst.UI_HIDDEN:
   1039             if settings.user.ui.Get('readoutType', 1):
   1040                 self.readoutCont.UpdateHpPercentages()
   1041             else:
   1042                 self.readoutCont.UpdateHpAbsolutes()
   1043 
   1044 
   1045 class BaseSpeedButtonCont(Container):
   1046     default_width = 12
   1047     default_height = 12
   1048     default_state = uiconst.UI_NORMAL
   1049     default_texturePath = ''
   1050 
   1051     def ApplyAttributes(self, attributes):
   1052         Container.ApplyAttributes(self, attributes)
   1053         texturePath = attributes.get('texturePath', self.default_texturePath)
   1054         Sprite(parent=self, texturePath=texturePath, align=uiconst.TOALL, state=uiconst.UI_DISABLED)
   1055         self.controller = attributes.controller
   1056 
   1057     def OnClick(self, *args):
   1058         ui_name = getattr(self, 'uniqueUiName', None)
   1059         if ui_name:
   1060             self.analyticID = ui_name
   1061             log_button_clicked(self)
   1062         PlaySound(uiconst.SOUND_BUTTON_CLICK)
   1063         self.ExecuteCommand()
   1064 
   1065     def OnMouseEnter(self, *args):
   1066         PlaySound(uiconst.SOUND_BUTTON_HOVER)
   1067 
   1068     def ExecuteCommand(self):
   1069         pass
   1070 
   1071 
   1072 class MaxSpeedButton(BaseSpeedButtonCont):
   1073     default_name = 'MaxSpeedButton'
   1074     default_texturePath = 'res:/UI/Texture/classes/ShipUI/plus.png'
   1075 
   1076     def ExecuteCommand(self):
   1077         self.controller.SetMaxSpeed()
   1078 
   1079     def GetHint(self):
   1080         return GetByLabel('UI/Inflight/SetFullSpeed', maxSpeed=self.controller.GetSpeedMaxFormatted())
   1081 
   1082 
   1083 class StopButton(BaseSpeedButtonCont):
   1084     default_name = 'StopButton'
   1085     uniqueUiName = pConst.UNIQUE_NAME_STOP_BUTTON
   1086     default_texturePath = 'res:/UI/Texture/classes/ShipUI/minus.png'
   1087     default_hint = GetByLabel('UI/Inflight/StopTheShip')
   1088 
   1089     def ExecuteCommand(self, *args):
   1090         self.controller.StopShip()
   1091 
   1092 
   1093 class CenterHudContainer(UiHiderMixin, Container):
   1094     uniqueUiName = pConst.UNIQUE_NAME_HUD_CENTER
   1095 
   1096     def ApplyAttributes(self, attributes):
   1097         super(CenterHudContainer, self).ApplyAttributes(attributes)
   1098 
   1099     def AnimateReveal(self):
   1100         PlaySound('onboarding_ui_sfx_play')
   1101         self.opacity = 0
   1102         self.Show()
   1103         animations.BlinkIn(self, startVal=0.2, endVal=1.0, loops=3)
   1104 
   1105 
   1106 class HudContainer(Container):
   1107     default_name = 'hudContainer'
   1108     default_width = SHIP_UI_WIDTH
   1109     default_height = SHIP_UI_HEIGHT
   1110 
   1111     def set_protobuf_event_context(self, event):
   1112         event.set_is_in_ship_hud()
   1113 
   1114 
   1115 def ResetSetFightersDetachedPos():
   1116     return settings.char.ui.Delete('fightersDetachedPosition')
   1117 
   1118 
   1119 def GetFightersDetachedPos():
   1120     return settings.char.ui.Get('fightersDetachedPosition', (uicore.desktop.width / 2, uicore.desktop.height / 2 - 60))
   1121 
   1122 
   1123 def is_compass_enabled():
   1124     return settings.user.ui.Get('showSensorOverlay', False)