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

fleetSvc.py (96431B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\parklife\fleetSvc.py
      2 from __future__ import absolute_import
      3 import copy
      4 import yaml
      5 import eveexceptions
      6 import gametime
      7 import uthread2
      8 from carbon.common.lib.const import SEC, MIN
      9 from caching.memoize import Memoize
     10 from carbon.common.script.sys.service import Service
     11 from carbon.common.script.util.format import FmtAmt
     12 from carbonui.control.contextMenu.menuData import MenuData
     13 from eve.client.script.ui.shared.agencyNew.contentGroups import contentGroupConst
     14 import evefleet.fleetSetupConst as fsConst
     15 from eve.client.script.ui.shared.fleet.fleetbroadcast import BroadcastSettings
     16 from eve.client.script.ui.shared.fleet.fleetregister import RegisterFleetWindow
     17 from eve.client.script.ui.shared.fleet.fleetwindow import FleetWindow
     18 from eve.client.script.ui.shared.fleet.fleetJoinRequestWnd import FleetJoinRequestWindow
     19 from eve.client.script.ui.shared.fleet.fleetCompositionWnd import FleetComposition
     20 from evefleet.client.fleetFinder_allowedEntities import GetDiffInAllowedAndBanned
     21 from eve.client.script.ui.shared.fleet.fleet import WingName, SquadronName
     22 from eve.client.script.ui.shared.fleet.fleetWatchList import WatchListPanel
     23 from eve.client.script.ui.shared.fleet.fleetBroadcastConst import BROADCAST_COLOR_SETTING, LISTEN_BROADCAST_SETTING, broadcastNames, flagToName, iconsByBroadcastType
     24 from eve.client.script.ui.shared.fleet.fleetbroadcastexports import fleetBroadcastScopeSetting
     25 from evefleet import BROADCAST_SHOW_OWN, WATCHLIST_COL_1, WATCHLIST_COL_2
     26 from eve.client.script.ui.util.linkUtil import GetCharIDFromTextLink
     27 from eve.common.script.net.eveMoniker import GetFleet
     28 from eve.common.script.sys.eveCfg import GetActiveShip, IsDockedInStructure
     29 from eve.common.script.sys.idCheckers import IsCharacter, IsEvePlayerCharacter, IsNPC
     30 from eveexceptions import UserError
     31 from eveexceptions.const import UE_LOCID, UE_OWNERID
     32 import evelink.client
     33 import evetypes
     34 from evefleet.client.util import GetFleetSetupOptionHint
     35 from evefleet.fleetAdvertObject import FleetAdvertObject
     36 from evefleet.watchListOrder import FleetWatchListOrder
     37 from inventorycommon.const import groupAsteroidBelt, groupMoon, groupPlanet, groupWarpGate, groupStargate, groupStation, groupSkyhook
     38 import trinity
     39 import uthread
     40 from eve.client.script.ui.util import uix
     41 import blue
     42 from . import states as state
     43 from eve.client.script.ui.shared.fleet import fleetbroadcastexports
     44 import log
     45 import carbonui.const as uiconst
     46 import localization
     47 import evefleet
     48 from eve.client.script.util.eveMisc import CSPAChargedActionForMany, CSPAChargedAction
     49 from eve.client.script.ui.shared.fleet.storeFleetSetupWnd import StoredFleetSetupListWnd
     50 from eve.client.script.ui.shared.fleet.storeFleetSetupWnd import StoreFleetSetupWnd
     51 import sharedSettings
     52 from player_messaging.client import get_supressed_result, set_suppress_result
     53 from player_messaging.client.ui_message import prompt_player_fsd_dialog
     54 from utillib import KeyVal
     55 from carbonui.uicore import uicore
     56 from .messagebus.fleetMessenger import FleetMessenger
     57 import eve.common.lib.appConst as const
     58 from carbonui.util.various_unsorted import SortListOfTuples
     59 from eve.client.script.ui.control.allUserEntries import AllUserEntries
     60 from eve.client.script.ui.util import utilWindows
     61 from uihider import CommandBlockerService
     62 FLEETBROADCASTTIMEOUT = 15
     63 UPDATEFLEETFINDERDELAY = 60
     64 UPDATEFLEETMEMBERDELAY_SEC = 30
     65 MIN_BROADCAST_TIME = 2
     66 RECONNECT_DELAY = 10
     67 FLEETCOMPOSITION_CACHE_TIME = 20
     68 MAX_NUM_BROADCASTS = 500
     69 MAX_NUM_LOOTEVENTS = 500
     70 CONNOT_BE_MOVED_INCOMPATIBLE = -1
     71 CANNOT_BE_MOVED = 0
     72 CAN_BE_COMMANDER = 1
     73 CAN_ONLY_BE_MEMBER = 2
     74 WATCHLIST_MEMBER_INFO_SETTING_KEY = 'fleetWathlistMemberInfo'
     75 cynoTypeNames = {const.typeCovertCynosuralFieldI: 'UI/Fleet/CynoCovert',
     76  const.typeIndustrialCynosuralField: 'UI/Fleet/CynoIndustrial',
     77  const.typeCynosuralFieldI: 'UI/Fleet/CynoStandard'}
     78 
     79 class FleetSvc(Service):
     80     __guid__ = 'svc.fleet'
     81     __notifyevents__ = ['OnFleetBroadcast',
     82      'ProcessSessionChange',
     83      'OnFleetInvite',
     84      'OnFleetJoin',
     85      'OnFleetJoinReject',
     86      'OnFleetLeave',
     87      'OnFleetDisbanded',
     88      'OnFleetMove',
     89      'OnFleetMemberChanged',
     90      'OnFleetMoveFailed',
     91      'OnFleetWingAdded',
     92      'OnFleetWingDeleted',
     93      'OnFleetSquadAdded',
     94      'OnFleetSquadDeleted',
     95      'OnFleetStateChange',
     96      'OnFleetJumpBeaconModuleChange',
     97      'OnFleetJumpBeaconDeployableChange',
     98      'OnBridgeModeChange',
     99      'OnFleetWingNameChanged',
    100      'OnFleetSquadNameChanged',
    101      'OnFleetOptionsChanged',
    102      'OnMemberlessFleetUnregistered',
    103      'OnJoinedFleet',
    104      'OnLeftFleet',
    105      'OnFleetJoinRequest',
    106      'OnFleetJoinRejected',
    107      'OnJoinRequestUpdate',
    108      'OnContactChange',
    109      'OnFleetLootEvent',
    110      'OnFleetMotdChanged',
    111      'ProcessShutdown',
    112      'OnSessionReset',
    113      'OnFleetRespawnPointsUpdate']
    114     __startupdependencies__ = ['settings']
    115     __dependencies__ = ['sessionMgr', 'clientPathfinderService']
    116     message_bus = None
    117 
    118     def Run(self, *etc):
    119         Service.Run(self, *etc)
    120         self.semaphore = uthread.Semaphore()
    121         self.message_bus = FleetMessenger(sm.GetService('publicGatewaySvc'))
    122         self.Clear()
    123         sm.FavourMe(self.OnFleetMemberChanged)
    124         fleetBroadcastScopeSetting.on_change.connect(self.OnFleetBroadcastScopeChanged)
    125 
    126     def GetFleetSetups(self):
    127         if self.fleetSetupsByName is None:
    128             self.characterSettings = sm.GetService('characterSettings')
    129             self.fleetSetupsByName = self.FetchFleetSetupsFromServer()
    130         return self.fleetSetupsByName
    131 
    132     def FetchFleetSetupsFromServer(self):
    133         ret = {}
    134         yamlStr = self.characterSettings.Get('fleetSetups')
    135         if yamlStr is not None:
    136             filters = yaml.safe_load(yamlStr)
    137             ret = {f[fsConst.FS_NAME]:f for f in filters}
    138         return ret
    139 
    140     def Clear(self):
    141         self.leader = None
    142         self.initedFleet = None
    143         self.members = {}
    144         self.wings = {}
    145         self.targetTags = {}
    146         self.fleetState = None
    147         self.activeModuleBeacons = {}
    148         self.activeDeployableBeacons = {}
    149         self.activeBridge = {}
    150         self.fleetID = None
    151         self.fleet = None
    152         self.favorites = set()
    153         self.favoriteOrder = FleetWatchListOrder()
    154         self.options = KeyVal(isFreeMove=False, isRegistered=False, autoJoinSquadID=None)
    155         self.isDamageUpdates = True
    156         self.joinRequests = {}
    157         self.CleanupBroadcasts()
    158         self.currentBroadcastOnItem = {}
    159         self.targetBroadcasts = {}
    160         self.currentTargetBroadcast = {}
    161         self.locationUpdateRegistrations = {}
    162         self.lastBroadcast = KeyVal(name=None, timestamp=0)
    163         self.broadcastHistory = []
    164         self.broadcastScope = fleetBroadcastScopeSetting.get()
    165         self.updateThreadRunning = False
    166         self.updateAdvertThreadRunning = False
    167         self.currentFleetAd = None
    168         self.lootHistory = []
    169         self.memberHistory = []
    170         self.fleetComposition = None
    171         self.fleetCompositionTimestamp = 0
    172         self.expectingInvite = None
    173         self.motd = None
    174         self.fleetSetupsByName = None
    175         self.lastLoadedSetup = None
    176         self.respawnPoints = []
    177 
    178     def CleanupBroadcasts(self):
    179         for itemID, (gbID, gbState, data) in getattr(self, 'currentBroadcastOnItem', {}).iteritems():
    180             sm.GetService('stateSvc').SetState(itemID, gbState, False, gbID, *data)
    181 
    182     def ProcessShutdown(self):
    183         if session.fleetid and len(self.members) > 0:
    184             self.LogNotice('I will attempt to reconnect to this fleet', session.fleetid, ' when the client starts up again')
    185             settings.char.ui.Set('fleetReconnect', (session.fleetid, blue.os.GetWallclockTime()))
    186             settingsSvc = sm.GetServiceIfRunning('settings')
    187             if settingsSvc:
    188                 settingsSvc.SaveSettings()
    189 
    190     def OnSessionReset(self):
    191         self.Clear()
    192 
    193     def OnFleetStateChange(self, fleetState):
    194         self.fleetState = fleetState
    195 
    196     def OnBridgeModeChange(self, shipID, solarsystemID, itemID, active):
    197         self.LogInfo('OnBridgeModeChange called:', shipID, solarsystemID, itemID, active)
    198         if active:
    199             self.activeBridge[shipID] = (solarsystemID, itemID)
    200         elif shipID in self.activeBridge:
    201             del self.activeBridge[shipID]
    202 
    203     def OnFleetJumpBeaconModuleChange(self, charID, solarsystemID, beaconID, typeID, active):
    204         self.LogInfo('OnFleetJumpBeaconModuleChange:', charID, solarsystemID, beaconID, typeID, active)
    205         if active:
    206             self.activeModuleBeacons[charID] = (solarsystemID, beaconID, typeID)
    207         else:
    208             self.activeModuleBeacons.pop(charID, None)
    209 
    210     def OnFleetJumpBeaconDeployableChange(self, deployableID, solarsystemID, beaconID, ownerID, active):
    211         self.LogInfo('OnFleetJumpBeaconDeployableChange:', deployableID, solarsystemID, beaconID, active)
    212         if active:
    213             self.activeDeployableBeacons[deployableID] = (solarsystemID, beaconID, ownerID)
    214         else:
    215             self.activeDeployableBeacons.pop(deployableID, None)
    216 
    217     def GetTargetTag(self, itemID):
    218         if self.fleetState:
    219             return self.fleetState.targetTags.get(itemID, None)
    220 
    221     def HasActiveBridge(self, shipID):
    222         return shipID in self.activeBridge
    223 
    224     def GetActiveBeacons(self):
    225         return self.activeModuleBeacons
    226 
    227     def HasActiveBeacon(self, charID):
    228         return charID in self.activeModuleBeacons
    229 
    230     def GetActiveBridgeForShip(self, shipID):
    231         if shipID not in self.activeBridge:
    232             return None
    233         return self.activeBridge[shipID]
    234 
    235     def GetActiveBeaconForChar(self, charID):
    236         if charID not in self.activeModuleBeacons:
    237             return None
    238         return self.activeModuleBeacons[charID]
    239 
    240     def InitFleet(self):
    241         if self.fleet is None:
    242             return
    243         oldOptions = self.options
    244         initState = self.fleet.GetInitState()
    245         self.fleetID = initState.fleetID
    246         self.members = initState.members
    247         self.wings = initState.wings
    248         self.options = initState.options
    249         self.motd = initState.motd
    250         cfg.eveowners.Prime(self.members.keys())
    251         self.fleetMemberLocations = {}
    252         if oldOptions != self.options:
    253             sm.ScatterEvent('OnFleetOptionsChanged_Local', oldOptions, self.options)
    254         sm.ScatterEvent('OnMyFleetInfoChanged')
    255         sm.ScatterEvent('OnMyFleetInited')
    256         uthread2.StartTasklet(self.TryToRestoreWatchlist)
    257 
    258     def TryToRestoreWatchlist(self):
    259         self._PruneWatchlistMemberSettings()
    260         if not session.fleetid:
    261             return
    262         watchlistInfoForFleet = settings.char.ui.Get(WATCHLIST_MEMBER_INFO_SETTING_KEY, {})
    263         infoForCurrentFleet = watchlistInfoForFleet.get(session.fleetid, None)
    264         if not infoForCurrentFleet:
    265             return
    266         _, charIDs, column1, column2 = infoForCurrentFleet
    267         self.AddFavorite(charIDs, False, False)
    268         favorites = self.GetFavorites()
    269         column1CharIDs = [ x for x in column1 if x in favorites ]
    270         column2CharIDs = [ x for x in column2 if x in favorites ]
    271         if column1CharIDs:
    272             self.UpdateWatchlistMemberOrder(column1CharIDs, WATCHLIST_COL_1, -1, False)
    273         if column2CharIDs:
    274             self.UpdateWatchlistMemberOrder(column2CharIDs, WATCHLIST_COL_2, -1, False)
    275         sm.ScatterEvent('OnFleetFavoriteAdded')
    276 
    277     def _PruneWatchlistMemberSettings(self):
    278         try:
    279             watchlistInfoForFleet = settings.char.ui.Get(WATCHLIST_MEMBER_INFO_SETTING_KEY, {})
    280             now = gametime.GetWallclockTime()
    281             pruneTime = now - 3 * const.DAY
    282             wasPruned = False
    283             for fleetID, fleetInfo in watchlistInfoForFleet.items():
    284                 timestamp, charIDs, column1, column2 = fleetInfo
    285                 if timestamp < pruneTime:
    286                     watchlistInfoForFleet.pop(fleetID, None)
    287                     wasPruned = True
    288 
    289             if wasPruned:
    290                 settings.char.ui.Set(WATCHLIST_MEMBER_INFO_SETTING_KEY, watchlistInfoForFleet)
    291         except Exception:
    292             self.LogException('Failed to _PruneWatchlistSettings')
    293 
    294     def SingleChoiceBox(self, title, body, choices, suppressID):
    295         supp = get_supressed_result(suppressID)
    296         if supp is not None and not uicore.uilib.Key(uiconst.VK_SHIFT):
    297             return supp
    298         ret, block = sm.GetService('gameui').RadioButtonMessageBox(text=body, title=title, buttons=uiconst.OKCANCEL, icon=uiconst.QUESTION, radioOptions=choices, height=210, width=300, suppText=localization.GetByLabel('UI/Common/SuppressionShowMessageRemember'))
    299         if ret[0] in [uiconst.ID_CANCEL, uiconst.ID_CLOSE]:
    300             return
    301         retNum = 1
    302         if ret[1] == 'radioboxOption2Selected':
    303             retNum = 2
    304         if block:
    305             set_suppress_result(suppressID, retNum)
    306         else:
    307             set_suppress_result(suppressID, None)
    308         return retNum
    309 
    310     def CreateFleet(self, setupName = None, adInfoData = None):
    311         if session.fleetid:
    312             raise UserError('FleetError')
    313         self.fleet = sm.RemoteSvc('fleetObjectHandler').CreateFleet()
    314         self.LogInfo('Created fleet %s' % self.fleet)
    315         createdAdData = self.fleet.Init(self.GetMyShipTypeID(), setupName, adInfoData=adInfoData)
    316         self.InitFleet()
    317         self.fleetID = self.fleet.GetFleetID()
    318         if createdAdData:
    319             createdAd = FleetAdvertObject(**createdAdData)
    320             self._FinializeRegistration(createdAd, False)
    321         return True
    322 
    323     def StartNewFleet(self, setupName = None):
    324         if self.fleet:
    325             return
    326         self.CreateFleet(setupName)
    327         self.LogFleetCreation(self.fleetID, evefleet.CREATE_SOURCE_FLEET_FINDER_WND)
    328 
    329     def Invite(self, charID, wingID, squadID, role):
    330         if self.fleet is None:
    331             if not self.CreateFleet():
    332                 return
    333             self.LogFleetCreation(self.fleetID, evefleet.CREATE_SOURCE_MENU)
    334         if IsNPC(charID) or not IsCharacter(charID):
    335             prompt_player_fsd_dialog('NotRealPilotInvite')
    336             return
    337         msgName = None
    338         if charID != session.charid:
    339             CSPAChargedAction('CSPAFleetCheck', self.fleet, 'Invite', charID, wingID, squadID, role)
    340 
    341     def LeaveFleet(self):
    342         if self.fleet is None and session.fleetid:
    343             sm.RemoteSvc('fleetMgr').ForceLeaveFleet()
    344         else:
    345             self.fleet.LeaveFleet()
    346             self.Clear()
    347 
    348     def IsMember(self, charID):
    349         return charID in self.members
    350 
    351     def GetMembers(self):
    352         return self.members
    353 
    354     def GetWings(self):
    355         if self.fleet is None:
    356             return {}
    357         return self.wings
    358 
    359     def GetMembersInWing(self, wingID):
    360         members = {}
    361         for mid, m in self.members.iteritems():
    362             if m.wingID == wingID:
    363                 members[mid] = m
    364 
    365         return members
    366 
    367     def GetMembersInSquad(self, squadID):
    368         members = {}
    369         for mid, m in self.members.iteritems():
    370             if m.squadID == squadID:
    371                 members[mid] = m
    372 
    373         return members
    374 
    375     def ChangeWingName(self, wingID):
    376         if self.fleet is None:
    377             return
    378         name = ''
    379         ret = utilWindows.NamePopup(localization.GetByLabel('UI/Fleet/ChangeWingName'), localization.GetByLabel('UI/Common/Name/TypeInName'), name, maxLength=evefleet.MAX_NAME_LENGTH)
    380         if ret is not None:
    381             self.fleet.ChangeWingName(wingID, ret[:evefleet.MAX_NAME_LENGTH])
    382 
    383     def ChangeSquadName(self, squadID):
    384         if self.fleet is None:
    385             return
    386         name = ''
    387         ret = utilWindows.NamePopup(localization.GetByLabel('UI/Fleet/ChangeSquadName'), localization.GetByLabel('UI/Common/Name/TypeInName'), name, maxLength=evefleet.MAX_NAME_LENGTH)
    388         if ret is not None:
    389             self.fleet.ChangeSquadName(squadID, ret[:evefleet.MAX_NAME_LENGTH])
    390 
    391     def SetAcceptsFleetWarpValue(self, value):
    392         self.fleet.SetTakesFleetWarp(value)
    393 
    394     def SetAcceptConduitJumpsValue(self, value):
    395         self.fleet.SetAcceptsConduitJumpsValue(value)
    396 
    397     def SetAcceptRegroupValue(self, value):
    398         self.fleet.SetAcceptsRegroupValue(value)
    399 
    400     def TakesFleetWarp(self):
    401         return self.members[session.charid].memberOptOuts.acceptsFleetWarp
    402 
    403     def AcceptsConduitJumps(self):
    404         return self.members[session.charid].memberOptOuts.acceptsConduitJumps
    405 
    406     def AcceptsFleetRegroups(self):
    407         return self.members[session.charid].memberOptOuts.acceptsFleetRegroups
    408 
    409     def GetOptions(self):
    410         return self.options
    411 
    412     def SetOptions(self, isFreeMove = None):
    413         options = copy.copy(self.options)
    414         if isFreeMove != None:
    415             options.isFreeMove = isFreeMove
    416         return self.fleet.SetOptions(options)
    417 
    418     def SetDamageUpdates(self, isit):
    419         self.isDamageUpdates = isit
    420         self.RegisterForDamageUpdates()
    421 
    422     def IsDamageUpdates(self):
    423         return self.isDamageUpdates
    424 
    425     def GetJoinRequests(self):
    426         if not self.joinRequests:
    427             self.joinRequests = self.fleet.GetJoinRequests()
    428         return self.joinRequests
    429 
    430     def GetFleetHierarchy(self, members = None):
    431         if members is None:
    432             members = self.GetMembers()
    433         ret = {'commander': None,
    434          'wings': {},
    435          'squads': {},
    436          'name': ''}
    437         for wingID, wing in self.GetWings().iteritems():
    438             ret['wings'][wingID] = {'commander': None,
    439              'squads': wing.squads.keys(),
    440              'name': wing.name}
    441             for squadID, squad in wing.squads.iteritems():
    442                 ret['squads'][squadID] = {'commander': None,
    443                  'members': [],
    444                  'name': squad.name}
    445 
    446         for rec in members.itervalues():
    447             if rec.squadID:
    448                 self.AddToFleet(ret, rec)
    449 
    450         return ret
    451 
    452     def AddToFleet(self, fleet, rec):
    453         try:
    454             if rec.squadID != -1:
    455                 allSquads = fleet['squads']
    456                 squad = allSquads[rec.squadID]
    457                 if rec.role == evefleet.fleetRoleSquadCmdr:
    458                     squad['commander'] = rec.charID
    459                     squad['members'].insert(0, rec.charID)
    460                 elif rec.role == evefleet.fleetRoleMember:
    461                     squad['members'].append(rec.charID)
    462                 else:
    463                     log.LogError('Unknown role in squad!', rec.role)
    464             elif rec.wingID != -1:
    465                 allWings = fleet['wings']
    466                 wing = allWings[rec.wingID]
    467                 if rec.role == evefleet.fleetRoleWingCmdr:
    468                     wing['commander'] = rec.charID
    469             elif rec.role == evefleet.fleetRoleLeader:
    470                 fleet['commander'] = rec.charID
    471             else:
    472                 log.LogTraceback("Don't know how to add this guy! {!r}".format(rec))
    473         except KeyError as e:
    474             log.LogTraceback()
    475 
    476     def CheckIfIsOKWithDemotion(self, role):
    477         myself = self.members[session.charid]
    478         if myself.job & evefleet.fleetJobCreator != 0:
    479             return True
    480         if myself.role < evefleet.fleetRoleSquadCmdr and role > myself.role and prompt_player_fsd_dialog('FleetConfirmDemoteSelf', {}, uiconst.YESNO) != uiconst.ID_YES:
    481             return False
    482         return True
    483 
    484     def MoveMember(self, charID, wingID, squadID, role):
    485         self.CheckIsInFleet()
    486         if charID == session.charid:
    487             if not self.CheckIfIsOKWithDemotion(role):
    488                 return
    489         if self.fleet.MoveMember(charID, wingID, squadID, role):
    490             sm.ScatterEvent('OnFleetMemberChanging', charID)
    491 
    492     def MassMove(self, charIDs, wingID, squadID, role):
    493         charIDs = self.FilterOutNonCharacters(charIDs)
    494         if len(charIDs) == 1:
    495             return self.MoveMember(charIDs[0], wingID, squadID, role)
    496         if session.charid in charIDs:
    497             isOkWithDemotion = self.CheckIfIsOKWithDemotion(role=role)
    498             if not isOkWithDemotion:
    499                 charIDs.remove(session.charid)
    500         if charIDs:
    501             wereMoved = self.fleet.MassMoveMembers(charIDs, wingID, squadID, role)
    502             if wereMoved:
    503                 sm.ScatterEvent('OnManyFleetMembersChanging', wereMoved)
    504 
    505     def MassInvite(self, charIDs, wingID, squadID, role):
    506         charIDs = self.FilterOutNonCharacters(charIDs)
    507         if self.fleet is None and charIDs:
    508             if not self.CreateFleet():
    509                 return
    510         if session.charid in charIDs:
    511             charIDs.remove(session.charid)
    512         if len(charIDs) == 1:
    513             charID = charIDs[0]
    514             self.Invite(charID, wingID, squadID, role)
    515             if role == evefleet.fleetRoleMember:
    516                 prompt_player_fsd_dialog('CharacterAddedAsSquadMember', {'char': charID})
    517             elif role == evefleet.fleetRoleSquadCmdr:
    518                 prompt_player_fsd_dialog('CharacterAddedAsSquadCommander', {'char': charID})
    519             elif role == evefleet.fleetRoleWingCmdr:
    520                 prompt_player_fsd_dialog('CharacterAddedAsWingCommander', {'char': charID})
    521             elif role == evefleet.fleetRoleLeader:
    522                 prompt_player_fsd_dialog('CharacterAddedAsFleetCommander', {'char': charID})
    523         elif charIDs:
    524             try:
    525                 CSPAChargedActionForMany('CSPAFleetCheckMany', self.fleet, 'MassInvite', charIDs, wingID, squadID, role)
    526             except UserError as e:
    527                 if e.msg in ('FleetErrorMany', 'FleetNotAllowedMany', 'FleetNoPositionFoundMany', 'FleetTooManyMembersMany', 'FleetInviteMultipleErrors'):
    528                     notInvited = e.dict.get('notInvited', [])
    529                     charNameList = [ cfg.eveowners.Get(charID).name for charID in notInvited ]
    530                     charNames = ', '.join(charNameList)
    531                     if e.dict:
    532                         e.dict['namelist'] = charNames
    533                     raise UserError(e.msg, e.dict)
    534                 else:
    535                     raise
    536 
    537     def FilterOutNonCharacters(self, charIDs):
    538         realCharIDs = [ eachID for eachID in charIDs if not IsNPC(eachID) and IsCharacter(eachID) ]
    539         return realCharIDs
    540 
    541     def CreateWing(self):
    542         self.CheckIsInFleet()
    543         wingID = self.fleet.CreateWing()
    544         if wingID:
    545             self.CreateSquad(wingID)
    546 
    547     def DeleteWing(self, wingID):
    548         self.CheckIsInFleet()
    549         self.fleet.DeleteWing(wingID)
    550 
    551     def CreateSquad(self, wingID):
    552         self.CheckIsInFleet()
    553         self.fleet.CreateSquad(wingID)
    554 
    555     def DeleteSquad(self, wingID):
    556         self.CheckIsInFleet()
    557         self.fleet.DeleteSquad(wingID)
    558 
    559     def SetAutoJoinSquadID(self, squadID):
    560         self.fleet.SetAutoJoinSquadID(squadID)
    561 
    562     def MakeLeader(self, charID):
    563         name = evelink.character_link(charID)
    564         if prompt_player_fsd_dialog('ConfirmMakeFleetBoss', {'charName': name}, uiconst.YESNO, suppress=uiconst.ID_YES, default=uiconst.ID_NO) == uiconst.ID_YES:
    565             self.fleet.MakeLeader(charID)
    566 
    567     def KickMember(self, charID):
    568         if charID == session.charid:
    569             self.LeaveFleet()
    570         else:
    571             self.fleet.KickMember(charID)
    572 
    573     def DisbandFleet(self):
    574         if not self.IsBoss():
    575             raise UserError('CannotDisbandFleetIfNotBoss')
    576         self.fleet.DisbandFleet()
    577 
    578     def __VerifyRightsToRestrict(self, channel):
    579         return True
    580         ret = False
    581         if session.fleetrole > 3:
    582             ret = False
    583         elif session.fleetrole == 3:
    584             squads = self.GetFleetHierarchy()['squads']
    585             if squads[channel]['commander'] == session.charid:
    586                 ret = True
    587         elif session.fleetrole == 2:
    588             wings = self.GetFleetHierarchy()['wings']
    589             if wings[channel]['commander'] == session.charid:
    590                 ret = True
    591         elif session.fleetrole == 1:
    592             if session.fleetid == channel[1]:
    593                 ret = True
    594         if not ret:
    595             raise UserError('FleetNotAllowed')
    596 
    597     def AddFavorite(self, charIDs, updateOrder = True, updateWnd = True):
    598         self.CheckIsInFleet()
    599         if session.charid in charIDs:
    600             charIDs.remove(session.charid)
    601         if not charIDs:
    602             return
    603         if len(self.favorites) >= evefleet.MAX_DAMAGE_SENDERS:
    604             raise UserError('FleetTooManyFavorites', {'num': evefleet.MAX_DAMAGE_SENDERS})
    605         newCharIDs = []
    606         for eachCharID in charIDs:
    607             if eachCharID not in self.members:
    608                 continue
    609             if self.GetFavorite(eachCharID):
    610                 continue
    611             newCharIDs.append(eachCharID)
    612 
    613         if not newCharIDs:
    614             return
    615         newCharsAdded = []
    616         for eachCharID in newCharIDs:
    617             if len(self.favorites) >= evefleet.MAX_DAMAGE_SENDERS:
    618                 break
    619             self.favorites.add(eachCharID)
    620             newCharsAdded.append(eachCharID)
    621 
    622         if not newCharsAdded:
    623             return
    624         fav = self.GetWatchlistMembers()
    625         sm.RemoteSvc('fleetMgr').AddToWatchlist(newCharsAdded, fav)
    626         if updateOrder:
    627             self.UpdateWatchlistMemberOrder(newCharsAdded, WATCHLIST_COL_1, -1, False)
    628         else:
    629             self.UpdateWatchlistSettings()
    630         if updateWnd:
    631             sm.ScatterEvent('OnFleetFavoriteAdded')
    632             wnd = WatchListPanel.Open(showActions=False, panelName=localization.GetByLabel('UI/Fleet/WatchList'))
    633             wnd.OnFleetFavoriteAdded()
    634 
    635     def UpdateWatchlistMemberOrder(self, allAffectedIDs, scrollColumnID, idx, allowMove):
    636         self.favoriteOrder.UpdateOrder(allAffectedIDs, scrollColumnID, idx, allowMove)
    637         self.UpdateWatchlistSettings()
    638 
    639     def UpdateWatchlistSettings(self):
    640         try:
    641             if not session.fleetid:
    642                 return
    643             watchlistInfoForFleet = settings.char.ui.Get(WATCHLIST_MEMBER_INFO_SETTING_KEY, {})
    644             watchlistInfoForFleet[session.fleetid] = (gametime.GetWallclockTime(),
    645              self.GetFavorites().copy(),
    646              self.favoriteOrder.GetColumn1Order(),
    647              self.favoriteOrder.GetColumn2Order())
    648             settings.char.ui.Set(WATCHLIST_MEMBER_INFO_SETTING_KEY, watchlistInfoForFleet)
    649         except Exception:
    650             self.LogException('Failed to update watchlist settings')
    651 
    652     def AddFavoriteSquad(self, squadID):
    653         squadMembers = [ memberID for memberID, m in self.members.iteritems() if m.squadID == squadID ]
    654         self.AddFavorite(squadMembers)
    655 
    656     def GetFavorite(self, charID):
    657         return charID in self.favorites
    658 
    659     def RemoveAllFavorites(self):
    660         self.favorites.clear()
    661         self.favoriteOrder.Reset()
    662         self.UpdateWatchlistSettings()
    663         sm.ScatterEvent('OnFleetFavoritesRemoved')
    664 
    665     def RemoveFavorite(self, charID):
    666         self.CheckIsInFleet()
    667         self.favorites.discard(charID)
    668         favs = self.GetWatchlistMembers()
    669         sm.RemoteSvc('fleetMgr').RemoveFromWatchlist(charID, favs)
    670         self.favoriteOrder.RemoveFromOrderedLists(charID)
    671         sm.ScatterEvent('OnFleetFavoriteRemoved', charID)
    672         self.UpdateWatchlistSettings()
    673 
    674     def ClearWatchlistBroadcastHistory(self):
    675         wnd = WatchListPanel.GetIfOpen()
    676         if wnd and not wnd.destroyed:
    677             wnd.ClearBroadcastHistory()
    678             prompt_player_fsd_dialog('CustomNotify', {'notify': localization.GetByLabel('UI/Fleet/BroadcastIconsCleared')})
    679 
    680     def ChangeFavoriteSorting(self, charID, orderID = -1, *args):
    681         if getattr(self, 'isChangingOrder', False):
    682             return
    683         try:
    684             setattr(self, 'isChangingOrder', True)
    685             favorite = self.GetFavorite(charID)
    686             if not favorite:
    687                 return
    688             favoriteIndex = favorite.orderID
    689             if favoriteIndex < 0:
    690                 return
    691             if favoriteIndex > len(self.favorites):
    692                 return
    693             self.favorites.remove(favorite)
    694             if orderID == -1:
    695                 orderID = len(self.favorites)
    696             newFavorite = KeyVal(charID=charID, orderID=orderID)
    697             self.favorites.insert(orderID, newFavorite)
    698         finally:
    699             setattr(self, 'isChangingOrder', False)
    700 
    701     def CloseWatchlistWindow(self):
    702         WatchListPanel.CloseIfOpen()
    703 
    704     def CloseFleetSetupList(self):
    705         StoredFleetSetupListWnd.CloseIfOpen()
    706 
    707     def GetFavorites(self):
    708         return self.favorites
    709 
    710     def IsFavorite(self, charid):
    711         return charid in self.favorites
    712 
    713     def GetMemberInfo(self, charID, fleetHierarchy = None):
    714         member = self.members.get(charID, None)
    715         if member is None:
    716             return
    717         wing = self.wings.get(member.wingID, None)
    718         wingName = squadName = ''
    719         fleet = fleetHierarchy or self.GetFleetHierarchy()
    720         if wing:
    721             wingName = WingName(fleet, wingID=member.wingID)
    722             squad = wing.squads.get(member.squadID, None)
    723             if squad:
    724                 squadName = SquadronName(fleet, squadID=member.squadID)
    725         jobName = ''
    726         if member.job & evefleet.fleetJobCreator:
    727             jobName = localization.GetByLabel('UI/Fleet/Ranks/Boss')
    728         roleName = fleetbroadcastexports.GetRankName(member)
    729         return KeyVal(charID=charID, charName=cfg.eveowners.Get(charID).name, wingID=member.wingID, wingName=wingName, squadID=member.squadID, squadName=squadName, role=member.role, roleName=roleName, job=member.job, jobName=jobName)
    730 
    731     def GetWatchlistMembers(self):
    732         if self.isDamageUpdates:
    733             return self.favorites
    734 
    735     def RegisterForDamageUpdates(self):
    736         fav = self.GetWatchlistMembers()
    737         sm.RemoteSvc('fleetMgr').RegisterForDamageUpdates(fav)
    738 
    739     def Regroup(self):
    740         bp = sm.GetService('michelle').GetRemotePark()
    741         if bp is not None:
    742             bp.CmdFleetRegroup()
    743             prompt_player_fsd_dialog('CustomNotify', {'notify': localization.GetByLabel('UI/Fleet/Regrouping')})
    744 
    745     def GetNearestBall(self, fromBall = None, getDist = 0):
    746         ballPark = sm.GetService('michelle').GetBallpark()
    747         if not ballPark:
    748             return
    749         lst = []
    750         validNearBy = {groupAsteroidBelt,
    751          groupMoon,
    752          groupPlanet,
    753          groupWarpGate,
    754          groupStargate,
    755          groupStation} | evetypes.GetGroupIDsByCategory(const.categoryStructure)
    756         for ballID, ball in ballPark.balls.iteritems():
    757             slimItem = ballPark.GetInvItem(ballID)
    758             if slimItem and slimItem.groupID in validNearBy:
    759                 if fromBall:
    760                     dist = trinity.TriVector(ball.x - fromBall.x, ball.y - fromBall.y, ball.z - fromBall.z).Length()
    761                     lst.append((dist, ball))
    762                 else:
    763                     lst.append((ball.surfaceDist, slimItem))
    764 
    765         lst.sort()
    766         if getDist:
    767             return lst[0]
    768         if lst:
    769             return lst[0][1]
    770 
    771     def CurrentFleetBroadcastOnItem(self, itemID, gbType = None):
    772         currGBID, currGBType, currGBData = self.currentBroadcastOnItem.get(itemID, (None, None, None))
    773         if gbType in (None, currGBType):
    774             return currGBData
    775         else:
    776             return
    777 
    778     def GetCurrentFleetBroadcastOnItem(self, itemID):
    779         return self.currentBroadcastOnItem.get(itemID, (None, None, None))
    780 
    781     def GetCurrentFleetBroadcasts(self):
    782         return self.currentBroadcastOnItem
    783 
    784     def CheckIsInFleet(self, inSpace = False):
    785         if self.fleet is None:
    786             if inSpace:
    787                 raise UserError('FleetNotInFleetInSpace')
    788             raise UserError('FleetNotInFleet')
    789         if inSpace:
    790             if not session.solarsystemid:
    791                 raise UserError('FleetCannotDoInStation')
    792             if IsDockedInStructure():
    793                 raise UserError('FleetCannotDoInStructure')
    794 
    795     def CheckCanAddFavorite(self, charid):
    796         if charid == session.charid:
    797             return False
    798         if self.fleet is None:
    799             return False
    800         if not self.IsMember(charid):
    801             return False
    802         if self.IsFavorite(charid):
    803             return False
    804         return True
    805 
    806     def CheckCanMoveFavorite(self, charid):
    807         if charid == session.charid:
    808             return False
    809         if self.fleet is None:
    810             return False
    811         if not self.IsMember(charid):
    812             return False
    813         if self.IsFavorite(charid):
    814             return True
    815         return True
    816 
    817     def GetFleetLocationAndInfo(self):
    818         ret = sm.GetService('michelle').GetRemotePark().GetFleetLocationAndInfo()
    819         for memberID, inf in ret.iteritems():
    820             ball = KeyVal(x=inf.pos[0], y=inf.pos[1], z=inf.pos[2])
    821             nearestBallID = self.GetNearestBall(ball).itemID
    822             inf.nearestBallID = nearestBallID
    823 
    824         return ret
    825 
    826     def GetFleetComposition(self):
    827         if self.fleet is None:
    828             return
    829         now = blue.os.GetWallclockTime()
    830         if self.fleetCompositionTimestamp < now:
    831             self.LogInfo('Fetching fleet composition')
    832             self.fleetComposition = self.fleet.GetFleetComposition()
    833             self.fleetCompositionTimestamp = now + FLEETCOMPOSITION_CACHE_TIME * SEC
    834         return self.fleetComposition
    835 
    836     def DistanceToFleetMate(self, solarSystemID, nearID):
    837         toSystem = cfg.evelocations.Get(solarSystemID)
    838         if toSystem is None or session.solarsystemid2 is None:
    839             raise AttributeError('Invalid solarsystem')
    840         fromSystem = cfg.evelocations.Get(session.solarsystemid2)
    841         dist = uix.GetLightYearDistance(fromSystem, toSystem)
    842         if dist is None:
    843             prompt_player_fsd_dialog('MapDistanceUnknown', {'fromSystem': cfg.FormatConvert(UE_LOCID, session.solarsystemid2),
    844              'toSystem': cfg.FormatConvert(UE_LOCID, solarSystemID)})
    845         else:
    846             jumps = self.clientPathfinderService.GetJumpCountFromCurrent(solarSystemID)
    847             prompt_player_fsd_dialog('MapDistance', {'fromSystem': cfg.FormatConvert(UE_LOCID, session.solarsystemid2),
    848              'toSystem': cfg.FormatConvert(UE_LOCID, solarSystemID),
    849              'dist': dist,
    850              'jumps': jumps})
    851 
    852     def RejectJoinRequest(self, charID):
    853         self.fleet.RejectJoinRequest(charID)
    854 
    855     def CanUpdateAdvert(self):
    856         if session.fleetid is None:
    857             return False
    858         if not self.IsBoss():
    859             return False
    860         if not self.IsFleetRegistered():
    861             return False
    862         return True
    863 
    864     def IsFleetRegistered(self):
    865         return getattr(self.options, 'isRegistered', False)
    866 
    867     def RemoveAndUpdateFleetFinderAdvert(self, what):
    868         if not self.CanUpdateAdvert():
    869             return
    870         fleetAd = self.RemoveFleetFinderAdvert()
    871         if fleetAd:
    872             if prompt_player_fsd_dialog('FleetUpdateFleetFinderAd_%s' % what, {}, uiconst.YESNO, suppress=uiconst.ID_YES) == uiconst.ID_YES:
    873                 sm.GetService('agencyNew').OpenWindow(contentGroupConst.contentGroupFleetUpRegister)
    874 
    875     def TryUpdateFleetFinderAdvertWithNewBoss(self):
    876         if not self.CanUpdateAdvert():
    877             return
    878         advertInfo = self.GetMyFleetFinderAdvert()
    879         if advertInfo and advertInfo.updateOnBossChange:
    880             allowedInfoFromStandings = self.GetAllowedAndBannedEntitiesFromStandings(advertInfo)
    881             allowedEntitiesInfo = KeyVal(membergroups_allowedEntities=allowedInfoFromStandings['membergroupsAllowedEntities'], public_allowedEntities=allowedInfoFromStandings['publicAllowedEntities'], membergroups_disallowedEntities=allowedInfoFromStandings['membergroupsDisallowedEntities'], public_disallowedEntities=allowedInfoFromStandings['publicDisallowedEntities'])
    882             fleetAdvert = self.UpdateFleetAdvertWithNewLeader(allowedEntitiesInfo)
    883             if fleetAdvert:
    884                 self.currentFleetAd = fleetAdvert
    885                 prompt_player_fsd_dialog('FleetUpdateFleetFinderAd_NewBoss2')
    886                 return
    887         self.currentFleetAd = None
    888         self.RemoveAndUpdateFleetFinderAdvert('NewBoss')
    889 
    890     def BroadcastTimeRestriction(self, name):
    891         if self.IsTimeRestricted(name):
    892             self.LogInfo('Will not send broadcast', name, 'as not enough time has passed since the last one')
    893             return True
    894         else:
    895             self.lastBroadcast.name = name
    896             self.lastBroadcast.timestamp = blue.os.GetWallclockTime()
    897             return False
    898 
    899     def IsTimeRestricted(self, name):
    900         lastBroadcastTime = self.lastBroadcast.timestamp
    901         if self.lastBroadcast.name == name:
    902             nextAllowedBroadcast = lastBroadcastTime + MIN_BROADCAST_TIME * SEC
    903         else:
    904             nextAllowedBroadcast = lastBroadcastTime + int(float(MIN_BROADCAST_TIME) / 3.0 * float(SEC))
    905         if nextAllowedBroadcast > blue.os.GetWallclockTime():
    906             return True
    907         return False
    908 
    909     def SendGlobalBroadcast(self, name, itemID, typeID = None):
    910         self.CheckIsInFleet(inSpace=True)
    911         if self.BroadcastTimeRestriction(name):
    912             return
    913         if name not in evefleet.ALL_BROADCASTS:
    914             raise RuntimeError('Illegal broadcast')
    915         self.fleet.SendBroadcast(name, self.broadcastScope, itemID, typeID)
    916 
    917     def SendBubbleBroadcast(self, name, itemID, typeID = None):
    918         self.CheckIsInFleet(inSpace=True)
    919         if self.BroadcastTimeRestriction(name):
    920             return
    921         if name not in evefleet.ALL_BROADCASTS:
    922             raise RuntimeError('Illegal broadcast')
    923         sm.RemoteSvc('fleetMgr').BroadcastToBubble(name, self.broadcastScope, itemID, typeID)
    924 
    925     def SendSystemBroadcast(self, name, itemID, typeID = None):
    926         self.CheckIsInFleet(inSpace=True)
    927         if self.BroadcastTimeRestriction(name):
    928             return
    929         if name not in evefleet.ALL_BROADCASTS:
    930             raise RuntimeError('Illegal broadcast')
    931         sm.RemoteSvc('fleetMgr').BroadcastToSystem(name, self.broadcastScope, itemID, typeID)
    932 
    933     def SendBroadcast_EnemySpotted(self):
    934         nearestBall = self.GetNearestBall()
    935         nearID = None
    936         if nearestBall is not None:
    937             nearID = nearestBall.itemID
    938         self.SendGlobalBroadcast(evefleet.BROADCAST_ENEMY_SPOTTED, nearID)
    939 
    940     def SendBroadcast_NeedBackup(self):
    941         nearestBall = self.GetNearestBall()
    942         nearID = None
    943         if nearestBall is not None:
    944             nearID = nearestBall.itemID
    945         self.SendGlobalBroadcast(evefleet.BROADCAST_NEED_BACKUP, nearID)
    946 
    947     def SendBroadcast_HoldPosition(self):
    948         nearestBall = self.GetNearestBall()
    949         nearID = None
    950         if nearestBall is not None:
    951             nearID = nearestBall.itemID
    952         self.SendGlobalBroadcast(evefleet.BROADCAST_HOLD_POSITION, nearID)
    953 
    954     def SendBroadcast_TravelTo(self, solarSystemID):
    955         self.SendGlobalBroadcast(evefleet.BROADCAST_TRAVEL_TO, solarSystemID)
    956 
    957     def SendBroadcast_HealArmor(self):
    958         self.SendBubbleBroadcast(evefleet.BROADCAST_HEAL_ARMOR, session.shipid)
    959 
    960     def SendBroadcast_HealShield(self):
    961         self.SendBubbleBroadcast(evefleet.BROADCAST_HEAL_SHIELD, session.shipid)
    962 
    963     def SendBroadcast_HealCapacitor(self):
    964         self.SendBubbleBroadcast(evefleet.BROADCAST_HEAL_CAPACITOR, session.shipid)
    965 
    966     def SendBroadcast_Target(self, itemID):
    967         self.SendBubbleBroadcast(evefleet.BROADCAST_TARGET, itemID)
    968 
    969     def SendBroadcast_Heal_Target(self, itemID):
    970         self.SendBubbleBroadcast(evefleet.BROADCAST_REP_TARGET, itemID)
    971 
    972     def SendBroadcast_WarpToItemID(self, itemID):
    973         typeID = self._GetTypeIDForItemID(itemID)
    974         if not self.CanAlignOrWarpToTypeID(typeID):
    975             return
    976         return self.SendBroadcast_WarpTo(itemID, typeID)
    977 
    978     def SendBroadcast_AlignToItemID(self, itemID):
    979         typeID = self._GetTypeIDForItemID(itemID)
    980         if not self.CanAlignOrWarpToTypeID(typeID):
    981             return
    982         return self.SendBroadcast_AlignTo(itemID, typeID)
    983 
    984     def SendBroadcast_JumpToItemID(self, itemID):
    985         typeID = self._GetTypeIDForItemID(itemID)
    986         if typeID is None or evetypes.GetGroupID(typeID) != const.groupStargate:
    987             return
    988         return self.SendBroadcast_JumpTo(itemID, typeID)
    989 
    990     def _GetTypeIDForItemID(self, itemID):
    991         bp = sm.GetService('michelle').GetBallpark()
    992         slimItem = bp.GetInvItem(itemID)
    993         if not slimItem:
    994             return None
    995         return slimItem.typeID
    996 
    997     def CanAlignOrWarpToTypeID(self, typeID):
    998         if typeID is None:
    999             return False
   1000         categoryID = evetypes.GetCategoryID(typeID)
   1001         if categoryID in (const.categoryEntity,
   1002          const.categoryDrone,
   1003          const.categoryShip,
   1004          const.categoryFighter):
   1005             return False
   1006         return True
   1007 
   1008     def SendBroadcast_WarpTo(self, itemID, typeID):
   1009         self.SendSystemBroadcast(evefleet.BROADCAST_WARP_TO, itemID, typeID)
   1010 
   1011     def SendBroadcast_AlignTo(self, itemID, typeID):
   1012         self.SendSystemBroadcast(evefleet.BROADCAST_ALIGN_TO, itemID, typeID)
   1013 
   1014     def SendBroadcast_JumpTo(self, itemID, typeID):
   1015         self.SendSystemBroadcast(evefleet.BROADCAST_JUMP_TO, itemID, typeID)
   1016 
   1017     def SendBroadcast_InPosition(self):
   1018         nearestBall = self.GetNearestBall()
   1019         nearID = None
   1020         typeID = None
   1021         if nearestBall is not None:
   1022             nearID = nearestBall.itemID
   1023             typeID = nearestBall.typeID
   1024         self.SendGlobalBroadcast(evefleet.BROADCAST_IN_POSITION, nearID, typeID)
   1025 
   1026     def SendBroadcast_JumpBeacon(self):
   1027         beacon = self.GetActiveBeaconForChar(session.charid)
   1028         if beacon is None:
   1029             raise UserError('NoActiveBeacon')
   1030         self.SendGlobalBroadcast(evefleet.BROADCAST_JUMP_BEACON, beacon[1], beacon[2])
   1031 
   1032     def SendBroadcast_Location(self):
   1033         locationID = session.solarsystemid2
   1034         nearestBall = self.GetNearestBall()
   1035         nearID = None
   1036         if nearestBall is not None:
   1037             nearID = nearestBall.itemID
   1038         self.SendGlobalBroadcast('Location', nearID)
   1039 
   1040     def SendBroadcast(self, broadcastID, *args):
   1041         if broadcastID == evefleet.BROADCAST_LOCATION:
   1042             self.SendBroadcast_Location(*args)
   1043         elif broadcastID == evefleet.BROADCAST_JUMP_BEACON:
   1044             self.SendBroadcast_JumpBeacon(*args)
   1045         elif broadcastID == evefleet.BROADCAST_HOLD_POSITION:
   1046             self.SendBroadcast_HoldPosition(*args)
   1047         elif broadcastID == evefleet.BROADCAST_TRAVEL_TO:
   1048             self.SendBroadcast_TravelTo(*args)
   1049         elif broadcastID == evefleet.BROADCAST_JUMP_TO:
   1050             self.SendBroadcast_JumpTo(*args)
   1051         elif broadcastID == evefleet.BROADCAST_ALIGN_TO:
   1052             self.SendBroadcast_AlignTo(*args)
   1053         elif broadcastID == evefleet.BROADCAST_NEED_BACKUP:
   1054             self.SendBroadcast_NeedBackup(*args)
   1055         elif broadcastID == evefleet.BROADCAST_WARP_TO:
   1056             self.SendBroadcast_WarpTo(*args)
   1057         elif broadcastID == evefleet.BROADCAST_HEAL_CAPACITOR:
   1058             self.SendBroadcast_HealCapacitor(*args)
   1059         elif broadcastID == evefleet.BROADCAST_HEAL_SHIELD:
   1060             self.SendBroadcast_HealShield(*args)
   1061         elif broadcastID == evefleet.BROADCAST_HEAL_ARMOR:
   1062             self.SendBroadcast_HealArmor(*args)
   1063         elif broadcastID == evefleet.BROADCAST_TARGET:
   1064             self.SendBroadcast_Target(*args)
   1065         elif broadcastID == evefleet.BROADCAST_REP_TARGET:
   1066             self.SendBroadcast_Heal_Target(*args)
   1067         elif broadcastID == evefleet.BROADCAST_ENEMY_SPOTTED:
   1068             self.SendBroadcast_EnemySpotted(*args)
   1069         elif broadcastID == evefleet.BROADCAST_IN_POSITION:
   1070             self.SendBroadcast_InPosition(*args)
   1071 
   1072     def OnFleetWingNameChanged(self, wingID, name):
   1073         self.wings = self.fleet.GetWings()
   1074         sm.ScatterEvent('OnFleetWingNameChanged_Local', wingID, name)
   1075         sm.ScatterEvent('OnMyFleetInfoChanged')
   1076 
   1077     def OnFleetSquadNameChanged(self, squadID, name):
   1078         self.wings = self.fleet.GetWings()
   1079         sm.ScatterEvent('OnFleetSquadNameChanged_Local', squadID, name)
   1080         sm.ScatterEvent('OnMyFleetInfoChanged')
   1081 
   1082     def OnFleetInvite(self, fleetID, inviteID, msgName, msgDict):
   1083         autoAccept = msgDict.get('autoAccept', False)
   1084         __fleetMoniker = GetFleet(fleetID)
   1085         if session.fleetid is not None:
   1086             __fleetMoniker.RejectInvite(True)
   1087             return
   1088         if settings.user.ui.Get('autoRejectInvitations', 0) and self.expectingInvite != fleetID and not autoAccept:
   1089             __fleetMoniker.RejectInvite(False)
   1090             return
   1091         if CommandBlockerService.instance().is_blocked(['window.open.fleetwindow']):
   1092             __fleetMoniker.RejectInvite(False)
   1093             return
   1094         self.expectingInvite = None
   1095         try:
   1096             name = evelink.character_link(inviteID)
   1097             msgDict['name'] = name
   1098             if autoAccept or prompt_player_fsd_dialog(msgName, msgDict, uiconst.YESNO, default=uiconst.ID_NO, modal=False) == uiconst.ID_YES:
   1099                 self.sessionMgr.PerformSelectiveSessionChange('fleet.', 'fleet.acceptinvite', True, __fleetMoniker.AcceptInvite, self.GetMyShipTypeID())
   1100                 self.fleet = __fleetMoniker
   1101                 self.InitFleet()
   1102             else:
   1103                 __fleetMoniker.RejectInvite()
   1104         except UserError as e:
   1105             prompt_player_fsd_dialog(e.msg, e.dict)
   1106 
   1107     def OnFleetJoin(self, member):
   1108         if member.charID == session.charid:
   1109             self.InitFleet()
   1110             sm.GetService('tactical').InvalidateFlags()
   1111         else:
   1112             self.members[member.charID] = member
   1113             label = localization.GetByLabel('UI/Fleet/FleetBroadcast/MemberHistoryJoined', charID=member.charID, role=fleetbroadcastexports.GetRankName(member))
   1114             self.AddToMemberHistory(member.charID, label)
   1115             self.UpdateFleetFinderInfo()
   1116             sm.GetService('tactical').InvalidateFlagsExtraLimited(member.charID)
   1117         sm.ScatterEvent('OnFleetJoin_Local', member)
   1118 
   1119     def OnFleetJoinReject(self, memberID, reasonCode):
   1120         if reasonCode and reasonCode in evefleet.fleetRejectionReasons:
   1121             reason = localization.GetByLabel(evefleet.fleetRejectionReasons[reasonCode])
   1122             msg = localization.GetByLabel('UI/Fleet/InviteRejectedWithReason', charID=memberID, reason=reason)
   1123         else:
   1124             msg = localization.GetByLabel('UI/Fleet/InviteRejected', charID=memberID)
   1125         prompt_player_fsd_dialog('CustomNotify', {'notify': msg})
   1126 
   1127     def OnFleetLeave(self, charID):
   1128         self._OnFleetLeave(charID)
   1129 
   1130     def _OnFleetLeave(self, charID):
   1131         label = localization.GetByLabel('UI/Fleet/FleetBroadcast/MemberHistoryLeft', charID=charID)
   1132         self.AddToMemberHistory(charID, label)
   1133         if charID == session.charid:
   1134             self.Clear()
   1135         if charID in self.members:
   1136             rec = self.members.pop(charID)
   1137             sm.GetService('tactical').InvalidateFlagsExtraLimited(charID)
   1138         else:
   1139             rec = KeyVal(charID=charID)
   1140         if charID in self.activeModuleBeacons:
   1141             del self.activeModuleBeacons[charID]
   1142         if charID in self.activeBridge:
   1143             del self.activeBridge[charID]
   1144         if self.IsFavorite(charID):
   1145             self.RemoveFavorite(charID)
   1146         if charID == self.leader:
   1147             self.leader = None
   1148         if charID != session.charid:
   1149             self.UpdateFleetFinderInfo()
   1150         sm.ScatterEvent('OnFleetLeave_Local', rec)
   1151 
   1152     def OnFleetDisbanded(self, charIDs):
   1153         for charID in charIDs:
   1154             self._OnFleetLeave(charID)
   1155 
   1156     def OnFleetMemberChanged(self, charID, fleetID, oldWingID, oldSquadID, oldRole, oldJob, oldMemberOptOuts, newWingID, newSquadID, newRole, newJob, newMemberOptOuts, isOnlyMember):
   1157         self.members[charID] = KeyVal()
   1158         self.members[charID].charID = charID
   1159         self.members[charID].wingID = newWingID
   1160         self.members[charID].squadID = newSquadID
   1161         self.members[charID].role = newRole
   1162         self.members[charID].job = newJob
   1163         self.members[charID].memberOptOuts = newMemberOptOuts
   1164         sm.ScatterEvent('OnFleetMemberChanged_Local', charID, fleetID, oldWingID, oldSquadID, oldRole, oldJob, oldMemberOptOuts, newWingID, newSquadID, newRole, newJob, newMemberOptOuts)
   1165         if oldRole != newRole:
   1166             roleLabel = localization.GetByLabel('UI/Fleet/FleetBroadcast/MemberHistoryChangedRole', charID=charID, role=fleetbroadcastexports.GetRoleName(newRole))
   1167             self.AddToMemberHistory(charID, roleLabel)
   1168         if oldJob != newJob:
   1169             if newJob & evefleet.fleetJobCreator:
   1170                 jobLabel = localization.GetByLabel('UI/Fleet/FleetBroadcast/MemberHistoryIsBoss', charID=charID)
   1171             else:
   1172                 jobLabel = localization.GetByLabel('UI/Fleet/FleetBroadcast/MemberHistoryIsNotBoss', charID=charID)
   1173             self.AddToMemberHistory(charID, jobLabel)
   1174         if newRole not in (None, -1):
   1175             self.UpdateTargetBroadcasts(charID)
   1176         if charID == session.charid:
   1177             self.fleetCompositionTimestamp = 0
   1178             sm.ScatterEvent('OnMyFleetInfoChanged')
   1179             if oldJob & evefleet.fleetJobCreator == 0 and newJob & evefleet.fleetJobCreator > 0:
   1180                 self.TryUpdateFleetFinderAdvertWithNewBoss()
   1181             elif oldJob & evefleet.fleetJobCreator > 0 and newJob & evefleet.fleetJobCreator == 0:
   1182                 self.currentFleetAd = None
   1183         if newJob != oldJob or newRole != oldRole:
   1184             info = self.GetMemberInfo(charID)
   1185             if newJob != oldJob:
   1186                 r = info.jobName
   1187             elif newRole != oldRole:
   1188                 r = info.roleName
   1189 
   1190     def OnFleetMoveFailed(self, charID, isKicked):
   1191         if isKicked:
   1192             prompt_player_fsd_dialog('CustomNotify', {'notify': localization.GetByLabel('UI/Fleet/MoveFailedKicked', charID=charID)})
   1193         else:
   1194             prompt_player_fsd_dialog('CustomNotify', {'notify': localization.GetByLabel('UI/Fleet/MoveFailed', charID=charID)})
   1195 
   1196     def OnFleetWingAdded(self, wingID):
   1197         self.wings = self.fleet.GetWings()
   1198         sm.ScatterEvent('OnFleetWingAdded_Local', wingID)
   1199 
   1200     def OnFleetWingDeleted(self, wingID):
   1201         self.wings = self.fleet.GetWings()
   1202         sm.ScatterEvent('OnFleetWingDeleted_Local', wingID)
   1203 
   1204     def OnFleetSquadAdded(self, wingID, squadID):
   1205         self.wings = self.fleet.GetWings()
   1206         sm.ScatterEvent('OnFleetSquadAdded_Local', wingID, squadID)
   1207 
   1208     def OnFleetSquadDeleted(self, squadID):
   1209         self.wings = self.fleet.GetWings()
   1210         sm.ScatterEvent('OnFleetSquadDeleted_Local', squadID)
   1211 
   1212     def OnFleetOptionsChanged(self, oldOptions, options):
   1213         self.options = options
   1214         if self.options.isRegistered != oldOptions.isRegistered:
   1215             self.GetFleetAdsForChar_Memoized.clear_memoized()
   1216             sm.ScatterEvent('OnFleetFinderAdvertChanged')
   1217             if self.options.isRegistered:
   1218                 self.AddBroadcast('FleetFinderAdvertAdded', evefleet.BROADCAST_NONE, self.GetBossID(), broadcastName=localization.GetByLabel('UI/Fleet/FleetBroadcast/FleetFinderAdvertAdded'))
   1219         if options.isFreeMove != oldOptions.isFreeMove:
   1220             self.AddBroadcast('FleetOptionsChanged', evefleet.BROADCAST_NONE, self.GetBossID(), broadcastName=[localization.GetByLabel('UI/Fleet/FleetBroadcast/FreeMoveUnset'), localization.GetByLabel('UI/Fleet/FleetBroadcast/FreeMoveSet')][options.isFreeMove])
   1221         sm.ScatterEvent('OnFleetOptionsChanged_Local', oldOptions, options)
   1222 
   1223     def OnMemberlessFleetUnregistered(self):
   1224         self.GetFleetAdsForChar_Memoized.clear_memoized()
   1225 
   1226     def OnJoinedFleet(self):
   1227         self.RefreshFleetWindow()
   1228         sm.GetService('cmd').OpenFleetWatchlist()
   1229 
   1230     def OnLeftFleet(self):
   1231         self.CloseFleetWindowAndReopenIfNeeded()
   1232         self.CloseWatchlistWindow()
   1233         self.CloseFleetCompositionWindow()
   1234         self.CloseJoinRequestWindow()
   1235         self.CloseRegisterFleetWindow()
   1236         self.CloseFleetBroadcastWindow()
   1237         self.CloseFleetSetupList()
   1238 
   1239     def OnFleetJoinRequest(self, info):
   1240         self.joinRequests[info.charID] = info
   1241         prompt_player_fsd_dialog('FleetMemberJoinRequest', {'name': (UE_OWNERID, info.charID),
   1242          'corpname': (UE_OWNERID, info.corpID)})
   1243         self.OpenJoinRequestWindow()
   1244 
   1245     def OnFleetJoinRejected(self, charID):
   1246         prompt_player_fsd_dialog('FleetJoinRequestRejected', {'name': (UE_OWNERID, charID)})
   1247 
   1248     def OnJoinRequestUpdate(self, joinRequests):
   1249         self.joinRequests = joinRequests
   1250         self.OpenJoinRequestWindow()
   1251 
   1252     def OnContactChange(self, contactIDs, contactType = None):
   1253         self.UpdateAllowedEntitiesInAdvert()
   1254 
   1255     def OpenJoinRequestWindow(self):
   1256         self.CloseJoinRequestWindow()
   1257         FleetJoinRequestWindow.Open()
   1258 
   1259     def CloseJoinRequestWindow(self):
   1260         FleetJoinRequestWindow.CloseIfOpen()
   1261 
   1262     def OpenFleetCompositionWindow(self):
   1263         self.CloseFleetCompositionWindow()
   1264         FleetComposition.Open()
   1265 
   1266     def CloseFleetCompositionWindow(self):
   1267         FleetComposition.CloseIfOpen()
   1268 
   1269     def CloseFleetBroadcastWindow(self):
   1270         BroadcastSettings.CloseIfOpen()
   1271 
   1272     def OpenRegisterFleetWindow(self, info = None):
   1273         if session.fleetid is None:
   1274             raise UserError('FleetNotFound')
   1275         if not self.IsBoss():
   1276             raise UserError('FleetNotCreator')
   1277         if info is None and self.options.isRegistered:
   1278             info = self.GetCurrentAdvertForMyFleet()
   1279         self.CloseRegisterFleetWindow()
   1280         RegisterFleetWindow.Open(fleetInfo=info)
   1281 
   1282     def CloseRegisterFleetWindow(self):
   1283         RegisterFleetWindow.CloseIfOpen()
   1284 
   1285     def RefreshFleetWindow(self):
   1286         self.InitFleet()
   1287         self.CloseFleetWindow()
   1288         FleetWindow.Open()
   1289 
   1290     def CloseFleetWindowAndReopenIfNeeded(self):
   1291         wnd = FleetWindow.GetIfOpen()
   1292         if wnd:
   1293             self.InitFleet()
   1294             self.RefreshFleetWindow()
   1295 
   1296     def CloseFleetWindow(self):
   1297         FleetWindow.CloseIfOpen()
   1298 
   1299     def AddBroadcast(self, name, scope, charID, solarSystemID = None, itemID = None, broadcastName = None, typeID = None):
   1300         time = blue.os.GetWallclockTime()
   1301         spaceAndOptionalGroupName = ' '
   1302         msgLocationID = itemID
   1303         if typeID is not None and itemID is not None and name in [evefleet.BROADCAST_IN_POSITION,
   1304          evefleet.BROADCAST_WARP_TO,
   1305          evefleet.BROADCAST_ALIGN_TO,
   1306          evefleet.BROADCAST_JUMP_TO,
   1307          evefleet.BROADCAST_TRAVEL_TO]:
   1308             groupID = evetypes.GetGroupID(typeID)
   1309             if groupID in (groupStargate, groupSkyhook):
   1310                 spaceAndOptionalGroupName = ' %s ' % localization.CleanImportantMarkup(evetypes.GetGroupName(typeID))
   1311             if groupID == groupSkyhook:
   1312                 planetID = self._GetPlanetIDFromSlimItem(itemID)
   1313                 if planetID:
   1314                     msgLocationID = planetID
   1315         typeIDForBroadcast = None
   1316         if name == evefleet.BROADCAST_ENEMY_SPOTTED:
   1317             label = localization.GetByLabel('UI/Fleet/FleetBroadcast/BroadcastEventEnemySpotted', charID=charID)
   1318         elif name == evefleet.BROADCAST_NEED_BACKUP:
   1319             label = localization.GetByLabel('UI/Fleet/FleetBroadcast/BroadcastEventNeedBackup', charID=charID)
   1320         elif name == evefleet.BROADCAST_HEAL_ARMOR:
   1321             label = localization.GetByLabel('UI/Fleet/FleetBroadcast/BroadcastEventHealArmor', charID=charID)
   1322             label += self._GetTypeNameTextFromBroadcastedItemID(itemID)
   1323         elif name == evefleet.BROADCAST_HEAL_SHIELD:
   1324             label = localization.GetByLabel('UI/Fleet/FleetBroadcast/BroadcastEventHealShield', charID=charID)
   1325             label += self._GetTypeNameTextFromBroadcastedItemID(itemID)
   1326         elif name == evefleet.BROADCAST_HEAL_CAPACITOR:
   1327             label = localization.GetByLabel('UI/Fleet/FleetBroadcast/BroadcastEventHealCapacitor', charID=charID)
   1328             label += self._GetTypeNameTextFromBroadcastedItemID(itemID)
   1329         elif name == evefleet.BROADCAST_IN_POSITION:
   1330             label = localization.GetByLabel('UI/Fleet/FleetBroadcast/BroadcastEventInPosition2', charID=charID, locationID=msgLocationID, spaceAndOptionalGroupName=spaceAndOptionalGroupName)
   1331         elif name == evefleet.BROADCAST_HOLD_POSITION:
   1332             label = localization.GetByLabel('UI/Fleet/FleetBroadcast/BroadcastEventHoldPosition', charID=charID)
   1333         elif name == evefleet.BROADCAST_JUMP_BEACON:
   1334             label = localization.GetByLabel('UI/Fleet/FleetBroadcast/BroadcastEventJumpToBeacon', charID=charID)
   1335             typeIDForBroadcast = typeID
   1336         elif name == evefleet.BROADCAST_LOCATION:
   1337             label = localization.GetByLabel('UI/Fleet/FleetBroadcast/BroadcastEventLocation', charID=charID, solarsystemID=solarSystemID)
   1338         elif name == evefleet.BROADCAST_WARP_TO:
   1339             label = localization.GetByLabel('UI/Fleet/FleetBroadcast/BroadcastEventWarpTo2', charID=charID, locationID=msgLocationID, spaceAndOptionalGroupName=spaceAndOptionalGroupName)
   1340         elif name == evefleet.BROADCAST_ALIGN_TO:
   1341             label = localization.GetByLabel('UI/Fleet/FleetBroadcast/BroadcastEventAlignTo2', charID=charID, locationID=msgLocationID, spaceAndOptionalGroupName=spaceAndOptionalGroupName)
   1342         elif name == evefleet.BROADCAST_JUMP_TO:
   1343             label = localization.GetByLabel('UI/Fleet/FleetBroadcast/BroadcastEventJumpTo2', charID=charID, locationID=msgLocationID, spaceAndOptionalGroupName=spaceAndOptionalGroupName)
   1344         elif name == evefleet.BROADCAST_TRAVEL_TO:
   1345             label = localization.GetByLabel('UI/Fleet/FleetBroadcast/BroadcastEventTravelTo2', charID=charID, locationID=msgLocationID, spaceAndOptionalGroupName=spaceAndOptionalGroupName)
   1346         elif name == evefleet.BROADCAST_TARGET:
   1347             targetName, targetTypeName = self._GetTargetNameAndType(itemID)
   1348             if targetName is not None and targetTypeName is not None:
   1349                 label = localization.GetByLabel('UI/Fleet/FleetBroadcast/BroadcastEventTargetWithType', targetName=targetName, targetTypeName=targetTypeName)
   1350             else:
   1351                 label = localization.GetByLabel('UI/Fleet/FleetBroadcast/BroadcastEventTarget')
   1352         elif name == evefleet.BROADCAST_REP_TARGET:
   1353             targetName, targetTypeName = self._GetTargetNameAndType(itemID)
   1354             if targetName is not None and targetTypeName is not None:
   1355                 label = localization.GetByLabel('UI/Fleet/FleetBroadcast/BroadcastEventHealTargetWithType', targetName=targetName, targetTypeName=targetTypeName)
   1356             else:
   1357                 label = localization.GetByLabel('UI/Fleet/FleetBroadcast/BroadcastEventHealTarget')
   1358         elif broadcastName is not None:
   1359             label = broadcastName
   1360         else:
   1361             log.LogTraceback('Unknown broadcast label:%s' % name)
   1362             label = 'Unknown broadcast label:%s' % name
   1363         where = fleetbroadcastexports.GetBroadcastWhere(name)
   1364         broadcast = KeyVal(name=name, charID=charID, solarSystemID=solarSystemID, itemID=itemID, time=time, broadcastLabel=label, scope=scope, where=where, typeID=typeIDForBroadcast)
   1365         self.broadcastHistory.insert(0, broadcast)
   1366         if len(self.broadcastHistory) > MAX_NUM_BROADCASTS:
   1367             self.broadcastHistory.pop()
   1368         if self.WantBroadcast(name, charID):
   1369             sm.ScatterEvent('OnFleetBroadcast_Local', broadcast)
   1370 
   1371     def _GetTargetNameAndType(self, itemID):
   1372         m = sm.GetService('michelle')
   1373         bp = m.GetBallpark()
   1374         slimItem = bp.GetInvItem(itemID)
   1375         if slimItem is None:
   1376             return (None, None)
   1377         targetName = uix.GetSlimItemName(slimItem)
   1378         targetTypeName = evetypes.GetName(slimItem.typeID)
   1379         return (targetName, targetTypeName)
   1380 
   1381     def _GetTypeNameTextFromBroadcastedItemID(self, itemID):
   1382         bp = sm.GetService('michelle').GetBallpark()
   1383         if not bp:
   1384             return ''
   1385         slimItem = bp.GetInvItem(itemID)
   1386         if slimItem is not None:
   1387             return ' (%s)' % evetypes.GetName(slimItem.typeID)
   1388         return ''
   1389 
   1390     def _GetPlanetIDFromSlimItem(self, itemID):
   1391         bp = sm.GetService('michelle').GetBallpark()
   1392         if not bp:
   1393             return
   1394         slimItem = bp.GetInvItem(itemID)
   1395         if slimItem is not None:
   1396             return slimItem.planetID
   1397 
   1398     def WantBroadcast(self, name, charID):
   1399         if name not in iconsByBroadcastType:
   1400             name = 'Event'
   1401         if charID == session.charid:
   1402             if settings.user.ui.Get(LISTEN_BROADCAST_SETTING % BROADCAST_SHOW_OWN, False):
   1403                 return True
   1404         if settings.user.ui.Get(LISTEN_BROADCAST_SETTING % name, True):
   1405             return True
   1406         return False
   1407 
   1408     def OnFleetBroadcast(self, name, scope, charID, solarSystemID, itemID, typeID):
   1409         self.LogInfo('OnFleetBroadcast', name, scope, charID, solarSystemID, itemID, ' I now have', len(self.broadcastHistory) + 1, 'broadcasts in my history')
   1410         self.AddBroadcast(name, scope, charID, solarSystemID, itemID, typeID=typeID)
   1411         if name == evefleet.BROADCAST_TARGET:
   1412             targets = self.targetBroadcasts.setdefault(charID, [])
   1413             if itemID in targets:
   1414                 targets.remove(itemID)
   1415                 targets.insert(0, itemID)
   1416             else:
   1417                 targets.append(itemID)
   1418             self.UpdateTargetBroadcasts(charID)
   1419         else:
   1420             stateID = getattr(state, 'gb%s' % name)
   1421             self.BroadcastState(itemID, stateID, charID)
   1422 
   1423     def BroadcastState(self, itemID, brState, *data):
   1424         gbID = self.NewFleetBroadcastID()
   1425         self.currentBroadcastOnItem[itemID] = (gbID, brState, data)
   1426         sm.GetService('stateSvc').SetState(itemID, brState, True, gbID, *data)
   1427         blue.pyos.synchro.SleepWallclock(FLEETBROADCASTTIMEOUT * 1000)
   1428         savedgbid, savedgbtype, saveddata = self.currentBroadcastOnItem.get(itemID, (None, None, None))
   1429         if savedgbid == gbID:
   1430             sm.GetService('stateSvc').SetState(itemID, brState, False, gbID, *data)
   1431             del self.currentBroadcastOnItem[itemID]
   1432 
   1433     def CycleBroadcastScope(self):
   1434         if self.broadcastScope == evefleet.BROADCAST_DOWN:
   1435             newScope = evefleet.BROADCAST_UP
   1436         elif self.broadcastScope == evefleet.BROADCAST_UP:
   1437             newScope = evefleet.BROADCAST_ALL
   1438         else:
   1439             newScope = evefleet.BROADCAST_DOWN
   1440         fleetBroadcastScopeSetting.set(newScope)
   1441 
   1442     def OnFleetBroadcastScopeChanged(self, value):
   1443         self.broadcastScope = value
   1444         prompt_player_fsd_dialog('FleetBroadcastScopeChange', {'name': fleetbroadcastexports.GetBroadcastScopeName(self.broadcastScope)})
   1445         sm.ScatterEvent('OnBroadcastScopeChange')
   1446 
   1447     def OnFleetLootEvent(self, lootEvents):
   1448         for k, v in lootEvents.iteritems():
   1449             loot = KeyVal(charID=k[0], solarSystemID=session.solarsystemid2, typeID=k[1], quantity=v, time=blue.os.GetWallclockTime())
   1450             for i, l in enumerate(self.lootHistory):
   1451                 if (l.typeID, l.charID, l.solarSystemID) == (loot.typeID, loot.charID, loot.solarSystemID):
   1452                     self.lootHistory[i].quantity += loot.quantity
   1453                     self.lootHistory[i].time = loot.time
   1454                     break
   1455             else:
   1456                 self.lootHistory.insert(0, loot)
   1457 
   1458         if len(self.lootHistory) > MAX_NUM_LOOTEVENTS:
   1459             self.lootHistory.pop()
   1460         sm.ScatterEvent('OnFleetLootEvent_Local')
   1461 
   1462     def GetLootHistory(self):
   1463         return self.lootHistory
   1464 
   1465     def GetBroadcastHistory(self):
   1466         history = [ h for h in self.broadcastHistory if self.WantBroadcast(h.name, h.charID) ]
   1467         return history
   1468 
   1469     def AddToMemberHistory(self, charID, event):
   1470         self.memberHistory.insert(0, KeyVal(charID=charID, event=event, time=blue.os.GetWallclockTime()))
   1471         if len(self.memberHistory) > MAX_NUM_BROADCASTS:
   1472             self.memberHistory.pop()
   1473 
   1474     def GetMemberHistory(self):
   1475         return self.memberHistory
   1476 
   1477     def UpdateTargetBroadcasts(self, charID):
   1478 
   1479         def BroadcastWithLabel(itemID, label):
   1480             gbID = self.NewFleetBroadcastID()
   1481             self.currentTargetBroadcast[itemID] = gbID
   1482             self.BroadcastState(itemID, state.gbTarget, charID, label)
   1483             if self.currentTargetBroadcast.get(itemID) == gbID:
   1484                 self.targetBroadcasts[charID].remove(itemID)
   1485 
   1486         role = self.members[charID].role
   1487         for i, id_ in enumerate(self.targetBroadcasts.get(charID, [])):
   1488             gbID, gbType, data = self.currentBroadcastOnItem.get(id_, (None, None, None))
   1489             if gbType == state.gbTarget:
   1490                 prevCharID, number = data
   1491                 if self.IsSuperior(charID, prevCharID):
   1492                     continue
   1493             if role == evefleet.fleetRoleSquadCmdr:
   1494                 label = localization.GetByLabel('UI/Fleet/FleetBroadcast/TargetCodeFleet', targetID=i + 1)
   1495             elif role == evefleet.fleetRoleSquadCmdr:
   1496                 label = localization.GetByLabel('UI/Fleet/FleetBroadcast/TargetCodeWing', targetID=i + 1)
   1497             elif role == evefleet.fleetRoleSquadCmdr:
   1498                 label = localization.GetByLabel('UI/Fleet/FleetBroadcast/TargetCodeSquad', targetID=i + 1)
   1499             else:
   1500                 label = localization.GetByLabel('UI/Fleet/FleetBroadcast/TargetCodeMember', targetID=i + 1)
   1501             uthread.pool('FleetSvc::UpdateTargetBroadcasts', BroadcastWithLabel, id_, label)
   1502 
   1503     @Memoize
   1504     def GetRankOrder(self):
   1505         return [evefleet.fleetRoleMember,
   1506          evefleet.fleetRoleSquadCmdr,
   1507          evefleet.fleetRoleWingCmdr,
   1508          evefleet.fleetRoleLeader]
   1509 
   1510     def IsSuperior(self, charID, otherCharID):
   1511 
   1512         def Rank(charID):
   1513             return self.GetRankOrder().index(self.members[charID].role)
   1514 
   1515         return Rank(charID) > Rank(otherCharID)
   1516 
   1517     def NewFleetBroadcastID(self):
   1518         if not hasattr(self, 'lastFleetBroadcastID'):
   1519             self.lastFleetBroadcastID = 0
   1520         self.lastFleetBroadcastID += 1
   1521         return self.lastFleetBroadcastID
   1522 
   1523     def ProcessSessionChange(self, isRemote, session, change):
   1524         if 'fleetid' in change:
   1525             self.activeModuleBeacons = {}
   1526             self.activeDeployableBeacons = {}
   1527             self.activeBridge = {}
   1528             self.initedFleet = None
   1529             myrec = self.members.get(session.charid, KeyVal(charID=session.charid))
   1530             self.members = {}
   1531             self.leader = None
   1532             if change['fleetid'][1] is None:
   1533                 self.fleet = None
   1534                 self.favorites.clear()
   1535                 self.favoriteOrder.Reset()
   1536                 sm.ScatterEvent('OnFleetLeave_Local', myrec)
   1537                 sm.ScatterEvent('OnLeftFleet')
   1538             else:
   1539                 sm.ScatterEvent('OnJoinedFleet')
   1540             sm.GetService('tactical').InvalidateFlags()
   1541         if 'solarsystemid' in change:
   1542             self.CleanupBroadcasts()
   1543             if session.solarsystemid is not None and session.fleetid is not None:
   1544                 self.RegisterForDamageUpdates()
   1545             self.UpdateFleetInfo()
   1546         if 'shipid' in change or 'structureid' in change:
   1547             self.UpdateFleetInfo()
   1548         if 'charid' in change:
   1549             if change['charid'][1] is not None:
   1550                 uthread.new(self.AttemptReconnectLazy)
   1551         if 'corpid' in change:
   1552             uthread.new(self.RemoveAndUpdateFleetFinderAdvert, 'ChangedCorp')
   1553 
   1554     def AttemptReconnectLazy(self):
   1555         blue.pyos.synchro.SleepSim(RECONNECT_DELAY * 1000)
   1556         try:
   1557             if session.fleetid is not None or session.charid is None:
   1558                 return
   1559             fleetReconnect = settings.char.ui.Get('fleetReconnect', None)
   1560             if fleetReconnect:
   1561                 if fleetReconnect[1] > blue.os.GetWallclockTime() - evefleet.RECONNECT_TIMEOUT * MIN:
   1562                     self.LogNotice('I will try to reconnect to a lost fleet', fleetReconnect[0])
   1563                     fleet = GetFleet(fleetReconnect[0])
   1564                     fleet.Reconnect()
   1565                 else:
   1566                     self.LogInfo('Reconnect request', fleetReconnect, ' out of date')
   1567         except Exception as e:
   1568             self.LogWarn('Unable to reconnect. Error =', e)
   1569         finally:
   1570             settings.char.ui.Set('fleetReconnect', None)
   1571 
   1572     def UpdateFleetFinderInfo(self):
   1573         if session.fleetid is None:
   1574             return
   1575         if not self.updateAdvertThreadRunning and self.IsBoss() and self.options.isRegistered:
   1576             self.updateAdvertThreadRunning = True
   1577             uthread.worker('Fleet::UpdateAdvertInfoThread', self.UpdateAdvertInfoThread)
   1578 
   1579     def UpdateAdvertInfoThread(self):
   1580         try:
   1581             self.LogInfo('Starting UpdateAdvertInfoThread...')
   1582             self.updateAdvertThreadRunning = True
   1583             blue.pyos.synchro.SleepWallclock(UPDATEFLEETFINDERDELAY * 1000)
   1584             if session.fleetid is None:
   1585                 return
   1586             if self.IsBoss() and self.options.isRegistered:
   1587                 numMembers = len(self.members)
   1588                 allowedInfoFromStandings = self.GetAllowedAndBannedEntitiesFromStandings(self.currentFleetAd)
   1589                 allowedDiffBundle = GetDiffInAllowedAndBanned(self.currentFleetAd, allowedInfoFromStandings)
   1590                 self.LogInfo('Calling UpdateAdvertInfo', session.solarsystemid2, numMembers, allowedDiffBundle)
   1591                 advertInfo = self.UpdateAdvertInfo(numMembers, KeyVal(allowedDiffBundle))
   1592                 if advertInfo:
   1593                     self.currentFleetAd = advertInfo
   1594         finally:
   1595             self.updateAdvertThreadRunning = False
   1596 
   1597     def GetAllowedAndBannedEntitiesFromStandings(self, currentAd):
   1598         membergroupsAllowedEntities = set()
   1599         publicAllowedEntities = set()
   1600         membergroupsDisallowedEntities = set()
   1601         publicDisallowedEntities = set()
   1602         ret = {'membergroupsAllowedEntities': membergroupsAllowedEntities,
   1603          'publicAllowedEntities': publicAllowedEntities,
   1604          'membergroupsDisallowedEntities': membergroupsDisallowedEntities,
   1605          'publicDisallowedEntities': publicDisallowedEntities}
   1606         addressbookSvc = sm.GetService('addressbook')
   1607         if currentAd.IsOpenToMemberEntities():
   1608             membergroupsMinStanding = currentAd.membergroups_minStanding
   1609             if membergroupsMinStanding is None:
   1610                 membergroupsAllowedEntities.update(self.GetAllowedGroupIDs(KeyVal(inviteScope=currentAd.inviteScope)))
   1611             else:
   1612                 atOrAboveStanding, belowStanding = addressbookSvc.GetContactsAboveAndBelowRelationship(membergroupsMinStanding)
   1613                 membergroupsAllowedEntities.update(atOrAboveStanding)
   1614                 if membergroupsMinStanding <= 0:
   1615                     membergroupsDisallowedEntities.update(belowStanding)
   1616         publicMinStanding = currentAd.public_minStanding
   1617         if publicMinStanding is not None:
   1618             atOrAboveStanding, belowStanding = addressbookSvc.GetContactsAboveAndBelowRelationship(publicMinStanding)
   1619             publicAllowedEntities.update(atOrAboveStanding)
   1620             if publicMinStanding <= 0:
   1621                 publicDisallowedEntities.update(belowStanding)
   1622         return ret
   1623 
   1624     def UpdateAllowedEntitiesInAdvert(self):
   1625         if session.fleetid is None or not self.IsBoss() or not self.options.isRegistered:
   1626             return
   1627         allowedInfoFromStandings = self.GetAllowedAndBannedEntitiesFromStandings(self.currentFleetAd)
   1628         advertInfo = self.UpdateAdvertAllowedEntities(allowedInfoFromStandings)
   1629         self.currentFleetAd = advertInfo
   1630 
   1631     def UpdateFleetInfo(self):
   1632         if session.fleetid is None:
   1633             return
   1634         self.UpdateFleetFinderInfo()
   1635         if not self.updateThreadRunning:
   1636             self.updateThreadRunning = True
   1637             uthread.worker('Fleet::UpdateFleetInfoThread', self.UpdateFleetInfoThread)
   1638 
   1639     def UpdateFleetInfoThread(self):
   1640         try:
   1641             self.LogInfo('Starting UpdateFleetInfoThread...')
   1642             self.updateThreadRunning = True
   1643             blue.pyos.synchro.SleepWallclock(UPDATEFLEETMEMBERDELAY_SEC * 1000)
   1644             if session.fleetid is None:
   1645                 return
   1646             self.LogInfo('Calling UpdateMemberInfo')
   1647             self.fleet.UpdateMemberInfo(self.GetMyShipTypeID())
   1648         finally:
   1649             self.updateThreadRunning = False
   1650 
   1651     def OnFleetMove(self):
   1652         oldSquadID = session.squadid
   1653         oldWingID = session.wingid
   1654         self.sessionMgr.PerformSelectiveSessionChange('fleet.', 'fleet.finishmove', True, self.fleet.FinishMove)
   1655 
   1656     def IsBoss(self):
   1657         myrec = self.GetMembers().get(session.charid)
   1658         return bool(myrec and myrec.job & evefleet.fleetJobCreator)
   1659 
   1660     def IsCommanderOrBoss(self):
   1661         return self.IsBoss() or session.fleetrole in evefleet.fleetCmdrRoles
   1662 
   1663     def GetAutoJoinSquadID(self):
   1664         if not session.fleetid:
   1665             return
   1666         return self.options.autoJoinSquadID
   1667 
   1668     def GetBossID(self):
   1669         myrec = self.GetMembers()
   1670         for mid, m in self.members.iteritems():
   1671             if m.job & evefleet.fleetJobCreator:
   1672                 return mid
   1673 
   1674     def IsMySubordinate(self, charID):
   1675         member = self.members.get(charID, None)
   1676         if member is None:
   1677             return False
   1678         isSubordinate = False
   1679         if session.fleetrole == evefleet.fleetRoleLeader or session.fleetrole == evefleet.fleetRoleWingCmdr and member.wingID == session.wingid or session.fleetrole == evefleet.fleetRoleSquadCmdr and member.squadID == session.squadid:
   1680             isSubordinate = True
   1681         return isSubordinate
   1682 
   1683     def CreateAndRegisterFleet(self, adInfo):
   1684         if session.fleetid:
   1685             if adInfo:
   1686                 self.RegisterFleet(adInfo)
   1687             elif self.IsFleetRegistered():
   1688                 self.RemoveFleetFinderAdvert()
   1689         else:
   1690             adInfoData = adInfo.GetData() if adInfo else None
   1691             self.CreateFleet(adInfoData=adInfoData)
   1692             self.LogFleetCreation(self.fleetID, evefleet.CREATE_SOURCE_FLEETUP)
   1693         self.GetFleetAdsForChar_Memoized.clear_memoized()
   1694 
   1695     def RegisterFleet(self, adInfo):
   1696         self.LogInfo('RegisterFleet', adInfo.GetData())
   1697         if session.fleetid is None:
   1698             raise UserError('FleetNotFound')
   1699         if not self.IsBoss():
   1700             raise UserError('FleetNotCreator')
   1701         isEdit = self.options.isRegistered
   1702         createdAdInfo = self.AddFleetFinderAdvert(adInfo)
   1703         self._FinializeRegistration(createdAdInfo, isEdit)
   1704 
   1705     def _FinializeRegistration(self, createdAd, isEdit):
   1706         if isEdit:
   1707             sm.ScatterEvent('OnFleetFinderAdvertChanged')
   1708         self.currentFleetAd = createdAd
   1709         settings.char.ui.Set('fleetAdvert_lastAdvert', createdAd.GetData(fullAdvancedOptions=False))
   1710 
   1711     def UnregisterFleet(self):
   1712         if session.fleetid is None:
   1713             raise UserError('FleetNotFound')
   1714         if not self.IsBoss():
   1715             raise UserError('FleetNotCreator')
   1716         if prompt_player_fsd_dialog('FleetRemoveFleetFinderAd', {}, uiconst.YESNO, suppress=uiconst.ID_YES) == uiconst.ID_YES:
   1717             self.RemoveFleetFinderAdvert()
   1718             self.currentFleetAd = None
   1719 
   1720     def GetFleetAdsForChar(self):
   1721         fleetAdvertDictsForFleets = sm.ProxySvc('fleetProxy').GetAvailableFleetAds()
   1722         return {k:FleetAdvertObject(**v) for k, v in fleetAdvertDictsForFleets.iteritems()}
   1723 
   1724     @Memoize(0.5)
   1725     def GetFleetAdsForChar_Memoized(self):
   1726         return self.GetFleetAdsForChar()
   1727 
   1728     def ApplyToJoinFleet(self, fleetID, autoAccept = False):
   1729         if session.fleetid is not None:
   1730             if session.fleetid == fleetID:
   1731                 raise UserError('FleetYouAreAlreadyInFleet')
   1732             confirmChangeFleet = self._ConfirmChangeFleet()
   1733             if not confirmChangeFleet:
   1734                 return
   1735             self.LeaveFleet()
   1736         self.expectingInvite = fleetID
   1737         ret = sm.ProxySvc('fleetProxy').ApplyToJoinFleet(fleetID, autoAccept)
   1738         if ret:
   1739             raise UserError('FleetApplicationReceived')
   1740 
   1741     def _ConfirmChangeFleet(self):
   1742         userInput = prompt_player_fsd_dialog('AskChangeFleet', buttons=uiconst.YESNO)
   1743         return userInput == uiconst.ID_YES
   1744 
   1745     def AskJoinFleetFromLink(self, fleetID):
   1746         fleets = self.GetFleetAdsForChar()
   1747         if fleetID not in fleets:
   1748             raise UserError('FleetJoinFleetFromLinkError')
   1749         self.ApplyToJoinFleet(fleetID)
   1750         self.LogFleetJoinAttempts(fleetID, evefleet.JOIN_SOURCE_LINK)
   1751 
   1752     def GetCurrentAdvertForMyFleet(self):
   1753         if session.fleetid is None or not self.options.isRegistered:
   1754             return
   1755         fleetAd = self.GetMyFleetFinderAdvert()
   1756         if fleetAd is None:
   1757             return
   1758         fleetAd.standing = None
   1759         if fleetAd.Get('solarSystemID', 0):
   1760             mapSvc = sm.GetService('map')
   1761             numJumps = self.clientPathfinderService.GetJumpCountFromCurrent(fleetAd.solarSystemID)
   1762             fleetAd.numJumps = numJumps
   1763             constellationID = mapSvc.GetParent(fleetAd.solarSystemID)
   1764             fleetAd.regionID = mapSvc.GetParent(constellationID)
   1765             fleetAd.standing = sm.GetService('standing').GetStanding(session.charid, fleetAd.leader.charID)
   1766         return fleetAd
   1767 
   1768     def SetListenBroadcast(self, name, isit):
   1769         if name not in iconsByBroadcastType and name != BROADCAST_SHOW_OWN:
   1770             name = 'Event'
   1771         settings.user.ui.Set(LISTEN_BROADCAST_SETTING % name, isit)
   1772         sm.ScatterEvent('OnFleetBroadcastFilterChange')
   1773 
   1774     def GetMyShipTypeID(self):
   1775         shipTypeID = None
   1776         if session.shipid and session.solarsystemid:
   1777             shipTypeID = sm.GetService('godma').GetItem(session.shipid).typeID
   1778         elif GetActiveShip():
   1779             activeShipID = GetActiveShip()
   1780             dl = sm.GetService('clientDogmaIM').GetDogmaLocation()
   1781             shipItem = dl.GetItem(activeShipID)
   1782             shipTypeID = shipItem.typeID
   1783         return shipTypeID
   1784 
   1785     def SetRemoteMotd(self, motd):
   1786         self.CheckIsInFleet()
   1787         self.fleet.SetMotdEx(motd)
   1788 
   1789     def GetMotd(self):
   1790         self.CheckIsInFleet()
   1791         if self.motd is None:
   1792             self.motd = self.fleet.GetMotd()
   1793         return self.motd
   1794 
   1795     def OnFleetMotdChanged(self, motd, reloadMotd):
   1796         self.LogInfo('OnFleetMotdChanged', motd, session.fleetid)
   1797         self.CheckIsInFleet()
   1798         self.motd = motd
   1799         channelWindow = sm.GetService('XmppChat').GetGroupChatWindow('fleet_' + str(session.fleetid))
   1800         if channelWindow is not None and reloadMotd:
   1801             channelWindow.SetMotd(sender=None, text=motd)
   1802         else:
   1803             self.LogError('OnFleetMotdChanged could not find fleet chat window', session.fleetid)
   1804 
   1805     def OnDropCommanderDropData(self, dragObject, draggedGuys, receivingNode, *args):
   1806         groupType = receivingNode.groupType
   1807         groupID = getattr(receivingNode, 'groupID', None)
   1808         canMove = self.CanMoveAtLeastOneToThisEntry(draggedGuys, receivingNode, groupType, groupID)
   1809         if canMove in (CANNOT_BE_MOVED, CONNOT_BE_MOVED_INCOMPATIBLE):
   1810             return
   1811         if groupType == 'squad':
   1812             return self.DropOnSquadCommander(draggedGuys, groupID, canMove)
   1813         if groupType == 'wing':
   1814             return self.DropOnWingcommander(draggedGuys, groupID, canMove)
   1815         if groupType == 'fleet':
   1816             return self.DropOnFleetCommander(draggedGuys, canMove)
   1817         if groupType == 'fleetMember':
   1818             return self.DropOnFleetMember(draggedGuys, receivingNode)
   1819 
   1820     def DropOnSquadCommander(self, draggedGuys, newSquadID, canMove, *args):
   1821         newWingID = self.FindWingID(newSquadID)
   1822         if newWingID is None:
   1823             return
   1824         if canMove == CAN_BE_COMMANDER and len(draggedGuys) == 1:
   1825             role = evefleet.fleetRoleSquadCmdr
   1826         else:
   1827             role = evefleet.fleetRoleMember
   1828         self.HandleDroppingInGroup(draggedGuys, newWingID, newSquadID, role=role)
   1829 
   1830     def DropOnWingcommander(self, draggedGuys, newWingID, canMove):
   1831         if canMove == CAN_BE_COMMANDER and len(draggedGuys) == 1:
   1832             role = evefleet.fleetRoleWingCmdr
   1833         else:
   1834             role = evefleet.fleetRoleMember
   1835         self.HandleDroppingInGroup(draggedGuys, newWingID, None, role=role)
   1836 
   1837     def DropOnFleetCommander(self, draggedGuys, canMove):
   1838         members = self.GetMembers()
   1839         if canMove == CAN_BE_COMMANDER and len(draggedGuys) == 1:
   1840             role = evefleet.fleetRoleLeader
   1841         else:
   1842             role = evefleet.fleetRoleMember
   1843         self.HandleDroppingInGroup(draggedGuys, None, None, role=role)
   1844 
   1845     def DropOnFleetMember(self, draggedGuys, receivingNode):
   1846         droppedOnMember = receivingNode.member
   1847         newWingID = droppedOnMember.get('wingID', None)
   1848         newSquadID = droppedOnMember.get('squadID', None)
   1849         self.HandleDroppingInGroup(draggedGuys, newWingID, newSquadID)
   1850 
   1851     def HandleDroppingInGroup(self, draggedGuys, newWingID, newSquadID, role = evefleet.fleetRoleMember):
   1852         self.CheckIsInFleet()
   1853         members = self.GetMembers()
   1854         tryMove = []
   1855         tryInvite = []
   1856 
   1857         def SkipBecauseAlreadyInWing(eachCharID):
   1858             if role != evefleet.fleetRoleMember:
   1859                 return False
   1860             member = members[eachCharID]
   1861             if newWingID and newSquadID is None and newWingID == member.get('wingID', None):
   1862                 return True
   1863             return False
   1864 
   1865         for eachDraggedGuy in draggedGuys:
   1866             charID = eachDraggedGuy.charID
   1867             if charID in members:
   1868                 if newWingID is None and newSquadID is None and role != evefleet.fleetRoleLeader:
   1869                     continue
   1870                 if not SkipBecauseAlreadyInWing(charID):
   1871                     tryMove.append(charID)
   1872             else:
   1873                 tryInvite.append(charID)
   1874 
   1875         self.MassMove(tryMove, newWingID, newSquadID, role=role)
   1876         self.MassInvite(tryInvite, newWingID, newSquadID, role=role)
   1877 
   1878     def FindWingID(self, squadID):
   1879         for wingID, wingInfo in self.wings.iteritems():
   1880             if getattr(wingInfo, 'squads', {}):
   1881                 if squadID in wingInfo['squads']:
   1882                     return wingID
   1883 
   1884     def CanMoveToSquad(self, canMoveAll, draggedGuy, groupType, isFreeMove, myMemberInfo, receivingNode, isMultiMove):
   1885         members = self.GetMembers()
   1886         if groupType == 'fleetMember':
   1887             droppedOnMember = receivingNode.member
   1888             newWingID = droppedOnMember.get('wingID', None)
   1889             newSquadID = droppedOnMember.get('squadID', None)
   1890             allowedMove = CAN_ONLY_BE_MEMBER
   1891         else:
   1892             shiftDown = uicore.uilib.Key(uiconst.VK_SHIFT)
   1893             newSquadID = receivingNode.groupID
   1894             squadCommander = self.GetSquadCommander(newSquadID)
   1895             newWingID = None
   1896             if squadCommander:
   1897                 allowedMove = CAN_ONLY_BE_MEMBER
   1898                 newWingID = squadCommander.get('wingID', None)
   1899             elif isMultiMove or shiftDown:
   1900                 allowedMove = CAN_ONLY_BE_MEMBER
   1901             else:
   1902                 allowedMove = CAN_BE_COMMANDER
   1903             if newWingID is None:
   1904                 newWingID = self.FindWingID(newSquadID)
   1905                 if newWingID is None:
   1906                     return CANNOT_BE_MOVED
   1907         memberCount = len([ guy for guy in members.itervalues() if guy.squadID == newSquadID and guy.charID != draggedGuy.charID ])
   1908         if memberCount >= evefleet.MAX_MEMBERS_IN_SQUAD:
   1909             return CANNOT_BE_MOVED
   1910         elif canMoveAll or isFreeMove:
   1911             return allowedMove
   1912         draggedGuysMemberInfo = self.GetDraggedGuysMemberInfo(draggedGuy)
   1913         if newWingID != session.wingid or draggedGuysMemberInfo and draggedGuysMemberInfo.wingID != session.wingid:
   1914             return CANNOT_BE_MOVED
   1915         elif myMemberInfo.role == evefleet.fleetRoleWingCmdr:
   1916             return allowedMove
   1917         elif not draggedGuysMemberInfo and myMemberInfo.role == evefleet.fleetRoleSquadCmdr and session.squadid == newSquadID:
   1918             return allowedMove
   1919         else:
   1920             return CANNOT_BE_MOVED
   1921 
   1922     def CanMoveAtLeastOneToThisEntry(self, draggedGuys, receivingNode, groupType, groupID):
   1923         isMultiMove = len(draggedGuys) > 1
   1924         moveSet = {self.CanMoveToThisEntry(eachGuy, receivingNode, groupType, groupID, isMultiMove) for eachGuy in draggedGuys}
   1925         if len(draggedGuys) == 1 and CAN_BE_COMMANDER in moveSet:
   1926             return CAN_BE_COMMANDER
   1927         if CAN_ONLY_BE_MEMBER in moveSet or CAN_BE_COMMANDER in moveSet:
   1928             return CAN_ONLY_BE_MEMBER
   1929         return CANNOT_BE_MOVED
   1930 
   1931     def CanMoveToThisEntry(self, draggedGuy, receivingNode, groupType, groupID, isMultiMove = False):
   1932         guid = getattr(draggedGuy, '__guid__', None)
   1933         if guid not in AllUserEntries() + ['TextLink']:
   1934             return CONNOT_BE_MOVED_INCOMPATIBLE
   1935         if guid == 'TextLink':
   1936             charID = GetCharIDFromTextLink(draggedGuy)
   1937             if not charID:
   1938                 return CONNOT_BE_MOVED_INCOMPATIBLE
   1939             draggedGuy.charID = charID
   1940         if not IsEvePlayerCharacter(draggedGuy.charID):
   1941             return CONNOT_BE_MOVED_INCOMPATIBLE
   1942         members = self.GetMembers()
   1943         myMemberInfo = members[session.charid]
   1944         canMoveAll = myMemberInfo.role == evefleet.fleetRoleLeader or myMemberInfo.job & evefleet.fleetJobCreator
   1945         shiftDown = uicore.uilib.Key(uiconst.VK_SHIFT)
   1946         isFreeMove = False
   1947         if self.GetOptions().isFreeMove and draggedGuy.charID == session.charid:
   1948             isFreeMove = True
   1949         if groupType == 'fleet':
   1950             if not canMoveAll:
   1951                 return CANNOT_BE_MOVED
   1952             fleetCommander = self.GetFleetCommander()
   1953             if fleetCommander or isMultiMove or shiftDown:
   1954                 return CAN_ONLY_BE_MEMBER
   1955             return CAN_BE_COMMANDER
   1956         if groupType == 'wing':
   1957             newWingID = groupID
   1958             wingCommander = self.GetWingCommander(newWingID)
   1959             if wingCommander or isMultiMove or shiftDown:
   1960                 return CAN_ONLY_BE_MEMBER
   1961             if canMoveAll:
   1962                 return CAN_BE_COMMANDER
   1963         elif groupType in ('squad', 'fleetMember'):
   1964             return self.CanMoveToSquad(canMoveAll, draggedGuy, groupType, isFreeMove, myMemberInfo, receivingNode, isMultiMove)
   1965         return CANNOT_BE_MOVED
   1966 
   1967     def GetFleetCommander(self):
   1968         members = self.GetMembers()
   1969         for eachMember in members.itervalues():
   1970             if eachMember.role == evefleet.fleetRoleLeader:
   1971                 return eachMember
   1972 
   1973     def GetWingCommander(self, wingID):
   1974         members = self.GetMembers()
   1975         for eachGuy in members.itervalues():
   1976             if eachGuy.wingID == wingID and eachGuy.role == evefleet.fleetRoleWingCmdr:
   1977                 return eachGuy
   1978 
   1979     def GetSquadCommander(self, squadID):
   1980         members = self.GetMembers()
   1981         for eachGuy in members.itervalues():
   1982             if eachGuy.squadID == squadID and eachGuy.role == evefleet.fleetRoleSquadCmdr:
   1983                 return eachGuy
   1984 
   1985     def GetDraggedGuysMemberInfo(self, draggedGuy):
   1986         members = self.GetMembers()
   1987         if draggedGuy.charID in members:
   1988             return members[draggedGuy.charID]
   1989 
   1990     def ValidateWingsHaveValidNames(self):
   1991         wingNames = set()
   1992         for wing in self.GetWings().itervalues():
   1993             wingName = wing.name.lower().strip()
   1994             if not wingName or wingName in wingNames:
   1995                 raise UserError('GiveWingsUniqueNames')
   1996             wingNames.add(wingName)
   1997 
   1998     def StoreSetup(self):
   1999         if self.fleet is None:
   2000             return
   2001         self.ValidateWingsHaveValidNames()
   2002         name = self.lastLoadedSetup or ''
   2003         currentMotd = self.GetMotd()
   2004         currentMaxSize = self.fleet.GetFleetMaxSize()
   2005         defaultSquadID = self.GetAutoJoinSquadID()
   2006         defaultSquadName = None
   2007         if defaultSquadID:
   2008             fleet = self.GetFleetHierarchy()
   2009             sq = fleet['squads'].get(defaultSquadID, None)
   2010             if sq:
   2011                 defaultSquadName = sq['name'] or localization.GetByLabel('UI/Fleet/FleetWindow/UnnamedSquad')
   2012         currentFleetInfo = KeyVal(oldSetupName=self.lastLoadedSetup, currentMotd=currentMotd, currentOptions=self.options, currentMaxSize=currentMaxSize, currentDefaultSquadName=defaultSquadName)
   2013         wnd = StoreFleetSetupWnd(currentFleetInfo=currentFleetInfo)
   2014         if wnd.ShowModal() != 1:
   2015             return
   2016         if wnd.result is not None:
   2017             self.StoreFleetSetup(wnd.result)
   2018 
   2019     def GetSetups(self):
   2020         m = MenuData()
   2021         fleetSetupsByName = self.GetFleetSetups()
   2022         listOfSetups = sorted(fleetSetupsByName.values(), key=lambda x: x[fsConst.FS_NAME].lower())
   2023         for eachSetup in listOfSetups:
   2024             setupName = eachSetup[fsConst.FS_NAME]
   2025             hint = GetFleetSetupOptionHint(eachSetup)
   2026             m.AddEntry(setupName, func=lambda sn = setupName: self.LoadSetup(sn), hint=hint)
   2027 
   2028         return m
   2029 
   2030     def StoreFleetSetup(self, storeOptions):
   2031         wingsInfo = {}
   2032         wings = self.GetWings().values()
   2033         defaultSquadID = self.GetAutoJoinSquadID()
   2034         wings = sorted(wings, key=lambda x: x.get('wingID', None))
   2035         defaultSquadSetting = None
   2036         for wingIdx, wing in enumerate(wings):
   2037             wingSquads = []
   2038             for squad in wing.squads.itervalues():
   2039                 wingSquads.append((squad.squadID, squad.name))
   2040 
   2041             orderedWingSquads = sorted(wingSquads)
   2042             orderedWingSquadIDs = [ x[0] for x in orderedWingSquads ]
   2043             if defaultSquadID in orderedWingSquadIDs:
   2044                 sqIDx = orderedWingSquadIDs.index(defaultSquadID)
   2045                 _, sqdName = orderedWingSquads[sqIDx]
   2046                 defaultSquadSetting = (wing.name, sqIDx, sqdName)
   2047             wingSquads = SortListOfTuples(orderedWingSquads)
   2048             wingsInfo[wing.name.lower()] = {fsConst.FS_WING_NAME: wing.name,
   2049              fsConst.FS_SQUAD_NAMES: wingSquads,
   2050              fsConst.FS_WING_IDX: wingIdx}
   2051 
   2052         setupName = storeOptions.setupName
   2053         fleetSetupsByName = self.GetFleetSetups()
   2054         fleetInfo = fleetSetupsByName.get(setupName, {})
   2055         fleetInfo[fsConst.FS_NAME] = setupName
   2056         fleetInfo[fsConst.FS_WINGS_INFO] = wingsInfo
   2057         if storeOptions.storeMotd:
   2058             fleetInfo[fsConst.FS_MOTD] = self.GetMotd()
   2059         else:
   2060             fleetInfo.pop(fsConst.FS_MOTD, None)
   2061         if storeOptions.storeFreeMove:
   2062             fleetInfo[fsConst.FS_IS_FREE_MOVE] = self.options.isFreeMove
   2063         else:
   2064             fleetInfo.pop(fsConst.FS_IS_FREE_MOVE, None)
   2065         if storeOptions.storeDefaultSquad and defaultSquadSetting:
   2066             fleetInfo[fsConst.FS_DEFAULT_SQUAD] = defaultSquadSetting
   2067         else:
   2068             fleetInfo.pop(fsConst.FS_DEFAULT_SQUAD, None)
   2069         if storeOptions.storeMaxSize:
   2070             fleetInfo[fsConst.FS_MAX_SIZE] = self.fleet.GetFleetMaxSize()
   2071         else:
   2072             fleetInfo.pop(fsConst.FS_MAX_SIZE, None)
   2073         fleetSetupsByName[setupName] = fleetInfo
   2074         self.SaveSetupOnServer()
   2075 
   2076     def SaveSetupOnServer(self):
   2077         fleetSetupsByName = self.GetFleetSetups()
   2078         yamlFilters = yaml.safe_dump(fleetSetupsByName.values())
   2079         self.characterSettings.Save('fleetSetups', yamlFilters)
   2080         sm.ScatterEvent('OnFleetSetupChanged')
   2081 
   2082     def DeleteFleetSetup(self, setupName):
   2083         fleetSetupsByName = self.GetFleetSetups()
   2084         setupRemoved = fleetSetupsByName.pop(setupName, None)
   2085         if setupRemoved:
   2086             self.SaveSetupOnServer()
   2087 
   2088     def LoadSetup(self, setupName):
   2089         prompt_player_fsd_dialog('AttemptingToLoadFleet')
   2090         self.lastLoadedSetup = setupName
   2091         self.fleet.LoadFleetSetup(setupName)
   2092 
   2093     def SetFleetMaxSize(self):
   2094         currentMaxSize = self.fleet.GetFleetMaxSize()
   2095         caption = localization.GetByLabel('UI/Fleet/FleetWindow/SetFleetMaxSize')
   2096         minValue = 1
   2097         maxValue = evefleet.MAX_MEMBERS_IN_FLEET
   2098         hint = localization.GetByLabel('UI/Fleet/FleetWindow/SetFleetNumberBetween', max=FmtAmt(maxValue))
   2099         ret = uix.QtyPopup(maxValue, minValue, currentMaxSize, caption=caption, hint=hint)
   2100         if ret and 'qty' in ret:
   2101             qty = ret['qty']
   2102             self.fleet.SetFleetMaxSize(qty)
   2103 
   2104     def LoadSharedBroadcastSettings(self, settingKey, settingName):
   2105         if prompt_player_fsd_dialog('ConfirmLoadBroadcastSettings', {}, uiconst.YESNO, default=uiconst.ID_NO) != uiconst.ID_YES:
   2106             return
   2107         data = sharedSettings.GetDataFromSettingKey(settingKey, None, 'ItemFilterLoadingError')
   2108         broadcastSettings = data['broadcastSettings']
   2109         broadcastSettingsDict = {flagToName.get(x[0]):(x[1], x[2]) for x in broadcastSettings}
   2110         broadcastSettingsDict.update({x[0]:(x[1], x[2]) for x in broadcastSettings if x[0] == BROADCAST_SHOW_OWN})
   2111         for name, labelName in broadcastNames.iteritems():
   2112             settingsForName = broadcastSettingsDict.get(name)
   2113             if settingsForName:
   2114                 checked, color = settingsForName
   2115             else:
   2116                 checked, color = True, None
   2117             if color:
   2118                 color = tuple(color)
   2119             settings.user.ui.Set(LISTEN_BROADCAST_SETTING % name, checked)
   2120             settings.user.ui.Set(BROADCAST_COLOR_SETTING % name, color)
   2121 
   2122         settings.char.ui.Set('sharedBroadcastDragCont_name', settingName)
   2123         sm.ScatterEvent('OnFleetBroadcastFilterChange')
   2124         BroadcastSettings.CloseIfOpen()
   2125         BroadcastSettings.Open()
   2126 
   2127     def GetCynoTypeText(self, cynoTypeID):
   2128         path = cynoTypeNames.get(cynoTypeID, None)
   2129         if not path:
   2130             return ''
   2131         return localization.GetByLabel(path)
   2132 
   2133     def GetAllowedGroupIDs(self, advertInfo):
   2134         allowedGroups = set()
   2135         if evefleet.IsOpenToCorp(advertInfo):
   2136             allowedGroups.add(session.corpid)
   2137         if session.allianceid is not None and evefleet.IsOpenToAlliance(advertInfo):
   2138             allowedGroups.add(session.allianceid)
   2139         if session.warfactionid is not None and evefleet.IsOpenToMilitia(advertInfo):
   2140             allowedGroups.add(session.warfactionid)
   2141         return allowedGroups
   2142 
   2143     def OnFleetRespawnPointsUpdate(self, respawnPoints):
   2144         self.respawnPoints = respawnPoints
   2145         sm.ScatterEvent('OnFleetRespawnPointsUpdate_Local')
   2146 
   2147     def GetRespawnPoints(self):
   2148         return self.respawnPoints
   2149 
   2150     @eveexceptions.EatsExceptions('protoClientLogs')
   2151     def LogFleetCreation(self, fleetID, uiSource):
   2152         self.message_bus.fleet_created(fleet_id=fleetID, ui_source=uiSource)
   2153 
   2154     @eveexceptions.EatsExceptions('protoClientLogs')
   2155     def LogFleetJoinAttempts(self, fleetID, uiSource):
   2156         self.message_bus.applied_to_join(fleet_id=fleetID, ui_source=uiSource)
   2157 
   2158     def AddFleetFinderAdvert(self, adInfoObject):
   2159         adInfoData = adInfoObject.GetData()
   2160         fleetAdvertDict = sm.ProxySvc('fleetProxy').AddFleetFinderAdvert(adInfoData)
   2161         self.GetFleetAdsForChar_Memoized.clear_memoized()
   2162         if fleetAdvertDict:
   2163             return FleetAdvertObject(**fleetAdvertDict)
   2164 
   2165     def UpdateFleetAdvertWithNewLeader(self, allowedEntitiesInfo):
   2166         fleetAdvertDict = sm.ProxySvc('fleetProxy').UpdateFleetAdvertWithNewLeader(allowedEntitiesInfo)
   2167         if fleetAdvertDict:
   2168             return FleetAdvertObject(**fleetAdvertDict)
   2169 
   2170     def RemoveFleetFinderAdvert(self):
   2171         fleetAdvertDict = sm.ProxySvc('fleetProxy').RemoveFleetFinderAdvert()
   2172         self.GetFleetAdsForChar_Memoized.clear_memoized()
   2173         if fleetAdvertDict:
   2174             return FleetAdvertObject(**fleetAdvertDict)
   2175 
   2176     def GetMyFleetFinderAdvert(self):
   2177         fleetAdvertDict = sm.ProxySvc('fleetProxy').GetMyFleetFinderAdvert()
   2178         if fleetAdvertDict:
   2179             return FleetAdvertObject(**fleetAdvertDict)
   2180 
   2181     def UpdateAdvertInfo(self, numMembers, allowedDiffKeyVal):
   2182         fleetAdvertDict = sm.ProxySvc('fleetProxy').UpdateAdvertInfo(numMembers, allowedDiffKeyVal)
   2183         if fleetAdvertDict:
   2184             return FleetAdvertObject(**fleetAdvertDict)
   2185 
   2186     def UpdateAdvertAllowedEntities(self, allowedInfoFromStandings):
   2187         fleetAdvertDict = sm.ProxySvc('fleetProxy').UpdateAdvertAllowedEntities(allowedInfoFromStandings)
   2188         if fleetAdvertDict:
   2189             return FleetAdvertObject(**fleetAdvertDict)