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

tournamentRefereeTools.py (74973B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\devtools\script\tournamentRefereeTools.py
      2 from collections import namedtuple
      3 import blue
      4 import carbonui.const as uiconst
      5 import evetypes
      6 import localization
      7 import telemetry
      8 import tournamentmanagement.const as tourneyConst
      9 import uthread
     10 import utillib
     11 from carbon.common.script.sys.service import Service
     12 from carbon.common.script.util import timerstuff
     13 from carbon.common.script.util.timerstuff import AutoTimer
     14 from carbonui.control.button import Button
     15 from carbonui.control.combo import Combo
     16 from carbonui.control.scrollentries import SE_BaseClassCore
     17 from carbonui.control.singlelineedits.singleLineEditText import SingleLineEditText
     18 from carbonui.primitives.container import Container
     19 from carbonui.primitives.containerAutoSize import ContainerAutoSize
     20 from carbonui.primitives.fill import Fill
     21 from carbonui.primitives.flowcontainer import FlowContainer
     22 from carbonui.primitives.layoutGrid import LayoutGrid
     23 from carbonui.uicore import uicore
     24 from carbonui.util.various_unsorted import MapIcon, SortListOfTuples, Transplant
     25 from eve.client.script.ui.control.entries.generic import Generic
     26 from eve.client.script.ui.control.entries.util import GetFromClass
     27 from eve.client.script.ui.control.eveIcon import Icon
     28 from carbonui.control.window import Window
     29 from eve.client.script.ui.control.eveLabel import EveLabelMedium
     30 from eve.client.script.ui.util import uix
     31 from eve.client.script.ui.control import eveIcon, eveLabel, eveScroll
     32 from eve.common.lib import appConst as const
     33 from eve.common.script.net import eveMoniker
     34 from eveexceptions import UserError
     35 from eveservices.menu import GetMenuService
     36 from player_messaging.client.ui_message import prompt_player_fsd_dialog
     37 EntryRow = namedtuple('EntryRow', 'character groupName typeName maxDistance distance')
     38 EntryRow.__guid__ = 'at.EntryRow'
     39 WINDOW_WIDTH = 525
     40 MINCOLWIDTH = 16
     41 PREGAME_RANGE_LINES = 34
     42 
     43 class ColumnLine(SE_BaseClassCore):
     44     __guid__ = 'listentry.ColumnLine'
     45 
     46     def __init__(self, **kwargs):
     47         self.overlay = None
     48         super(ColumnLine, self).__init__(**kwargs)
     49 
     50     @telemetry.ZONE_METHOD
     51     def Startup(self, args):
     52         self._clicks = 0
     53         self.clickTimer = None
     54         self.columns = []
     55         self.cursor = uiconst.UICURSOR_DEFAULT
     56 
     57     @telemetry.ZONE_METHOD
     58     def Load(self, node):
     59         self.LoadLite(node)
     60         node.needReload = 0
     61         if node.Get('selectable', 1) and node.Get('selected', 0):
     62             self.Select()
     63         else:
     64             self.Deselect()
     65         if self.overlay is not None:
     66             if self.overlay.parent == self:
     67                 self.overlay.SetParent(None)
     68             self.overlay = None
     69         if node.Get('overlay', None) is not None:
     70             node.overlay.SetParent(self, idx=0)
     71             self.overlay = node.overlay
     72         self.UpdateOverlay()
     73         self.UpdateTriangles()
     74 
     75     def _OnSizeChange_NoBlock(self, *args):
     76         self.UpdateOverlay()
     77 
     78     def UpdateOverlay(self):
     79         if self.overlay is not None:
     80             customTabstops = GetCustomTabstops(self.sr.node.columnID)
     81             if customTabstops:
     82                 totalColWidth = sum(customTabstops)
     83             else:
     84                 totalColWidth = sum([ each.width for each in self.columns ])
     85             self.overlay.left = max(totalColWidth, self.width - self.overlay.width)
     86 
     87     def LoadLite(self, node):
     88         i = 0
     89         for each in node.texts:
     90             self.LoadColumn(i, each)
     91             i += 1
     92 
     93         for each in self.columns[i:]:
     94             each.Close()
     95 
     96         self.columns = self.columns[:i]
     97         self.UpdateColumnOrder(0)
     98 
     99     @telemetry.ZONE_METHOD
    100     def UpdateColumnOrder(self, updateEntries = 1, onlyVisible = False):
    101         displayOrder = settings.user.ui.Get('columnDisplayOrder_%s' % self.sr.node.columnID, None) or [ i for i in xrange(len(self.columns)) ]
    102         customTabstops = GetCustomTabstops(self.sr.node.columnID)
    103         if displayOrder is not None and len(displayOrder) == len(self.columns):
    104             left = 0
    105             for columnIdx in displayOrder:
    106                 colWidth = customTabstops[columnIdx]
    107                 col = self.columns[columnIdx]
    108                 col.left = left
    109                 col.width = colWidth
    110                 if not self.sr.node.Get('editable', 0):
    111                     col.width -= 2
    112                 left += colWidth
    113 
    114         self.sr.node.customTabstops = customTabstops
    115         if updateEntries:
    116             associates = self.FindAssociatingEntries()
    117             for node in associates:
    118                 if node.panel and (not onlyVisible or onlyVisible and node.panel.state != uiconst.UI_HIDDEN):
    119                     node.panel.UpdateColumnOrder(0)
    120                     node.panel.UpdateOverlay()
    121 
    122     def OnMouseEnter(self, *args):
    123         SE_BaseClassCore.OnMouseEnter(self, *args)
    124         if self.sr.node:
    125             if self.sr.node.Get('OnMouseEnter', None):
    126                 self.sr.node.OnMouseEnter(self)
    127 
    128     def OnMouseExit(self, *args):
    129         SE_BaseClassCore.OnMouseExit(self, *args)
    130         if self.sr.node:
    131             if self.sr.node.Get('OnMouseExit', None):
    132                 self.sr.node.OnMouseExit(self)
    133 
    134     def OnClick(self, *args):
    135         if self.sr.node:
    136             if self.sr.node.Get('selectable', 1):
    137                 self.sr.node.scroll.SelectNode(self.sr.node)
    138             prompt_player_fsd_dialog('ListEntryClick')
    139             if self.sr.node.Get('OnClick', None):
    140                 self.sr.node.OnClick(self)
    141 
    142     def GetMenu(self):
    143         if self.sr.node and self.sr.node.Get('scroll', None) and getattr(self.sr.node.scroll, 'GetSelectedNodes', None):
    144             self.sr.node.scroll.GetSelectedNodes(self.sr.node)
    145         if self.sr.node and self.sr.node.Get('GetMenu', None):
    146             return self.sr.node.GetMenu(self)
    147         if getattr(self, 'itemID', None) or getattr(self, 'typeID', None):
    148             return GetMenuService().GetMenuFromItemIDTypeID(getattr(self, 'itemID', None), getattr(self, 'typeID', None))
    149         return []
    150 
    151     def LoadColumn(self, idx, textOrObject):
    152         node = self.sr.node
    153         if len(self.columns) > idx:
    154             col = self.columns[idx]
    155             col.height = self.height
    156         else:
    157             col = Container(name='column_%s' % idx, parent=self, align=uiconst.TOPLEFT, height=self.height, clipChildren=1, state=uiconst.UI_PICKCHILDREN, idx=0)
    158             col.textCtrl = eveLabel.Label(text='', parent=col, fontsize=self.sr.node.Get('fontsize', 12), letterspace=self.sr.node.Get('letterspace', 0), uppercase=self.sr.node.Get('uppercase', 0), state=uiconst.UI_DISABLED, align=uiconst.CENTERLEFT, maxLines=1)
    159             col.textCtrl.left = self.sr.node.Get('padLeft', 6)
    160             col.editHandle = None
    161             col.triangle = None
    162             col.columnIdx = idx
    163             col.OnDblClick = (self.OnHeaderDblClick, col)
    164             col.OnClick = (self.OnHeaderClick, col)
    165             col.GetMenu = lambda *args: self.OnHeaderGetMenu(col)
    166             col.cursor = uiconst.UICURSOR_DEFAULT
    167             self.columns.append(col)
    168         for each in col.children[:]:
    169             if each not in (col.textCtrl,
    170              col.editHandle,
    171              col.triangle,
    172              textOrObject):
    173                 each.Close()
    174 
    175         if isinstance(textOrObject, basestring):
    176             col.textCtrl.state = uiconst.UI_DISABLED
    177             col.textCtrl.text = textOrObject
    178         else:
    179             col.textCtrl.state = uiconst.UI_HIDDEN
    180             if textOrObject not in col.children:
    181                 Transplant(textOrObject, col, 0)
    182         if self.sr.node.Get('editable', 0):
    183             col.state = uiconst.UI_NORMAL
    184             if col.editHandle:
    185                 col.editHandle.state = uiconst.UI_NORMAL
    186             else:
    187                 par = Container(name='scaleHandle_%s' % idx, parent=col, align=uiconst.TOPRIGHT, height=self.height - 2, width=5, idx=0, state=uiconst.UI_NORMAL)
    188                 par.OnMouseDown = (self.StartScaleCol, par)
    189                 par.OnMouseUp = (self.EndScaleCol, par)
    190                 par.OnMouseMove = (self.ScalingCol, par)
    191                 par.cursor = uiconst.UICORSOR_HORIZONTAL_RESIZE
    192                 par.columnIdx = idx
    193                 col.editHandle = par
    194         else:
    195             col.state = uiconst.UI_PICKCHILDREN
    196             if col.editHandle:
    197                 col.editHandle.state = uiconst.UI_HIDDEN
    198 
    199     def FindAssociatingEntries(self):
    200         ret = []
    201         for node in self.sr.node.scroll.GetNodes()[self.sr.node.idx + 1:]:
    202             if node.Get('columnID', None) == self.sr.node.columnID:
    203                 ret.append(node)
    204 
    205         return ret
    206 
    207     def GetHeight(self, *args):
    208         node, _ = args
    209         node.height = uix.GetTextHeight(''.join([ text for text in node.texts if isinstance(text, basestring) ]), maxLines=1) + 1
    210         return node.height
    211 
    212     def StartScaleCol(self, sender, *args):
    213         if uicore.uilib.rightbtn:
    214             return
    215         l, t, w, h = sender.parent.GetAbsolute()
    216         sl, st, sw, sh = sender.GetAbsolute()
    217         associates = self.FindAssociatingEntries()
    218         self._startScalePosition = uicore.uilib.x
    219         self._startScalePositionDiff = sl - uicore.uilib.x
    220         self._scaleColumnIdx = sender.columnIdx
    221         self._scaleColumnInitialWidth = sender.parent.width
    222         self._minLeft = l + MINCOLWIDTH
    223         self.scaleEntries = associates
    224         self.ScalingCol(sender)
    225 
    226     def ScalingCol(self, sender, *args):
    227         if getattr(self, '_startScalePosition', None):
    228             diff = uicore.uilib.x - self._startScalePosition
    229             sender.parent.width = max(MINCOLWIDTH, self._scaleColumnInitialWidth + diff)
    230             self.sr.node.customTabstops[self._scaleColumnIdx] = sender.parent.width
    231             self.UpdateColumnOrder(onlyVisible=True)
    232 
    233     def EndScaleCol(self, sender, *args):
    234         prefsID = self.sr.node.Get('columnID', None)
    235         if prefsID:
    236             current = settings.user.ui.Get('listentryColumns_%s' % prefsID, self.sr.node.customTabstops)
    237             current[self._scaleColumnIdx] = sender.parent.width
    238             settings.user.ui.Set('listentryColumns_%s' % prefsID, current)
    239         self.sr.node.customTabstops[self._scaleColumnIdx] = sender.parent.width
    240         self.scaleEntries = None
    241         self._startScalePosition = 0
    242 
    243     def ChangeSort(self, sender, *args):
    244         columnID = self.sr.node.Get('columnID', None)
    245         if columnID:
    246             current = settings.user.ui.Get('columnSorts_%s' % columnID, {})
    247             if sender.columnIdx in current:
    248                 direction = not current[sender.columnIdx]
    249             else:
    250                 direction = False
    251             current[sender.columnIdx] = direction
    252             settings.user.ui.Set('columnSorts_%s' % columnID, current)
    253             current = settings.user.ui.Get('activeSortColumns', {})
    254             current[columnID] = sender.columnIdx
    255             settings.user.ui.Set('activeSortColumns', current)
    256         self.UpdateTriangles()
    257         self.UpdateColumnOrder()
    258         associates = self.FindAssociatingEntries()
    259         self.UpdateColumnSort(associates, columnID)
    260         callback = self.sr.node.Get('OnSortChange', None)
    261         if callback:
    262             callback()
    263 
    264     def UpdateColumnSort(self, entries, columnID):
    265         if not entries:
    266             return
    267         startIdx = entries[0].idx
    268         endIdx = entries[-1].idx
    269         entries = SortColumnEntries(entries, columnID)
    270         self.sr.node.scroll.sr.nodes = self.sr.node.scroll.sr.nodes[:startIdx] + entries + self.sr.node.scroll.sr.nodes[endIdx + 1:]
    271         idx = 0
    272         for entry in self.sr.node.scroll.GetNodes()[startIdx:]:
    273             if entry.Get('needReload', 0) and entry.panel:
    274                 entry.panel.LoadLite(entry)
    275             idx += 1
    276 
    277         self.sr.node.scroll.UpdatePosition()
    278 
    279     def GetSortDirections(self):
    280         prefsID = self.sr.node.Get('columnID', None)
    281         if prefsID:
    282             return settings.user.ui.Get('columnSorts_%s' % prefsID, {})
    283         return {}
    284 
    285     def OnHeaderDblClick(self, sender, *args):
    286         self._clicks += 1
    287         self.ExecClick(sender)
    288 
    289     def OnHeaderClick(self, sender, *args):
    290         self._clicks += 1
    291         self.clickTimer = AutoTimer(250, self.ExecClick, sender)
    292 
    293     def OnHeaderGetMenu(self, sender, *args):
    294         m = [(localization.GetByLabel('UI/Control/Entries/ColumnMoveForward'), self.ChangeColumnOrder, (sender, -1)), (localization.GetByLabel('UI/Control/Entries/ColumnMoveBackward'), self.ChangeColumnOrder, (sender, 1))]
    295         return m
    296 
    297     def ChangeColumnOrder(self, column, direction):
    298         currentDisplayOrder = settings.user.ui.Get('columnDisplayOrder_%s' % self.sr.node.columnID, None) or [ i for i in xrange(len(self.sr.node.texts)) ]
    299         newDisplayOrder = currentDisplayOrder[:]
    300         currentlyInDisplayOrder = currentDisplayOrder.index(column.columnIdx)
    301         newDisplayOrder.pop(currentlyInDisplayOrder)
    302         newDisplayOrder.insert(max(0, direction + currentlyInDisplayOrder), column.columnIdx)
    303         settings.user.ui.Set('columnDisplayOrder_%s' % self.sr.node.columnID, newDisplayOrder)
    304         self.UpdateColumnOrder()
    305 
    306     def ExecClick(self, sender, *args):
    307         if self._clicks > 1:
    308             self.ResetColumn(sender)
    309         elif self._clicks == 1:
    310             self.ChangeSort(sender)
    311         if not self.destroyed:
    312             self._clicks = 0
    313             self.clickTimer = None
    314 
    315     def ResetColumn(self, sender, *args):
    316         shift = uicore.uilib.Key(uiconst.VK_SHIFT)
    317         if shift:
    318             idxs = []
    319             for i in xrange(len(self.sr.node.texts)):
    320                 idxs.append(i)
    321 
    322         else:
    323             idxs = [sender.columnIdx]
    324         associates = self.FindAssociatingEntries()
    325         for columnIdx in idxs:
    326             textsInColumn = []
    327             columnWidths = []
    328             for node in [self.sr.node] + associates:
    329                 text = node.texts[columnIdx]
    330                 textsInColumn.append(text)
    331                 padLeft = node.Get('padLeft', 6)
    332                 padRight = node.Get('padRight', 6)
    333                 fontsize = node.Get('fontsize', 12)
    334                 hspace = node.Get('letterspace', 0)
    335                 uppercase = node.Get('uppercase', 0)
    336                 extraSpace = 0
    337                 if node is self.sr.node and self.sr.node.Get('editable', 0):
    338                     extraSpace = 10
    339                 if isinstance(text, basestring):
    340                     textWidth = uix.GetTextWidth(text, fontsize, hspace, uppercase)
    341                     if text:
    342                         columnWidths.append(padLeft + textWidth + padRight + 3 + extraSpace)
    343                     else:
    344                         columnWidths.append(3 + extraSpace)
    345                 else:
    346                     textWidth = text.width
    347                     columnWidths.append(text.width)
    348 
    349             self.sr.node.customTabstops[columnIdx] = newWidth = max(columnWidths)
    350             self.UpdateColumnOrder()
    351 
    352     def UpdateTriangles(self):
    353         activeColumn = settings.user.ui.Get('activeSortColumns', {}).get(self.sr.node.columnID, 0)
    354         sortDirections = self.GetSortDirections()
    355         for column in self.columns:
    356             direction = sortDirections.get(column.columnIdx, True)
    357             if column.columnIdx == activeColumn and self.sr.node.Get('editable', 0):
    358                 if not column.triangle:
    359                     column.triangle = Icon(align=uiconst.CENTERRIGHT, pos=(3, 0, 16, 16), parent=column, idx=0, name='directionIcon', icon='ui_1_16_16')
    360                 column.triangle.state = uiconst.UI_DISABLED
    361                 if direction == 1:
    362                     MapIcon(column.triangle, 'ui_1_16_16')
    363                 else:
    364                     MapIcon(column.triangle, 'ui_1_16_15')
    365             elif column.triangle:
    366                 column.triangle.state = uiconst.UI_HIDDEN
    367 
    368     @classmethod
    369     def GetCopyData(cls, node):
    370         displayOrder = settings.user.ui.Get('columnDisplayOrder_%s' % node.columnID, None) or [ i for i in xrange(len(node.texts)) ]
    371         retString = []
    372         for i in displayOrder:
    373             if i <= len(node.texts) - 1:
    374                 retString.append(node.texts[i])
    375 
    376         return '<t>'.join(retString)
    377 
    378 
    379 def GetCustomTabstops(columnID):
    380     return settings.user.ui.Get('listentryColumns_%s' % columnID, None)
    381 
    382 
    383 def SortColumnEntries(nodes, columnID):
    384     if not nodes:
    385         return nodes
    386     displayOrder = settings.user.ui.Get('columnDisplayOrder_%s' % columnID, None) or [ i for i in xrange(len(nodes[0].sortData)) ]
    387     c = 0
    388     sortData = []
    389     for node in nodes:
    390         if not c:
    391             c = len(node.sortData)
    392         elif c != len(node.sortData):
    393             raise RuntimeError('Mismatch in column sizes')
    394         sortData.append((ReorderSortData(node.sortData[:], columnID, displayOrder), node))
    395 
    396     sortDirections = settings.user.ui.Get('columnSorts_%s' % columnID, [0, {}])
    397     sortData = SortListOfTuples(sortData)
    398     activeColumn = settings.user.ui.Get('activeSortColumns', {}).get(columnID, 0)
    399     if activeColumn in sortDirections and sortDirections[activeColumn] is False:
    400         sortData.reverse()
    401     return sortData
    402 
    403 
    404 def ReorderSortData(sortData, columnID, displayOrder):
    405     if not sortData:
    406         return sortData
    407     if len(displayOrder) != len(sortData):
    408         return sortData
    409     ret = []
    410     activeColumn = settings.user.ui.Get('activeSortColumns', {}).get(columnID, 0)
    411     if activeColumn in displayOrder:
    412         di = displayOrder.index(activeColumn)
    413     else:
    414         di = 0
    415     for columnIdx in displayOrder[di:]:
    416         ret.append(sortData[columnIdx])
    417 
    418     return ret
    419 
    420 
    421 def InitCustomTabstops(columnID, entries):
    422     idxs = []
    423     for i in xrange(len(entries[0].texts)):
    424         idxs.append(i)
    425 
    426     if not len(idxs):
    427         return
    428     current = GetCustomTabstops(columnID)
    429     if current is not None:
    430         if len(current) == len(idxs):
    431             return
    432     retval = []
    433     for columnIdx in idxs:
    434         textsInColumn = []
    435         columnWidths = []
    436         for node in entries:
    437             text = node.texts[columnIdx]
    438             textsInColumn.append(text)
    439             padLeft = node.Get('padLeft', 6)
    440             padRight = node.Get('padRight', 6)
    441             fontsize = node.Get('fontsize', 12)
    442             hspace = node.Get('letterspace', 0)
    443             uppercase = node.Get('uppercase', 0)
    444             if isinstance(text, basestring):
    445                 textWidth = uix.GetTextWidth(text, fontsize, hspace, uppercase)
    446             else:
    447                 textWidth = text.width
    448             extraSpace = 0
    449             if node.Get('editable', 0):
    450                 extraSpace = 10
    451             columnWidths.append(padLeft + textWidth + padRight + 3 + extraSpace)
    452 
    453         retval.append(max(columnWidths))
    454 
    455     settings.user.ui.Set('listentryColumns_%s' % columnID, retval)
    456 
    457 
    458 class AtCompetitorEntry(ColumnLine):
    459     __guid__ = 'listentry.AtCompetitorEntry'
    460 
    461     def Startup(self, args):
    462         super(AtCompetitorEntry, self).Startup(args)
    463         self.warn = Fill(name='warn', parent=self, padTop=1, color=(1.0, 0.0, 0.0, 0.125), state=uiconst.UI_HIDDEN)
    464 
    465 
    466 class TournamentRefereeTool(Window):
    467     __guid__ = 'form.TournamentRefereeTool'
    468     __notifyevents__ = ['OnCompetitorTrackingUpdate', 'OnCompetitorTrackingStart', 'OnRemovalAsRef']
    469     default_arenaRadius = 125
    470     default_windowID = 'TournamentRefereeTool'
    471 
    472     def ApplyAttributes(self, attributes):
    473         Window.ApplyAttributes(self, attributes)
    474         self.SetCaption('Tournament Referee Tool')
    475         self.SetMinSize((WINDOW_WIDTH, 550))
    476         self.centerItemID = None
    477         self.arenaRadius = 125000
    478         self.isToggling = False
    479         self.playersCanLock = True
    480         self.playersCanWarp = True
    481         self.playersCanLaunchDrones = False
    482         self.matchDetails = [None,
    483          None,
    484          None,
    485          None]
    486         self.ConstructLayout()
    487         sm.RegisterForNotifyEvent(self, 'ProcessTournamentMatchUpdate')
    488         self.solarsystemID = None
    489         self.pregameRangeTimer = None
    490         self.countdownTimer = None
    491 
    492     def ConstructLayout(self):
    493         localTournamentService = sm.GetService('allianceTournamentSvc')
    494         tourneyMgr = sm.RemoteSvc('tourneyMgr')
    495         tournaments, tourneySystems, activeSystems = tourneyMgr.GetTourneySetup()
    496         locations = []
    497         for systemID, (_combatBeacons, _warpBubbles) in tourneySystems.iteritems():
    498             locations.append(systemID)
    499 
    500         uthread.new(cfg.evelocations.Prime, locations)
    501         self.beaconOptions = {'red': [('1A', 0),
    502                  ('1B', 1),
    503                  ('1C', 2),
    504                  ('1D', 3)],
    505          'blue': [('2A', 0),
    506                   ('2B', 1),
    507                   ('2C', 2),
    508                   ('2D', 3)]}
    509         self.resetButton = Button(label='Reset Match', parent=self.content, align=uiconst.TOBOTTOM, func=self.ResetMatch, state=uiconst.UI_HIDDEN, padding=(0, 4, 0, 0))
    510         tourneyOptions = []
    511         for tourneyID, name in tournaments.iteritems():
    512             if len(tourneyMgr.GetPotentialMatches(tourneyID)) > 0:
    513                 tourneyOptions.append((name, tourneyID))
    514 
    515         systemOptions = []
    516         defaultSelectSolarSystem = '__random__'
    517         for systemID in tourneySystems:
    518             systemName = cfg.evelocations.Get(systemID).locationName
    519             if systemID in activeSystems:
    520                 systemName = '!' + systemName + ' - IN USE!'
    521             systemOptions.append((systemName, systemID))
    522             if systemID == session.solarsystemid:
    523                 defaultSelectSolarSystem = systemID
    524 
    525         self.allianceSelections = ContainerAutoSize(name='aliSelect', parent=self.content, align=uiconst.TOTOP)
    526         self.pointOptions = []
    527         self.banOptions = []
    528         self.timeSelected = 600
    529         self.overtimeSelected = 300
    530 
    531         def TourneySelChanged(_combo, _tName, tourneyID):
    532             if tourneyID is None:
    533                 self.matchSelect.options = []
    534             else:
    535                 possibleMatches = tourneyMgr.GetPotentialMatches(tourneyID)
    536                 menuOptions = []
    537                 allDetails = tourneyMgr.GetAllCurrentSeriesMatchDetails(tourneyID)
    538                 for seriesID, details in possibleMatches.iteritems():
    539                     seriesText = '[S%d M%d/%d] %s vs %s' % (seriesID + 1,
    540                      allDetails[seriesID][0] + 1,
    541                      allDetails[seriesID][1],
    542                      details['redTeam'],
    543                      details['blueTeam'])
    544                     if details['system'] > 0:
    545                         seriesText += ' - {}'.format(cfg.evelocations.Get(details['system']).locationName)
    546                     menuOptions.append((seriesText, (seriesID, details['redTeam'], details['blueTeam'])))
    547 
    548                 self.matchSelect.LoadOptions(menuOptions)
    549                 tournamentDetails = tourneyMgr.QueryTournamentDetails(tourneyID)
    550                 ships = tournamentDetails['allowedShipList']
    551                 self.banOptions = sorted(ships)
    552                 maxPoints = tournamentDetails['maximumPointsMatch']
    553                 self.pointOptions = [(str(maxPoints), maxPoints)]
    554                 self.pointsSelect.LoadOptions(self.pointOptions)
    555                 self.timeSelected = tournamentDetails['matchLength']
    556                 self.overtimeSelected = tournamentDetails['overtimeLength']
    557                 self.timeSelect.SelectItemByValue(self.timeSelected)
    558                 self.overtimeSelect.SelectItemByValue(self.overtimeSelected)
    559                 self.externalRulesetID = tournamentDetails['externalRulesetID']
    560 
    561         self.tourneySelect = Combo(label='Select Tournament:', parent=self.allianceSelections, align=uiconst.TOTOP, padding=(0, 16, 0, 24), name='tourneySel', options=[('Select Tourney', None)] + tourneyOptions, callback=TourneySelChanged)
    562         self.matchSelect = Combo(label='Select Match:', parent=self.allianceSelections, align=uiconst.TOTOP, padding=(0, 0, 0, 24), name='matchSel', options=[('Pick Tourney First', None)])
    563         self.solSystemSelect = Combo(label='Select Solar System', parent=self.allianceSelections, align=uiconst.TOTOP, options=systemOptions, pos=(0, 0, 0, 0), padding=(0, 0, 0, 24), select=defaultSelectSolarSystem)
    564         self.pointsSelect = Combo(label='Points Allowed', parent=self.allianceSelections, align=uiconst.TOTOP, name='pointSel', options=self.pointOptions, padding=(0, 0, 0, 24))
    565         self.timeSelect = Combo(label='Match Length', parent=self.allianceSelections, align=uiconst.TOTOP, name='timeSel', options=(('10:00', 600),
    566          ('9:00', 540),
    567          ('8:00', 480),
    568          ('7:00', 420),
    569          ('6:00', 360),
    570          ('5:00', 300),
    571          ('2:00', 120)), select=self.timeSelected, padding=(0, 0, 0, 24))
    572         self.overtimeSelect = Combo(label='Overtime Length', parent=self.allianceSelections, align=uiconst.TOTOP, name='timeSel', options=(('5:00', 300),
    573          ('4:00', 240),
    574          ('3:00', 180),
    575          ('2:00', 120),
    576          ('1:00', 60),
    577          ('None', 0)), select=self.overtimeSelected, padding=(0, 0, 0, 8))
    578         self.startButton = Button(label='Start Match', parent=self.allianceSelections, align=uiconst.TOTOP, pos=(0, 0, 0, 0), padding=(0, 0, 0, 8), func=self.StartMatch)
    579         self.teleportButtonSystem = Button(label='Teleport Self to System', parent=self.allianceSelections, align=uiconst.TOTOP, pos=(0, 0, 0, 0), padding=(0, 0, 0, 8), func=self.TeleportRef)
    580         self.teleportButtonHome = Button(label='Teleport Self Home', parent=self.allianceSelections, align=uiconst.TOTOP, pos=(0, 0, 0, 0), padding=(0, 0, 0, 8), func=self.TeleportRefHome)
    581         self.shipPilotCheck = Container(name='shipPilotCheck', parent=self.content, state=uiconst.UI_HIDDEN, align=uiconst.TOALL)
    582         Button(label="LET'S DO THIS", parent=self.shipPilotCheck, align=uiconst.TORIGHT, func=self.CompleteShipPilotCheck)
    583         boxes = Container(name='boxContainer', parent=self.shipPilotCheck, align=uiconst.TOALL)
    584         redTeamBox = Container(name='redTeamBox', parent=boxes, align=uiconst.TOTOP_PROP, height=0.5)
    585         blueTeamBox = Container(name='blueTeamBox', parent=boxes, align=uiconst.TOTOP_PROP, height=0.5)
    586         blueButtons = FlowContainer(name='shipPilotCheckButtons', parent=blueTeamBox, align=uiconst.TOBOTTOM, padding=(0, 16, 4, 4))
    587         redButtons = FlowContainer(name='shipPilotCheckButtons', parent=redTeamBox, align=uiconst.TOBOTTOM, padding=(0, 16, 4, 4))
    588         Button(label='Refresh Info', parent=blueButtons, align=uiconst.TOLEFT, func=self.RefreshShipPilotCheck, args=(1,))
    589         self.blueLockBtn = Button(label='Lock Member List', parent=blueButtons, align=uiconst.TOLEFT, func=self.LockMemberList, args=(1,))
    590         self.blueUnlockBtn = Button(label='Unlock Member List', parent=blueButtons, align=uiconst.TOLEFT, func=self.UnlockMemberList, args=(1,), state=uiconst.UI_HIDDEN)
    591         self.blueTeleportBtn = Button(label='Teleport Players', parent=blueButtons, align=uiconst.TOLEFT, func=self.TeleportPlayers, args=(1,), state=uiconst.UI_HIDDEN)
    592         self.blueTeleportBeacon = Combo(label='Beacon select', parent=blueButtons, align=uiconst.TOLEFT, state=uiconst.UI_HIDDEN, options=[])
    593         self.blueOverFleetBtn = Button(label='Override Fleet Boss charID', parent=blueButtons, align=uiconst.TOLEFT, func=self.OverrideFleetBossID, args=(1,))
    594         self.blueAddPlayerBtn = Button(label='Add Player', parent=blueButtons, align=uiconst.TOLEFT, func=self.AddPlayer, args=(1,), state=uiconst.UI_HIDDEN)
    595         self.blueReturnBtn = Button(label='Return Players', parent=blueButtons, align=uiconst.TOLEFT, func=self.ReturnTeamPlayers, args=(1,), state=uiconst.UI_HIDDEN)
    596         Button(label='Refresh Info', parent=redButtons, align=uiconst.TOLEFT, func=self.RefreshShipPilotCheck, args=(0,))
    597         self.redLockBtn = Button(label='Lock Member List', parent=redButtons, align=uiconst.TOLEFT, func=self.LockMemberList, args=(0,))
    598         self.redUnlockBtn = Button(label='Unlock Member List', parent=redButtons, align=uiconst.TOLEFT, func=self.UnlockMemberList, args=(0,), state=uiconst.UI_HIDDEN)
    599         self.redTeleportBtn = Button(label='Teleport Players', parent=redButtons, align=uiconst.TOLEFT, func=self.TeleportPlayers, args=(0,), state=uiconst.UI_HIDDEN)
    600         self.redTeleportBeacon = Combo(label='Beacon select', parent=redButtons, align=uiconst.TOLEFT, state=uiconst.UI_HIDDEN, options=[])
    601         self.redOverFleetBtn = Button(label='Override Fleet Boss charID', parent=redButtons, align=uiconst.TOLEFT, func=self.OverrideFleetBossID, args=(0,))
    602         self.redAddPlayerBtn = Button(label='Add Player', parent=redButtons, align=uiconst.TOLEFT, func=self.AddPlayer, args=(0,), state=uiconst.UI_HIDDEN)
    603         self.redReturnBtn = Button(label='Return Players', parent=redButtons, align=uiconst.TOLEFT, func=self.ReturnTeamPlayers, args=(0,), state=uiconst.UI_HIDDEN)
    604         self.shipcheckBlueTeamLabel = eveLabel.Label(text='Blue Team', parent=blueTeamBox, color=(0.5, 0.5, 1, 1), align=uiconst.TOTOP)
    605         self.shipcheckRedTeamLabel = eveLabel.Label(text='Red Team', parent=redTeamBox, color=(1, 0.3, 0.3, 1), align=uiconst.TOTOP)
    606         self.shipcheckRedScroll = eveScroll.Scroll(parent=redTeamBox, id='atRedTeamScroll')
    607         self.shipcheckBlueScroll = eveScroll.Scroll(parent=blueTeamBox, id='atBlueTeamScroll')
    608         self.pregameList = Container(name='pregameList', parent=self.content, state=uiconst.UI_HIDDEN, align=uiconst.TOALL)
    609         pregameRangeContainer = Container(name='rangeCont', parent=self.pregameList, align=uiconst.TORIGHT, width=110)
    610         for idx in xrange(PREGAME_RANGE_LINES):
    611             label = eveLabel.Label(text='', parent=pregameRangeContainer, align=uiconst.TOTOP, padTop=5, padLeft=4, maxLines=1)
    612             setattr(self, 'pregameDistance%d' % (idx,), label)
    613 
    614         lockCont = Container(name='locks', parent=self.pregameList, align=uiconst.TOBOTTOM, height=50)
    615         self.pregameLockStatus = eveLabel.EveLabelLarge(text="Player locking: <color='green'>Allowed</color>", parent=lockCont, align=uiconst.TOLEFT_PROP, width=0.5, padTop=10)
    616         tempContainer = Container(parent=lockCont, align=uiconst.TORIGHT_PROP, width=0.5)
    617         Button(label='Toggle Locking', parent=tempContainer, func=self.ToggleLocks, align=uiconst.TORIGHT, padTop=5, padBottom=5)
    618         warpCont = Container(name='warps', parent=self.pregameList, align=uiconst.TOBOTTOM, height=50)
    619         self.pregameWarpStatus = eveLabel.EveLabelLarge(text="Player warping: <color='red'>Disallowed</color>", parent=warpCont, align=uiconst.TOLEFT_PROP, width=0.5, padTop=10)
    620         tempContainer = Container(parent=warpCont, align=uiconst.TORIGHT_PROP, width=0.5)
    621         Button(label='Toggle Warping', parent=tempContainer, func=self.ToggleWarping, align=uiconst.TORIGHT, padTop=5, padBottom=5)
    622         moveCont = Container(name='move', parent=self.pregameList, align=uiconst.TOBOTTOM, height=50)
    623         self.pregameMoveStatus = eveLabel.EveLabelLarge(text="Player movement: <color='red'>Disallowed</color>", parent=moveCont, align=uiconst.TOLEFT_PROP, width=0.5, padTop=10)
    624         tempContainer = Container(parent=moveCont, align=uiconst.TORIGHT_PROP, width=0.5)
    625         Button(label='Toggle Movement', parent=tempContainer, func=self.ToggleMovement, align=uiconst.TORIGHT, padTop=5, padBottom=5)
    626         droneCont = Container(name='drones', parent=self.pregameList, align=uiconst.TOBOTTOM, height=50)
    627         self.pregameDroneStatus = eveLabel.EveLabelLarge(text="Player drones: <color='red'>Disallowed</color>", parent=droneCont, align=uiconst.TOLEFT_PROP, width=0.5, padTop=10)
    628         tempContainer = Container(parent=droneCont, align=uiconst.TORIGHT_PROP, width=0.5)
    629         Button(label='Toggle Drones', parent=tempContainer, func=self.ToggleDrones, align=uiconst.TORIGHT, padTop=5, padBottom=5)
    630         stepOne = Container(name='stepOne', parent=self.pregameList, align=uiconst.TOTOP, height=50)
    631         self.pregameStepOneText = eveLabel.EveLabelLarge(text="Step 1 - Let them warp, don't let them lock", parent=stepOne, align=uiconst.TOLEFT_PROP, width=0.5, padTop=10)
    632         tempContainer = Container(parent=stepOne, align=uiconst.TORIGHT_PROP, width=0.5)
    633         Button(label='Do Step 1', parent=tempContainer, func=self.PreGameStepOne, align=uiconst.TORIGHT, padTop=5, padBottom=5)
    634         stepThree = Container(name='stepThree', parent=self.pregameList, align=uiconst.TOTOP, height=50)
    635         self.pregameStepThreeText = eveLabel.EveLabelLarge(text='Step 2 - Lock warping again', parent=stepThree, align=uiconst.TOLEFT_PROP, width=0.5, padTop=10)
    636         tempContainer = Container(parent=stepThree, align=uiconst.TORIGHT_PROP, width=0.5)
    637         Button(label='Do Step 2', parent=tempContainer, func=self.PreGameStepThree, align=uiconst.TORIGHT, padTop=5, padBottom=5)
    638         stepFour = Container(name='stepFour', parent=self.pregameList, align=uiconst.TOTOP, height=50)
    639         self.pregameStepFourText = eveLabel.EveLabelLarge(text='Step 3 - Start Countdown', parent=stepFour, align=uiconst.TOLEFT_PROP, width=0.5, padTop=10)
    640         tempContainer = Container(parent=stepFour, align=uiconst.TORIGHT_PROP, width=0.5)
    641         Button(label='Do Step 3', parent=tempContainer, func=self.PreGameStepFour, align=uiconst.TORIGHT, padTop=5, padBottom=5)
    642         self.countdownCont = Container(name='Countdown', parent=self.pregameList, align=uiconst.TOALL, state=uiconst.UI_HIDDEN)
    643         self.countdownText = eveLabel.Label(parent=self.countdownCont, align=uiconst.CENTERTOP, fontsize=48, color=(1, 0, 0, 1))
    644         self.countdownAbort = Button(label='Abort!', parent=self.countdownCont, align=uiconst.TOALL, func=self.AbortCountdown, padTop=65, padBottom=28)
    645         self.countdownAbort.width = self.countdownAbort.height = 0
    646         self.countdownAbort.sr.label.fontsize = 32
    647         self.ConstructRangeCheckLayout(localTournamentService)
    648         self.postgameList = Container(name='postgameList', parent=self.content, state=uiconst.UI_HIDDEN, align=uiconst.TOALL)
    649         stepOne = Container(name='stepOne', parent=self.postgameList, align=uiconst.TOTOP, height=50)
    650         self.postgameStepOneText = eveLabel.EveLabelLarge(text='Step 1 - Send everyone back home', parent=stepOne, align=uiconst.TOLEFT_PROP, width=0.75, padTop=10)
    651         tempContainer = Container(parent=stepOne, align=uiconst.TORIGHT_PROP, width=0.2)
    652         stepOneBtn = Button(label='Do Step 1', parent=tempContainer, func=self.PostGameStepOne, align=uiconst.TOLEFT, padTop=5, padBottom=5, state=uiconst.UI_HIDDEN)
    653         stepTwo = Container(name='stepTwo', parent=self.postgameList, align=uiconst.TOTOP, height=50)
    654         self.postgameStepTwoText = eveLabel.EveLabelLarge(text='Step 2 - Clean up the grid', parent=stepTwo, align=uiconst.TOLEFT_PROP, width=0.75, padTop=10)
    655         tempContainer = Container(parent=stepTwo, align=uiconst.TORIGHT_PROP, width=0.2)
    656         stepTwoBtn = Button(label='Do Step 2', parent=tempContainer, func=self.PostGameStepTwo, align=uiconst.TOLEFT, padTop=5, padBottom=5, state=uiconst.UI_HIDDEN)
    657         stepThree = Container(name='stepThree', parent=self.postgameList, align=uiconst.TOBOTTOM, height=50)
    658         self.postgameStepThreeText = eveLabel.EveLabelLarge(text='Step 3 (optional) - Teleport home', parent=stepThree, align=uiconst.TOLEFT_PROP, width=0.75, padTop=10)
    659         tempContainer = Container(parent=stepThree, align=uiconst.TORIGHT_PROP, width=0.2)
    660         stepThreeBtn = Button(label='Do Step 3', parent=tempContainer, func=self.PostGameStepThree, align=uiconst.TOLEFT, padTop=5, padBottom=5, state=uiconst.UI_HIDDEN)
    661         self.postGameButtons = [stepOneBtn, stepTwoBtn, stepThreeBtn]
    662         self.OnCompetitorTrackingStart(localTournamentService.competitorsByShipID)
    663 
    664     def ConstructRangeCheckLayout(self, localTournamentService):
    665         self.rangeCheck = Container(name='range check', state=uiconst.UI_HIDDEN, parent=self.content, align=uiconst.TOALL)
    666         self.layout_grid = LayoutGrid(parent=self.rangeCheck, align=uiconst.TOTOP, columns=3, cellSpacing=(8, 8))
    667         EveLabelMedium(text='Center Item ID', parent=self.layout_grid, align=uiconst.CENTERLEFT)
    668         self.centerItemIDEdit = SingleLineEditText(name='centerItemIDEdit', setvalue=str(session.shipid), width=120)
    669         self.layout_grid.AddCell(self.centerItemIDEdit, colSpan=2)
    670         EveLabelMedium(text='Arena Radius km', parent=self.layout_grid, align=uiconst.CENTERLEFT)
    671         self.arenaRadiusEdit = SingleLineEditText(name='arenaRadiusEdit', setvalue=self.default_arenaRadius, parent=self.layout_grid, ints=(1, 250))
    672         Button(label='Match Completed', parent=self.layout_grid, func=self.MatchCompleted)
    673         startButtonText = 'Stop Tracking' if localTournamentService.isCompetitorsTrackingActive else 'Start'
    674         self.startButton = Button(label=startButtonText)
    675         self.startButton.OnClick = self.OnToggleStart
    676         self.layout_grid.AddCell(self.startButton, colSpan=3)
    677         self.scroll = eveScroll.Scroll(name='competitorsList', parent=self.rangeCheck, align=uiconst.TOALL, padTop=8)
    678 
    679     def WaitForSessionChanges(self, buttons):
    680         blue.synchro.SleepWallclock(11000)
    681         for button in buttons:
    682             button.state = uiconst.UI_NORMAL
    683 
    684     def StartMatch(self, *args):
    685         if session.solarsystemid != self.solSystemSelect.GetValue():
    686             raise UserError('You must be in the selected system to start a match')
    687         self.banningScreen = Container(name='banningScreen', parent=self.content, state=uiconst.UI_HIDDEN, align=uiconst.TOALL, width=5)
    688         self.redTeamBanningHeader = eveLabel.EveLabelLarge(text='RedTeam', parent=self.banningScreen, align=uiconst.TOTOP, color=(1, 0.3, 0.3, 1))
    689         self.redBanBox = ContainerAutoSize(name='redBanBox', parent=self.banningScreen, align=uiconst.TOTOP, top=4, _only_use_callback_when_size_changes=True, callback=self.StartMatchWindowSize)
    690         self.redBans = {}
    691         self.redBanChoice = Combo(label='Red Team Ban:', parent=self.banningScreen, options=self.banOptions, top=20, align=uiconst.TOTOP)
    692         Button(label="Perform Red's Ban", parent=self.banningScreen, func=self.SendBan, args=(0, self.redBanChoice), align=uiconst.TOTOP)
    693         self.blueTeamBanningHeader = eveLabel.EveLabelLarge(text='BlueTeam', parent=self.banningScreen, align=uiconst.TOTOP, top=30, color=(0.5, 0.5, 1, 1))
    694         self.blueBanBox = ContainerAutoSize(name='blueBanBox', parent=self.banningScreen, align=uiconst.TOTOP, top=4, _only_use_callback_when_size_changes=True, callback=self.StartMatchWindowSize)
    695         self.blueBans = {}
    696         self.blueBanChoice = Combo(label='Blue Team Ban:', parent=self.banningScreen, options=self.banOptions, top=20, align=uiconst.TOTOP)
    697         Button(label="Perform Blue's Ban", parent=self.banningScreen, func=self.SendBan, args=(1, self.blueBanChoice), align=uiconst.TOTOP)
    698         autoBanBox = Container(name='autoBanBox', parent=self.banningScreen, align=uiconst.TOTOP, height=200, top=10)
    699         autoBanOverrides = Container(name='autoBanOverrides', parent=autoBanBox, align=uiconst.TOLEFT, width=200)
    700         autoBanOutput = Container(name='autoBanOutput', parent=autoBanBox)
    701         autoBanRedCapt = ContainerAutoSize(name='redCapt', parent=autoBanOverrides, align=uiconst.TOTOP, top=4)
    702         eveLabel.Label(text='Red Captain:', parent=autoBanRedCapt, top=4)
    703         self.autoBanRedCharName = eveLabel.Label(text='?', parent=autoBanRedCapt, top=4, left=100)
    704         self.autoBanRedCharID = SingleLineEditText(name='redCaptID', setvalue='', OnFocusLost=self.LookupBanChars, align=uiconst.TOTOP, parent=autoBanOverrides, top=4)
    705         autoBanBlueCapt = ContainerAutoSize(name='blueCapt', parent=autoBanOverrides, align=uiconst.TOTOP, top=4)
    706         eveLabel.Label(text='Blue Captain:', parent=autoBanBlueCapt, top=4)
    707         self.autoBanBlueCharName = eveLabel.Label(text='?', parent=autoBanBlueCapt, top=4, left=100)
    708         self.autoBanBlueCharID = SingleLineEditText(name='blueCaptID', setvalue='', OnFocusLost=self.LookupBanChars, align=uiconst.TOTOP, parent=autoBanOverrides, top=4)
    709         autoBanNumberBox = Container(name='autoBanNumberBox', parent=autoBanOverrides, align=uiconst.TOTOP, top=4, height=SingleLineEditText.default_height)
    710         eveLabel.Label(text='Number of AutoBans: ', parent=Container(parent=autoBanNumberBox), align=uiconst.CENTERLEFT)
    711         self.autoBanQty = SingleLineEditText(parent=autoBanNumberBox, name='autoBanQty', setvalue='3', align=uiconst.TORIGHT, width=50)
    712         Button(label='Initiate Autobans', parent=autoBanOverrides, func=self.Autobans, align=uiconst.TOTOP, top=4)
    713         eveLabel.Label(text='Auto Ban Output:', parent=autoBanOutput, align=uiconst.TOTOP, top=4, padLeft=125)
    714         self.autoBanText = eveLabel.Label(text='', parent=autoBanOutput, align=uiconst.TOTOP, height=100, top=4, padLeft=125)
    715         Button(label='Finalize Bans', parent=self.banningScreen, func=self.FinalizeBans, top=5, align=uiconst.TOTOP)
    716         selectedMatch = self.matchSelect.GetValue()
    717         self.redTeam = selectedMatch[1]
    718         self.blueTeam = selectedMatch[2]
    719         self.matchMoniker = eveMoniker.Moniker('tourneyMgr', self.solSystemSelect.GetValue())
    720         self.matchDetails = [-1,
    721          None,
    722          None,
    723          None]
    724         matchID, beaconIDs, captainIDs = self.matchMoniker.CreateMatch(self.tourneySelect.GetValue(), selectedMatch[0], self.pointsSelect.GetValue(), self.timeSelect.GetValue(), self.overtimeSelect.GetValue(), self.externalRulesetID)
    725         self.SetCaption('Tournament Match: ' + self.matchSelect.GetKey())
    726         self.matchDetails[0] = matchID
    727         self.matchDetails[3] = beaconIDs
    728         self.autoBanRedCharID.SetValue(str(captainIDs[0]))
    729         self.redTeamBanningHeader.text = 'Red Team - %s' % (self.redTeam,)
    730         self.autoBanBlueCharID.SetValue(str(captainIDs[1]))
    731         self.blueTeamBanningHeader.text = 'Blue Team - %s' % (self.blueTeam,)
    732         self.LookupBanChars()
    733         self.StartMatchWindowSize()
    734 
    735     def StartMatchWindowSize(self):
    736         width, height = self.GetWindowSizeForContentSize(height=self.blueBanBox.height + self.redBanBox.height, width=self.blueBanBox.width + self.redBanBox.width)
    737         self.SetMinSize(size=(WINDOW_WIDTH, 500 + height), refresh=True)
    738 
    739     def ProcessTournamentMatchUpdate(self, matchState):
    740         myMatchID = self.matchDetails[0]
    741         if matchState['matchID'] != myMatchID:
    742             if myMatchID != -1:
    743                 return
    744         uiScreenPerState = {tourneyConst.tournamentStateBanning: [self.banningScreen],
    745          tourneyConst.tournamentStatePregame: [self.shipPilotCheck],
    746          tourneyConst.tournamentStateWarpin: [self.pregameList],
    747          tourneyConst.tournamentStateCountdown: [self.pregameList, self.countdownCont, self.countdownAbort],
    748          tourneyConst.tournamentStateStarting: [self.pregameList, self.countdownCont],
    749          tourneyConst.tournamentStateInProgress: [self.rangeCheck],
    750          tourneyConst.tournamentStateComplete: [self.postgameList],
    751          tourneyConst.tournamentStateClosed: [self.allianceSelections]}
    752         allScreens = (self.allianceSelections,
    753          self.banningScreen,
    754          self.shipPilotCheck,
    755          self.pregameList,
    756          self.rangeCheck,
    757          self.postgameList,
    758          self.countdownCont,
    759          self.countdownAbort)
    760         for screen in allScreens:
    761             if screen in uiScreenPerState[matchState['state']]:
    762                 screen.state = uiconst.UI_NORMAL
    763             else:
    764                 screen.state = uiconst.UI_HIDDEN
    765 
    766         if matchState['state'] == tourneyConst.tournamentStateWarpin:
    767             if self.pregameRangeTimer is None:
    768                 self.pregameRangeTimer = timerstuff.AutoTimer(1000, self.UpdatePregameRanges)
    769         else:
    770             self.pregameRangeTimer = None
    771         if 'matchStartTime' in matchState:
    772             self.matchStartTime = matchState['matchStartTime']
    773         if matchState['state'] in (tourneyConst.tournamentStateCountdown, tourneyConst.tournamentStateStarting):
    774             if self.countdownTimer is None:
    775                 self.UpdateTimer()
    776                 self.countdownTimer = timerstuff.AutoTimer(100, self.UpdateTimer)
    777         else:
    778             self.countdownTimer = None
    779         if matchState['state'] == tourneyConst.tournamentStateInProgress:
    780             if self.centerItemID is None:
    781                 self.startButton.SetLabel('Stop Tracking')
    782                 closestBeacon = None
    783                 ballpark = sm.GetService('michelle').GetBallpark()
    784                 for beaconID in self.matchDetails[3]:
    785                     try:
    786                         distance = ballpark.GetBall(beaconID).surfaceDist
    787                         if not closestBeacon or distance < closestBeacon[1]:
    788                             closestBeacon = (beaconID, distance)
    789                     except AttributeError:
    790                         pass
    791 
    792                 self.centerItemIDEdit.SetValue(str(closestBeacon[0]))
    793                 self.centerItemID = closestBeacon[0]
    794                 self.arenaRadius = 125000
    795                 self.arenaRadiusEdit.SetValue('125')
    796                 localTournamentService = sm.GetService('allianceTournamentSvc')
    797                 localTournamentService.StartTracking(self.centerItemID, self.arenaRadius)
    798         elif self.centerItemID is not None:
    799             localTournamentService = sm.GetService('allianceTournamentSvc')
    800             localTournamentService.StopTracking()
    801             self.centerItemID = None
    802             uthread.new(self.WaitForSessionChanges, self.postGameButtons)
    803         if 'solarsystemID' in matchState:
    804             if self.solarsystemID != matchState['solarsystemID']:
    805                 self.solarsystemID = matchState['solarsystemID']
    806                 self.redTeleportBeacon.LoadOptions(self.beaconOptions['red'])
    807                 self.blueTeleportBeacon.LoadOptions(self.beaconOptions['blue'])
    808 
    809         def CreateBanElement(typeID, parent, teamIdx):
    810 
    811             def RemoveBan(*args):
    812                 self.matchMoniker.UnBanShipType(self.matchDetails[0], typeID, teamIdx)
    813 
    814             banContainer = Container(name='banEle', parent=parent, align=uiconst.TOTOP, height=16)
    815             Button(label='X', parent=banContainer, func=RemoveBan, align=uiconst.TOLEFT)
    816             eveLabel.Label(text=evetypes.GetName(typeID), parent=banContainer, align=uiconst.TOLEFT)
    817             return banContainer
    818 
    819         if 'redTeamBans' in matchState:
    820             redBans = matchState['redTeamBans']
    821             keysToRemove = set(self.redBans.keys())
    822             for typeID in redBans:
    823                 if typeID in keysToRemove:
    824                     keysToRemove.remove(typeID)
    825                 if typeID not in self.redBans:
    826                     self.redBans[typeID] = CreateBanElement(typeID, self.redBanBox, 0)
    827 
    828             for typeID in keysToRemove:
    829                 self.redBans[typeID].Close()
    830                 del self.redBans[typeID]
    831 
    832         if 'blueTeamBans' in matchState:
    833             blueBans = matchState['blueTeamBans']
    834             keysToRemove = set(self.blueBans.keys())
    835             for typeID in blueBans:
    836                 if typeID in keysToRemove:
    837                     keysToRemove.remove(typeID)
    838                 if typeID not in self.blueBans:
    839                     self.blueBans[typeID] = CreateBanElement(typeID, self.blueBanBox, 1)
    840 
    841             for typeID in keysToRemove:
    842                 self.blueBans[typeID].Close()
    843                 del self.blueBans[typeID]
    844 
    845         if 'autoBanText' in matchState:
    846             autoBanText = matchState['autoBanText']
    847             self.autoBanText.text = autoBanText
    848         updatePilotDisplay = False
    849         if 'redTeamDetails' in matchState:
    850             self.matchDetails[1] = matchState['redTeamDetails']
    851             updatePilotDisplay = True
    852         if 'blueTeamDetails' in matchState:
    853             self.matchDetails[2] = matchState['blueTeamDetails']
    854             updatePilotDisplay = True
    855         if updatePilotDisplay:
    856             self.UpdateShipPilotDisplay()
    857         if 'redTeamLocked' in matchState:
    858             if matchState['redTeamLocked']:
    859                 self.redLockBtn.state = uiconst.UI_HIDDEN
    860                 self.redUnlockBtn.state = uiconst.UI_NORMAL
    861                 self.redOverFleetBtn.state = uiconst.UI_HIDDEN
    862                 self.redAddPlayerBtn.state = uiconst.UI_NORMAL
    863                 self.redTeleportBtn.state = uiconst.UI_NORMAL
    864                 self.redTeleportBeacon.state = uiconst.UI_NORMAL
    865             else:
    866                 self.redLockBtn.state = uiconst.UI_NORMAL
    867                 self.redUnlockBtn.state = uiconst.UI_HIDDEN
    868                 self.redOverFleetBtn.state = uiconst.UI_NORMAL
    869                 self.redAddPlayerBtn.state = uiconst.UI_HIDDEN
    870                 self.redTeleportBtn.state = uiconst.UI_HIDDEN
    871                 self.redTeleportBeacon.state = uiconst.UI_HIDDEN
    872         if 'blueTeamLocked' in matchState:
    873             if matchState['blueTeamLocked']:
    874                 self.blueLockBtn.state = uiconst.UI_HIDDEN
    875                 self.blueUnlockBtn.state = uiconst.UI_NORMAL
    876                 self.blueOverFleetBtn.state = uiconst.UI_HIDDEN
    877                 self.blueAddPlayerBtn.state = uiconst.UI_NORMAL
    878                 self.blueTeleportBtn.state = uiconst.UI_NORMAL
    879                 self.blueTeleportBeacon.state = uiconst.UI_NORMAL
    880             else:
    881                 self.blueLockBtn.state = uiconst.UI_NORMAL
    882                 self.blueUnlockBtn.state = uiconst.UI_HIDDEN
    883                 self.blueOverFleetBtn.state = uiconst.UI_NORMAL
    884                 self.blueAddPlayerBtn.state = uiconst.UI_HIDDEN
    885                 self.blueTeleportBtn.state = uiconst.UI_HIDDEN
    886                 self.blueTeleportBeacon.state = uiconst.UI_HIDDEN
    887         if 'warpRestrict' in matchState:
    888             self.playersCanWarp = not matchState['warpRestrict']
    889             if self.playersCanWarp:
    890                 self.pregameWarpStatus.text = "Player warping: <color='green'>Allowed</color>"
    891             else:
    892                 self.pregameWarpStatus.text = "Player warping: <color='red'>Disallowed</color>"
    893         if 'moveRestrict' in matchState:
    894             self.playersCanMove = not matchState['moveRestrict']
    895             if self.playersCanMove:
    896                 self.pregameMoveStatus.text = "Player movement: <color='green'>Allowed</color>"
    897             else:
    898                 self.pregameMoveStatus.text = "Player movement: <color='red'>Disallowed</color>"
    899         if 'lockRestrict' in matchState:
    900             self.playersCanLock = not matchState['lockRestrict']
    901             if self.playersCanLock:
    902                 self.pregameLockStatus.text = "Player locking: <color='green'>Allowed</color>"
    903             else:
    904                 self.pregameLockStatus.text = "Player locking: <color='red'>Disallowed</color>"
    905         if 'droneRestrict' in matchState:
    906             self.playersCanLaunchDrones = not matchState['droneRestrict']
    907             if self.playersCanLaunchDrones:
    908                 self.pregameDroneStatus.text = "Player drones: <color='green'>Allowed</color>"
    909             else:
    910                 self.pregameDroneStatus.text = "Player drones: <color='red'>Disallowed</color>"
    911         if matchState['state'] in (tourneyConst.tournamentStatePregame, tourneyConst.tournamentStateWarpin):
    912             self.resetButton.state = uiconst.UI_NORMAL
    913         else:
    914             self.resetButton.state = uiconst.UI_HIDDEN
    915 
    916     def LookupBanChars(self, *args):
    917         try:
    918             self.autoBanRedCharName.text = cfg.eveowners.Get(int(self.autoBanRedCharID.GetValue())).ownerName
    919         except Exception:
    920             self.autoBanRedCharName.text = '?'
    921 
    922         try:
    923             self.autoBanBlueCharName.text = cfg.eveowners.Get(int(self.autoBanBlueCharID.GetValue())).ownerName
    924         except Exception:
    925             self.autoBanRedCharName.text = '?'
    926 
    927     def Autobans(self, *args):
    928         tourneyID = self.tourneySelect.GetValue()
    929         self.matchMoniker.StartAutobanning(self.matchDetails[0], tourneyID, redCharID=int(self.autoBanRedCharID.GetValue()), blueCharID=int(self.autoBanBlueCharID.GetValue()), totalNumBans=int(self.autoBanQty.GetValue()))
    930 
    931     def FinalizeBans(self, *args):
    932         self.matchMoniker.FinalizeBans(self.matchDetails[0])
    933         self.MakeUnKillable()
    934 
    935     def SendBan(self, teamIdx, comboBox, *args):
    936         curBans = self.matchMoniker.BanShipType(self.matchDetails[0], comboBox.GetValue(), teamIdx)
    937 
    938     def ToggleLocks(self, *args):
    939         self.matchMoniker.OverrideLockRestrict(self.matchDetails[0], self.playersCanLock)
    940 
    941     def ToggleWarping(self, *args):
    942         self.matchMoniker.OverrideWarpRestrict(self.matchDetails[0], self.playersCanWarp)
    943 
    944     def ToggleMovement(self, *args):
    945         self.matchMoniker.OverrideMoveRestrict(self.matchDetails[0], self.playersCanMove)
    946 
    947     def ToggleDrones(self, *args):
    948         self.matchMoniker.OverrideDroneRestrict(self.matchDetails[0], self.playersCanLaunchDrones)
    949 
    950     def CompleteShipPilotCheck(self, *args):
    951         if self.matchMoniker.IsOtherMatchInProgress():
    952             raise UserError('TournamentOtherMatchInSystemInProgress')
    953         problems = []
    954         tharWereErrors = False
    955         if self.redLockBtn.state != uiconst.UI_HIDDEN:
    956             problems.append('Red team membership not locked')
    957         if self.blueLockBtn.state != uiconst.UI_HIDDEN:
    958             problems.append('Blue team membership not locked')
    959         for teamIdx in (1, 2):
    960             for charID, shipType, shipID, locationID, pointVal, errors in self.matchDetails[teamIdx]:
    961                 if errors:
    962                     tharWereErrors = True
    963                 if locationID != self.solarsystemID:
    964                     problems.append("%s doesn't look like they're here" % (cfg.eveowners[charID].ownerName,))
    965 
    966         if tharWereErrors:
    967             problems.append('Errors remain')
    968         if problems:
    969             ret = prompt_player_fsd_dialog('CustomQuestion', {'header': "This doesn't seem right",
    970              'question': 'The following problems remain:<br><br>' + '<br>'.join(problems) + '<br><br>Ignore and get this party started?'}, uiconst.YESNO)
    971             if ret != uiconst.ID_YES:
    972                 return
    973         self.pregameStepOneText.text = "Step 1 - Let them warp, don't let them lock"
    974         self.pregameStepThreeText.text = 'Step 2 - Lock warping again'
    975         self.pregameStepFourText.text = 'Step 3 - Start Countdown'
    976         self.matchMoniker.CompletePregameChecks(self.matchDetails[0])
    977 
    978     def UpdatePregameRanges(self):
    979         bp = sm.GetService('michelle').GetBallpark()
    980         metersInKM = 1000.0
    981         maximumDistance = 55
    982         lines = ["<color='red'>" + self.redTeam]
    983         for charID, shipType, shipID, locationID, pointVal, errors in self.matchDetails[1]:
    984             try:
    985                 dist = bp.GetBall(shipID).surfaceDist
    986                 if dist / metersInKM > maximumDistance:
    987                     color = 'yellow'
    988                 else:
    989                     color = 'red'
    990                 lines.append("<color='%s'> %.1fkm %s" % (color, dist / metersInKM, cfg.eveowners.Get(charID).ownerName))
    991             except Exception:
    992                 lines.append("<color='red'> -- %s" % (cfg.eveowners.Get(charID).ownerName,))
    993 
    994         lines.append("<color='blue'>" + self.blueTeam)
    995         for charID, shipType, shipID, locationID, pointVal, errors in self.matchDetails[2]:
    996             try:
    997                 dist = bp.GetBall(shipID).surfaceDist
    998                 if dist / metersInKM > maximumDistance:
    999                     color = 'yellow'
   1000                 else:
   1001                     color = 'blue'
   1002                 lines.append("<color='%s'> %.1fkm %s" % (color, dist / metersInKM, cfg.eveowners.Get(charID).ownerName))
   1003             except:
   1004                 lines.append("<color='blue'> -- %s" % (cfg.eveowners.Get(charID).ownerName,))
   1005 
   1006         if len(lines) > PREGAME_RANGE_LINES:
   1007             hidden = len(lines) - PREGAME_RANGE_LINES + 1
   1008             lines = lines[:PREGAME_RANGE_LINES - 1]
   1009             lines.append("<color='yellow'>+%d more" % (hidden,))
   1010         lines += [''] * (PREGAME_RANGE_LINES - len(lines))
   1011         for idx, line in enumerate(lines):
   1012             getattr(self, 'pregameDistance%d' % (idx,)).text = line
   1013 
   1014     def PreGameStepOne(self, *args):
   1015         self.matchMoniker.OverrideLockRestrict(self.matchDetails[0], True)
   1016         self.matchMoniker.OverrideWarpRestrict(self.matchDetails[0], False)
   1017         self.pregameStepOneText.text = "<color='green'>%s</color>" % (self.pregameStepOneText.text,)
   1018 
   1019     def PreGameStepThree(self, *args):
   1020         self.matchMoniker.OverrideWarpRestrict(self.matchDetails[0], True)
   1021         self.pregameStepThreeText.text = "<color='green'>%s</color>" % (self.pregameStepThreeText.text,)
   1022 
   1023     def PreGameStepFour(self, *args):
   1024         self.matchMoniker.StartCountdown(self.matchDetails[0])
   1025         self.pregameStepFourText.text = "<color='green'>%s</color>" % (self.pregameStepFourText.text,)
   1026 
   1027     def UpdateTimer(self):
   1028         timeDiffMS = max(0, -blue.os.TimeDiffInMs(self.matchStartTime, blue.os.GetWallclockTime()))
   1029         self.countdownText.text = '%.1f' % (float(timeDiffMS) / 1000.0,)
   1030 
   1031     def AbortCountdown(self, *args):
   1032         stopped = self.matchMoniker.AbortCountdown(self.matchDetails[0])
   1033         if stopped:
   1034             self.pregameStepFourText.text = 'Step 4 - Start Countdown'
   1035 
   1036     def MatchCompleted(self, *args):
   1037         ret = prompt_player_fsd_dialog('CustomQuestion', {'header': 'Is it really over?',
   1038          'question': 'Are you certain you want to prematurely end the match?'}, uiconst.YESNO)
   1039         if ret != uiconst.ID_YES:
   1040             return
   1041         self.matchMoniker.MatchCompleted(self.matchDetails[0])
   1042         self.postgameStepOneText.text = 'Step 1 - Send everyone back home'
   1043         self.postgameStepTwoText.text = 'Step 2 - Clean up the grid'
   1044         self.postgameStepThreeText.text = 'Step 3 (optional) - Teleport home'
   1045 
   1046     def PostGameStepOne(self, *args):
   1047         self.matchMoniker.ReturnAllPlayers(self.matchDetails[0])
   1048         self.postgameStepOneText.text = "<color='green'>%s</color>" % (self.postgameStepOneText.text,)
   1049         self.MakeKillable()
   1050 
   1051     def PostGameStepTwo(self, *args):
   1052         self.matchMoniker.CleanupGrid(self.matchDetails[0])
   1053         self.postgameStepTwoText.text = "<color='green'>%s</color>" % (self.postgameStepTwoText.text,)
   1054 
   1055     def PostGameStepThree(self, *args):
   1056         self.matchMoniker.TeleportRefHome(self.matchDetails[0])
   1057         self.postgameStepThreeText.text = "<color='green'>%s</color>" % (self.postgameStepThreeText.text,)
   1058 
   1059     def OverrideFleetBossID(self, teamIdx, *args):
   1060         format = [{'type': 'edit',
   1061           'key': 'fleetbossid',
   1062           'setfocus': True,
   1063           'label': u'New Fleet Boss Char ID'}]
   1064         retVal = uix.HybridWnd(format, caption=u'Specify new fleet boss charID', windowID='overrideFleetBoss', minW=410, minH=100)
   1065         if retVal:
   1066             newFleetBossID = int(retVal['fleetbossid'])
   1067             if newFleetBossID:
   1068                 self.matchMoniker.OverrideFleetBossID(self.matchDetails[0], teamIdx, newFleetBossID)
   1069 
   1070     def OverrideError(self, teamIdx, charID, errorString):
   1071         ret = prompt_player_fsd_dialog('CustomQuestion', {'header': 'YOU SURE DUDE?!?',
   1072          'question': 'Override<br>%s<br>for pilot %s?' % (errorString, cfg.eveowners.Get(charID).ownerName)}, uiconst.YESNO)
   1073         if ret != uiconst.ID_YES:
   1074             return
   1075         self.matchMoniker.OverrideError(self.matchDetails[0], charID, errorString)
   1076 
   1077     def OverrideAllErrors(self, teamIdx, charID, errorStrings):
   1078         errorList = ''
   1079         for errorString in errorStrings:
   1080             errorList += '<br>' + errorString
   1081 
   1082         ret = prompt_player_fsd_dialog('CustomQuestion', {'header': 'YOU ABSOLUTELY SURE DUDE?!?',
   1083          'question': 'Override ALL ERRORS for pilot %s?<br><br>Errors are: %s' % (cfg.eveowners.Get(charID).ownerName, errorList)}, uiconst.YESNO)
   1084         if ret != uiconst.ID_YES:
   1085             return
   1086         for errorString in errorStrings:
   1087             self.matchMoniker.OverrideError(self.matchDetails[0], charID, errorString)
   1088 
   1089     def RemovePlayer(self, teamIdx, charID):
   1090         ret = prompt_player_fsd_dialog('CustomQuestion', {'header': "JUST CHECKIN'",
   1091          'question': 'Remove %s from their team?' % cfg.eveowners.Get(charID).ownerName}, uiconst.YESNO)
   1092         if ret != uiconst.ID_YES:
   1093             return
   1094         self.matchMoniker.RemovePlayer(self.matchDetails[0], teamIdx, charID)
   1095 
   1096     def GetPrematchPilotMenu(self, node):
   1097         basePilotMenu = GetMenuService().GetMenuFromItemIDTypeID(node.sr.node.id[1], const.typeCharacter)
   1098         errorMenu = []
   1099         for error in node.sr.node.errors:
   1100             errorMenu.append(('Override: %s' % (error,), self.OverrideError, (node.sr.node.id[0], node.sr.node.id[1], error)))
   1101 
   1102         if errorMenu:
   1103             errorMenu.append(None)
   1104         errorMenu.append(('Remove Player', self.RemovePlayer, (node.sr.node.id[0], node.sr.node.id[1])))
   1105         errorMenu.append(None)
   1106         errorMenu.append(('Override all errors (!!!)', self.OverrideAllErrors, (node.sr.node.id[0], node.sr.node.id[1], node.sr.node.errors)))
   1107         return errorMenu + basePilotMenu
   1108 
   1109     def UpdateShipPilotDisplay(self):
   1110         redTeam = []
   1111         pointTotal = 0
   1112         for charID, shipType, shipID, locationID, pointVal, errors in self.matchDetails[1]:
   1113             data = utillib.KeyVal(id=(0, charID), errors=errors, label='%s<t>%s<t>%s<t>%s<t><color=red>%s</color>' % (cfg.eveowners.Get(charID).ownerName,
   1114              evetypes.GetName(shipType),
   1115              cfg.evelocations.Get(locationID).locationName,
   1116              pointVal,
   1117              ' - '.join(errors)), GetMenu=self.GetPrematchPilotMenu, hint='\n'.join(errors) if errors else 'No errors')
   1118             listEntry = GetFromClass(Generic, data)
   1119             redTeam.append(listEntry)
   1120             pointTotal += pointVal
   1121 
   1122         self.shipcheckRedScroll.Load(contentList=redTeam, headers=['Name',
   1123          'Ship Type',
   1124          'Location',
   1125          'Point Value',
   1126          'Errors'], noContentHint='No pilots found')
   1127         self.shipcheckRedTeamLabel.text = 'Red Team - %s - %d points' % (self.redTeam, pointTotal)
   1128         blueTeam = []
   1129         pointTotal = 0
   1130         for charID, shipType, shipID, locationID, pointVal, errors in self.matchDetails[2]:
   1131             data = utillib.KeyVal(id=(1, charID), errors=errors, label='%s<t>%s<t>%s<t>%s<t><color=red>%s</color>' % (cfg.eveowners.Get(charID).ownerName,
   1132              evetypes.GetName(shipType),
   1133              cfg.evelocations.Get(locationID).locationName,
   1134              pointVal,
   1135              ' - '.join(errors)), GetMenu=self.GetPrematchPilotMenu, hint='\n'.join(errors) if errors else 'No errors')
   1136             listEntry = GetFromClass(Generic, data)
   1137             blueTeam.append(listEntry)
   1138             pointTotal += pointVal
   1139 
   1140         self.shipcheckBlueScroll.Load(contentList=blueTeam, headers=['Name',
   1141          'Ship Type',
   1142          'Location',
   1143          'Point Value',
   1144          'Errors'], noContentHint='No pilots found')
   1145         self.shipcheckBlueTeamLabel.text = 'Blue Team - %s - %d points' % (self.blueTeam, pointTotal)
   1146 
   1147     def RefreshShipPilotCheck(self, whichTeam, *args):
   1148         self.matchMoniker.UpdateFleetDetails(self.matchDetails[0], whichTeam)
   1149 
   1150     def LockMemberList(self, whichTeam, *args):
   1151         memberList = [ x[0] for x in self.matchDetails[whichTeam + 1] ]
   1152         self.matchMoniker.LockMemberList(self.matchDetails[0], whichTeam, memberList)
   1153 
   1154     def UnlockMemberList(self, whichTeam, *args):
   1155         self.matchMoniker.UnlockMemberList(self.matchDetails[0], whichTeam)
   1156 
   1157     def AddPlayer(self, whichTeam):
   1158         format = [{'type': 'edit',
   1159           'key': 'charid',
   1160           'setfocus': True,
   1161           'label': u'Char ID to add'}]
   1162         retVal = uix.HybridWnd(format, caption=u'Gimme a dude', windowID='addPlayerToTournament', minW=250, minH=100)
   1163         if retVal:
   1164             newCharID = int(retVal['charid'])
   1165             if newCharID:
   1166                 self.matchMoniker.AddPlayer(self.matchDetails[0], whichTeam, newCharID)
   1167 
   1168     def TeleportPlayers(self, whichTeam, *args):
   1169         if whichTeam == 0:
   1170             beaconIdx = self.redTeleportBeacon.GetValue()
   1171             self.redReturnBtn.state = uiconst.UI_NORMAL
   1172         else:
   1173             beaconIdx = self.blueTeleportBeacon.GetValue()
   1174             self.blueReturnBtn.state = uiconst.UI_NORMAL
   1175         self.matchMoniker.TeleportPlayers(self.matchDetails[0], whichTeam, beaconIdx)
   1176 
   1177     def ReturnTeamPlayers(self, whichTeam, *args):
   1178         self.matchMoniker.ReturnTeamPlayers(self.matchDetails[0], whichTeam)
   1179 
   1180     def TeleportRef(self, *args):
   1181         teleportMoniker = eveMoniker.Moniker('tourneyMgr', self.solSystemSelect.GetValue())
   1182         teleportMoniker.TeleportToArena()
   1183 
   1184     def TeleportRefHome(self, *args):
   1185         teleportMoniker = eveMoniker.Moniker('tourneyMgr', self.solSystemSelect.GetValue())
   1186         teleportMoniker.TeleportToStation()
   1187 
   1188     def ResetMatch(self, *args):
   1189         ret = prompt_player_fsd_dialog('CustomQuestion', {'header': 'Mildly Annoying',
   1190          'question': 'Reseting is a moderate inconveniance, you sure you want that?'}, uiconst.YESNO)
   1191         if ret != uiconst.ID_YES:
   1192             return
   1193         self.matchMoniker.ResetMatch(self.matchDetails[0])
   1194         self.MakeKillable()
   1195 
   1196     def Close(self, *args, **kwds):
   1197         matchID = self.matchDetails[0]
   1198         if matchID > 0:
   1199             self.matchMoniker.RemoveSelfFromReferees(matchID)
   1200         Window.Close(self, *args, **kwds)
   1201 
   1202     def OnRemovalAsRef(self, matchID, *args):
   1203         if matchID == self.matchDetails[0]:
   1204             Window.Close(self)
   1205 
   1206     def OnToggleStart(self, *args):
   1207         if self.isToggling:
   1208             return
   1209         try:
   1210             self.isToggling = True
   1211             self.centerItemID = int(self.centerItemIDEdit.GetValue())
   1212             self.arenaRadius = int(self.arenaRadiusEdit.GetValue()) * 1000
   1213             localTournamentService = sm.GetService('allianceTournamentSvc')
   1214             if localTournamentService.isCompetitorsTrackingActive:
   1215                 localTournamentService.StopTracking()
   1216                 self.startButton.SetLabel('Start')
   1217             else:
   1218                 localTournamentService.StartTracking(self.centerItemID, self.arenaRadius)
   1219                 self.startButton.SetLabel('Stop')
   1220         finally:
   1221             self.isToggling = False
   1222 
   1223     def UpdateColumnSort(self, scroll, entries, columnID):
   1224         if not entries:
   1225             return
   1226         startIdx = entries[0].idx
   1227         endIdx = entries[-1].idx
   1228         entries = SortColumnEntries(entries, columnID)
   1229         scroll.sr.nodes = scroll.sr.nodes[:startIdx] + entries + scroll.sr.nodes[endIdx + 1:]
   1230         idx = 0
   1231         for entry in scroll.GetNodes()[startIdx:]:
   1232             if entry.panel:
   1233                 entry.panel.SetOrder(-1)
   1234             entry.idx = startIdx + idx
   1235             if entry.Get('needReload', 0) and entry.panel:
   1236                 entry.panel.LoadLite(entry)
   1237             idx += 1
   1238 
   1239     def OnCompetitorTrackingUpdate(self, competitorsByShipID):
   1240         scrolllist = []
   1241         for node in self.scroll.GetNodes():
   1242             data = competitorsByShipID.get(node.shipID, None)
   1243             if data:
   1244                 distance = '%0.1f km' % (data.distance * 0.001)
   1245                 maxDistance = '%0.1f km' % (data.maxDistance * 0.001)
   1246                 if distance != node.texts.distance:
   1247                     node.needReload = True
   1248                     node.texts = node.texts._replace(distance=distance, maxDistance=maxDistance)
   1249                     node.sortData = node.sortData._replace(distance=data.distance, maxDistance=data.maxDistance)
   1250                 if data.maxDistance > self.arenaRadius:
   1251                     node.panel.warn.state = uiconst.UI_DISABLED
   1252                     if data.shipLost:
   1253                         node.overlay.state = uiconst.UI_HIDDEN
   1254                     else:
   1255                         node.overlay.state = uiconst.UI_PICKCHILDREN
   1256                 scrolllist.append(node)
   1257 
   1258         self.UpdateColumnSort(self.scroll, scrolllist, 'AtCompetitorsScroll')
   1259 
   1260     def OnCompetitorTrackingStart(self, competitorsByShipID):
   1261         scrolllist = []
   1262         for shipData in competitorsByShipID.values():
   1263             data = utillib.KeyVal()
   1264             data.shipID = shipData.shipID
   1265             data.texts = EntryRow(character=shipData.ownerName, groupName=shipData.groupName, typeName=shipData.typeName, maxDistance='%.1f km' % (shipData.maxDistance * 0.001), distance='%.1f km' % (shipData.distance * 0.001))
   1266             data.sortData = EntryRow(character=shipData.ownerName, groupName=shipData.groupName, typeName=shipData.typeName, maxDistance=shipData.maxDistance, distance=shipData.distance)
   1267             data.columnID = 'AtCompetitorsScroll'
   1268             data.isSelected = False
   1269             data.GetMenu = lambda x: GetMenuService().GetMenuFromItemIDTypeID(shipData.shipID, shipData.typeID)
   1270             iconPar = Container(name='iconParent', parent=None, align=uiconst.TOPLEFT, width=16, height=16, state=uiconst.UI_HIDDEN)
   1271             icon = eveIcon.Icon(parent=iconPar, icon='ui_38_16_182', pos=(0, 0, 16, 16), align=uiconst.RELATIVE)
   1272             icon.hint = 'Destroy Ship'
   1273             icon.shipID = shipData.shipID
   1274             icon.OnClick = (self.DestroyShip, icon)
   1275             data.overlay = iconPar
   1276             entry = GetFromClass(AtCompetitorEntry, data)
   1277             scrolllist.append(entry)
   1278 
   1279         scrolllist = SortColumnEntries(scrolllist, 'AtCompetitorsScroll')
   1280         data = utillib.KeyVal()
   1281         data.texts = ('Character', 'Group', 'Type', 'Max Distance', 'Distance')
   1282         data.columnID = 'AtCompetitorsScroll'
   1283         data.editable = True
   1284         data.showBottomLine = True
   1285         data.selectable = False
   1286         data.hilightable = False
   1287         scrolllist.insert(0, GetFromClass(AtCompetitorEntry, data))
   1288         if scrolllist:
   1289             InitCustomTabstops('AtCompetitorsScroll', scrolllist)
   1290             self.scroll.LoadContent(contentList=scrolllist)
   1291 
   1292     def DestroyShip(self, button):
   1293         localTournamentService = sm.GetService('allianceTournamentSvc')
   1294         shipData = localTournamentService.competitorsByShipID[button.shipID]
   1295         header = 'Destroy Ship'
   1296         warning = 'Are you sure you want to destroy this ship?<br /><br />Character: <b>%s</b><br />Type: <b>%s</b>' % (shipData.ownerName, shipData.typeName)
   1297         if prompt_player_fsd_dialog('CustomWarning', {'header': header,
   1298          'warning': warning}, uiconst.YESNO) == uiconst.ID_YES:
   1299             sm.GetService('slash').SlashCmd('/heal %d 0' % button.shipID)
   1300             channelID = (('solarsystemid2', session.solarsystemid2),)
   1301             message = u'<url=showinfo:%d>%s</url> piloted by <url=showinfo:1377//%s>%s</url> has been disqualified for boundary violations.' % (shipData.typeID,
   1302              shipData.typeName,
   1303              shipData.charID,
   1304              shipData.ownerName)
   1305             c = sm.StartService('LSC').GetChannelWindow(channelID[0])
   1306             c.Speak(message, session.charid, localEcho=True)
   1307             sm.StartService('LSC').SendMessage(channelID, message)
   1308 
   1309 
   1310 class RefWindowSpawningWindow(Window):
   1311     __guid__ = 'form.RefWindowSpawningWindow'
   1312     default_windowID = 'tournament'
   1313     default_fixedWidth = 280
   1314     default_fixedHeight = 110
   1315 
   1316     def ApplyAttributes(self, attributes):
   1317         Window.ApplyAttributes(self, attributes)
   1318         self.SetScope(uiconst.SCOPE_ALL)
   1319         self.SetCaption('Match Starter')
   1320         self.SetMinSize([self.width, self.height])
   1321         self.MakeUnResizeable()
   1322         self.ConstructLayout()
   1323         self.nextWndIDNum = 0
   1324 
   1325     def ConstructLayout(self):
   1326         Button(label='Create New Match', parent=self.content, padding=(5, 5, 5, 5), func=self.NewMatch, align=uiconst.TOTOP)
   1327 
   1328     def NewMatch(self, *args):
   1329         TournamentRefereeTool.Open(windowID='TournamentRefereeTool%d' % (self.nextWndIDNum,))
   1330         self.nextWndIDNum += 1
   1331 
   1332 
   1333 class AllianceTournamentSvc(Service):
   1334     __guid__ = 'svc.allianceTournamentSvc'
   1335     __dependencies__ = ['michelle']
   1336 
   1337     def Run(self, *args):
   1338         self.isCompetitorsTrackingActive = False
   1339         self.competitorsByShipID = {}
   1340 
   1341     def Show(self):
   1342         RefWindowSpawningWindow.Open()
   1343 
   1344     def StartTracking(self, centerItemID, arenaRadius):
   1345         self.LogInfo('Start tracking arena center item', centerItemID, 'using a radius of', arenaRadius, 'meters')
   1346         self.centerItemID = centerItemID
   1347         self.arenaRadius = arenaRadius
   1348         uthread.new(self._MonitorCompetitorsTask).context = 'at::competitortracking'
   1349 
   1350     def StopTracking(self):
   1351         self.LogInfo('Stop tracking competitors')
   1352         self.isCompetitorsTrackingActive = False
   1353         self.competitorsByShipID = {}
   1354         sm.ScatterEvent('OnCompetitorTrackingStart', {})
   1355 
   1356     def _RegisterCompetitors(self):
   1357         self.competitorsByShipID = {}
   1358         ballpark = self.michelle.GetBallpark()
   1359         for ball, slim in ballpark.GetBallsAndItems():
   1360             if ball.id != session.shipid and slim.categoryID == const.categoryShip and slim.groupID != const.groupCapsule and slim.charID:
   1361                 distance = ballpark.DistanceBetween(self.centerItemID, ball.id)
   1362                 if distance is None or distance >= self.arenaRadius:
   1363                     continue
   1364                 data = utillib.KeyVal(charID=slim.ownerID, shipID=ball.id, typeID=slim.typeID, groupID=slim.groupID, ownerName=cfg.eveowners.Get(slim.ownerID).ownerName, typeName=evetypes.GetName(slim.typeID), groupName=evetypes.GetGroupNameByGroup(slim.groupID), distance=distance, maxDistance=distance, shipLost=False)
   1365                 self.competitorsByShipID[ball.id] = data
   1366 
   1367         sm.ScatterEvent('OnCompetitorTrackingStart', self.competitorsByShipID)
   1368 
   1369     def _MonitorCompetitorsTask(self):
   1370         if self.isCompetitorsTrackingActive:
   1371             return
   1372         try:
   1373             self._RegisterCompetitors()
   1374             self.isCompetitorsTrackingActive = True
   1375             self.LogInfo('Starting monitoring task')
   1376             while self.isCompetitorsTrackingActive:
   1377                 blue.pyos.synchro.SleepWallclock(500)
   1378                 ballpark = sm.GetService('michelle').GetBallpark()
   1379                 ball = ballpark.GetBall(self.centerItemID)
   1380                 if not ball:
   1381                     self.LogInfo('We lost the center ball.  Must abort tracking')
   1382                     self.StopTracking()
   1383                 for shipID, data in self.competitorsByShipID.iteritems():
   1384                     ball = ballpark.GetBall(shipID)
   1385                     if ball:
   1386                         data.distance = ballpark.DistanceBetween(self.centerItemID, shipID)
   1387                         data.maxDistance = max(data.maxDistance, data.distance)
   1388                     else:
   1389                         data.shipLost = True
   1390 
   1391                 sm.ScatterEvent('OnCompetitorTrackingUpdate', self.competitorsByShipID)
   1392 
   1393         finally:
   1394             self.isCompetitorsTrackingActive = False
   1395             self.LogInfo('Stopping monitoring task')