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

hacking.py (33399B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\crate\view\hacking.py
      2 import blue
      3 import uthread2
      4 from carbonui import const as uiconst, ButtonVariant, TextAlign, TextColor
      5 from carbonui.text.styles import TextHeadline, TextBody
      6 from carbonui.button.group import ButtonGroup
      7 from carbonui.control.button import Button
      8 from carbonui.primitives.container import Container
      9 from carbonui.primitives.containerAutoSize import ContainerAutoSize
     10 from carbonui.primitives.sprite import Sprite, StreamingVideoSprite
     11 from carbonui.primitives.vectorline import VectorLine
     12 from carbonui.uianimations import animations
     13 from carbonui.util import color
     14 from crates.const import MAX_MULTI_OPEN
     15 from eve.client.script.ui.control.eveIcon import Icon
     16 from eve.client.script.ui.control.eveLabel import EveCaptionMedium
     17 from eve.client.script.ui.control.themeColored import GradientThemeColored
     18 from eve.client.script.ui.control.tooltips import TooltipPersistentPanel
     19 from eve.client.script.ui.control.pointerPanel import FadeOutPanelAndClose, RefreshPanelPosition
     20 import geo2
     21 import itertools
     22 import localization
     23 import log
     24 import math
     25 import random
     26 import trinity
     27 import uthread
     28 from carbonui.uicore import uicore
     29 from eveexceptions import ExceptionEater
     30 ACTIVE_CONNECTION_ANIM_DURATION = 0.15
     31 HINT_TOOLTIP_SETTINGS_KEY = 'CrateWindowLootHintEnabled'
     32 
     33 def res(filename):
     34     return 'res:/UI/Texture/classes/ItemPacks/{}'.format(filename)
     35 
     36 
     37 def center(text):
     38     return u'<center>{}</center>'.format(text)
     39 
     40 
     41 def getColorUIHighlight(alpha = 1.0):
     42     elementColor = sm.GetService('uiColor').GetUIColor(uiconst.COLORTYPE_UIHILIGHT)
     43     return color.GetColor(elementColor, alpha=alpha)
     44 
     45 
     46 class HackingView(Container):
     47     default_clipChildren = True
     48 
     49     def ApplyAttributes(self, attributes):
     50         super(HackingView, self).ApplyAttributes(attributes)
     51         self.controller = attributes.controller
     52         self._claimedLootCount = 0
     53         self.processingMultiHackTasklet = None
     54         self.controller.onCrateMultiHackEnded.connect(self.OnMultiHackEnded)
     55         self.Layout()
     56         uthread.new(self.AnimEntry)
     57 
     58     def Layout(self):
     59         self.overlayCont = Container(parent=self, align=uiconst.VERTICALLY_CENTERED, padLeft=380, height=80, opacity=0.0)
     60         GradientThemeColored(bgParent=self.overlayCont, padding=(1, 0, 1, 0), rgbData=[(0, (0.5, 0.5, 0.5))], alphaData=[(0, 0.0), (0.4, 0.7), (0.6, 0.8)], rotation=0)
     61         VectorLine(parent=self.overlayCont, align=uiconst.TOPLEFT, translationTo=(0, 0), translationFrom=(600, 0), widthTo=5, widthFrom=5, colorTo=(1.0, 1.0, 1.0, 0.0), texturePath=res('Hacking_HatchPattern.png'), spriteEffect=trinity.TR2_SFX_COPY, textureWidth=8)
     62         VectorLine(parent=self.overlayCont, align=uiconst.BOTTOMLEFT, translationTo=(0, 0), translationFrom=(600, 0), widthTo=5, widthFrom=5, colorTo=(1.0, 1.0, 1.0, 0.0), texturePath=res('Hacking_HatchPattern.png'), spriteEffect=trinity.TR2_SFX_COPY, textureWidth=8)
     63         labelCont = Container(parent=self.overlayCont, align=uiconst.TOALL, padding=(180, 30, 20, 20))
     64         self.hackingLabel = TextHeadline(parent=self.overlayCont, align=uiconst.VERTICALLY_CENTERED, text=localization.GetByLabel('UI/Crate/HackingInProgress'), textAlign=TextAlign.CENTER)
     65         self.gridCont = Container(parent=self, align=uiconst.TOALL, padLeft=380)
     66         self.grid = HackingGrid(parent=self.gridCont, align=uiconst.TOALL, loot=self.controller.loot, specialLoot=self.controller.specialLoot, onLootClaimed=self.OnLootClaimed)
     67         self.back = StreamingVideoSprite(parent=self, align=uiconst.TOPLEFT, state=uiconst.UI_DISABLED, width=800, height=550, top=-25, videoPath='res:/video/hacking/bgLoop_alpha.webm', spriteEffect=trinity.TR2_SFX_COPY, disableAudio=True)
     68         buttonGroupCont = ContainerAutoSize(parent=self, align=uiconst.BOTTOMRIGHT, minWidth=400, idx=0, top=8)
     69         self.buttonGroup = ButtonGroup(parent=buttonGroupCont, align=uiconst.CENTER)
     70 
     71     def AnimEntry(self):
     72         animations.FadeTo(self)
     73         uthread.new(self.AnimBackground)
     74         uthread.new(self.grid.AnimObstacles)
     75         uthread.new(self.grid.AnimEntry)
     76 
     77     def AnimBackground(self):
     78         animations.FadeTo(self.back, startVal=self.back.opacity, endVal=0.4, duration=0.3, sleep=True)
     79         animations.FadeTo(self.back, startVal=0.4, endVal=0.2, duration=6.0, loops=uiconst.ANIM_REPEAT, curveType=uiconst.ANIM_WAVE)
     80 
     81     def AnimBackgroundPulse(self):
     82         animations.FadeTo(self.back, startVal=0.6, endVal=0.4, duration=0.15, loops=3)
     83         blue.synchro.SleepWallclock(450)
     84         uthread.new(self.AnimBackground)
     85 
     86     def OnLootClaimed(self):
     87         item = None
     88         try:
     89             item = self.controller.ClaimLoot()
     90         except Exception:
     91             log.LogException('Error claiming loot item')
     92 
     93         if not self.controller.loot and not self.controller.specialLoot:
     94             self.RevealButtons()
     95         uthread.new(self.AnimBackgroundPulse)
     96         return item
     97 
     98     def RevealButtons(self):
     99         self.buttonGroup.FlushButtons()
    100         if self.controller.stacksize > 0:
    101             if self.controller.stacksize > MAX_MULTI_OPEN:
    102                 text = localization.GetByLabel('UI/Crate/HackMultiple', quantity=MAX_MULTI_OPEN)
    103             else:
    104                 text = localization.GetByLabel('UI/Crate/HackAll', quantity=self.controller.stacksize)
    105             Button(parent=self.buttonGroup, label=text, func=self.HackMultiple, variant=ButtonVariant.GHOST)
    106             text = localization.GetByLabel('UI/Crate/HackNext', quantity=self.controller.stacksize)
    107             Button(parent=self.buttonGroup, label=text, func=self.OpenAnotherCrate)
    108         else:
    109             Button(parent=self.buttonGroup, label=localization.GetByLabel('UI/Crate/Finish'), func=self.controller.Finish, args=())
    110         for button in self.buttonGroup.buttons:
    111             animations.FadeTo(button)
    112 
    113     def DisableButtons(self, *args):
    114         for button in self.buttonGroup.buttons:
    115             button.Disable()
    116 
    117     def OpenAnotherCrate(self, button):
    118         self.DisableButtons()
    119         self.controller.OpenCrate()
    120 
    121     def HackMultiple(self, button):
    122         self.DisableButtons()
    123         try:
    124             self._KillMultiHackTasklet()
    125             self.processingMultiHackTasklet = uthread.new(self.ShowProcessingMultiHack)
    126             self.controller.OpenMultipleCrates()
    127         except Exception:
    128             self.RevealButtons()
    129             raise
    130 
    131         self.RevealButtons()
    132 
    133     def ShowProcessingMultiHack(self):
    134         self.hackingLabel.SetText(localization.GetByLabel('UI/Crate/HackingInProgress'))
    135         animations.FadeIn(self.overlayCont)
    136         self.gridCont.Flush()
    137         self.grid = HackingGrid(parent=self.gridCont, align=uiconst.TOALL, loot=[], specialLoot=[], onLootClaimed=self.OnLootClaimed)
    138         self.grid.ShowContiniousCellAnimation()
    139 
    140     def OnMultiHackEnded(self, cratesOpened, numCratesToOpen):
    141         self._KillMultiHackTasklet()
    142         self.hackingLabel.opacity = 0.0
    143         if cratesOpened == 0:
    144             text = localization.GetByLabel('UI/Crate/HackingFailed')
    145         elif cratesOpened != numCratesToOpen:
    146             text = localization.GetByLabel('UI/Crate/HackingPartiallyCompleted', numCratesOpened=cratesOpened, numCratesToOpen=numCratesToOpen)
    147         else:
    148             text = localization.GetByLabel('UI/Crate/HackingCompleted')
    149         self.hackingLabel.SetText(text)
    150         animations.FadeTo(self.hackingLabel, endVal=TextColor.NORMAL[3], timeOffset=0.1)
    151 
    152     def _KillMultiHackTasklet(self):
    153         if self.processingMultiHackTasklet:
    154             self.processingMultiHackTasklet.kill()
    155             self.processingMultiHackTasklet = None
    156 
    157     def Close(self):
    158         with ExceptionEater():
    159             self._KillMultiHackTasklet()
    160             self.controller.onCrateMultiHackEnded.disconnect(self.OnMultiHackEnded)
    161         super(HackingView, self).Close()
    162 
    163 
    164 def weighted_choice(choices):
    165     total = sum((w for c, w in choices))
    166     r = random.uniform(0, total)
    167     upto = 0
    168     for c, w in choices:
    169         if upto + w >= r:
    170             return c
    171         upto += w
    172 
    173 
    174 class HackingGrid(Container):
    175     gridWidth = 5
    176     gridHeight = 8
    177     gridTop = 55
    178     gridLeft = 22
    179     columnSpacing = 78.6
    180     rowSpacing = 46
    181     _neighborOffsetsOdd = ((-1, -1),
    182      (0, -1),
    183      (1, 0),
    184      (0, 1),
    185      (-1, 1),
    186      (-1, 0))
    187     _neighborOffsetsEven = ((0, -1),
    188      (1, -1),
    189      (1, 0),
    190      (1, 1),
    191      (0, 1),
    192      (-1, 0))
    193     _bleedHorizontalTexturePaths = ['res:/UI/Texture/classes/hacking/lineBleed/horiz01.png',
    194      'res:/UI/Texture/classes/hacking/lineBleed/horiz02.png',
    195      'res:/UI/Texture/classes/hacking/lineBleed/horiz03.png',
    196      'res:/UI/Texture/classes/hacking/lineBleed/horiz04.png',
    197      'res:/UI/Texture/classes/hacking/lineBleed/horiz05.png',
    198      'res:/UI/Texture/classes/hacking/lineBleed/horiz06.png',
    199      'res:/UI/Texture/classes/hacking/lineBleed/horiz07.png',
    200      'res:/UI/Texture/classes/hacking/lineBleed/horiz08.png',
    201      'res:/UI/Texture/classes/hacking/lineBleed/horiz09.png',
    202      'res:/UI/Texture/classes/hacking/lineBleed/horiz10.png',
    203      'res:/UI/Texture/classes/hacking/lineBleed/horiz11.png',
    204      'res:/UI/Texture/classes/hacking/lineBleed/horiz12.png',
    205      'res:/UI/Texture/classes/hacking/lineBleed/horiz13.png',
    206      'res:/UI/Texture/classes/hacking/lineBleed/horiz14.png',
    207      'res:/UI/Texture/classes/hacking/lineBleed/horiz15.png',
    208      'res:/UI/Texture/classes/hacking/lineBleed/horiz16.png',
    209      'res:/UI/Texture/classes/hacking/lineBleed/horiz17.png']
    210     _bleedDiagonalTexturePaths = ['res:/UI/Texture/classes/hacking/lineBleed/diag01.png',
    211      'res:/UI/Texture/classes/hacking/lineBleed/diag02.png',
    212      'res:/UI/Texture/classes/hacking/lineBleed/diag03.png',
    213      'res:/UI/Texture/classes/hacking/lineBleed/diag04.png',
    214      'res:/UI/Texture/classes/hacking/lineBleed/diag05.png',
    215      'res:/UI/Texture/classes/hacking/lineBleed/diag06.png',
    216      'res:/UI/Texture/classes/hacking/lineBleed/diag07.png',
    217      'res:/UI/Texture/classes/hacking/lineBleed/diag08.png',
    218      'res:/UI/Texture/classes/hacking/lineBleed/diag09.png',
    219      'res:/UI/Texture/classes/hacking/lineBleed/diag10.png',
    220      'res:/UI/Texture/classes/hacking/lineBleed/diag11.png']
    221 
    222     def ApplyAttributes(self, attributes):
    223         super(HackingGrid, self).ApplyAttributes(attributes)
    224         self.loot = attributes.loot
    225         self.specialLoot = attributes.specialLoot
    226         self.onLootClaimed = attributes.onLootClaimed
    227         self._showHint = settings.user.ui.Get(HINT_TOOLTIP_SETTINGS_KEY, True)
    228         self._hint = None
    229         self._lootCells = []
    230         self.cellObjects = {}
    231         self.path = self.GeneratePath()
    232         self.DistributeLootOnPath()
    233         self.SprinkleObstacles()
    234         self.PunchHolesInGrid()
    235         for cell in self.cells:
    236             if cell in self.cellObjects:
    237                 continue
    238             x, y = self.getCellPosition(cell)
    239             self.cellObjects[cell] = RegularCell(parent=self, align=uiconst.RELATIVE, left=x, top=y)
    240 
    241         self.DrawCellConnections()
    242 
    243     def DistributeLootOnPath(self):
    244         remainingLoot = self.loot + self.specialLoot
    245         lootChancePerCell = len(remainingLoot) / float(len(self.path) - 2)
    246         accumulatedLootChance = lootChancePerCell
    247         for cell in self.path:
    248             if remainingLoot:
    249                 if accumulatedLootChance >= 1.0:
    250                     remainingLoot.pop()
    251                     x, y = self.getCellPosition(cell)
    252                     self.cellObjects[cell] = ItemCell(parent=self, align=uiconst.RELATIVE, left=x - ItemCell.default_width / 2.0, top=y - ItemCell.default_height / 2.0, onClaimed=self.OnLootClaimed)
    253                     self._lootCells.append(cell)
    254                     accumulatedLootChance -= 1.0
    255                 accumulatedLootChance += lootChancePerCell
    256             else:
    257                 break
    258 
    259     def SprinkleObstacles(self):
    260         self.obstacles = []
    261         candidates = set(self.cells)
    262         candidates = list(candidates.difference(self.path))
    263         candidates = filter(lambda c: 0 < c[1] < self.gridHeight - 1, candidates)
    264         for i in xrange(random.randint(5, 7)):
    265             if not candidates:
    266                 break
    267             cell = random.choice(candidates)
    268             x, y = self.getCellPosition(cell)
    269             obstacle = Obstacle(parent=self, align=uiconst.RELATIVE, left=x, top=y)
    270             self.cellObjects[cell] = obstacle
    271             self.obstacles.append(obstacle)
    272             candidates.remove(cell)
    273             for c in self.getCellNeighbors(cell):
    274                 if c in candidates:
    275                     candidates.remove(c)
    276 
    277     def PunchHolesInGrid(self):
    278         candidates = set(self.cells)
    279         candidates = candidates.difference(self.path)
    280         candidates = list(candidates.difference(self.cellObjects.iterkeys()))
    281         for i in xrange(random.randint(5, 7)):
    282             if not candidates:
    283                 break
    284             cell = random.choice(candidates)
    285             self.cellObjects[cell] = None
    286             candidates.remove(cell)
    287 
    288     def OnLootClaimed(self):
    289         item = self.onLootClaimed()
    290         self._showHint = False
    291         settings.user.ui.Set(HINT_TOOLTIP_SETTINGS_KEY, False)
    292         if self._hint:
    293             FadeOutPanelAndClose(self._hint)
    294         return item
    295 
    296     def AnimObstacles(self):
    297 
    298         def RevealThread(cell, timeOffset):
    299             blue.synchro.SleepWallclock(timeOffset * 1000)
    300             self.cellObjects[cell].AnimReveal()
    301 
    302         x_center, y_center = self.getCellPosition((0, 0))
    303         x_extent, y_extent = self.getCellPosition((self.gridWidth, self.gridHeight))
    304         extent = math.sqrt((x_extent - x_center) ** 2 + (y_extent - y_center) ** 2)
    305         timeFactor = 1.5 / extent
    306         for cell in self.cells:
    307             if self.cellObjects[cell] is None:
    308                 continue
    309             x, y = self.getCellPosition(cell)
    310             x_offset = abs(x - x_center)
    311             y_offset = abs(y - y_center)
    312             timeOffset = timeFactor * math.sqrt(x_offset ** 2 + y_offset ** 2)
    313             uthread.new(RevealThread, cell, timeOffset)
    314 
    315     def AnimEntry(self):
    316         for i, cell in enumerate(self.path):
    317             if i == 0:
    318                 self.DrawActiveFringeConnection(cell, self.getCellNeighbors(cell)[0])
    319             else:
    320                 self.DrawActiveConnection(self.path[i - 1], cell)
    321             uthread.new(self.cellObjects[cell].AnimExplore)
    322             blue.synchro.SleepWallclock(ACTIVE_CONNECTION_ANIM_DURATION * 1000)
    323 
    324         lastCell = self.getCellNeighbors(self.path[-1])[3]
    325         self.DrawActiveFringeConnection(self.path[-1], lastCell, inbound=False)
    326         uthread.new(self.RevealHint)
    327 
    328     def ShowContiniousCellAnimation(self):
    329         x_center, y_center = self.getCellPosition((0, 0))
    330         x_extent, y_extent = self.getCellPosition((self.gridWidth, self.gridHeight))
    331         extent = math.sqrt((x_extent - x_center) ** 2 + (y_extent - y_center) ** 2)
    332         timeFactor = 1.5 / extent
    333         while True:
    334             if self.destroyed:
    335                 break
    336             self.AnimObstacles()
    337             uthread2.Sleep(1.5)
    338 
    339     def RevealHint(self):
    340         blue.synchro.SleepWallclock(5000)
    341         if self._showHint:
    342             cell = random.choice(self._lootCells)
    343             node = self.cellObjects[cell]
    344             self._hint = uicore.uilib.tooltipHandler.LoadPersistentTooltip(node, customTooltipClass=HintTooltip)
    345 
    346     def Close(self):
    347         if self._hint:
    348             FadeOutPanelAndClose(self._hint)
    349         super(HackingGrid, self).Close()
    350 
    351     def GeneratePath(self):
    352         path = []
    353 
    354         def weight(c):
    355             return self.gridWidth / 2.0 - abs(c - (self.gridWidth - 1) / 2.0)
    356 
    357         weightedColumns = [ (c, weight(c)) for c in xrange(self.gridWidth) ]
    358         column = weighted_choice(weightedColumns)
    359         path.append((column, 0))
    360         while True:
    361             horizontalMult = 0.0 if len(path) == 1 else 2.0
    362             weights = (0 + max(0, 2 - path[-1][0]) * horizontalMult,
    363              1 + max(0, 2 - path[-1][0]) * 0.5,
    364              1 + max(0, path[-1][0] - 2) * 0.5,
    365              0 + max(0, path[-1][0] - 2) * horizontalMult)
    366             neighbors = self.getCellNeighbors(path[-1])[2:]
    367             weightedNeighbors = zip(neighbors, weights)
    368             candidates = filter(lambda x: self.IsCellInsideGrid(x[0]), weightedNeighbors)
    369             nextCell = weighted_choice(candidates)
    370             path.append(nextCell)
    371             if nextCell[1] == self.gridHeight - 1:
    372                 break
    373 
    374         return path
    375 
    376     def DrawCellConnections(self):
    377         for cell in self.cells:
    378             if self.cellObjects[cell] is None:
    379                 continue
    380             neighbors = filter(self.IsCellInsideGrid, self.getCellNeighbors(cell)[:3])
    381             for neighbor in neighbors:
    382                 if self.cellObjects[neighbor] is None:
    383                     continue
    384                 x_origin, y_origin = self.getCellPosition(cell)
    385                 x, y = self.getCellPosition(neighbor)
    386                 VectorLine(parent=self, translationFrom=(x_origin, y_origin), translationTo=(x, y), colorFrom=(1, 1, 1, 0.3), colorTo=(1, 1, 1, 0.8), widthFrom=0.3, widthTo=0.3)
    387 
    388         self.DrawFringePaths()
    389 
    390     def DrawActiveConnection(self, fromCell, toCell):
    391         x_origin, y_origin = self.getCellPosition(fromCell)
    392         x, y = self.getCellPosition(toCell)
    393         line = VectorLine(parent=self, translationFrom=(x_origin, y_origin), translationTo=(x, y), colorFrom=getColorUIHighlight(alpha=0.5), colorTo=getColorUIHighlight(alpha=0.5), widthFrom=1, widthTo=1, blendMode=trinity.TR2_SBM_ADDX2)
    394         midpoint = geo2.Vec2Lerp((x_origin, y_origin), (x, y), 0.5)
    395         if y_origin == y:
    396             texturePath = random.choice(self._bleedHorizontalTexturePaths)
    397             height = 50
    398             left = midpoint[0] - 48
    399             top = midpoint[1] - height / 2.0
    400         else:
    401             texturePath = random.choice(self._bleedDiagonalTexturePaths)
    402             height = 72
    403             left = midpoint[0] - 32
    404             top = midpoint[1] - height / 2.0
    405             if y_origin < y and x_origin > x or y_origin > y and x_origin < x:
    406                 top += height
    407                 height *= -1
    408         bleed = Sprite(parent=self, align=uiconst.RELATIVE, state=uiconst.UI_DISABLED, left=left, top=top, width=64, height=height, texturePath=texturePath, color=getColorUIHighlight(), blendMode=trinity.TR2_SBM_ADDX2)
    409 
    410         def BleedAnimation():
    411             animations.FadeTo(bleed, startVal=bleed.opacity, endVal=0.2, duration=5.0, loops=uiconst.ANIM_REPEAT, curveType=uiconst.ANIM_WAVE)
    412 
    413         animations.FadeTo(bleed, endVal=0.5, duration=0.1, loops=3, timeOffset=random.random() * 0.6, callback=BleedAnimation)
    414         glow = VectorLine(parent=self, translationFrom=(x_origin, y_origin), translationTo=(x, y), colorFrom=color.GetColor(getColorUIHighlight(), alpha=1.0), colorTo=color.GetColor(getColorUIHighlight(), alpha=1.0), widthFrom=30, widthTo=30, texturePath=res('Hacking_ConnectionGlow.png'), spriteEffect=trinity.TR2_SFX_COPY)
    415         animations.MorphVector2(line, 'translationTo', startVal=(x_origin, y_origin), endVal=(x, y), duration=ACTIVE_CONNECTION_ANIM_DURATION)
    416         animations.MorphVector2(glow, 'translationTo', startVal=(x_origin, y_origin), endVal=(x, y), duration=ACTIVE_CONNECTION_ANIM_DURATION)
    417         animations.SpColorMorphTo(line, attrName='colorTo', startColor=color.GetColor(getColorUIHighlight(), alpha=0.0), endColor=color.GetColor(getColorUIHighlight(), alpha=1.0), duration=ACTIVE_CONNECTION_ANIM_DURATION, timeOffset=ACTIVE_CONNECTION_ANIM_DURATION)
    418         animations.SpColorMorphTo(glow, attrName='colorTo', startColor=color.GetColor(getColorUIHighlight(), alpha=0.0), endColor=color.GetColor(getColorUIHighlight(), alpha=1.0), duration=ACTIVE_CONNECTION_ANIM_DURATION)
    419         animations.MorphScalar(glow, 'widthTo', endVal=30, duration=ACTIVE_CONNECTION_ANIM_DURATION)
    420 
    421     def DrawActiveFringeConnection(self, insideCell, outsideCell, inbound = True):
    422         x_origin, y_origin = self.getCellPosition(insideCell)
    423         x, y = self.getCellPosition(outsideCell)
    424         line = VectorLine(parent=self, translationFrom=(x_origin, y_origin), translationTo=(x, y), colorFrom=getColorUIHighlight(alpha=0.5), colorTo=color.GetColor(getColorUIHighlight(), alpha=0.0), widthFrom=1, widthTo=1, blendMode=trinity.TR2_SBM_ADDX2)
    425         VectorLine(parent=self, translationFrom=(x_origin, y_origin), translationTo=(x, y), colorFrom=getColorUIHighlight(alpha=0.5), colorTo=color.GetColor(getColorUIHighlight(), alpha=0.0), widthFrom=30, widthTo=30, texturePath=res('Hacking_ConnectionGlow.png'), spriteEffect=trinity.TR2_SFX_COPY, blendMode=trinity.TR2_SBM_ADDX2)
    426         if inbound:
    427             animations.MorphVector2(line, 'translationFrom', startVal=(x, y), endVal=(x_origin, y_origin), duration=ACTIVE_CONNECTION_ANIM_DURATION)
    428         else:
    429             animations.MorphVector2(line, 'translationTo', startVal=(x_origin, y_origin), endVal=(x, y), duration=ACTIVE_CONNECTION_ANIM_DURATION)
    430         animations.MorphScalar(line, 'opacity', startVal=0.0, endVal=1.0, duration=ACTIVE_CONNECTION_ANIM_DURATION)
    431 
    432     def IsCellOutsideGrid(self, cell):
    433         col, row = cell
    434         return col < 0 or self.gridWidth <= col or row < 0 or self.gridHeight <= row
    435 
    436     def IsCellInsideGrid(self, cell):
    437         return not self.IsCellOutsideGrid(cell)
    438 
    439     def DrawFringePaths(self):
    440         for cell in self.cells:
    441             if self.cellObjects[cell] is None:
    442                 continue
    443             outsideCells = filter(self.IsCellOutsideGrid, self.getCellNeighbors(cell))
    444             for col, row in outsideCells:
    445                 if 0 <= col < self.gridWidth and 0 <= row < self.gridHeight:
    446                     continue
    447                 x_origin, y_origin = self.getCellPosition(cell)
    448                 x, y = self.getCellPosition((col, row))
    449                 VectorLine(parent=self, translationFrom=(x_origin, y_origin), translationTo=(x, y), colorFrom=(1, 1, 1, 0.2), colorTo=(1, 1, 1, 0.0), widthFrom=0.5, widthTo=0.5)
    450 
    451     @property
    452     def cells(self):
    453         return itertools.product(xrange(self.gridWidth), xrange(self.gridHeight))
    454 
    455     def getCellPosition(self, coord):
    456         x, y = coord
    457         rowOffset = self.columnSpacing / 2.0 if y % 2 == 0 else 0
    458         return (x * self.columnSpacing + rowOffset + self.gridLeft, y * self.rowSpacing + self.gridTop)
    459 
    460     def getCellNeighbors(self, coord):
    461         column, row = coord
    462         if row % 2 == 0:
    463             neighborOffsets = self._neighborOffsetsEven
    464         else:
    465             neighborOffsets = self._neighborOffsetsOdd
    466         return [ (column + c, row + r) for c, r in neighborOffsets ]
    467 
    468 
    469 class RegularCell(Container):
    470     default_clipChildren = False
    471     default_state = uiconst.UI_DISABLED
    472     default_width = 1
    473     default_height = 1
    474 
    475     def ApplyAttributes(self, attributes):
    476         super(RegularCell, self).ApplyAttributes(attributes)
    477         self.Layout()
    478 
    479     def Layout(self):
    480         self.closed = Sprite(parent=self, align=uiconst.CENTER, width=64, height=64, texturePath='res:/UI/Texture/classes/hacking/tileUnflipped.png', color=(0.3, 0.3, 0.3))
    481 
    482     def AnimReveal(self):
    483         animations.SpGlowFadeOut(self.closed, duration=0.3)
    484         animations.MorphScalar(self.closed, 'glowExpand', startVal=0.5, endVal=10.0, duration=0.3)
    485 
    486     def AnimExplore(self):
    487         opened = Sprite(parent=self, align=uiconst.CENTER, width=40, height=40, texturePath='res:/UI/Texture/classes/hacking/tileExplored.png', color=getColorUIHighlight(), opacity=2.0, idx=0)
    488         offset = ACTIVE_CONNECTION_ANIM_DURATION / 2.0
    489         animations.FadeOut(self.closed, timeOffset=offset)
    490         animations.FadeIn(opened, duration=0.1, timeOffset=offset)
    491         animations.SpGlowFadeOut(opened, duration=0.3, timeOffset=offset + 0.1)
    492         animations.MorphScalar(opened, 'glowExpand', startVal=0.5, endVal=10.0, duration=0.3, timeOffset=offset + 0.1)
    493 
    494 
    495 class ItemCell(Container):
    496     default_clipChildren = False
    497     default_state = uiconst.UI_DISABLED
    498     default_width = 64
    499     default_height = 64
    500 
    501     def ApplyAttributes(self, attributes):
    502         super(ItemCell, self).ApplyAttributes(attributes)
    503         self.onClaimed = attributes.onClaimed
    504         self._claimed = False
    505         self.Layout()
    506 
    507     def Layout(self):
    508         self.nodeSprite = Sprite(parent=self, align=uiconst.CENTER, state=uiconst.UI_DISABLED, width=64, height=64, texturePath='res:/UI/Texture/classes/hacking/tileUnflipped.png', color=(0.3, 0.3, 0.3))
    509         self.frame = StreamingVideoSprite(parent=self, align=uiconst.CENTER, state=uiconst.UI_DISABLED, width=72, height=72, videoPath=res('nodeIntro_R4.webm'), spriteEffect=trinity.TR2_SFX_COPY, disableAudio=True, color=getColorUIHighlight(1.5))
    510         self.frame.Pause()
    511         self.nodeSpinner = StreamingVideoSprite(parent=self, align=uiconst.CENTER, state=uiconst.UI_DISABLED, width=64, height=64, videoPath=res('baseNodeLoop.webm'), videoLoop=True, spriteEffect=trinity.TR2_SFX_COPY, blendMode=trinity.TR2_SBM_ADDX2, disableAudio=True, opacity=0.0, color=getColorUIHighlight())
    512         self.nodeSpinner.Pause()
    513         self.node = Sprite(parent=self, align=uiconst.CENTER, state=uiconst.UI_DISABLED, width=32, height=32, texturePath=res('itemNodeCenter.png'), blendMode=trinity.TR2_SBM_ADDX2, color=(0, 0, 0, 0))
    514         self.back = Sprite(parent=self, align=uiconst.CENTER, state=uiconst.UI_DISABLED, width=64, height=64, texturePath=res('itemNodeBack.png'), color=getColorUIHighlight(), opacity=0.0)
    515 
    516     def AnimReveal(self):
    517         animations.FadeOut(self.nodeSprite, duration=0.1)
    518         animations.FadeIn(self.node, endVal=1.0, duration=0.1)
    519         animations.SpGlowFadeOut(self.node, duration=0.3, timeOffset=0.1)
    520         animations.MorphScalar(self.node, 'glowExpand', startVal=0.5, endVal=10.0, duration=0.3, timeOffset=0.1)
    521 
    522     def AnimExplore(self):
    523         self.frame.Play()
    524         animations.FadeOut(self.nodeSprite, duration=0.1)
    525         animations.FadeIn(self.nodeSpinner, duration=0.5, callback=self.Enable)
    526         self.nodeSpinner.Play()
    527         animations.FadeIn(self.back, endVal=0.1, duration=0.2)
    528         animations.FadeIn(self.node, endVal=1.0, duration=0.25)
    529         animations.SpColorMorphTo(self.node, endColor=getColorUIHighlight(), duration=0.2)
    530         animations.MorphVector2(self.node, 'scale', startVal=(0.5, 0.5), duration=0.5, sleep=True)
    531 
    532         def PulseNode():
    533             offset = random.normalvariate(2.0, 1.0)
    534             animations.MorphVector2(self.node, 'scale', startVal=(1.0, 1.0), endVal=(0.8, 0.8), duration=0.15, timeOffset=offset)
    535             blue.synchro.SleepWallclock((offset + 0.15) * 1000)
    536             animations.MorphVector2(self.node, 'scale', startVal=(0.8, 0.8), endVal=(1.0, 1.0), duration=0.4)
    537             blue.synchro.SleepWallclock(500)
    538             if not self.destroyed:
    539                 uthread.new(PulseNode)
    540 
    541         uthread.new(PulseNode)
    542 
    543     def ClaimLoot(self):
    544         if self._claimed:
    545             return
    546         self._claimed = True
    547         self.item = self.onClaimed()
    548         self.SetState(uiconst.UI_PICKCHILDREN)
    549         uthread.new(self.AnimRevealItem)
    550 
    551     def AddIcon(self):
    552         size = self.item.get_icon_size()
    553         icon = self.item.get_icon()
    554         if icon:
    555             self.icon = Sprite(name='LootItem_Sprite', parent=self, align=uiconst.CENTER, state=uiconst.UI_NORMAL, width=size, height=size, texturePath=self.item.get_icon(), idx=0)
    556         else:
    557             self.icon = Icon(name='LootItem_Icon', parent=self, align=uiconst.CENTER, state=uiconst.UI_NORMAL, width=size, height=size, typeID=self.item.typeID, isCopy=self.item.is_copy(), idx=0)
    558         self.icon.LoadTooltipPanel = self.LoadItemTooltipPanel
    559 
    560     def AnimRevealItem(self):
    561         self.frame.SetVideoPath(res('nodeHarvest_R4.webm'))
    562         animations.FadeOut(self.nodeSpinner, duration=0.2)
    563         animations.MorphVector2(self.node, 'scale', startVal=(1.0, 1.0), endVal=(2.0, 2.0), duration=0.15, sleep=True)
    564         animations.MorphVector2(self.node, 'scale', startVal=(2.0, 2.0), endVal=(0.5, 0.5), duration=0.1)
    565         animations.FadeOut(self.node, duration=0.15)
    566         blue.synchro.SleepWallclock(25)
    567         backdrop = Sprite(parent=self, align=uiconst.CENTER, state=uiconst.UI_DISABLED, width=48, height=48, texturePath=res('Hacking_NodeExploredBackground.png'), opacity=0.0)
    568         animations.FadeIn(backdrop, duration=0.25, timeOffset=0.1)
    569         self.AddIcon()
    570         if self.icon:
    571             animations.FadeTo(self.icon, duration=0.15)
    572             animations.MorphVector2(self.icon, 'scale', startVal=(4.0, 4.0), endVal=(0.9, 0.9), duration=0.15, sleep=True)
    573             animations.MorphVector2(self.icon, 'scale', startVal=(0.9, 0.9), endVal=(1.0, 1.0), duration=0.15)
    574             animations.SpGlowFadeOut(self.icon, duration=0.3)
    575             animations.MorphScalar(self.icon, 'glowExpand', startVal=0.5, endVal=10.0, duration=0.3)
    576         animations.SpColorMorphTo(self.frame, endColor=getColorUIHighlight(2.0), duration=0.15)
    577 
    578     def OnClick(self):
    579         self.ClaimLoot()
    580 
    581     def OnMouseEnter(self):
    582         animations.SpColorMorphTo(self.frame, endColor=getColorUIHighlight(4.0), duration=0.15)
    583         animations.FadeTo(self.back, startVal=self.back.opacity, endVal=0.4, duration=0.3)
    584 
    585     def OnMouseExit(self):
    586         animations.SpColorMorphTo(self.frame, endColor=getColorUIHighlight(2.0), duration=0.3)
    587         animations.FadeTo(self.back, startVal=self.back.opacity, endVal=0.1, duration=0.3)
    588 
    589     def LoadItemTooltipPanel(self, panel, owner):
    590         panel.LoadStandardSpacing()
    591         panel.AddLabelMedium(text=self.item.get_name())
    592 
    593 
    594 class Obstacle(Container):
    595     default_state = uiconst.UI_DISABLED
    596     default_clipChildren = False
    597     default_width = 1
    598     default_height = 1
    599     _obstacleTexturePaths = ['res:/UI/Texture/classes/hacking/defSoftAntiVirus.png',
    600      'res:/UI/Texture/classes/hacking/defSoftFirewall.png',
    601      'res:/UI/Texture/classes/hacking/defSoftHoneyPotHealing.png',
    602      'res:/UI/Texture/classes/hacking/defSoftHoneyPotStrength.png',
    603      'res:/UI/Texture/classes/hacking/defSoftIds.png']
    604     _noiseTexturePaths = [res('Hacking_VirusNoise01.png'),
    605      res('Hacking_VirusNoise02.png'),
    606      res('Hacking_VirusNoise03.png'),
    607      res('Hacking_VirusNoise04.png')]
    608 
    609     def ApplyAttributes(self, attributes):
    610         super(Obstacle, self).ApplyAttributes(attributes)
    611         self.Layout()
    612 
    613     def Layout(self):
    614         self.node = Sprite(parent=self, align=uiconst.CENTER, width=64, height=64, texturePath='res:/UI/Texture/classes/hacking/tileUnflipped.png', color=(0.3, 0.3, 0.3))
    615         self.virus = Sprite(parent=self, align=uiconst.CENTER, width=64, height=64, texturePath=random.choice(self._obstacleTexturePaths), color=getColorUIHighlight(), opacity=0.0)
    616         self.frame = Sprite(parent=self, align=uiconst.CENTER, width=64, height=64, texturePath='res:/UI/Texture/classes/hacking/tileIconFrame.png', color=getColorUIHighlight(), opacity=0.0)
    617         self.back = Sprite(parent=self, align=uiconst.CENTER, width=48, height=48, texturePath=res('hacking_NodeExploredBackground.png'), opacity=0.0)
    618         self.noise = Sprite(parent=self, align=uiconst.CENTER, width=150, height=100, color=getColorUIHighlight(), opacity=0.25, textureSecondaryPath='res:/UI/Texture/classes/hacking/healRing1.png', spriteEffect=trinity.TR2_SFX_MASK, blendMode=trinity.TR2_SBM_ADD)
    619 
    620     def AnimReveal(self):
    621         animations.FadeOut(self.node, duration=0.2)
    622         animations.FadeIn(self.frame, endVal=0.5, duration=0.2)
    623         animations.FadeIn(self.back, duration=0.3)
    624         animations.SpGlowFadeOut(self.frame, duration=0.3, timeOffset=0.2)
    625         animations.MorphScalar(self.frame, 'glowExpand', startVal=0.5, endVal=10.0, duration=0.3, timeOffset=0.2)
    626         animations.BlinkIn(self.virus, sleep=True)
    627         animations.SpColorMorphTo(self.virus, endColor=(0.2, 0.2, 0.2, 1.0))
    628         animations.SpColorMorphTo(self.frame, endColor=(0.3, 0.3, 0.3, 0.3))
    629         uthread.new(self.AnimPulseNoise)
    630 
    631     def AnimPulseNoise(self):
    632         offset = 2.0 + random.random() * 12.0
    633         self.noise.SetTexturePath(random.choice(self._noiseTexturePaths))
    634         animations.MorphVector2(self.noise, 'scaleSecondary', startVal=(5.0, 5.0), endVal=(0.25, 0.25), duration=4.0, timeOffset=offset, sleep=True)
    635         if not self.destroyed:
    636             uthread.new(self.AnimPulseNoise)
    637 
    638 
    639 class HintTooltip(TooltipPersistentPanel):
    640     default_pointerDirection = uiconst.POINT_LEFT_2
    641 
    642     def ApplyAttributes(self, attributes):
    643         super(HintTooltip, self).ApplyAttributes(attributes)
    644         self.owner = attributes.get('owner')
    645         self.pickState = uiconst.TR2_SPS_ON
    646         uicore.uilib.RegisterForTriuiEvents(uiconst.UI_MOUSEMOVEDRAG, self.OnGlobalMouseMoveDrag)
    647 
    648     def LoadTooltip(self, panel, owner):
    649         self.AddLabelMedium(text=localization.GetByLabel('UI/Crate/HackingHint'), padding=(24, 8, 24, 8))
    650 
    651     def ShowPanel(self, owner):
    652         animations.FadeTo(self, duration=1.5)
    653 
    654     def OnClick(self, *args):
    655         FadeOutPanelAndClose(self)
    656 
    657     def OnGlobalMouseMoveDrag(self, *args):
    658         if not self.destroyed:
    659             RefreshPanelPosition(self)
    660             return True
    661         return False