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

crimewatchSvc.py (27223B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\services\crimewatchSvc.py
      2 import clonegrade
      3 import dogma.data
      4 import blue
      5 import uthread
      6 import utillib
      7 from carbon.common.script.sys.service import Service
      8 from chroma import Color
      9 from corruptionsuppression.systemEffects import DoesConcordReact
     10 from crimewatch.const import containerGroupsWithLootRights
     11 from crimewatch.util import IsItemFreeForAggression
     12 from eve.client.script.ui.crimewatch.duelInviteWindow import DuelInviteWindow
     13 from eve.common.lib import appConst as const
     14 from eve.common.script.net import eveMoniker
     15 from eve.common.script.sys import eveCfg, idCheckers
     16 from eve.common.script.sys.eveCfg import IsControllingStructure
     17 from eve.common.script.util import facwarCommon
     18 from eveuniverse.security import is_high_sec_or_higher, is_low_sec_or_higher, is_safe_sec_solar_system
     19 from inventorycommon.const import categoryStructure
     20 from inventorycommon.util import IsNPC
     21 from localization.formatters.timeIntervalFormatters import GetByLabel
     22 from omega.client.clone_grade_svc import get_clone_grade_svc
     23 from security.client.securityColor import get_security_status_color
     24 from spacecomponents.common.components.enforcecriminalflag import has_enforce_suspect_flag, has_enforce_criminal_flag
     25 from spacecomponents.common.helper import HasEnforceCriminalFlagComponent
     26 from evewar import warConst
     27 from eve.common.script.sys.idCheckers import IsWormholeSystem
     28 from eve.common.script.util.facwarCommon import IsSameFwFaction, IsCombatEnemyFaction
     29 
     30 class CrimewatchService(Service):
     31     __guid__ = 'svc.crimewatchSvc'
     32     __dependencies__ = ['michelle',
     33      'godma',
     34      'war',
     35      'facwar',
     36      'corp',
     37      'securitySvc']
     38     __startupdependencies__ = []
     39     __notifyevents__ = ['ProcessSessionChange',
     40      'OnWeaponsTimerUpdate',
     41      'OnPvpTimerUpdate',
     42      'OnNpcTimerUpdate',
     43      'OnCriminalTimerUpdate',
     44      'OnDisapprovalTimerUpdate',
     45      'OnSystemCriminalFlagUpdates',
     46      'OnSystemDisapprovalFlagUpdates',
     47      'OnCrimewatchEngagementCreated',
     48      'OnCrimewatchEngagementEnded',
     49      'OnCrimewatchEngagementStartTimeout',
     50      'OnCrimewatchEngagementStopTimeout',
     51      'OnDuelChallenge',
     52      'OnSecurityStatusUpdate',
     53      'OnJumpTimersUpdated',
     54      'OnCorpAggressionSettingsChange',
     55      'OnSessionReset',
     56      'OnSubscriptionChanged',
     57      'OnSessionChanged']
     58 
     59     def Run(self, *args):
     60         Service.Run(self, *args)
     61         self.Initialize()
     62 
     63     def Initialize(self):
     64         self.weaponsTimerState = None
     65         self.weaponsTimerExpiry = None
     66         self.pvpTimerState = None
     67         self.pvpTimerExpiry = None
     68         self.npcTimerState = None
     69         self.npcTimerExpiry = None
     70         self.criminalTimerState = None
     71         self.criminalTimerExpiry = None
     72         self.disapprovalTimerState = None
     73         self.disapprovalTimerExpiry = None
     74         self.criminalFlagsByCharID = {}
     75         self.disapprovalFlagsByCharID = {}
     76         self.myEngagements = {}
     77         self.safetyLevel = const.shipSafetyLevelFull
     78         self.duelWindow = None
     79         self.mySecurityStatus = None
     80         self.jumpTimers = None
     81         self.corpAggressionSettings = None
     82 
     83     def OnSubscriptionChanged(self):
     84         if session.charid and session.solarsystemid:
     85             _, _, _, safetyLevel = eveMoniker.CharGetCrimewatchLocation().GetClientStates()
     86             self._UpdateSafetyLevel(safetyLevel)
     87             sm.ScatterEvent('OnReCheckAlphaLock')
     88 
     89     def ProcessSessionChange(self, isRemote, session, change):
     90         if 'locationid' in change or 'charid' in change:
     91             myCombatTimers, myEngagements, flaggedCharacters, _ = eveMoniker.CharGetCrimewatchLocation().GetClientStates()
     92             self.LogInfo('ProcessSessionChange', myCombatTimers, myEngagements, flaggedCharacters)
     93             weaponTimerState, pvpTimerState, npcTimerState, criminalTimerState, disapprovalTimerState = myCombatTimers
     94             self.weaponsTimerState, self.weaponsTimerExpiry = weaponTimerState
     95             self.pvpTimerState, self.pvpTimerExpiry = pvpTimerState
     96             self.npcTimerState, self.npcTimerExpiry = npcTimerState
     97             self.criminalTimerState, self.criminalTimerExpiry = criminalTimerState
     98             self.disapprovalTimerState, self.disapprovalTimerExpiry = disapprovalTimerState
     99             self.myEngagements = myEngagements
    100             sm.ScatterEvent('OnCombatTimersUpdated')
    101             criminals, suspects = flaggedCharacters
    102             self.criminalFlagsByCharID.clear()
    103             self.UpdateSuspectsAndCriminals(criminals, suspects)
    104             if self.duelWindow is not None:
    105                 self.duelWindow.Close()
    106                 self.duelWindow = None
    107         if 'corpid' in change:
    108             self.RefreshCorpAggressionSettings()
    109 
    110     def OnSessionChanged(self, isRemote, session, change):
    111         if not session.solarsystemid:
    112             return
    113         if 'solarsystemid' in change or 'shipid' in change:
    114             _, _, _, safetyLevel = eveMoniker.CharGetCrimewatchLocation().GetClientStates()
    115             self.LogInfo('OnSessionChanged', safetyLevel)
    116             self._UpdateSafetyLevel(safetyLevel)
    117 
    118     def _ReCheckAlphaLock(self):
    119         if self._IsAlphaClone():
    120             sm.ScatterEvent('OnReCheckAlphaLock')
    121 
    122     def _IsAlphaClone(self):
    123         clone_grade = get_clone_grade_svc().get_clone_grade_with_grace_period()
    124         return clone_grade == clonegrade.CLONE_STATE_ALPHA
    125 
    126     def OnSessionReset(self):
    127         self.Initialize()
    128 
    129     def _UpdateSafetyLevel(self, safetyLevel):
    130         if self.IsSafetyLockedToFullLevel():
    131             safetyLevel = const.shipSafetyLevelFull
    132         old = self.safetyLevel
    133         self.safetyLevel = safetyLevel
    134         if self.safetyLevel != old:
    135             sm.ScatterEvent('OnSafetyLevelChanged', self.safetyLevel)
    136         elif self._IsAlphaClone():
    137             sm.ScatterEvent('OnReCheckAlphaLock')
    138 
    139     def IsSafetyLockedToFullLevel(self):
    140         if not session.solarsystemid:
    141             return False
    142         if is_safe_sec_solar_system(session.solarsystemid):
    143             color = Color(*get_security_status_color(1.0)).hex_argb
    144             return GetByLabel('UI/Crimewatch/SafetyLevel/SafetyLockedToFullInSafeSec', color=color)
    145         if IsControllingStructure():
    146             securityClass = self.securitySvc.get_modified_security_class(session.solarsystemid)
    147             if is_high_sec_or_higher(securityClass):
    148                 color = Color(*get_security_status_color(0.9)).hex_argb
    149                 return GetByLabel('UI/Crimewatch/SafetyLevel/SafetyLockedToFullInHighSecStructures', color=color)
    150         return False
    151 
    152     def IsSafetyAlphaLocked(self):
    153         if self._IsAlphaClone():
    154             if session.solarsystemid2:
    155                 securityClass = self.securitySvc.get_modified_security_class(session.solarsystemid2)
    156                 return is_high_sec_or_higher(securityClass)
    157         return False
    158 
    159     def GetSlimItemDataForCharID(self, charID):
    160         slimItem = None
    161         if eveCfg.InShipInSpace():
    162             ballpark = self.michelle.GetBallpark()
    163             if ballpark:
    164                 for _slimItem in ballpark.slimItems.itervalues():
    165                     if _slimItem.charID == charID:
    166                         slimItem = _slimItem
    167                         break
    168 
    169         if slimItem is None:
    170             pubInfo = sm.RemoteSvc('charMgr').GetPublicInfo(charID)
    171             info = cfg.eveowners.Get(charID)
    172             slimItem = utillib.KeyVal()
    173             slimItem.charID = charID
    174             slimItem.typeID = info.typeID
    175             slimItem.corpID = pubInfo.corporationID
    176             slimItem.warFactionID = pubInfo.warFactionID
    177             slimItem.allianceID = pubInfo.allianceID
    178             slimItem.securityStatus = self.GetCharacterSecurityStatus(charID)
    179             slimItem.groupID = const.groupCharacter
    180             slimItem.categoryID = const.categoryOwner
    181             slimItem.itemID = None
    182             slimItem.ownerID = charID
    183         return slimItem
    184 
    185     def UpdateSuspectsAndCriminals(self, criminals, suspects, decriminalizedCharIDs = ()):
    186         for charID in decriminalizedCharIDs:
    187             try:
    188                 del self.criminalFlagsByCharID[charID]
    189             except KeyError:
    190                 pass
    191 
    192         for charID in criminals:
    193             self.criminalFlagsByCharID[charID] = const.criminalTimerStateActiveCriminal
    194 
    195         for charID in suspects:
    196             self.criminalFlagsByCharID[charID] = const.criminalTimerStateActiveSuspect
    197 
    198         criminalizedCharIDs = set(self.criminalFlagsByCharID.iterkeys())
    199         sm.ScatterEvent('OnSuspectsAndCriminalsUpdate', criminalizedCharIDs, set(decriminalizedCharIDs))
    200 
    201     def UpdateDisapprovals(self, newNaughtyChars, noLongerNaughty):
    202         for charID in noLongerNaughty:
    203             self.disapprovalFlagsByCharID.pop(charID, None)
    204 
    205         for charID in newNaughtyChars:
    206             self.disapprovalFlagsByCharID[charID] = const.disapprovalTimerStateActive
    207 
    208         sm.ScatterEvent('OnDisapprovalUpdate', newNaughtyChars, noLongerNaughty)
    209 
    210     def OnWeaponsTimerUpdate(self, state, expiryTime):
    211         self.LogInfo('OnWeaponsTimerUpdate', state, expiryTime)
    212         self.weaponsTimerState = state
    213         self.weaponsTimerExpiry = expiryTime
    214 
    215     def OnPvpTimerUpdate(self, state, expiryTime):
    216         self.LogInfo('OnPvpTimerUpdate', state, expiryTime)
    217         self.pvpTimerState = state
    218         self.pvpTimerExpiry = expiryTime
    219 
    220     def OnNpcTimerUpdate(self, state, expiryTime):
    221         self.LogInfo('OnNpcTimerUpdate', state, expiryTime)
    222         self.npcTimerState = state
    223         self.npcTimerExpiry = expiryTime
    224 
    225     def OnCriminalTimerUpdate(self, state, expiryTime):
    226         self.LogInfo('OnCriminalTimerUpdate', state, expiryTime)
    227         self.criminalTimerState = state
    228         self.criminalTimerExpiry = expiryTime
    229 
    230     def OnDisapprovalTimerUpdate(self, state, expiryTime):
    231         self.LogInfo('OnDisapprovalTimerUpdate', state, expiryTime)
    232         self.disapprovalTimerState = state
    233         self.disapprovalTimerExpiry = expiryTime
    234 
    235     def OnSystemCriminalFlagUpdates(self, newIdles, newSuspects, newCriminals):
    236         self.LogInfo('OnSystemCriminalFlagUpdates', newIdles, newSuspects, newCriminals)
    237         self.UpdateSuspectsAndCriminals(newCriminals, newSuspects, newIdles)
    238 
    239     def OnSystemDisapprovalFlagUpdates(self, newIdles, newNaughyBoysAndGirls):
    240         self.LogInfo('OnSystemDisapprovalFlagUpdates', newIdles, newNaughyBoysAndGirls)
    241         self.UpdateDisapprovals(newNaughyBoysAndGirls, newIdles)
    242 
    243     def OnCrimewatchEngagementCreated(self, otherCharId, timeout):
    244         self.LogInfo('OnCrimewatchEngagementCreated', otherCharId, timeout)
    245         self.myEngagements[otherCharId] = timeout
    246         sm.ScatterEvent('OnCrimewatchEngagementUpdated', otherCharId, timeout)
    247         if self.duelWindow is not None and self.duelWindow.charID == otherCharId:
    248             self.duelWindow.Close()
    249             self.duelWindow = None
    250 
    251     def OnCrimewatchEngagementEnded(self, otherCharId):
    252         self.LogInfo('OnCrimewatchEngagementEnded', otherCharId)
    253         if otherCharId in self.myEngagements:
    254             del self.myEngagements[otherCharId]
    255         sm.ScatterEvent('OnCrimewatchEngagementUpdated', otherCharId, None)
    256 
    257     def OnCrimewatchEngagementStartTimeout(self, otherCharId, timeout):
    258         self.LogInfo('OnCrimewatchEngagementStartTimeout', otherCharId, timeout)
    259         self.myEngagements[otherCharId] = timeout
    260         sm.ScatterEvent('OnCrimewatchEngagementUpdated', otherCharId, timeout)
    261 
    262     def OnCrimewatchEngagementStopTimeout(self, otherCharId):
    263         self.LogInfo('OnCrimewatchEngagementStopTimeout', otherCharId)
    264         self.myEngagements[otherCharId] = const.crimewatchEngagementTimeoutOngoing
    265         sm.ScatterEvent('OnCrimewatchEngagementUpdated', otherCharId, const.crimewatchEngagementTimeoutOngoing)
    266 
    267     def GetMyEngagements(self):
    268         return self.myEngagements.copy()
    269 
    270     def GetMyBoosters(self):
    271         if not session.charid:
    272             return []
    273         myGodmaItem = sm.GetService('godma').GetItem(session.charid)
    274         if hasattr(myGodmaItem, 'boosters'):
    275             return myGodmaItem.boosters
    276         return []
    277 
    278     def GetBoosterEffects(self, booster):
    279         godma = sm.GetService('godma')
    280         dogmaLocation = sm.GetService('clientDogmaIM').GetDogmaLocation()
    281         staticMgr = dogmaLocation.dogmaStaticMgr
    282         boosterEffectsNegative = []
    283         boosterEffectsPositive = []
    284         if booster:
    285             boosterEffects = staticMgr.GetPassiveFilteredEffectsByType(booster.boosterTypeID)
    286             for effectID in boosterEffects:
    287                 eff = dogma.data.get_effect(effectID)
    288                 chanceAttributeID = staticMgr.effects[effectID].fittingUsageChanceAttributeID
    289                 if chanceAttributeID and effectID in booster.sideEffectIDs:
    290                     boosterEffectsNegative.append(eff)
    291                 if not chanceAttributeID:
    292                     boosterEffectsPositive.append(eff)
    293 
    294         return {'negative': boosterEffectsNegative,
    295          'positive': boosterEffectsPositive}
    296 
    297     def GetWeaponsTimer(self):
    298         return (self.weaponsTimerState, self.weaponsTimerExpiry)
    299 
    300     def GetNpcTimer(self):
    301         return (self.npcTimerState, self.npcTimerExpiry)
    302 
    303     def GetPvpTimer(self):
    304         return (self.pvpTimerState, self.pvpTimerExpiry)
    305 
    306     def GetCriminalTimer(self):
    307         return (self.criminalTimerState, self.criminalTimerExpiry)
    308 
    309     def GetDisapprovalTimer(self):
    310         return (self.disapprovalTimerState, self.disapprovalTimerExpiry)
    311 
    312     def GetSafetyLevel(self):
    313         return self.safetyLevel
    314 
    315     def SetSafetyLevel(self, safetyLevel):
    316         eveMoniker.CharGetCrimewatchLocation().SetSafetyLevel(safetyLevel)
    317         self.safetyLevel = safetyLevel
    318         sm.ScatterEvent('OnSafetyLevelChanged', self.safetyLevel)
    319 
    320     def IsCriminal(self, charID):
    321         return self.criminalFlagsByCharID.get(charID) == const.criminalTimerStateActiveCriminal
    322 
    323     def IsSuspect(self, charID):
    324         return self.criminalFlagsByCharID.get(charID) == const.criminalTimerStateActiveSuspect
    325 
    326     def IsDisapproved(self, charID):
    327         return self.disapprovalFlagsByCharID.get(charID) == const.disapprovalTimerStateActive
    328 
    329     def IsCriminallyFlagged(self, charID):
    330         return charID in self.criminalFlagsByCharID
    331 
    332     def IsDisapprovalFlagged(self, charID):
    333         return charID in self.disapprovalFlagsByCharID
    334 
    335     def HasLimitedEngagmentWith(self, charID):
    336         return charID in self.myEngagements
    337 
    338     def HaveWeaponsTimer(self):
    339         return self.weaponsTimerState != const.weaponsTimerStateIdle
    340 
    341     def GetRequiredSafetyLevelForActivatingAccelerationGate(self, gateID):
    342         bp = self.michelle.GetBallpark()
    343         item = bp.GetInvItem(gateID)
    344         if item is None:
    345             return (const.shipSafetyLevelFull, None, None)
    346         if item.isFactionWarfareGate or item.isInsurgencyGate:
    347             if session.warfactionid is None:
    348                 return (const.shipSafetyLevelPartial, 'DunGateNonFactionWarfareSafetyPreventsSuspect', None)
    349             sameFactionOrEnemy = IsCombatEnemyFaction(session.warfactionid, item.ownerID) or IsSameFwFaction(session.warfactionid, item.ownerID)
    350             if not sameFactionOrEnemy:
    351                 return (const.shipSafetyLevelPartial, 'DunGateNonFactionWarfareSafetyPreventsSuspect', None)
    352         if item.dunKeyLock == const.dungeonKeySoftLockOwnerSuspect:
    353             gateOwnerID = item.dunOwnerLock
    354             if not gateOwnerID or item.dunOwnerLock == session.charid or sm.GetService('fleet').IsMember(item.dunOwnerLock):
    355                 return (const.shipSafetyLevelFull, None, None)
    356             else:
    357                 return (const.shipSafetyLevelPartial, 'DunGateFactionCampaignSuspectSiteSafetyPreventsSuspect', 'ConfirmActivateGateSuspect')
    358         return (const.shipSafetyLevelFull, None, None)
    359 
    360     def GetRequiredSafetyLevelForAssistanc(self, targetID):
    361         if self.IsCriminal(targetID):
    362             return const.shipSafetyLevelNone
    363         elif self.IsSuspect(targetID):
    364             return const.shipSafetyLevelPartial
    365         else:
    366             return const.shipSafetyLevelFull
    367 
    368     def GetSafetyLevelRestrictionForAttackingTarget(self, targetID, effect = None):
    369         securityClass = self.securitySvc.get_modified_security_class(session.solarsystemid)
    370         minSafetyLevel = const.shipSafetyLevelFull
    371         if is_low_sec_or_higher(securityClass):
    372             item = self.michelle.GetItem(targetID)
    373             if not item:
    374                 if effect.rangeAttributeID is not None:
    375                     minSafetyLevel = const.shipSafetyLevelNone
    376             elif idCheckers.IsSystemOrNPC(item.ownerID):
    377                 minSafetyLevel = self._GetMinimumSafetyLevelForNpc(item, securityClass)
    378             elif not self.CanAttackFreely(item):
    379                 if is_high_sec_or_higher(securityClass):
    380                     if not DoesConcordReact(sm.GetService('clientDogmaIM').GetDogmaLocation(), item.groupID, True):
    381                         minSafetyLevel = const.shipSafetyLevelPartial
    382                     else:
    383                         minSafetyLevel = const.shipSafetyLevelNone
    384                 elif item.groupID == const.groupCapsule:
    385                     minSafetyLevel = const.shipSafetyLevelNone
    386                 else:
    387                     minSafetyLevel = const.shipSafetyLevelPartial
    388         return minSafetyLevel
    389 
    390     def GetSafetyLevelRestrictionForAidingTarget(self, targetID):
    391         secClass = self.securitySvc.get_modified_security_class(session.solarsystemid)
    392         minSafetyLevel = const.shipSafetyLevelFull
    393         if is_low_sec_or_higher(secClass):
    394             item = self.michelle.GetItem(targetID)
    395             if item and item.ownerID != session.charid:
    396                 if self.IsCriminallyFlagged(item.ownerID):
    397                     if self.IsCriminal(item.ownerID):
    398                         minSafetyLevel = const.shipSafetyLevelNone
    399                     elif self.IsSuspect(item.ownerID):
    400                         minSafetyLevel = const.shipSafetyLevelPartial
    401                 else:
    402                     minSafetyLevel = const.shipSafetyLevelFull
    403         return minSafetyLevel
    404 
    405     def CanAttackFreely(self, item):
    406         if idCheckers.IsSystem(item.ownerID) or item.ownerID == session.charid:
    407             return True
    408         securityClass = self.securitySvc.get_modified_security_class(session.solarsystemid)
    409         if securityClass == const.securityClassZeroSec:
    410             return True
    411         if self.IsCriminallyFlagged(item.ownerID):
    412             return True
    413         if self.HasLimitedEngagmentWith(item.ownerID):
    414             return True
    415         if idCheckers.IsCharacter(item.ownerID) and eveCfg.IsOutlawStatus(item.securityStatus):
    416             return True
    417         if session.warfactionid:
    418             if hasattr(item, 'warFactionID') and facwarCommon.IsCombatEnemyFaction(item.warFactionID, session.warfactionid):
    419                 return True
    420         belongToPlayerCorp = not IsNPC(session.corpid)
    421         if belongToPlayerCorp and not is_safe_sec_solar_system(session.solarsystemid):
    422             if item.ownerID == session.corpid:
    423                 if self.GetCorpAggressionSettings().IsFriendlyFireLegalAtTime(blue.os.GetWallclockTime()):
    424                     return True
    425             otherCorpID = getattr(item, 'corpID', None)
    426             if otherCorpID is not None:
    427                 if otherCorpID == session.corpid:
    428                     if self.GetCorpAggressionSettings().IsFriendlyFireLegalAtTime(blue.os.GetWallclockTime()):
    429                         return True
    430                 if self.war.GetRelationship(otherCorpID) == warConst.warRelationshipAtWarCanFight:
    431                     return True
    432             otherAllianceID = getattr(item, 'allianceID', None)
    433             if otherAllianceID is not None:
    434                 if self.war.GetRelationship(otherAllianceID) == warConst.warRelationshipAtWarCanFight:
    435                     return True
    436         if IsItemFreeForAggression(item.groupID):
    437             return True
    438         return False
    439 
    440     def _GetActiveEffectsByItem(self, itemID):
    441         effectIDs = set()
    442         item = self.michelle.GetItem(itemID)
    443         if item is not None:
    444             for row in dogma.data.get_type_effects(item.typeID):
    445                 effectID, isDefault = row.effectID, row.isDefault
    446                 if isDefault:
    447                     effectIDs.add(effectID)
    448 
    449         return [ dogma.data.get_effect(effectID) for effectID in effectIDs ]
    450 
    451     def IsAnyEffectOffensive(self, itemIDs):
    452         for itemID in itemIDs:
    453             for effect in self._GetActiveEffectsByItem(itemID):
    454                 if effect.isOffensive:
    455                     return True
    456 
    457         return False
    458 
    459     def GetRequiredSafetyLevelForEffect(self, effect, targetID = None):
    460         requiredSafetyLevel = const.shipSafetyLevelFull
    461         if effect is not None:
    462             if targetID is None and effect.effectCategory == const.dgmEffTarget:
    463                 targetID = sm.GetService('target').GetActiveTargetID()
    464                 if targetID is None:
    465                     return requiredSafetyLevel
    466             requiredSafetyLevel = const.shipSafetyLevelFull
    467             if effect.isOffensive:
    468                 requiredSafetyLevel = self.GetSafetyLevelRestrictionForAttackingTarget(targetID, effect)
    469             elif effect.isAssistance:
    470                 requiredSafetyLevel = self.GetSafetyLevelRestrictionForAidingTarget(targetID)
    471         return requiredSafetyLevel
    472 
    473     def CheckUnsafe(self, requiredSafetyLevel):
    474         if requiredSafetyLevel < self.safetyLevel:
    475             return True
    476         else:
    477             return False
    478 
    479     def SafetyActivated(self, requiredSafetyLevel):
    480         self.LogInfo('Safeties activated', self.safetyLevel, requiredSafetyLevel)
    481         sm.ScatterEvent('OnCrimewatchSafetyCheckFailed')
    482 
    483     def CheckCanTakeItems(self, containerID):
    484         if session.solarsystemid is None:
    485             return True
    486         bp = self.michelle.GetBallpark()
    487         if bp and not IsWormholeSystem(bp.solarsystemID) and self.GetSafetyLevel() == const.shipSafetyLevelFull:
    488             item = bp.GetInvItem(containerID)
    489             if item is not None:
    490                 if item.groupID in containerGroupsWithLootRights:
    491                     bp = self.michelle.GetBallpark()
    492                     if bp and not bp.HaveLootRight(containerID):
    493                         return False
    494         return True
    495 
    496     def IsOkToScoopItem(self, itemID):
    497         securityClass = self.securitySvc.get_modified_security_class(session.solarsystemid)
    498         if securityClass == const.securityClassZeroSec:
    499             return True
    500         if self.GetSafetyLevel() != const.shipSafetyLevelFull:
    501             return True
    502         bp = self.michelle.GetBallpark()
    503         if not bp:
    504             return True
    505         item = bp.GetInvItem(itemID)
    506         if item is not None:
    507             if item.categoryID == categoryStructure and item.ownerID not in (session.charid, session.corpid):
    508                 return False
    509         return True
    510 
    511     def GetRequiredSafetyLevelForEngagingDrones(self, droneIDs, targetID):
    512         safetyLevel = const.shipSafetyLevelFull
    513         if targetID is not None:
    514             effects = []
    515             for droneID in droneIDs:
    516                 effects += self._GetActiveEffectsByItem(droneID)
    517 
    518             if effects:
    519                 safetyLevels = [ self.GetRequiredSafetyLevelForEffect(effect, targetID) for effect in effects ]
    520                 safetyLevel = min(safetyLevels)
    521         return safetyLevel
    522 
    523     def OnDuelChallenge(self, fromCharID, fromCorpID, fromAllianceID, expiryTime):
    524         self.LogInfo('OnDuelChallenge', fromCharID, fromCorpID, fromAllianceID, expiryTime)
    525         wnd = DuelInviteWindow(charID=fromCharID, corpID=fromCorpID, allianceID=fromAllianceID)
    526         try:
    527             self.duelWindow = wnd
    528             wnd.StartTimeout(expiryTime)
    529             result = wnd.ShowDialog(modal=False)
    530             accept = None
    531             if 'accept' in wnd.result:
    532                 accept = True
    533             elif 'decline' in wnd.result:
    534                 accept = False
    535             if 'block' in wnd.result:
    536                 uthread.new(sm.GetService('addressbook').BlockOwner, fromCharID)
    537             if accept is not None:
    538                 eveMoniker.CharGetCrimewatchLocation().RespondToDuelChallenge(fromCharID, expiryTime, accept)
    539         finally:
    540             self.duelWindow = None
    541 
    542     def StartDuel(self, charID):
    543         if charID in self.myEngagements:
    544             self.LogInfo('The char', charID, 'is already in limited engagement with us. No duel request sent.')
    545         else:
    546             eveMoniker.CharGetCrimewatchLocation().StartDuelChallenge(charID)
    547 
    548     def GetMySecurityStatus(self):
    549         if self.mySecurityStatus is None:
    550             self.mySecurityStatus = eveMoniker.CharGetCrimewatchLocation().GetMySecurityStatus()
    551         return self.mySecurityStatus
    552 
    553     def GetCharacterSecurityStatus(self, charID):
    554         return eveMoniker.CharGetCrimewatchLocation().GetCharacterSecurityStatus(charID)
    555 
    556     def OnSecurityStatusUpdate(self, newSecurityStatus):
    557         self.mySecurityStatus = newSecurityStatus
    558 
    559     def GetSecurityStatusTransactions(self):
    560         return eveMoniker.CharGetCrimewatchLocation().GetSecurityStatusTransactions()
    561 
    562     def GetJumpTimers(self):
    563         if self.jumpTimers is None:
    564             self.jumpTimers = sm.RemoteSvc('jumpTimers').GetTimers(session.charid)
    565         return self.jumpTimers
    566 
    567     def OnJumpTimersUpdated(self, *args):
    568         self.jumpTimers = args
    569 
    570     def RefreshCorpAggressionSettings(self):
    571         self.corpAggressionSettings = self.corp.GetCorpRegistry().GetAggressionSettings()
    572 
    573     def GetCorpAggressionSettings(self):
    574         if self.corpAggressionSettings is None:
    575             self.RefreshCorpAggressionSettings()
    576         return self.corpAggressionSettings
    577 
    578     def OnCorpAggressionSettingsChange(self, aggressionSettings):
    579         self.corpAggressionSettings = aggressionSettings
    580 
    581     def _GetMinimumSafetyLevelForNpc(self, item, securityClass):
    582         if item.ownerID == const.ownerCONCORD:
    583             return const.shipSafetyLevelNone
    584         if item.groupID in const.illegalTargetNpcOwnedGroups:
    585             if is_high_sec_or_higher(securityClass):
    586                 return const.shipSafetyLevelNone
    587             else:
    588                 return const.shipSafetyLevelPartial
    589         elif HasEnforceCriminalFlagComponent(item.typeID):
    590             if has_enforce_criminal_flag(item.typeID):
    591                 return const.shipSafetyLevelNone
    592             if has_enforce_suspect_flag(item.typeID):
    593                 return const.shipSafetyLevelPartial
    594         return const.shipSafetyLevelFull
    595 
    596     def GetSecurityWhereAttacked(self):
    597         characterSecurity = sm.GetService('crimewatchSvc').GetMySecurityStatus()
    598         if characterSecurity <= -4.5:
    599             return 0.5
    600         if characterSecurity <= -4.0:
    601             return 0.6
    602         if characterSecurity <= -3.5:
    603             return 0.7
    604         if characterSecurity <= -3.0:
    605             return 0.8
    606         if characterSecurity <= -2.5:
    607             return 0.9
    608         if characterSecurity <= -2.0:
    609             return 1.0