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

crimewatchTimers.py (29638B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\crimewatch\crimewatchTimers.py
      2 import collections
      3 import logging
      4 from math import pi
      5 import enum
      6 import gametime
      7 import trinity
      8 import uthread
      9 import uthread2
     10 from abyss.common.constants import DEFAULT_ABYSS_CONTENT_DURATION
     11 from carbon.common.lib.const import HOUR, SEC
     12 from carbonui import uiconst
     13 from carbonui.primitives.container import Container
     14 from carbonui.primitives.sprite import Sprite
     15 from carbonui.primitives.transform import Transform
     16 from carbonui.uicore import uicore
     17 from carbonui.util.color import Color
     18 from datetimeutils import datetime_to_filetime
     19 from eve.client.script.ui import eveColor
     20 from eve.client.script.ui.control import eveLabel
     21 from eve.client.script.ui.control.gaugeCircular import GaugeCircular
     22 from eve.client.script.ui.crimewatch.crimewatchConst import Colors
     23 from eve.client.script.ui.crimewatch.crimewatchHints import TimerHint, EngagementTimerHint, BoosterTimerHint, JumpTimerHint, JumpTimerFatigueHint, EmanationLockHint
     24 from eve.client.script.ui.shared.neocom.corporation.war.atWarIcon import AtWarCont
     25 from eve.common.lib import appConst as const
     26 from localization import GetByLabel
     27 from pvpFilaments.common.constants import DEFAULT_PVP_CONTENT_DURATION
     28 from stargate.client import get_gate_lock_messenger
     29 from stargate.client.gateLockController import GateLockController
     30 from uihider import UiHiderMixin
     31 from uihighlighting import uniqueNameConst, UiHighlightDirections
     32 from stargate.client.const import GATE_LOCK_MAX_TIMER_TIME, SETTINGS_FIRST_HIGHLIGHT_SHOWN
     33 from voidspace.common.constants import VOID_SPACE_ENCOUNTER_LIFE_TIME
     34 logger = logging.getLogger(__name__)
     35 ALPHA_EMPTY = 0.2
     36 BLINK_BEFORE_DONE_TIME = SEC * 5
     37 TimerData = collections.namedtuple('TimerData', ['icon',
     38  'smallIcon',
     39  'color',
     40  'tooltip',
     41  'maxTimeout',
     42  'resetAudioEvent',
     43  'endingAudioEvent',
     44  'timerFunc',
     45  'hintClass',
     46  'callbackName',
     47  'timerAttributeName'])
     48 
     49 class TimerType(enum.IntEnum):
     50     Weapons = 0
     51     Npc = 1
     52     Pvp = 2
     53     Suspect = 3
     54     Criminal = 4
     55     Engagement = 5
     56     Booster = 6
     57     JumpActivation = 7
     58     JumpFatigue = 8
     59     JumpCloak = 9
     60     Invulnerable = 10
     61     AbyssalContentExpiration = 11
     62     AbyssalPvpExpiration = 14
     63     VoidSpaceContentExpiration = 16
     64     Disapproval = 17
     65     EmanationLock = 18
     66 
     67 
     68 CRIMEWATCH_TIMER_DATA = {TimerType.Disapproval: TimerData('res:/UI/Texture/Crimewatch/Crimewatch_Disapproval.png', 'res:/UI/Texture/Crimewatch/Crimewatch_Disapproval_Small.png', Colors.Disapproval.GetRGBA(), 'UI/Crimewatch/Timers/DisapprovalTimerTooltip', const.disapprovalTimerTimeout, 'crimewatch_weapons_timer_play', 'crimewatch_weapons_timer_end_play', gametime.GetSimTime, None, None, 'disapprovalTimer'),
     69  TimerType.Weapons: TimerData('res:/UI/Texture/Crimewatch/Crimewatch_Locked.png', 'res:/UI/Texture/Crimewatch/Crimewatch_Locked_Small.png', Colors.Red.GetRGBA(), 'UI/Crimewatch/Timers/WeaponsTimerTooltip', const.weaponsTimerTimeout, 'crimewatch_weapons_timer_play', 'crimewatch_weapons_timer_end_play', gametime.GetSimTime, None, None, 'weaponsTimer'),
     70  TimerType.Npc: TimerData('res:/UI/Texture/Crimewatch/Crimewatch_Combat.png', 'res:/UI/Texture/Crimewatch/Crimewatch_Combat_Small.png', Colors.Yellow.GetRGBA(), 'UI/Crimewatch/Timers/PveTimerTooltip', const.npcTimerTimeout, 'crimewatch_log_off_timer_new_play', 'crimewatch_log_off_timer_end_play', gametime.GetSimTime, None, None, 'npcTimer'),
     71  TimerType.Pvp: TimerData('res:/UI/Texture/Crimewatch/Crimewatch_Combat.png', 'res:/UI/Texture/Crimewatch/Crimewatch_Combat_Small.png', Colors.Red.GetRGBA(), 'UI/Crimewatch/Timers/PvpTimerTooltiip', const.pvpTimerTimeout, 'crimewatch_log_off_timer_new_play', 'crimewatch_log_off_timer_end_play', gametime.GetSimTime, None, None, 'pvpTimer'),
     72  TimerType.Suspect: TimerData('res:/UI/Texture/Crimewatch/Crimewatch_SuspectCriminal.png', 'res:/UI/Texture/Crimewatch/Crimewatch_SuspectCriminal_Small.png', Colors.Suspect.GetRGBA(), 'UI/Crimewatch/Timers/SuspectTimerTooltip', const.criminalTimerTimeout, 'crimewatch_criminal_timer_play', 'crimewatch_criminal_timer_end_play', gametime.GetSimTime, None, None, 'criminalTimer'),
     73  TimerType.Criminal: TimerData('res:/UI/Texture/Crimewatch/Crimewatch_SuspectCriminal.png', 'res:/UI/Texture/Crimewatch/Crimewatch_SuspectCriminal_Small.png', Colors.Criminal.GetRGBA(), 'UI/Crimewatch/Timers/CriminalTimerTooltip', const.criminalTimerTimeout, 'crimewatch_criminal_timer_play', 'crimewatch_criminal_timer_end_play', gametime.GetSimTime, None, None, 'criminalTimer'),
     74  TimerType.Engagement: TimerData('res:/UI/Texture/Crimewatch/Crimewatch_LimitedEngagement.png', None, Colors.Engagement.GetRGBA(), None, const.crimewatchEngagementDuration, 'crimewatch_engagement_timer_play', 'crimewatch_engagement_timer_end_play', gametime.GetWallclockTime, EngagementTimerHint, None, 'engagementTimer'),
     75  TimerType.Booster: TimerData('res:/UI/Texture/Crimewatch/booster.png', None, Colors.Boosters.GetRGBA(), None, const.crimewatchEngagementDuration, 'boostertimer_timerstart_play', 'boostertimer_timerend_play', gametime.GetWallclockTime, BoosterTimerHint, None, 'boosterTimer'),
     76  TimerType.JumpActivation: TimerData('res:/UI/Texture/Crimewatch/Crimewatch_JumpActivation.png', None, (0.945,
     77                             0.353,
     78                             0.141,
     79                             1.0), 'UI/Crimewatch/Timers/JumpActivationTooltip', 1, 'jump_activation_timer_play', 'jump_activation_timer_end_play', gametime.GetWallclockTime, JumpTimerHint, 'DeleteWhenFinished', 'jumpActivationTimer'),
     80  TimerType.JumpFatigue: TimerData('res:/UI/Texture/Crimewatch/Crimewatch_JumpFatigue.png', None, (0.0,
     81                          1.0,
     82                          1.0,
     83                          1.0), 'UI/Crimewatch/Timers/JumpFatigueTooltip', 1, 'jump_fatigue_timer_play', 'jump_fatigue_timer_end_play', gametime.GetWallclockTime, JumpTimerFatigueHint, 'DeleteWhenFinished', 'jumpFatigueTimer'),
     84  TimerType.JumpCloak: TimerData('res:/UI/Texture/Crimewatch/jump_cloak.png', None, (0.0,
     85                        1.0,
     86                        1.0,
     87                        1.0), 'UI/Crimewatch/Timers/JumpCloakTooltip', 1, None, None, gametime.GetSimTime, None, 'DeleteWhenFinished', 'jumpCloakTimer'),
     88  TimerType.Invulnerable: TimerData('res:/UI/Texture/Crimewatch/invulnerable.png', None, (0.0,
     89                           1.0,
     90                           1.0,
     91                           1.0), 'UI/Crimewatch/Timers/InvulTooltip', 1, None, None, gametime.GetSimTime, None, 'DeleteWhenFinished', 'invulnTimer'),
     92  TimerType.AbyssalContentExpiration: TimerData('res:/UI/Texture/crimewatch/deadspace.png', None, Colors.Red.GetRGBA(), 'UI/Crimewatch/Timers/AbyssalContentExpiryTooltip', DEFAULT_ABYSS_CONTENT_DURATION, None, None, gametime.GetSimTime, None, 'DeleteWhenFinished', 'abyssalContentExpirationTimer'),
     93  TimerType.AbyssalPvpExpiration: TimerData('res:/UI/Texture/crimewatch/deadspace.png', None, (0.541,
     94                                   0.169,
     95                                   0.886,
     96                                   1.0), 'UI/Crimewatch/Timers/AbyssalContentExpiryTooltip', DEFAULT_PVP_CONTENT_DURATION, None, None, gametime.GetSimTime, None, 'DeleteWhenFinished', 'abyssalContentExpirationTimer'),
     97  TimerType.VoidSpaceContentExpiration: TimerData('res:/UI/Texture/crimewatch/deadspace.png', None, Colors.Red.GetRGBA(), 'UI/Crimewatch/Timers/voidSpaceContentExpiryTooltip', VOID_SPACE_ENCOUNTER_LIFE_TIME, None, None, gametime.GetSimTime, None, 'DeleteWhenFinished', 'voidSpaceContentExpirationTimer'),
     98  TimerType.EmanationLock: TimerData('res:/UI/Texture/eveicon/system_icons/link_16px.png', None, Colors.Yellow.GetRGBA(), 'UI/Crimewatch/Timers/EmanationLockTimerTooltip', GATE_LOCK_MAX_TIMER_TIME, None, None, gametime.GetWallclockTime, EmanationLockHint, 'DeleteWhenFinished', 'emanationLockTimer')}
     99 
    100 class Timer(Container):
    101     default_width = 46
    102     default_align = uiconst.TOLEFT
    103     default_hintClass = TimerHint
    104 
    105     def ApplyAttributes(self, attributes):
    106         Container.ApplyAttributes(self, attributes)
    107         timerData = CRIMEWATCH_TIMER_DATA[attributes.Get('timerType')]
    108         self.hintClass = timerData.hintClass or self.default_hintClass
    109         self.state = uiconst.UI_PICKCHILDREN
    110         self.timerHint = None
    111         self.showHint = False
    112         self.expiryTime = None
    113         self.iconBlink = None
    114         self.rewind = False
    115         self.ratio = 0.0
    116         self.counterText = None
    117         self.animationThread = None
    118         self.timerType = attributes.Get('timerType')
    119         self.timerData = attributes.get('timerData', timerData)
    120         self.GetTime = self.timerData.timerFunc
    121         self.activeAnimationCurves = None
    122         self.callback = attributes.get('callback', None)
    123         self.updateTimestamp = None
    124         self.customData = None
    125         self.content = Transform(parent=self, name='content', align=uiconst.TOPLEFT, pos=(0, 0, 32, 32), state=uiconst.UI_NORMAL)
    126         self.content.OnMouseEnter = self.OnMouseEnter
    127         self.iconTransform = Transform(parent=self.content, name='iconTransform', align=uiconst.CENTER, width=16, height=16, state=uiconst.UI_DISABLED)
    128         self.icon = Sprite(name='icon', parent=self.iconTransform, pos=(0, 0, 16, 16), texturePath=self.timerData.icon, color=self.timerData.color, state=uiconst.UI_DISABLED, align=uiconst.CENTER)
    129         self.gaugeCircular = GaugeCircular(parent=self.content, radius=16, align=uiconst.CENTER, colorStart=self.timerData.color, colorEnd=self.timerData.color, colorBg=self._GetGaugeBgColor(self.timerData.color), lineWidth=2.5, clockwise=False, showMarker=False, state=uiconst.UI_DISABLED)
    130         self.pointerContainer = Transform(name='pointer_container', parent=self.content, width=32, height=32, idx=0)
    131         self.pointerClipper = Container(parent=self.pointerContainer, pos=(9, -10, 15, 13), clipChildren=True, align=uiconst.TOPLEFT, state=uiconst.UI_DISABLED)
    132         self.pointerSprite = Sprite(name='cycle_pointer', parent=self.pointerClipper, pos=(0, 0, 15, 19), texturePath='res:/UI/Texture/Crimewatch/Crimewatch_TimerPoint_WithShadow.png', color=self.timerData.color, align=uiconst.TOPLEFT, state=uiconst.UI_DISABLED)
    133         self.iconTransform.scalingCenter = (0.5, 0.5)
    134         uicore.animations.Tr2DScaleTo(self.iconTransform, startScale=(0.8, 0.8), endScale=(1.0, 1.0), duration=0.75, curveType=uiconst.ANIM_OVERSHOT)
    135 
    136     def _GetGaugeBgColor(self, color):
    137         return Color(*color).SetAlpha(ALPHA_EMPTY).GetRGBA()
    138 
    139     def SetTimerType(self, timerType):
    140         self.timerData = CRIMEWATCH_TIMER_DATA[timerType]
    141         r, g, b, a = self.timerData.color
    142         self.icon.SetRGBA(r, g, b, a)
    143         self.pointerSprite.SetRGBA(r, g, b, a)
    144         self.gaugeCircular.SetColor(self.timerData.color, self.timerData.color)
    145         self.gaugeCircular.SetColorBg(self._GetGaugeBgColor(self.timerData.color))
    146         self.timerType = timerType
    147 
    148     def SetRatio(self, ratio):
    149         self.ratio = min(1.0, max(0.0, ratio))
    150         rotation = min(pi * 2, 2 * pi * self.ratio)
    151         self.pointerContainer.rotation = rotation
    152         self.gaugeCircular.SetValue(ratio, animate=False)
    153 
    154     def SetExpiryTime(self, expiryTime, doAlert, maxDuration = None, updateTimestamp = None):
    155         logger.debug('Setting timer %s expiry time to %s', self.name, expiryTime)
    156         self.Reset(expiryTime, doAlert, maxDuration=maxDuration)
    157         self.expiryTime = expiryTime
    158         self.updateTimestamp = updateTimestamp
    159         if expiryTime is None:
    160             self.PlayActiveAnimation()
    161         else:
    162             self.animationThread = uthread.new(self.Animate_Thread)
    163 
    164     def Reset(self, resetTo, doAlert, maxDuration = None):
    165         if self.animationThread is not None:
    166             self.animationThread.kill()
    167         if maxDuration and maxDuration != self.timerData.maxTimeout:
    168             self.timerData = self.timerData._replace(maxTimeout=maxDuration)
    169         uthread.new(self.Rewind_Thread, resetTo, doAlert)
    170 
    171     def Rewind_Thread(self, resetTo, doAlert):
    172         if self.rewind:
    173             return
    174         if doAlert and self.timerData.resetAudioEvent is not None:
    175             sm.GetService('audio').SendUIEvent(self.timerData.resetAudioEvent)
    176         self.rewind = True
    177         ratio = self.ratio
    178         startTime = self.GetTime()
    179         distance = 1 - ratio
    180         cycleSpeed = float(distance * 500)
    181         while not self.destroyed and self.ratio < (self.GetRatio(resetTo - self.GetTime()) if resetTo is not None else 1.0):
    182             elapsedTime = gametime.GetTimeDiffInMs(startTime, self.GetTime())
    183             toAdd = elapsedTime / cycleSpeed
    184             self.SetRatio(ratio + toAdd)
    185             uthread2.sleep(0.025)
    186 
    187         self.rewind = False
    188 
    189     def FlipFlop(self, sprite, duration = 1.0, startValue = 0.0, endValue = 1.0, loops = 5):
    190         curve = trinity.Tr2CurveScalar()
    191         curve.AddKey(0, startValue, trinity.Tr2CurveInterpolation.LINEAR)
    192         curve.AddKey(0.01 * duration, endValue, trinity.Tr2CurveInterpolation.LINEAR)
    193         curve.AddKey(0.5 * duration, endValue, trinity.Tr2CurveInterpolation.LINEAR)
    194         curve.AddKey(0.51 * duration, startValue, trinity.Tr2CurveInterpolation.LINEAR)
    195         curve.AddKey(duration, startValue, trinity.Tr2CurveInterpolation.LINEAR)
    196         return uicore.animations.Play(curve, sprite, 'opacity', loops, None, False)
    197 
    198     def GetRatio(self, timeLeft):
    199         ratio = timeLeft / float(self.timerData.maxTimeout)
    200         ratio = min(1.0, max(0.0, ratio))
    201         return ratio
    202 
    203     def Animate_Thread(self):
    204         self.StopActiveAnimation()
    205         while not self.destroyed and self.expiryTime is not None:
    206             if not self.rewind:
    207                 if self.ratio <= 0.0:
    208                     break
    209                 timeLeft = self.expiryTime - self.GetTime()
    210                 ratio = self.GetRatio(timeLeft)
    211                 self.SetRatio(ratio)
    212                 if timeLeft < BLINK_BEFORE_DONE_TIME:
    213                     self.PlayIconBlink()
    214                 else:
    215                     self.StopIconBlink()
    216             uthread2.sleep(0.05)
    217 
    218         if self.callback:
    219             self.callback(self)
    220 
    221     def PlayIconBlink(self):
    222         if self.iconBlink is None:
    223             self.iconBlink = self.FlipFlop(self.icon, startValue=1.0, endValue=0.0)
    224             if self.timerData.endingAudioEvent:
    225                 sm.GetService('audio').SendUIEvent(self.timerData.endingAudioEvent)
    226 
    227     def StopIconBlink(self):
    228         if self.iconBlink is not None:
    229             self.iconBlink.Stop()
    230             self.iconBlink = None
    231             self.icon.opacity = 1.0
    232 
    233     def EndAnimation(self):
    234         self.SetRatio(0.0)
    235         uicore.animations.MoveOutBottom(self.pointerSprite, amount=9, duration=0.3, sleep=False)
    236         self.content.scalingCenter = (0.5, 0.5)
    237         uicore.animations.Tr2DScaleTo(self.content, startScale=(1.0, 1.0), endScale=(0.8, 0.8), duration=0.4, sleep=True)
    238 
    239     def OnMouseEnter(self, *args):
    240         uthread.new(self.ShowHide)
    241 
    242     def ShowHide(self):
    243         uthread2.sleep(0.25)
    244         if uicore.uilib.mouseOver is self.content:
    245             self.showHint = True
    246             if self.timerHint is None:
    247                 left, top, width, height = self.content.GetAbsolute()
    248                 self.timerHint = self.hintClass(parent=uicore.layer.abovemain, left=left + 16, top=top + 16, timerData=self.timerData, parentTimer=self)
    249 
    250     def ShiftLeft(self):
    251         uicore.animations.MoveInFromRight(self, self.width, duration=0.5)
    252 
    253     def SetCounter(self, count):
    254         if count is None or count <= 1:
    255             if self.counterText is not None:
    256                 self.counterText.Close()
    257                 self.counterText = None
    258         else:
    259             if self.counterText is None:
    260                 self.counterText = eveLabel.EveHeaderLarge(parent=self.content, name='counter', left=34, top=-2, bold=True, color=self.timerData.color)
    261             text = str(count) if count < 10 else '9+'
    262             self.counterText.text = text
    263 
    264     def PlayActiveAnimation(self):
    265         self.activeAnimationCurves = ((self.pointerSprite, self.FlipFlop(self.pointerSprite, startValue=1.0, endValue=0.75, duration=1.0, loops=uiconst.ANIM_REPEAT)), (self.gaugeCircular, self.FlipFlop(self.gaugeCircular, startValue=1.0, endValue=0.75, duration=1.0, loops=uiconst.ANIM_REPEAT)))
    266 
    267     def StopActiveAnimation(self):
    268         if self.activeAnimationCurves is not None:
    269             for sprite, animCurve in self.activeAnimationCurves:
    270                 animCurve.Stop()
    271                 sprite.opacity = 1.0
    272 
    273             self.activeAnimationCurves = None
    274 
    275 
    276 class TimerContainer(UiHiderMixin, Container):
    277     __notifyevents__ = ['OnWeaponsTimerUpdate',
    278      'OnPvpTimerUpdate',
    279      'OnCriminalTimerUpdate',
    280      'OnNpcTimerUpdate',
    281      'OnCombatTimersUpdated',
    282      'OnCrimewatchEngagementUpdated',
    283      'OnBoosterUpdated',
    284      'OnDisapprovalTimerUpdate',
    285      'OnJumpTimersUpdated',
    286      'OnJumpCloakUpdated',
    287      'OnJumpCloakCancelled',
    288      'OnRestoringInvulnUpdated',
    289      'OnInvulunOnUndockingUpdated',
    290      'OnInvulnCancelled',
    291      'OnAbyssalContentFinished',
    292      'OnVoidSpaceContentFinished',
    293      'OnWarStatusUpdated',
    294      'OnEmanationLockUpdated']
    295     default_name = 'unique_UI_crimewatchTimers'
    296     uniqueUiName = uniqueNameConst.UNIQUE_NAME_CRIMEWATCH_TIMERS
    297     default_height = 32
    298     default_width = 96 + 16
    299     default_padBottom = 6
    300     default_align = uiconst.TOTOP
    301 
    302     def ApplyAttributes(self, attributes):
    303         super(TimerContainer, self).ApplyAttributes(attributes)
    304         self.warCont = AtWarCont(parent=self, align=uiconst.TOLEFT)
    305         sm.RegisterNotify(self)
    306         self.uiCriticalSectionsByTimerType = {}
    307         for timerType, timerData in CRIMEWATCH_TIMER_DATA.iteritems():
    308             setattr(self, timerData.timerAttributeName, None)
    309             if timerType not in self.uiCriticalSectionsByTimerType:
    310                 criticalSection = uthread.CriticalSection('uiTimerCriticalSection:%s' % timerData.timerAttributeName)
    311                 self.uiCriticalSectionsByTimerType[timerType] = criticalSection
    312 
    313         self.crimewatchSvc = sm.GetService('crimewatchSvc')
    314         self.warSvc = sm.GetService('war')
    315         uthread.new(self.OnCombatTimersUpdated)
    316 
    317     def Release(self):
    318         sm.UnregisterNotify(self)
    319         for timerData in CRIMEWATCH_TIMER_DATA.itervalues():
    320             timer = getattr(self, timerData.timerAttributeName, None)
    321             if timer is not None and timer.animationThread is not None:
    322                 timer.animationThread.kill()
    323 
    324     def OnCombatTimersUpdated(self):
    325         self.OnWeaponsTimerUpdate(doAlert=False, *self.crimewatchSvc.GetWeaponsTimer())
    326         self.OnNpcTimerUpdate(doAlert=False, *self.crimewatchSvc.GetNpcTimer())
    327         self.OnPvpTimerUpdate(doAlert=False, *self.crimewatchSvc.GetPvpTimer())
    328         self.OnCriminalTimerUpdate(doAlert=False, *self.crimewatchSvc.GetCriminalTimer())
    329         self.OnDisapprovalTimerUpdate(doAlert=False, *self.crimewatchSvc.GetDisapprovalTimer())
    330         self.OnCrimewatchEngagementUpdated(None, None, doAlert=False)
    331         self.OnBoosterUpdated()
    332         self.OnJumpTimersUpdated(doAlert=False, *self.crimewatchSvc.GetJumpTimers())
    333         self.OnWarStatusUpdated(self.warSvc.IsPlayerCurrentlyAtWarOrInFW(), doAlert=False)
    334         uthread2.start_tasklet(self._UpdateEmanationLock)
    335 
    336     def OnWeaponsTimerUpdate(self, state, expiryTime, doAlert = True):
    337         with self.uiCriticalSectionsByTimerType[TimerType.Weapons]:
    338             if state in (const.weaponsTimerStateActive, const.weaponsTimerStateInherited):
    339                 timer = self.GetTimer(TimerType.Weapons)
    340                 timer.SetExpiryTime(None, doAlert)
    341             elif expiryTime is not None:
    342                 timer = self.GetTimer(TimerType.Weapons)
    343                 timer.SetExpiryTime(expiryTime, doAlert)
    344             else:
    345                 self.DeleteTimer(TimerType.Weapons)
    346 
    347     def OnNpcTimerUpdate(self, state, expiryTime, doAlert = True):
    348         with self.uiCriticalSectionsByTimerType[TimerType.Npc]:
    349             if state in (const.npcTimerStateActive, const.npcTimerStateInherited):
    350                 timer = self.GetTimer(TimerType.Npc)
    351                 timer.SetExpiryTime(None, doAlert)
    352             elif expiryTime is not None:
    353                 timer = self.GetTimer(TimerType.Npc)
    354                 timer.SetExpiryTime(expiryTime, doAlert)
    355             else:
    356                 self.DeleteTimer(TimerType.Npc)
    357 
    358     def OnPvpTimerUpdate(self, state, expiryTime, doAlert = True):
    359         with self.uiCriticalSectionsByTimerType[TimerType.Pvp]:
    360             if state in (const.pvpTimerStateActive, const.pvpTimerStateInherited):
    361                 timer = self.GetTimer(TimerType.Pvp)
    362                 timer.SetExpiryTime(None, doAlert)
    363             elif expiryTime is not None:
    364                 timer = self.GetTimer(TimerType.Pvp)
    365                 timer.SetExpiryTime(expiryTime, doAlert)
    366             else:
    367                 self.DeleteTimer(TimerType.Pvp)
    368 
    369     def OnDisapprovalTimerUpdate(self, state, expiryTime, doAlert = True):
    370         with self.uiCriticalSectionsByTimerType[TimerType.Disapproval]:
    371             if state in (const.disapprovalTimerStateActive, const.disapprovalTimerStateInherited):
    372                 timer = self.GetTimer(TimerType.Disapproval)
    373                 timer.SetExpiryTime(None, doAlert)
    374             elif state == const.disapprovalTimerStateTimer and expiryTime is not None:
    375                 timer = self.GetTimer(TimerType.Disapproval)
    376                 timer.SetExpiryTime(expiryTime, doAlert)
    377             else:
    378                 self.DeleteTimer(TimerType.Disapproval)
    379 
    380     def OnCriminalTimerUpdate(self, state, expiryTime, doAlert = True):
    381         with self.uiCriticalSectionsByTimerType[TimerType.Suspect]:
    382             if state in (const.criminalTimerStateActiveSuspect, const.criminalTimerStateInheritedSuspect):
    383                 timer = self.GetTimer(TimerType.Suspect)
    384                 timer.SetExpiryTime(None, doAlert)
    385             elif state == const.criminalTimerStateTimerSuspect and expiryTime is not None:
    386                 timer = self.GetTimer(TimerType.Suspect)
    387                 timer.SetExpiryTime(expiryTime, doAlert)
    388             elif state in (const.criminalTimerStateActiveCriminal, const.criminalTimerStateInheritedCriminal):
    389                 timer = self.GetTimer(TimerType.Criminal)
    390                 timer.SetExpiryTime(None, doAlert)
    391             elif state == const.criminalTimerStateTimerCriminal and expiryTime is not None:
    392                 timer = self.GetTimer(TimerType.Criminal)
    393                 timer.SetExpiryTime(expiryTime, doAlert)
    394             else:
    395                 self.DeleteTimer(TimerType.Suspect)
    396 
    397     def OnCrimewatchEngagementUpdated(self, otherCharId, timeout, doAlert = True):
    398         engagements = self.crimewatchSvc.GetMyEngagements()
    399         if len(engagements) == 0:
    400             self.DeleteTimer(TimerType.Engagement)
    401         else:
    402             timer = self.GetTimer(TimerType.Engagement)
    403             onGoingEngagement = any((_timeout == const.crimewatchEngagementTimeoutOngoing for _timeout in engagements.itervalues()))
    404             if onGoingEngagement:
    405                 timeout = None
    406             else:
    407                 timeout = max((_timeout for _timeout in engagements.itervalues()))
    408             timer.SetExpiryTime(timeout, doAlert)
    409             timer.SetCounter(len(engagements))
    410 
    411     def OnBoosterUpdated(self, doAlert = True):
    412         boosters = self.crimewatchSvc.GetMyBoosters()
    413         boosterList = [ b for b in boosters if b.boosterDuration ]
    414         if len(boosterList) == 0:
    415             self.DeleteTimer(TimerType.Booster)
    416         else:
    417             timer = self.GetTimer(TimerType.Booster)
    418             boosterList.sort(key=lambda x: x.expiryTime, reverse=True)
    419             longestLastingBooster = boosterList[0]
    420             timeout = longestLastingBooster.expiryTime
    421             maxDuration = longestLastingBooster.boosterDuration
    422             timer.SetExpiryTime(timeout, doAlert, maxDuration * 10000)
    423             timer.SetCounter(len(boosterList))
    424 
    425     def OnJumpTimersUpdated(self, jumpActivation, jumpFatigue, lastUpdated, doAlert = False):
    426         if jumpFatigue and jumpFatigue > gametime.GetWallclockTime():
    427             timer = self.GetTimer(TimerType.JumpFatigue)
    428             timer.SetExpiryTime(jumpFatigue, doAlert, jumpFatigue - lastUpdated)
    429         else:
    430             self.DeleteTimer(TimerType.JumpFatigue)
    431         if jumpActivation and jumpActivation > gametime.GetWallclockTime():
    432             self.GetTimer(TimerType.JumpActivation).SetExpiryTime(jumpActivation, doAlert, jumpActivation - lastUpdated)
    433         else:
    434             self.DeleteTimer(TimerType.JumpActivation)
    435 
    436     def OnWarStatusUpdated(self, atWar, doAlert = True):
    437         self.warCont.ShowIcon(animate=doAlert) if atWar else self.warCont.HideIcon(animate=doAlert)
    438 
    439     def OnJumpCloakUpdated(self, shipID, endTime, duration):
    440         self.GetTimer(TimerType.JumpCloak).SetExpiryTime(endTime, maxDuration=duration, doAlert=True)
    441 
    442     def OnJumpCloakCancelled(self, shipID):
    443         self.DeleteTimer(TimerType.JumpCloak)
    444 
    445     def OnRestoringInvulnUpdated(self, shipID, endTime, duration):
    446         self._SetInvulnTimerExpiryTime(endTime, duration)
    447 
    448     def OnInvulunOnUndockingUpdated(self, shipID, endTime, duration):
    449         self._SetInvulnTimerExpiryTime(endTime, duration)
    450 
    451     def _SetInvulnTimerExpiryTime(self, endTime, duration):
    452         with self.uiCriticalSectionsByTimerType[TimerType.Invulnerable]:
    453             timer = self.GetTimer(TimerType.Invulnerable)
    454             timer.SetExpiryTime(endTime, maxDuration=duration, doAlert=True)
    455 
    456     def OnInvulnCancelled(self, shipID):
    457         with self.uiCriticalSectionsByTimerType[TimerType.Invulnerable]:
    458             self.DeleteTimer(TimerType.Invulnerable)
    459 
    460     def SetAbyssalTimer(self, endTime):
    461         self.SetTimerContentExpiry(TimerType.AbyssalContentExpiration, endTime)
    462 
    463     def SetAbyssalPvpTimer(self, endTime):
    464         self.SetTimerContentExpiry(TimerType.AbyssalPvpExpiration, endTime)
    465 
    466     def SetVoidSpaceTimer(self, endTime):
    467         self.SetTimerContentExpiry(TimerType.VoidSpaceContentExpiration, endTime)
    468 
    469     def SetTimerContentExpiry(self, timerType, endTime):
    470         with self.uiCriticalSectionsByTimerType[timerType]:
    471             timer = self.GetTimer(timerType)
    472             timer.SetExpiryTime(endTime, False)
    473 
    474     def OnAbyssalContentFinished(self, content_id, timer_type):
    475         if not sm.GetService('abyss').is_active_content(content_id):
    476             return
    477         with self.uiCriticalSectionsByTimerType[timer_type]:
    478             self.DeleteTimer(timer_type)
    479 
    480     def OnVoidSpaceContentFinished(self, content_id, timer_type):
    481         if not sm.GetService('voidSpaceSvc').is_content_active(content_id):
    482             return
    483         with self.uiCriticalSectionsByTimerType[timer_type]:
    484             self.DeleteTimer(timer_type)
    485 
    486     def OnEmanationLockUpdated(self, lock_details):
    487         if lock_details is None or lock_details.expiry_time < gametime.now():
    488             with self.uiCriticalSectionsByTimerType[TimerType.EmanationLock]:
    489                 self.DeleteTimer(TimerType.EmanationLock)
    490                 return
    491         with self.uiCriticalSectionsByTimerType[TimerType.EmanationLock]:
    492             timer = self.GetTimer(TimerType.EmanationLock)
    493             timer.SetExpiryTime(datetime_to_filetime(lock_details.expiry_time), doAlert=True)
    494         hasShownHighlight = settings.char.ui.Get(SETTINGS_FIRST_HIGHLIGHT_SHOWN, False)
    495         if hasShownHighlight:
    496             return
    497         settings.char.ui.Set(SETTINGS_FIRST_HIGHLIGHT_SHOWN, True)
    498         sm.GetService('uiHighlightingService').highlight_ui_element_by_name(ui_element_name=timer.name, message=GetByLabel('UI/Crimewatch/Timers/EmanationLockHighlight'), default_direction=UiHighlightDirections.RIGHT, offset=-10, fadeout_seconds=10)
    499 
    500     def _UpdateEmanationLock(self):
    501         messenger = get_gate_lock_messenger(sm.GetService('publicGatewaySvc'))
    502         lock_details = GateLockController.get_instance(messenger).get_current_system_lock()
    503         self.OnEmanationLockUpdated(lock_details)
    504 
    505     def DeleteTimer(self, timerType):
    506         idx = None
    507         timerName = CRIMEWATCH_TIMER_DATA[timerType].timerAttributeName
    508         timer = getattr(self, timerName, None)
    509         if timer is not None:
    510             if timer in self.children:
    511                 idx = self.children.index(timer)
    512             timer.EndAnimation()
    513             timer.Close()
    514             setattr(self, timerName, None)
    515             logger.debug('Closed Timer %s', timerName)
    516         if idx is not None:
    517             for timer in self.children[idx:idx + 1]:
    518                 timer.ShiftLeft()
    519 
    520     def DeleteWhenFinished(self, timer):
    521         with self.uiCriticalSectionsByTimerType[timer.timerType]:
    522             self.DeleteTimer(timer.timerType)
    523 
    524     def GetTimer(self, timerType):
    525         timerData = CRIMEWATCH_TIMER_DATA[timerType]
    526         timerAttributeName = timerData.timerAttributeName
    527         timer = getattr(self, timerAttributeName)
    528         if timer is None:
    529             timer = Timer(parent=self, name=timerAttributeName, timerType=timerType, callback=getattr(self, timerData.callbackName) if timerData.callbackName else None)
    530             setattr(self, timerAttributeName, timer)
    531             logger.debug('Created Timer %s', timerAttributeName)
    532         if timer.timerType != timerType:
    533             timer.SetTimerType(timerType)
    534         return timer
    535 
    536     def GetExistingTimer(self, timerType):
    537         timerName = CRIMEWATCH_TIMER_DATA[timerType].timerAttributeName
    538         return getattr(self, timerName, None)