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

characterCreationContainer_new.py (67012B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\login\characterCreationContainer_new.py
      2 import logging
      3 import math
      4 import random
      5 import time
      6 import types
      7 import blue
      8 import telemetry
      9 import carbonui.const as uiconst
     10 import charactercreator.client.animparams as animparams
     11 import charactercreator.client.characterCreationMetrics as ccm
     12 import charactercreator.client.scalingUtils as ccScalingUtils
     13 import charactercreator.const as ccConst
     14 import eve.common.lib.appConst as const
     15 import evegraphics.settings as gfxsettings
     16 import everesourceprefetch
     17 import gatekeeper
     18 import geo2
     19 import localization
     20 import paperdoll
     21 import trinity
     22 import uthread
     23 from carbon.client.script.environment.AudioUtil import PlaySound
     24 from carbonui.control.button import Button
     25 from carbonui.control.buttonIcon import ButtonIcon
     26 from carbonui.primitives.container import Container
     27 from carbonui.primitives.fill import Fill
     28 from carbonui.uianimations import animations
     29 from carbonui.uicore import uicore
     30 from carbonui.util.bunch import Bunch
     31 from charactercreator.client.characterCreationSteps_new import ModeStorage, GetStepOrder, GetStepsUpToAndIncluding
     32 from charactercreator.client.logging.stepLogger import StepLogger
     33 from charactercreator.const import FINALIZE_BUTTON_ANALYTIC_ID, NEXT_BUTTON_ANALYTIC_ID, BACK_BUTTON_ANALYTIC_ID
     34 from characterdata.races import CHARACTER_CREATION_RACE_IDS
     35 from eve.client.script.ui.camera.charCreationCamera import CONTROL_NONE
     36 from eve.client.script.ui.login.charcreation_new import charCreationSignals, soundConst
     37 from eve.client.script.ui.login.charcreation_new.charCreationButtons import EXIT_BUTTON_SIZE
     38 from eve.client.script.ui.login.charcreation_new.dollManager import GetCharacterCreationDollManager
     39 from eve.client.script.ui.login.charcreation_new.loadingWheel import CharacterCreationLoadingWheel
     40 from eve.client.script.ui.login.charcreation_new.portraitManager import GetCharacterCreationPortraitManager
     41 from eve.client.script.ui.login.charcreation_new.sceneManager import GetCharacterCreationSceneManager
     42 from eve.client.script.ui.login.charcreation_new.steps.characterCustomization import CharacterCustomization
     43 from eve.client.script.ui.login.charcreation_new.steps.characterNaming import CharacterNaming
     44 from eve.client.script.ui.login.charcreation_new.steps.characterPortrait import CharacterPortrait
     45 from eve.client.script.ui.login.charcreation_new.steps.dollSelection.characterDollSelection import CharacterDollSelection
     46 from eve.client.script.ui.login.charcreation_new.steps.empireSelectionStep import EmpireSelectionStep
     47 from eve.client.script.ui.shared.colorThemes import DEFAULT_COLORTHEMEID, COLOR_THEME_ID_BY_RACE
     48 from eve.client.script.ui.util.disconnectNotice import DisconnectNotice
     49 from eve.common.lib import appConst
     50 from eveexceptions import UserError
     51 from eveprefs import prefs, boot
     52 from locks import TempLock
     53 from player_messaging.client.ui_message import prompt_player_fsd_dialog
     54 from storylines.client.airnpe import is_air_npe_enabled
     55 NETWORKMODE_BASIC = 1
     56 NETWORKMODE_PORTRAIT = 2
     57 LEFT_SIDE_WITH = 180
     58 LEFT_SIDE_HEIGHT = 400
     59 RIGHT_SIDE_WIDTH = 350
     60 RIGHT_SIDE_HEIGHT = 150
     61 BOTTOM_LEFT_SIDE_WIDTH = 220
     62 BOTTOM_LEFT_SIDE_HEIGHT = ccConst.BUTTON_AREA_HEIGHT
     63 BUTTON_WIDTH = 145
     64 BUTTON_HEIGHT = 36
     65 BUTTON_HEIGHT_PRIMARY = 50
     66 BUTTON_WIDTH_PRIMARY = 200
     67 BUTTON_FONT_SIZE = 18
     68 BACKGROUND_TRANSITION_SECONDS = 2.5
     69 RESOURCE_WHEEL_SEPARATION = 8
     70 DOLLSTATES_TO_RETURN_TO_CC = [paperdoll.State.no_existing_customization, paperdoll.State.force_recustomize]
     71 RACE_TO_RESOURCE_NAME = {const.raceCaldari: 'caldari',
     72  const.raceAmarr: 'amarr',
     73  const.raceMinmatar: 'minmatar',
     74  const.raceGallente: 'gallente'}
     75 TOP_NAVIGATION_LINE_HEIGHT = 1
     76 BOTTOM_NAVIGATION_LINE_HEIGHT = 1
     77 TOP_NAVIGATION_LINE_OPACITY = 0.25
     78 BOTTOM_NAVIGATION_LINE_OPACITY = 0.25
     79 EXIT_BUTTON_MARGIN = 10
     80 BG_OPACITY = 1.0
     81 BG_WIDTH = 2117
     82 BG_HEIGHT = 1200
     83 VIGNETTE_RGB_DATA = ((0.0, (0.0, 0.0, 0.0)), (0.5, (0.0, 0.0, 0.0)), (1.0, (0.0, 0.0, 0.0)))
     84 VIGNETTE_ALPHA_DATA = ((0.0, 0.0), (0.8, 0.5), (1.0, 1.0))
     85 VIGNETTE_OPACITY = 0.7
     86 logger = logging.getLogger(__name__)
     87 
     88 class CharacterData(object):
     89 
     90     def __init__(self, charID):
     91         self.charID = charID
     92         self.bloodlineID = None
     93         self.genderID = None
     94         self.dna = None
     95 
     96 
     97 class CharacterCreationContainer_new(Container):
     98     __notifyevents__ = ['OnSetDevice',
     99      'OnGraphicSettingsChanged',
    100      'OnHideUI',
    101      'OnShowUI',
    102      'OnDollUpdated',
    103      'OnMapShortcut',
    104      'OnUIRefresh',
    105      'OnDisconnect']
    106     __update_on_reload__ = 1
    107 
    108     def ApplyAttributes(self, attributes):
    109         super(CharacterCreationContainer_new, self).ApplyAttributes(attributes)
    110         sm.RegisterNotify(self)
    111         self.sceneManager = GetCharacterCreationSceneManager()
    112         self.portraitManager = GetCharacterCreationPortraitManager()
    113         self.dollManager = GetCharacterCreationDollManager()
    114         self.needSculptingTutorial = True
    115         self.needNavigationTutorial = True
    116         self.dollSelected = False
    117         self.customizationStepSkipped = False
    118         self.step = None
    119         self.vignette = None
    120         self.exitButton = None
    121         self._characterData = {}
    122         self._activeCharID = None
    123         self._spareCharID = ccConst.SPARE_CHAR_ID
    124         self._characterData[self._spareCharID] = CharacterData(self._spareCharID)
    125         self._dollsDirty = False
    126         charCreationSignals.onEmpireFactionSelected.connect(self.OnEmpireFactionSelected)
    127 
    128     def SetNameCallback(self, name):
    129         self.charName = name
    130 
    131     @telemetry.ZONE_METHOD
    132     def OnSetDevice(self, *args):
    133         self.UpdateLayout()
    134         self.portraitManager.UpdateBackdrop()
    135 
    136     def OnDisconnect(self, reason = 0, msg = ''):
    137         disconnectNotice = DisconnectNotice(logProvider=self)
    138         disconnectNotice.OnDisconnect(reason, msg)
    139 
    140     @telemetry.ZONE_METHOD
    141     def UpdateLayout(self):
    142         try:
    143             self.LockEverything()
    144             with TempLock('CharacterCreationLayerUpdate'):
    145                 for each in self.mainCont.children:
    146                     if hasattr(each, 'UpdateLayout'):
    147                         each.UpdateLayout()
    148 
    149                 desktopWidth = uicore.desktop.width
    150                 self.topNavigationContainer.width = desktopWidth
    151                 self.topNavigationContainer.height = ccScalingUtils.GetTopNavHeight()
    152         finally:
    153             self.UnlockEverything()
    154 
    155     @telemetry.ZONE_METHOD
    156     def OnOpenView(self, **kwargs):
    157         self.startTime = blue.os.GetWallclockTime()
    158         self.sceneManager.Initialize()
    159         self.portraitManager.Initialize()
    160         uicore.cmd.commandMap.UnloadAllAccelerators()
    161         uicore.cmd.commandMap.LoadAcceleratorsByCategory('general')
    162         uicore.cmd.commandMap.LoadAcceleratorsByCategory('charactercreator')
    163         self.freezingAnimation = False
    164         from eve.client.script.ui.shared.preview import PreviewCharacterWnd, PreviewWnd
    165         previewCharWnd = PreviewCharacterWnd.GetIfOpen()
    166         if previewCharWnd:
    167             self.previewCharWindowWasOpenOn = (previewCharWnd.charID, previewCharWnd.dna)
    168             previewCharWnd.CloseByUser()
    169         else:
    170             self.previewCharWindowWasOpenOn = None
    171         previewWnd = PreviewWnd.GetIfOpen()
    172         if previewWnd:
    173             self.previewWindowWasOpenOn = previewWnd.typeID
    174             previewWnd.CloseByUser()
    175         else:
    176             self.previewWindowWasOpenOn = None
    177         self.characterSvc = sm.GetService('character')
    178         self.sceneManagerSvc = sm.GetService('sceneManager')
    179         self.fastCharacterCreation = gfxsettings.Get(gfxsettings.GFX_CHAR_FAST_CHARACTER_CREATION)
    180         self.mode = ccConst.MODE_FULLINITIAL_CUSTOMIZATION
    181         self.modeStorage = ModeStorage()
    182         self.modeObject = self.modeStorage.GetModeFor(self.mode)
    183         self.dollSelected = False
    184         self.customizationStepSkipped = False
    185         self.raceID = None
    186         self.schoolID = None
    187         self.charName = ''
    188         sm.GetService('cc').ClearMyAvailabelTypeIDs()
    189         self._stepID = None
    190         self.floor = None
    191         self.showingHelp = 0
    192         self.topNavigationContainer = None
    193         self.CreateUI()
    194         self.technologyViewsTracker = None
    195         self.audioService = sm.GetService('audio')
    196         self.worldLevel = self.audioService.GetWorldVolume()
    197         self.audioService.SetWorldVolume(0.0)
    198         self.stepLogger = StepLogger(blue.os.GetWallclockTime)
    199         self.finalStepLogger = None
    200         self.metrics = None
    201         self.approve_in_progress = False
    202         self.save_in_progress = False
    203         charCreationSignals.onDollSelectionRandomizeStarting.connect(self.OnDollRandomizeStarting)
    204         charCreationSignals.onDollSelectionDollRandomized.connect(self.OnDollRandomized)
    205 
    206     def Finalize(self):
    207         self.Save()
    208 
    209     def CreateLoadingWheel(self):
    210         self.loadingWheel = CharacterCreationLoadingWheel(name='loadingWheel', parent=self, align=uiconst.CENTER, state=uiconst.UI_NORMAL)
    211         self.loadingWheel.forcedOn = 0
    212 
    213     def CreateUI(self):
    214         self.uiContainer = Container(name='uiContainer', parent=self, align=uiconst.TOALL)
    215         self.CreateLoadingWheel()
    216         self.leftSide = Container(name='leftSide', parent=self.uiContainer, align=uiconst.TOPLEFT, pos=(0,
    217          0,
    218          LEFT_SIDE_WITH,
    219          LEFT_SIDE_HEIGHT))
    220         self.rightSide = Container(name='rightSide', parent=self.uiContainer, align=uiconst.BOTTOMRIGHT, pos=(0,
    221          BOTTOM_LEFT_SIDE_HEIGHT - BUTTON_HEIGHT,
    222          RIGHT_SIDE_WIDTH,
    223          RIGHT_SIDE_HEIGHT))
    224         self.bottomLeftSide = Container(name='bottomLeftSide', parent=self.uiContainer, align=uiconst.BOTTOMLEFT, pos=(0,
    225          0,
    226          BOTTOM_LEFT_SIDE_WIDTH,
    227          BOTTOM_LEFT_SIDE_HEIGHT))
    228         self.buttonNavLeft = Container(name='buttonNavLeft', parent=self.bottomLeftSide, align=uiconst.TOTOP, height=BUTTON_HEIGHT, padLeft=10)
    229         self.buttonNavRight = Container(name='buttonNavRight', parent=self.rightSide, align=uiconst.TOBOTTOM, height=BUTTON_HEIGHT_PRIMARY, padRight=20)
    230         self.finalizeBtn = Button(name='finalizeButtonCC', parent=self.buttonNavRight, align=uiconst.TORIGHT, label=localization.GetByLabel('UI/CharacterCreation/EnterGame'), func=self.Finalize, left=10, args=(), state=uiconst.UI_HIDDEN)
    231         self.saveBtn = Button(name='saveButtonCC', parent=self.buttonNavRight, align=uiconst.TORIGHT, label=localization.GetByLabel('UI/CharacterCreation/Finalize'), func=self.Save, left=10, args=(), state=uiconst.UI_HIDDEN, analyticID=FINALIZE_BUTTON_ANALYTIC_ID)
    232         self.approveBtn = Button(name='nextButtonCC', parent=self.buttonNavRight, align=uiconst.TORIGHT, label=localization.GetByLabel('UI/Generic/Next'), func=self.Approve, left=10, args=())
    233         self.backBtn = Button(name='backButtonCC', parent=self.buttonNavLeft, align=uiconst.TOLEFT, label=localization.GetByLabel('UI/Commands/Back'), func=self.Back, args=(), left=10)
    234         self.blackOut = Fill(name='blackOutFill', parent=self, color=(0.0, 0.0, 0.0, 0.0))
    235         self.mainCont = Container(name='mainCont', parent=self, align=uiconst.TOALL)
    236         self.topNavigationContainer = Container(name='topNavigationContainer', parent=self.uiContainer, align=uiconst.TOTOP_NOPUSH, width=self.uiContainer.width, height=ccScalingUtils.GetTopNavHeight(), bgColor=(0.0, 0.0, 0.0, 1.0))
    237         self.topNavigationContainer.SetOrder(1)
    238         self.UpdateLayout()
    239 
    240     def OnAvailabilityCheck(self, name):
    241         if self.finalStepLogger:
    242             self.finalStepLogger.AddTriedName(name)
    243 
    244     def OnUIRefresh(self):
    245         while self.dollManager.GetDoll(self._activeCharID) is None and self.dollManager.GetDoll(self._activeCharID).IsBusyUpdating():
    246             blue.synchro.Yield()
    247 
    248         self.Flush()
    249         self.CreateUI()
    250         self.SwitchStep(self.stepID)
    251 
    252     def OnHelpMouseEnter(self, btn, *args):
    253         PlaySound('ui_icc_button_mouse_over_play')
    254         btn.sr.icon.SetAlpha(1.0)
    255 
    256     def OnHelpMouseExit(self, btn, *args):
    257         if not self.showingHelp:
    258             btn.sr.icon.SetAlpha(0.5)
    259 
    260     def SetHintText(self, modifier, hintText = ''):
    261         if self.stepID in (ccConst.CUSTOMIZATIONSTEP, ccConst.PORTRAITSTEP):
    262             self.step.SetHintText(modifier, hintText)
    263 
    264     def SetMode(self, modeID):
    265         self.mode = modeID
    266         self.modeObject = self.modeStorage.GetModeFor(modeID)
    267 
    268     def GetModeIDForDollState(self, dollState):
    269         mode = ccConst.MODE_LIMITED_RECUSTOMIZATION
    270         if dollState == paperdoll.State.full_recustomizing:
    271             mode = ccConst.MODE_FULL_BLOODLINECHANGE
    272         elif dollState in (paperdoll.State.resculpting, paperdoll.State.no_existing_customization, paperdoll.State.force_recustomize):
    273             mode = ccConst.MODE_FULL_RECUSTOMIZATION
    274         return mode
    275 
    276     def GetActiveCharacterData(self):
    277         return self._characterData[self._activeCharID]
    278 
    279     def SetBloodline(self, charID, bloodlineID):
    280         self._characterData[charID].bloodlineID = bloodlineID
    281 
    282     def SetActiveBloodline(self, bloodlineID):
    283         self._characterData[self._activeCharID].bloodlineID = bloodlineID
    284 
    285     def SetActiveGender(self, genderID):
    286         self._characterData[self._activeCharID].genderID = genderID
    287 
    288     @telemetry.ZONE_METHOD
    289     def SetCharDetails(self, charID = None, gender = None, raceID = None, bloodlineID = None, dollState = None):
    290         self.portraitManager.ClearFacePortrait()
    291         self.dollState = dollState
    292         self.raceID = raceID
    293         self._activeCharID = 0
    294         self._characterData[0] = CharacterData(0)
    295         if charID is not None:
    296             if charID not in self._characterData:
    297                 self._characterData[charID] = CharacterData(charID)
    298             self._characterData[charID].bloodlineID = bloodlineID
    299             self._characterData[charID].genderID = int(gender)
    300             self._activeCharID = charID
    301             if dollState not in (paperdoll.State.force_recustomize, paperdoll.State.no_existing_customization):
    302                 self._characterData[self._activeCharID].dna = sm.GetService('paperdoll').GetMyPaperDollData(self._activeCharID)
    303             mode = self.GetModeIDForDollState(dollState)
    304         else:
    305             mode = ccConst.MODE_FULLINITIAL_CUSTOMIZATION
    306         self.SetMode(mode)
    307         self.availableSteps = self.modeObject.GetSteps()
    308         stepID = self.availableSteps[0]
    309         self.stepsUsed = {stepID}
    310         self.mainCont.Flush()
    311         self.stepLogger.Start()
    312         if dollState in (paperdoll.State.force_recustomize, paperdoll.State.no_existing_customization) or mode == ccConst.MODE_FULLINITIAL_CUSTOMIZATION:
    313             self.metrics = ccm.CharacterCreationMetrics(mode, ccConst.LOGIN)
    314         else:
    315             self.metrics = ccm.CharacterCreationMetrics(mode, ccConst.HANGAR)
    316         self.needSculptingTutorial = self.CanChangeBaseAppearance()
    317         if self.raceID is None:
    318             self._ChangeRace(self._GetRandomRaceID())
    319         if self.GetActiveCharacterData().bloodlineID is None or self.GetActiveCharacterData().genderID is None:
    320             self.SetActiveBloodline(self.SelectRandomBloodline())
    321             self.SetActiveGender(self.SelectRandomGender())
    322         self.SwitchStep(stepID)
    323 
    324     @telemetry.ZONE_METHOD
    325     def GetInfo(self):
    326         data = self.GetActiveCharacterData()
    327         return Bunch(charID=self._activeCharID, raceID=self.raceID, bloodlineID=data.bloodlineID, genderID=data.genderID, dna=data.dna, schoolID=self.schoolID, stepID=self.stepID, charName=self.charName)
    328 
    329     def CanChangeBaseAppearance(self, *args):
    330         return self.modeObject.CanChangeBaseAppearance()
    331 
    332     def GetMode(self, *args):
    333         return self.mode
    334 
    335     def CanChangeBloodline(self, *args):
    336         return self.modeObject.CanChangeBloodLine()
    337 
    338     def CanChangeGender(self, *args):
    339         return self.modeObject.CanChangeGender()
    340 
    341     def CanChangeName(self, *args):
    342         return self.modeObject.CanChangeName()
    343 
    344     def _AddExitButton(self, toStep):
    345         if self.exitButton and not self.exitButton.destroyed:
    346             self.exitButton.Close()
    347         self.exitButton = ButtonIcon(name='exitButton', parent=self.topNavigationContainer, func=self.CancelCharacterCreation, texturePath='res:/UI/Texture/Icons/73_16_45.png', align=uiconst.TOPRIGHT, left=EXIT_BUTTON_MARGIN, top=EXIT_BUTTON_MARGIN, width=EXIT_BUTTON_SIZE, height=EXIT_BUTTON_SIZE, idx=0)
    348 
    349     def ConstructNavigationIfNeeded(self, toStep):
    350         self._AddExitButton(toStep)
    351         self.topNavigationContainer.Hide()
    352         sm.GetService('gameui').MoveResourceLoadingIndicator(newAlignment=uiconst.BOTTOMLEFT, newLeft=20, newTop=RESOURCE_WHEEL_SEPARATION + ccConst.BUTTON_AREA_HEIGHT)
    353 
    354     @telemetry.ZONE_METHOD
    355     def SwitchStep(self, toStep, *args):
    356         if GetStepOrder(toStep) > GetStepOrder(self.stepID):
    357             if not self.PassedStepCheck(toStep):
    358                 return
    359         try:
    360             self.LockEverything()
    361             self.backBtn.state = uiconst.UI_NORMAL
    362             charCreationSignals.onStepSwitched(self.stepID, toStep)
    363             self.FadeToBlack(why=localization.GetByLabel('UI/Generic/Loading'))
    364             with TempLock('CharacterCreationLayerUpdate'):
    365                 self.UnfreezeAnimationIfNeeded()
    366                 self.BlockOnUpdate()
    367                 self.ConstructNavigationIfNeeded(toStep)
    368                 if self.stepID == ccConst.CUSTOMIZATIONSTEP:
    369                     sm.StartService('audio').SendUIEvent(unicode('ui_icc_sculpting_mouse_over_loop_stop'))
    370                     self.step.StoreHistorySliderPosition()
    371                 elif self.stepID == ccConst.PORTRAITSTEP:
    372                     sm.StartService('audio').SendUIEvent(unicode('ui_icc_sculpting_mouse_over_loop_stop'))
    373                     self.sceneManager.StorePortraitCameraSettings()
    374                 self.step = None
    375                 raceTheme = COLOR_THEME_ID_BY_RACE.get(self.raceID, DEFAULT_COLORTHEMEID)
    376                 sm.GetService('uiColor').SetNoCharTheme(raceTheme)
    377                 self.BlockOnUpdate()
    378                 self._UpdateNavigationButtons(toStep)
    379                 self.mainCont.Flush()
    380                 self.Cleanup()
    381                 self.BlockOnUpdate()
    382                 self.metrics.new_page(toStep)
    383                 self.StartStep(toStep)
    384                 self.stepID = toStep
    385                 self.stepsUsed.add(toStep)
    386                 self.portraitManager.UpdateBackdrop()
    387         finally:
    388             self.UnlockEverything()
    389             self.setupDone = 1
    390             self.FadeFromBlack()
    391             self.UnfreezeAnimationIfNeeded()
    392             sm.ScatterEvent('OnCharacterCreationStep', self.stepID, toStep)
    393 
    394     @property
    395     def stepID(self):
    396         return self._stepID
    397 
    398     @stepID.setter
    399     def stepID(self, newStepID):
    400         self._stepID = newStepID
    401         self.UpdateButtonAnalyticIDs(newStepID)
    402 
    403     def UpdateButtonAnalyticIDs(self, newStepID):
    404         self.backBtn.analyticID = BACK_BUTTON_ANALYTIC_ID % newStepID
    405         self.approveBtn.analyticID = NEXT_BUTTON_ANALYTIC_ID % newStepID
    406 
    407     def BlockOnUpdate(self):
    408         info = self.GetInfo()
    409         characters = self.characterSvc.characters
    410         if info.charID in characters and self.characterSvc.GetSingleCharactersDoll(info.charID).busyUpdating:
    411             start_time = time.clock()
    412             while self.characterSvc.GetSingleCharactersDoll(info.charID).busyUpdating and time.clock() - start_time < 60:
    413                 blue.synchro.Yield()
    414 
    415             if self.characterSvc.GetSingleCharactersDoll(info.charID).busyUpdating:
    416                 raise UserError('uiwarning01')
    417 
    418     def _UpdateNavigationButtons(self, toStep):
    419         self.approveBtn.Show()
    420         self.backBtn.Show()
    421         isGoingToFinalStep = toStep == self.availableSteps[-1]
    422         if toStep == ccConst.EMPIRESTEP:
    423             self.approveBtn.state = uiconst.UI_HIDDEN
    424             self.finalizeBtn.state = uiconst.UI_HIDDEN
    425             self.saveBtn.state = uiconst.UI_HIDDEN
    426         elif isGoingToFinalStep:
    427             self.approveBtn.state = uiconst.UI_HIDDEN
    428             isNewChar = self._activeCharID in (ccConst.DEFAULT_CHAR_ID, ccConst.SPARE_CHAR_ID)
    429             if isNewChar:
    430                 self.finalizeBtn.state = uiconst.UI_HIDDEN
    431                 self.saveBtn.state = uiconst.UI_HIDDEN
    432             else:
    433                 self.finalizeBtn.state = uiconst.UI_HIDDEN
    434                 self.saveBtn.state = uiconst.UI_NORMAL
    435         elif toStep == ccConst.DOLLSTEP:
    436             self.finalizeBtn.state = uiconst.UI_HIDDEN
    437             self.saveBtn.state = uiconst.UI_HIDDEN
    438             self.approveBtn.state = uiconst.UI_HIDDEN
    439         else:
    440             self.approveBtn.state = uiconst.UI_NORMAL
    441             self.finalizeBtn.state = uiconst.UI_HIDDEN
    442             self.saveBtn.state = uiconst.UI_HIDDEN
    443 
    444     def _HideBackButtonIfNoCharacters(self):
    445         if not sm.GetService('cc').GetCharactersToSelect():
    446             self.backBtn.Hide()
    447 
    448     def StartStep(self, toStep):
    449         self.stepLogger.SetStep(toStep)
    450         if toStep == ccConst.EMPIRESTEP:
    451             self.StartEmpireSelectionStep()
    452         elif toStep == ccConst.CUSTOMIZATIONSTEP:
    453             self.StartCustomizationStep()
    454         elif toStep == ccConst.PORTRAITSTEP:
    455             self.StartPortraitStep()
    456         elif toStep == ccConst.NAMINGSTEP:
    457             self.StartNamingStep()
    458         elif toStep == ccConst.DOLLSTEP:
    459             self.StartDollSelectionStep()
    460         else:
    461             raise NotImplementedError()
    462 
    463     def StartEmpireSelectionStep(self):
    464         self.dollSelected = False
    465         self.raceID = None
    466         self.schoolID = None
    467         self.step = EmpireSelectionStep(parent=self.mainCont)
    468         charCreationSignals.onEmpireFactionButtonClicked.connect(self.OnEmpireFactionButtonClicked)
    469         charCreationSignals.onEmpireSchoolButtonClicked.connect(self.SelectSchool)
    470         charCreationSignals.onEmpireSchoolSelected.connect(self.OnEmpireSchoolSelected)
    471 
    472     def OnEmpireFactionSelected(self, factionID):
    473         if self.sceneManager.avatarScene is not None:
    474             self._dollsDirty = True
    475 
    476     def OnEmpireFactionButtonClicked(self, factionID):
    477         raceID = appConst.raceByFaction[factionID]
    478         if raceID != self.raceID:
    479             self.SelectRace(raceID)
    480         self.PlayFactionSelectionSound(factionID)
    481 
    482     def PlayFactionSelectionSound(self, factionID):
    483         soundPath = ''
    484         if factionID == appConst.factionCaldariState:
    485             soundPath = soundConst.CALDARI_SELECTION_LOOP
    486         elif factionID == appConst.factionAmarrEmpire:
    487             soundPath = soundConst.AMARR_SELECTION_LOOP
    488         elif factionID == appConst.factionGallenteFederation:
    489             soundPath = soundConst.GALLENTE_SELECTION_LOOP
    490         elif factionID == appConst.factionMinmatarRepublic:
    491             soundPath = soundConst.MINMATAR_SELECTION_LOOP
    492         if not soundPath:
    493             return
    494         PlaySound(soundPath)
    495 
    496     def StopFactionSelectionSound(self):
    497         factionID = appConst.factionByRace.get(self.raceID, None)
    498         soundPath = ''
    499         if factionID == appConst.factionCaldariState:
    500             soundPath = soundConst.CALDARI_SELECTION_LOOP_STOP
    501         elif factionID == appConst.factionAmarrEmpire:
    502             soundPath = soundConst.AMARR_SELECTION_LOOP_STOP
    503         elif factionID == appConst.factionGallenteFederation:
    504             soundPath = soundConst.GALLENTE_SELECTION_LOOP_STOP
    505         elif factionID == appConst.factionMinmatarRepublic:
    506             soundPath = soundConst.MINMATAR_SELECTION_LOOP_STOP
    507         if not soundPath:
    508             return
    509         PlaySound(soundPath)
    510 
    511     def OnEmpireSchoolSelected(self, schoolID):
    512         if schoolID:
    513             self.AnimShowApproveButton()
    514         else:
    515             self.approveBtn.Hide()
    516 
    517     def OnDollSelectionDollClicked(self, charID):
    518         if charID is not None:
    519             self.SelectDoll(charID)
    520 
    521     def AnimShowApproveButton(self):
    522         self.approveBtn.state = uiconst.UI_NORMAL
    523         animations.FadeTo(self.approveBtn, 0.0, 1.0, duration=0.3, timeOffset=1.0, callback=self.approveBtn.Enable)
    524 
    525     def IsNamingStep(self, step):
    526         return step == ccConst.NAMINGSTEP
    527 
    528     @telemetry.ZONE_METHOD
    529     def FadeToBlack(self, why = ''):
    530         self.ShowLoading(why=why, forceOn=True)
    531         animations.FadeIn(self.blackOut, duration=0.5, sleep=True)
    532 
    533     @telemetry.ZONE_METHOD
    534     def FadeFromBlack(self):
    535         animations.FadeOut(self.blackOut, duration=0.5)
    536         self.HideLoading(forceOff=1)
    537 
    538     @telemetry.ZONE_METHOD
    539     def PassedStepCheck(self, toStep, *args):
    540         if toStep not in self.availableSteps:
    541             raise UserError('CCStepUnavailable')
    542         if toStep == ccConst.CUSTOMIZATIONSTEP and (self.raceID is None or self.GetActiveCharacterData().bloodlineID is None or self.GetActiveCharacterData().genderID is None):
    543             raise UserError('CCMustSelectRaceAndBloodline')
    544         isNamingStep = self.IsNamingStep(toStep)
    545         if (toStep == ccConst.PORTRAITSTEP or isNamingStep) and not prefs.GetValue('ignoreCCValidation', False):
    546             info = self.GetInfo()
    547             self.dollManager.ToggleClothes(forcedValue=0)
    548             self.characterSvc.ValidateDollCustomizationComplete(info.charID)
    549         currentStepID = None
    550         if self.step:
    551             currentStepID = self.step.stepID
    552         if self.step:
    553             self.step.ValidateStepComplete()
    554         return True
    555 
    556     @telemetry.ZONE_METHOD
    557     def Approve(self, skipCustomization = True, *args):
    558         if not self.approve_in_progress:
    559             self.PlayApprovalSound()
    560             self.approve_in_progress = True
    561             self.stepLogger.IncrementNextTryCount()
    562             idx = self.availableSteps.index(self.stepID)
    563             try:
    564                 if len(self.availableSteps) > idx + 1:
    565                     if self.stepID == ccConst.DOLLSTEP:
    566                         self.customizationStepSkipped = skipCustomization
    567                         if skipCustomization and len(self.availableSteps) > idx + 2:
    568                             nextStep = self.availableSteps[idx + 2]
    569                         else:
    570                             nextStep = self.availableSteps[idx + 1]
    571                     else:
    572                         nextStep = self.availableSteps[idx + 1]
    573                     self.SwitchStep(nextStep)
    574                     uicore.registry.SetFocus(self)
    575             finally:
    576                 self.approve_in_progress = False
    577 
    578     def PlayApprovalSound(self):
    579         if self.stepID == ccConst.CUSTOMIZATIONSTEP:
    580             PlaySound(soundConst.CUSTOMIZATION_CONFIRMATION)
    581         else:
    582             PlaySound(soundConst.NEXT_STEP)
    583 
    584     @telemetry.ZONE_METHOD
    585     def GoToCustomization(self, *args):
    586         self.Approve(skipCustomization=False)
    587 
    588     @telemetry.ZONE_METHOD
    589     def RandomizeSelectedDoll(self, *args):
    590         if self.dollSelected:
    591             charCreationSignals.onDollSelectionRandomizeStarting(self._activeCharID)
    592             self.RandomizeCharacter(self._activeCharID, True)
    593             doll = self.characterSvc.GetSingleCharactersDoll(self._activeCharID)
    594             while doll.busyUpdating:
    595                 blue.synchro.Yield()
    596 
    597             charCreationSignals.onDollSelectionDollRandomized(self._activeCharID)
    598 
    599     def OnDollRandomizeStarting(self, charID):
    600         self.backBtn.state = uiconst.UI_DISABLED
    601 
    602     def OnDollRandomized(self, charID):
    603         self.backBtn.state = uiconst.UI_NORMAL
    604 
    605     def RandomizeCharacter(self, charID = None, fullRandomize = False):
    606         if charID is None:
    607             charID = self._activeCharID
    608         doll = self.characterSvc.GetSingleCharactersDoll(charID)
    609         if doll.busyUpdating:
    610             return
    611         self.LockNavigation()
    612         PlaySound(soundConst.RANDOMIZE)
    613         if fullRandomize:
    614             self.RerollCharacter(charID)
    615         else:
    616             self.dollManager.ToggleClothes(forcedValue=0)
    617             if self._characterData[charID].genderID == ccConst.GENDERID_FEMALE:
    618                 itemList = ccConst.femaleRandomizeItems.keys()
    619             else:
    620                 itemList = ccConst.maleRandomizeItems.keys()
    621             canChangeBaseAppearance = self.CanChangeBaseAppearance()
    622             blacklist = ccConst.randomizerCategoryBlacklist[:]
    623             if not canChangeBaseAppearance:
    624                 blacklist += ccConst.recustomizationRandomizerBlacklist
    625             if boot.region == 'optic' and self._characterData[charID].genderID == ccConst.GENDERID_FEMALE:
    626                 blacklist.append(ccConst.scarring)
    627             categoryList = []
    628             for item in itemList:
    629                 if item not in blacklist:
    630                     categoryList.append(item)
    631 
    632             self.characterSvc.RandomizeCharacterGroups(charID, self.raceID, categoryList, doUpdate=False, fullRandomization=True)
    633             if canChangeBaseAppearance:
    634                 self.characterSvc.RandomizeCharacterSculpting(charID, self.raceID, doUpdate=False)
    635             decalModifiers = doll.buildDataManager.GetModifiersByCategory(ccConst.tattoo)
    636             for modifier in decalModifiers:
    637                 modifier.IsDirty = True
    638 
    639         self.characterSvc.UpdateDoll(charID, 'RandomizeCharacter')
    640         self.UnlockNavigation()
    641 
    642     def CheckAndGetName(self):
    643         isAvailable = self.step.CheckAvailability()
    644         if isAvailable.charName is None:
    645             prompt_player_fsd_dialog('CustomInfo', {'info': isAvailable.reason})
    646         return isAvailable.charName
    647 
    648     @telemetry.ZONE_METHOD
    649     def Save(self, *args):
    650         if not self.save_in_progress:
    651             self.save_in_progress = True
    652             self.BlockOnUpdate()
    653             if self.finalStepLogger:
    654                 self.finalStepLogger.Finalize()
    655             self.stepLogger.IncrementNextTryCount()
    656             try:
    657                 selectedPortraitInfo = self.portraitManager.GetActivePortraitInfo()
    658                 if selectedPortraitInfo and selectedPortraitInfo.backgroundID < const.NCC_MIN_NORMAL_BACKGROUND_ID:
    659                     prompt_player_fsd_dialog('CustomInfo', {'info': localization.GetByLabel('UI/CharacterCreation/CannotSaveWithThisBackground')})
    660                     return
    661                 self.LockEverything()
    662                 if self.step:
    663                     self.step.ValidateStepComplete()
    664                 if self.stepID != self.availableSteps[-1]:
    665                     raise UserError('CCCannotSave')
    666                 if self._activeCharID is not None and self.modeObject.AskForPortraitConfirmation():
    667                     self.UpdateExistingCharacter()
    668                     return
    669                 if self.GetActiveCharacterData().bloodlineID is None or self.GetActiveCharacterData().genderID is None:
    670                     raise UserError('CCCannotSave2')
    671                 else:
    672                     characterName = self.CheckAndGetName()
    673                     if characterName is None:
    674                         return
    675                     self.SaveAndEnterCurrentCharacter(characterName)
    676             finally:
    677                 if self and not self.destroyed:
    678                     self.UnlockEverything()
    679                     self.save_in_progress = False
    680 
    681     def SaveAndEnterCurrentCharacter(self, characterName):
    682         PlaySound(soundConst.FINALIZE)
    683         charID = self.SaveCurrentCharacter(characterName, self.portraitManager.activePortraitIndex)
    684         self.metrics.complete_cc(charid=charID)
    685         if charID:
    686             self.characterSvc.CachePortraitInfo(charID, self.portraitManager.GetActivePortraitInfo())
    687             self.tryLoginElseCharacterSelection(charID)
    688 
    689     def tryLoginElseCharacterSelection(self, charID):
    690         try:
    691             if gatekeeper.user.IsInCohort(gatekeeper.cohortToCharacterSelectionTest):
    692                 sm.GetService('cc').GetCharactersToSelect(force=True)
    693                 uthread.pool('GameUI::ActivateView::charsel', sm.GetService('viewState').ActivateView, 'charsel')
    694             else:
    695                 skipTutorial = self.CheckSkipTutorial() if is_air_npe_enabled() else True
    696                 sm.GetService('cc').FinishCharacterCreation(charID, skipTutorial)
    697         except:
    698             cc = sm.GetService('cc')
    699             cc.StopIntro()
    700             cc.GetCharactersToSelect(force=True)
    701             sm.GetService('loading').ProgressWnd(localization.GetByLabel('UI/CharacterCreation/CharacterSelection'), localization.GetByLabel('UI/CharacterCreation/FailedToEnterGame'), 2, 2)
    702             uthread.pool('GameUI::ActivateView::charsel', sm.GetService('viewState').ActivateView, 'charsel')
    703             raise
    704 
    705     def CheckSkipTutorial(self):
    706         if sm.RemoteSvc('charUnboundMgr').GetNumCharacters() > 1:
    707             if prompt_player_fsd_dialog('AskStartWithTutorial', {}, uiconst.YESNO, default=uiconst.ID_NO) == uiconst.ID_NO:
    708                 return True
    709         return False
    710 
    711     def OnEsc(self):
    712         self.Back()
    713 
    714     def CancelCharacterCreation(self):
    715         if self.modeObject.ExitToStation():
    716             msg = 'AskCancelCharCustomization'
    717         else:
    718             msg = 'AskCancelCharCreation'
    719         self.audioService.SendUIEvent('ui_es_pop_up_play')
    720         popUp = prompt_player_fsd_dialog(msg, {}, uiconst.YESNO)
    721         self.audioService.SendUIEvent('ui_es_pop_up_stop')
    722         if popUp != uiconst.ID_YES:
    723             return
    724         self.needNavigationTutorial = True
    725         self.needSculptingTutorial = True
    726         self.audioService.SendUIEvent('ui_es_stop')
    727         self.metrics.cancel_cc()
    728         if self.modeObject.ExitToStation():
    729             self.ExitToStation(updateDoll=False)
    730         else:
    731             sm.StartService('cc').GoBack()
    732         self.StopFactionSelectionSound()
    733 
    734     @telemetry.ZONE_METHOD
    735     def Back(self, *args):
    736         idx = self.availableSteps.index(self.stepID)
    737         if idx == 0:
    738             self.CancelCharacterCreation()
    739         else:
    740             if self.stepID == ccConst.NAMINGSTEP and self.customizationStepSkipped and idx > 1:
    741                 nextStep = self.availableSteps[idx - 2]
    742             else:
    743                 nextStep = self.availableSteps[idx - 1]
    744             PlaySound(soundConst.PREVIOUS_STEP)
    745             self.SwitchStep(nextStep)
    746             uicore.registry.SetFocus(self)
    747 
    748     @telemetry.ZONE_METHOD
    749     def LockEverything(self, *args):
    750         if not getattr(self, 'setupDone', 0):
    751             return
    752         containers = [self, self.topNavigationContainer]
    753         for container in containers:
    754             container.pickState = uiconst.TR2_SPS_OFF
    755 
    756         self.LockNavigation()
    757 
    758     @telemetry.ZONE_METHOD
    759     def LockNavigation(self, *args):
    760         if not getattr(self, 'setupDone', 0):
    761             return
    762         self.buttonNavRight.state = uiconst.UI_DISABLED
    763         self.buttonNavLeft.state = uiconst.UI_DISABLED
    764 
    765     @telemetry.ZONE_METHOD
    766     def UnlockEverything(self, *args):
    767         containers = [self, self.topNavigationContainer]
    768         for container in containers:
    769             container.pickState = uiconst.TR2_SPS_CHILDREN
    770 
    771         self.UnlockNavigation()
    772 
    773     @telemetry.ZONE_METHOD
    774     def UnlockNavigation(self, *args):
    775         if not self.parent.isopen:
    776             return
    777         self.buttonNavRight.state = uiconst.UI_PICKCHILDREN
    778         self.buttonNavLeft.state = uiconst.UI_PICKCHILDREN
    779 
    780     @telemetry.ZONE_METHOD
    781     def SelectRaceIfDifferent(self, raceID, check = 1):
    782         if self.raceID != raceID:
    783             self._ChangeRace(raceID)
    784         self._UpdateResourcePriority(raceID)
    785 
    786     @telemetry.ZONE_METHOD
    787     def SelectRace(self, raceID):
    788         self._ChangeRace(raceID)
    789         self._UpdateResourcePriority(raceID)
    790         charCreationSignals.onEmpireFactionSelected(appConst.factionByRace[raceID])
    791 
    792     def _ChangeRace(self, raceID):
    793         self.ClearSteps(what='race')
    794         self.dollManager.ResetClothesStorage()
    795         self.raceID = raceID
    796         if hasattr(self.step, 'OnRaceSelected'):
    797             self.step.OnRaceSelected(raceID)
    798         self.portraitManager.UpdateBackdrop()
    799 
    800     def _GetRandomRaceID(self):
    801         return random.choice(CHARACTER_CREATION_RACE_IDS)
    802 
    803     def _UpdateResourcePriority(self, raceID):
    804         raceAsString = RACE_TO_RESOURCE_NAME.get(raceID, None)
    805         if raceAsString:
    806             everesourceprefetch.ScheduleFront('interior_' + raceAsString)
    807             everesourceprefetch.ScheduleFront('bloodline_select_' + raceAsString)
    808 
    809     @telemetry.ZONE_METHOD
    810     def SelectBloodline(self, bloodlineID):
    811         if self.GetActiveCharacterData().bloodlineID != bloodlineID:
    812             self.ClearSteps(what='bloodline')
    813             if self.stepID == ccConst.CUSTOMIZATIONSTEP:
    814                 uthread.new(self._SwitchBloodlines, bloodlineID, fadeout=True)
    815             self.dollManager.ResetClothesStorage()
    816 
    817     def _SwitchBloodlines(self, bloodlineID, fadeout = False, resetToFace = False):
    818         if fadeout:
    819             self.FadeToBlack(why=localization.GetByLabel('UI/Common/Loading'))
    820         self.characterSvc.StopEditing()
    821         self.LockEverything()
    822         genderID = self.GetActiveCharacterData().genderID
    823         appearanceInfo = self.characterSvc.CopyCharacterAppearanceInfoForBloodline(self._activeCharID)
    824         self.SetActiveBloodline(bloodlineID)
    825         self.characterSvc.RemoveCharacter(self._activeCharID)
    826         self.AddCharacter(self._activeCharID, bloodlineID, self.raceID, genderID, dna=appearanceInfo)
    827         doll = self.characterSvc.GetSingleCharactersDoll(self._activeCharID)
    828         while doll.busyUpdating:
    829             blue.synchro.Yield()
    830 
    831         self.sceneManager.camera.UpdateAvatar(self.characterSvc.GetSingleCharactersAvatar(self._activeCharID))
    832         self.step.ReloadTattooMenu()
    833         self.step.GoToAssetMode(forcedMode=1)
    834         if resetToFace:
    835             if hasattr(self.step, 'LoadFaceMode'):
    836                 uthread.new(self.step.LoadFaceMode)
    837         self.UnlockEverything()
    838         if fadeout:
    839             self.FadeFromBlack()
    840 
    841     @telemetry.ZONE_METHOD
    842     def SelectBloodlineAndGender(self, bloodlineID, genderID, check = 1):
    843         isBloodlineChanged = self.GetActiveCharacterData().bloodlineID != bloodlineID
    844         isGenderChanged = self.GetActiveCharacterData().genderID != genderID
    845         self.WarnOfUnsavedChanges(check, isBloodlineChanged, isGenderChanged)
    846         if isBloodlineChanged or isGenderChanged:
    847             self.ClearSteps(what='bloodline')
    848         self.SetActiveBloodline(bloodlineID)
    849         self.SetActiveGender(genderID)
    850         self.dollManager.ResetClothesStorage()
    851         if getattr(self.step, 'sr') and getattr(self.step.sr, 'historySlider'):
    852             self.step.sr.historySlider.LoadHistory(0)
    853 
    854     def SelectRandomBloodline(self):
    855         bloodlineIDs = [1,
    856          2,
    857          3,
    858          4,
    859          5,
    860          6,
    861          7,
    862          8,
    863          11,
    864          12,
    865          13,
    866          14]
    867         return random.choice(bloodlineIDs)
    868 
    869     def SelectRandomGender(self):
    870         genderIDs = [0, 1]
    871         return random.choice(genderIDs)
    872 
    873     def WarnOfUnsavedChanges(self, check, isBloodlineChanged, isGenderChanged):
    874         isBloodlineUnsaved = isBloodlineChanged and self.GetActiveCharacterData().bloodlineID is not None
    875         isGenderUnsaved = isGenderChanged and self.GetActiveCharacterData().genderID is not None
    876         areThereUnsavedChanges = isBloodlineUnsaved or isGenderUnsaved
    877         isCustomized = self.stepID == ccConst.CUSTOMIZATIONSTEP and ccConst.CUSTOMIZATIONSTEP in self.stepsUsed
    878         shouldWarn = check and areThereUnsavedChanges and isCustomized
    879         if shouldWarn:
    880             dnaLog = self.dollManager.GetDollDNAHistory()
    881             if dnaLog and len(dnaLog) > 1:
    882                 message = 'CharCreationLoseChangeBloodline' if isBloodlineUnsaved else 'CharCreationLoseChangeGender'
    883                 if prompt_player_fsd_dialog(message, {}, uiconst.YESNO) != uiconst.ID_YES:
    884                     return
    885 
    886     @telemetry.ZONE_METHOD
    887     def SelectSchool(self, schoolID):
    888         if schoolID == self.schoolID:
    889             return
    890         if self.finalStepLogger:
    891             self.finalStepLogger.SetSchool(schoolID)
    892         self.schoolID = schoolID
    893         uicore.layer.charactercreation.controller.metrics.pick_school(schoolID)
    894         charCreationSignals.onEmpireSchoolSelected(schoolID)
    895 
    896     @telemetry.ZONE_METHOD
    897     def OnDollUpdated(self, charID, redundantUpdate, fromWhere, *args):
    898         if fromWhere in ('AddCharacter', 'OnSetDevice', 'InitAvatarPositions_Naming'):
    899             return
    900         self.ClearSteps(what='dollUpdated')
    901 
    902     @telemetry.ZONE_METHOD
    903     def OnGraphicSettingsChanged(self, changes):
    904         shouldGoBack = False
    905         if gfxsettings.GFX_CHAR_FAST_CHARACTER_CREATION in changes:
    906             header = localization.GetByLabel('UI/CharacterCreation/LoseChangesHeader')
    907             text = localization.GetByLabel('UI/CharacterCreation/LoseChanges')
    908             if prompt_player_fsd_dialog('CustomQuestion', {'header': header,
    909              'question': text}, uiconst.YESNO) == uiconst.ID_YES:
    910                 self.sceneManager.avatarScene = None
    911                 shouldGoBack = True
    912             else:
    913                 gfxsettings.Set(gfxsettings.GFX_CHAR_FAST_CHARACTER_CREATION, 0, pending=False)
    914         if shouldGoBack:
    915             if self._activeCharID is not None:
    916                 trinity.WaitForResourceLoads()
    917                 self.ExitToStation()
    918             else:
    919                 sm.GetService('cc').GoBack()
    920         if gfxsettings.UI_NCC_GREEN_SCREEN in changes:
    921             self.portraitManager.UpdateBackdrop()
    922             if self.floor:
    923                 self.floor.display = not gfxsettings.Get(gfxsettings.UI_NCC_GREEN_SCREEN)
    924 
    925     def ReloadStep(self):
    926         self.ShowLoading()
    927         try:
    928             self.SwitchStep(self.stepID)
    929         finally:
    930             self.HideLoading()
    931 
    932     @telemetry.ZONE_METHOD
    933     def TearDown(self):
    934         self.Cleanup()
    935         self.characterSvc.TearDown()
    936         self.floor = None
    937         self.sceneManager.TearDown()
    938         self.portraitManager.TearDown()
    939         self.dollManager.TearDown()
    940         self.freezingAnimation = False
    941         self.dollSelected = False
    942         self.customizationStepSkipped = False
    943 
    944     @telemetry.ZONE_METHOD
    945     def StartDollSelectionStep(self, *args):
    946         self.customizationStepSkipped = False
    947         info = self.GetInfo()
    948         self.sceneManagerSvc.Show2DBackdropScene()
    949         self.SetupSceneAndCharacter(extraDoll=True)
    950         avatar = self.characterSvc.GetSingleCharactersAvatar(info.charID)
    951         if avatar is not None:
    952             network = animparams.GetParamsPerAvatar(avatar, info.charID)
    953             network.SetControlParameter('ControlParameters|NetworkMode', NETWORKMODE_BASIC)
    954             self.sceneManager.SetupCamera(avatar, ccConst.CAMERA_MODE_BODY, callback=self.CameraMoveCB, frontClip=0.1, backClip=100.0, controlStyle=CONTROL_NONE)
    955         self.sceneManager.SetTwoDollsLighting()
    956         charCreationSignals.onDollSelectionDollClicked.connect(self.OnDollSelectionDollClicked)
    957         characterIDs = sorted([self._activeCharID, self._spareCharID])
    958         self.step = CharacterDollSelection(parent=self.mainCont, characterIDs=characterIDs, selectedCharacterID=self._activeCharID)
    959         self.step.InitAvatarPositions()
    960         self.sceneManager.scene.SetupShadowMaps()
    961 
    962     @telemetry.ZONE_METHOD
    963     def StartCustomizationStep(self, *args):
    964         info = self.GetInfo()
    965         self.sceneManagerSvc.Show2DBackdropScene()
    966         self.SetupSceneAndCharacter()
    967         avatar = self.characterSvc.GetSingleCharactersAvatar(info.charID)
    968         if avatar is not None:
    969             network = animparams.GetParamsPerAvatar(avatar, info.charID)
    970             network.SetControlParameter('ControlParameters|NetworkMode', NETWORKMODE_BASIC)
    971             self.sceneManager.SetupCamera(avatar, ccConst.CAMERA_MODE_FACE, callback=self.CameraMoveCB, frontClip=0.1, backClip=100.0)
    972         self.sceneManager.SetSingleDollLighting()
    973         self.step = CharacterCustomization(parent=self.mainCont)
    974         self.step.InitAvatarPositions()
    975         if self.CanChangeBaseAppearance():
    976             self.StartEditMode(showPreview=True, callback=self.step.ChangeSculptingCursor)
    977         self.sceneManager.camera.ToggleMode(ccConst.CAMERA_MODE_FACE, avatar=avatar, transformTime=500.0)
    978         self.sceneManager.scene.SetupShadowMaps()
    979 
    980     def CameraMoveCB(self, viewMatrix):
    981         info = self.GetInfo()
    982         if info.charID not in self.characterSvc.characters:
    983             return
    984         if not len(self.characterSvc.characters):
    985             return
    986         avatar = self.characterSvc.GetSingleCharactersAvatar(info.charID)
    987         matrix = geo2.MatrixInverse(viewMatrix.transform)
    988         view = matrix[3][:3]
    989         if info.genderID:
    990             height = 1.7
    991         else:
    992             height = 1.6
    993         head = (0, height, 0)
    994         rot = avatar.rotation
    995         vec = (view[0] - head[0], view[1] - head[1], view[2] - head[2])
    996         vecLength = geo2.Vec3Distance((0, 0, 0), vec)
    997         vec = geo2.QuaternionTransformVector(geo2.QuaternionInverse(rot), vec)
    998         norm = 1.0 / math.sqrt(vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2])
    999         vec = (vec[0] * norm, vec[1] * norm, vec[2] * norm)
   1000         leftright = math.atan2(vec[2], vec[0])
   1001         updown = math.asin(vec[1])
   1002         leftright = 1 - abs(leftright) / 3.1415927 * 2
   1003         updown = 2 * updown / 3.1415927
   1004         dist = (vecLength - 1.0) / 7.0
   1005         network = animparams.GetParamsPerAvatar(avatar, info.charID)
   1006         network.SetControlParameter('ControlParameters|HeadLookLeftRight', leftright)
   1007         network.SetControlParameter('ControlParameters|HeadLookUpDown', updown)
   1008         network.SetControlParameter('ControlParameters|CameraDistance', dist)
   1009 
   1010     @telemetry.ZONE_METHOD
   1011     def StartPortraitStep(self):
   1012         info = self.GetInfo()
   1013         if not self.portraitManager.alreadyLoadedOldPortraitData:
   1014             if self.modeObject.GetOldPortraitData():
   1015                 self.portraitManager.FetchOldPortraitData(info.charID)
   1016         self.step = CharacterPortrait(parent=self.mainCont)
   1017         self.SetupSceneAndCharacter()
   1018         avatar = self.characterSvc.GetSingleCharactersAvatar(info.charID)
   1019         if avatar is not None and avatar.animationUpdater is not None:
   1020             network = animparams.GetParamsPerAvatar(avatar, info.charID)
   1021             network.SetControlParameter('ControlParameters|NetworkMode', NETWORKMODE_PORTRAIT)
   1022         self.sceneManager.SetupForPortraitStep(avatar)
   1023         self.characterSvc.StartPosing(charID=info.charID, callback=self.step.ChangeSculptingCursor)
   1024 
   1025     @telemetry.ZONE_METHOD
   1026     def StartNamingStep(self, *args):
   1027         info = self.GetInfo()
   1028         avatar = self.characterSvc.GetSingleCharactersAvatar(info.charID)
   1029         self.SetupSceneAndCharacter()
   1030         self.sceneManager.SetUpNamingStepCamera(self.characterSvc.GetSingleCharactersAvatar(info.charID))
   1031         self.SetAvatarAnimationNetworkMode(NETWORKMODE_PORTRAIT)
   1032         while not self.characterSvc.sculpting:
   1033             blue.synchro.Yield()
   1034 
   1035         self.sceneManager.SetupCamera(avatar, ccConst.CAMERA_MODE_PORTRAIT, callback=self.CameraMoveCB)
   1036         self.sceneManager.camera.Update()
   1037         self.sceneManager.SetSingleDollCloseupLighting(self.GetInfo().genderID)
   1038         self.step = CharacterNaming(parent=self.mainCont)
   1039         self.step.InitAvatarPositions()
   1040         doll = self.characterSvc.GetSingleCharactersDoll(info.charID)
   1041         while doll.busyUpdating:
   1042             blue.synchro.Yield()
   1043 
   1044         self.portraitManager.SetBackdrop(random.choice(ccConst.defaultBackgroundOptions))
   1045         self.CapturePortrait(0)
   1046         self.sceneManager.SetUpNamingStepCamera(self.characterSvc.GetSingleCharactersAvatar(info.charID))
   1047         self.sceneManager.SetupCamera(avatar, ccConst.CAMERA_MODE_FACE)
   1048         self.SetAvatarAnimationNetworkMode(NETWORKMODE_BASIC)
   1049         charCreationSignals.onNameInputChanged.connect(self.OnNameInputChanged)
   1050 
   1051     def OnNameInputChanged(self, isValidName):
   1052         if isValidName:
   1053             if not self.saveBtn.display:
   1054                 self.saveBtn.Show()
   1055                 animations.FadeTo(self.saveBtn, 0.0, 1.0, duration=0.3)
   1056         else:
   1057             self.saveBtn.Hide()
   1058 
   1059     def SetAvatarAnimationNetworkMode(self, networkMode):
   1060         info = self.GetInfo()
   1061         avatar = self.characterSvc.GetSingleCharactersAvatar(info.charID)
   1062         if avatar is not None:
   1063             if info.charID in self.characterSvc.characters:
   1064                 network = animparams.GetParamsPerAvatar(avatar, info.charID)
   1065                 network.SetControlParameter('ControlParameters|NetworkMode', networkMode)
   1066                 while network.rebuilding:
   1067                     blue.synchro.Yield()
   1068 
   1069     @telemetry.ZONE_METHOD
   1070     def ClearSteps(self, what = None, force = 0, *args):
   1071         if self.stepID is None:
   1072             return
   1073         stepAlreadyCleared = getattr(self.step, 'stepAlreadyCleared', 0)
   1074         if not force and stepAlreadyCleared:
   1075             return
   1076         currentStepID = self.stepID
   1077         currentStepOrder = GetStepOrder(currentStepID)
   1078         if currentStepOrder <= GetStepOrder(ccConst.CUSTOMIZATIONSTEP):
   1079             self.portraitManager.ClearFacePortrait()
   1080         firstStep = self.availableSteps[0]
   1081         self.stepsUsed = GetStepsUpToAndIncluding(currentStepID) or {firstStep}
   1082         if self.step:
   1083             self.step.stepAlreadyCleared = 1
   1084 
   1085     @telemetry.ZONE_METHOD
   1086     def Cleanup(self):
   1087         self.characterSvc.StopEditing()
   1088         info = self.GetInfo()
   1089         charID = info.charID
   1090         if charID in self.characterSvc.characters:
   1091             self.characterSvc.StopPosing(charID)
   1092         self.sceneManager.Cleanup()
   1093         self.portraitManager.Cleanup()
   1094 
   1095     def SelectDoll(self, charID):
   1096         if charID != self._activeCharID:
   1097             self._spareCharID = self._activeCharID
   1098             self._activeCharID = charID
   1099         self.dollManager.ClearDnaLog()
   1100         charCreationSignals.onDollSelectionDollSelected(charID)
   1101         self.dollSelected = True
   1102 
   1103     @telemetry.ZONE_METHOD
   1104     def SetupSceneAndCharacter(self, extraDoll = False):
   1105         createExtraDoll = self.mode == ccConst.MODE_FULLINITIAL_CUSTOMIZATION
   1106         if not createExtraDoll:
   1107             extraDoll = False
   1108         info = self.GetInfo()
   1109         charA = min(self._activeCharID, self._spareCharID)
   1110         charB = max(self._activeCharID, self._spareCharID)
   1111         if self.sceneManager.SetAvatarScene(info):
   1112             self.AddCharacter(self._activeCharID, info.bloodlineID, info.raceID, info.genderID, dna=info.dna)
   1113             if createExtraDoll:
   1114                 spareBloodlineID = self.SelectRandomBloodline()
   1115                 spareGenderID = 1 - info.genderID
   1116                 self._characterData[self._spareCharID].bloodlineID = spareBloodlineID
   1117                 self._characterData[self._spareCharID].genderID = spareGenderID
   1118                 self.AddCharacter(self._spareCharID, spareBloodlineID, self.raceID, spareGenderID, dna=info.dna)
   1119         elif self._dollsDirty:
   1120             self.RerollCharacter(self._activeCharID, cameraUpdate=False)
   1121             self.RerollCharacter(self._spareCharID, cameraUpdate=False)
   1122             self._dollsDirty = False
   1123         elif extraDoll:
   1124             avatarA = self.characterSvc.GetSingleCharactersAvatar(charA)
   1125             avatarA.display = True
   1126             avatarB = self.characterSvc.GetSingleCharactersAvatar(charB)
   1127             avatarB.display = True
   1128         else:
   1129             avatarA = self.characterSvc.GetSingleCharactersAvatar(self._activeCharID)
   1130             avatarA.display = True
   1131             if createExtraDoll:
   1132                 avatarB = self.characterSvc.GetSingleCharactersAvatar(self._spareCharID)
   1133                 avatarB.display = False
   1134         self.BlockOnUpdate()
   1135 
   1136     @telemetry.ZONE_METHOD
   1137     def AddCharacter(self, charID, bloodlineID, raceID, genderID, scene = None, dna = None, validateColors = True, position = (0.0, 0.0, 0.0)):
   1138         self.dollManager.AddCharacter(charID, bloodlineID, raceID, genderID, scene or self.sceneManager.scene, dna, validateColors, position=position)
   1139 
   1140     @telemetry.ZONE_METHOD
   1141     def RerollCharacter(self, charID, cameraUpdate = True):
   1142         self.characterSvc.RemoveCharacter(charID)
   1143         bloodlineID = self.SelectRandomBloodline()
   1144         self.dollManager.AddCharacter(charID, bloodlineID, self.raceID, self._characterData[charID].genderID, self.sceneManager.scene, None, True)
   1145         if self.step and hasattr(self.step, 'InitAvatarPositions'):
   1146             self.step.InitAvatarPositions()
   1147         doll = self.characterSvc.GetSingleCharactersDoll(charID)
   1148         while doll.busyUpdating:
   1149             blue.synchro.Yield()
   1150 
   1151         if cameraUpdate:
   1152             self.sceneManager.camera.UpdateAvatar(self.characterSvc.GetSingleCharactersAvatar(charID))
   1153 
   1154     @telemetry.ZONE_METHOD
   1155     def GetAvailableStyles(self, modifier):
   1156         info = self.GetInfo()
   1157         gender = info.genderID
   1158         bloodline = info.bloodlineID
   1159         currentModifier = self.characterSvc.GetModifierByCategory(info.charID, modifier)
   1160         itemTypes = self.characterSvc.GetAvailableTypesByCategory(modifier, gender, info.raceID)
   1161         activeIndex = None
   1162         if currentModifier:
   1163             currentType = currentModifier.GetTypeData()
   1164             for i, each in enumerate(itemTypes):
   1165                 if each[1][0] == currentType[0] and each[1][1] == currentType[1] and each[1][2] == currentType[2]:
   1166                     activeIndex = i
   1167 
   1168         return (itemTypes, activeIndex)
   1169 
   1170     def GetModifierIntensity(self, modifierPath):
   1171         info = self.GetInfo()
   1172         modifier = self.characterSvc.GetModifiersByCategory(info.charID, modifierPath)
   1173         if modifier:
   1174             return modifier[0].weight
   1175         return 0.0
   1176 
   1177     @telemetry.ZONE_METHOD
   1178     def GetAvailableColors(self, modifier):
   1179         info = self.GetInfo()
   1180         colors, activeColorIndex = self.characterSvc.GetCharacterColorVariations(info.charID, modifier)
   1181         colors = tuple(colors)
   1182         retColors = []
   1183         for name, color in colors:
   1184             if color and type(color[0]) == types.TupleType:
   1185                 r = g = b = 0
   1186                 for _r, _g, _b, _a in color:
   1187                     r += _r
   1188                     g += _g
   1189                     b += _b
   1190 
   1191                 r = r / float(len(color))
   1192                 g = g / float(len(color))
   1193                 b = b / float(len(color))
   1194                 retColors.append((name, (r,
   1195                   g,
   1196                   b,
   1197                   1.0), color))
   1198             else:
   1199                 retColors.append((name, color, color))
   1200 
   1201         return (retColors, activeColorIndex)
   1202 
   1203     @telemetry.ZONE_METHOD
   1204     def SetRandomHairDarkness(self, doUpdate = True):
   1205         self.SetHairDarkness(random.random(), doUpdate=doUpdate)
   1206 
   1207     @telemetry.ZONE_METHOD
   1208     def SetHairDarkness(self, darkness, doUpdate = True):
   1209         info = self.GetInfo()
   1210         self.characterSvc.SetHairDarkness(info.charID, darkness)
   1211         if doUpdate:
   1212             sm.GetService('character').UpdateDoll(info.charID, fromWhere='SetHairDarkness')
   1213 
   1214     @telemetry.ZONE_METHOD
   1215     def SaveCurrentCharacter(self, charactername, portraitID):
   1216         total = 3
   1217         sm.GetService('loading').ProgressWnd(localization.GetByLabel('UI/CharacterCreation/Registering'), localization.GetByLabel('UI/CharacterCreation/CompilePrefs'), 1, total)
   1218         try:
   1219             if self.portraitManager.portraitInfo[portraitID] is None:
   1220                 raise UserError('CharCreationNoPortrait')
   1221             info = self.GetInfo()
   1222             charInfo = self.characterSvc.GetCharacterAppearanceInfo(info.charID)
   1223             data = self.GetActiveCharacterData()
   1224             charID = sm.GetService('cc').CreateCharacterWithDoll(charactername, data.bloodlineID, data.genderID, appConst.ancestryUndefined, charInfo, self.portraitManager.portraitInfo[portraitID], info.schoolID, info.raceID)
   1225         except UserError as what:
   1226             if not what.msg.startswith('CharNameInvalid'):
   1227                 prompt_player_fsd_dialog(*what.args)
   1228                 return
   1229             sm.GetService('loading').ProgressWnd(localization.GetByLabel('UI/CharacterCreation/Registering'), localization.GetByLabel('UI/CharacterCreation/FailedForSomeReason'), 3, total)
   1230         except RuntimeError as e:
   1231             logger.error('Failed to create character: %s' % e)
   1232             sm.GetService('loading').ProgressWnd(localization.GetByLabel('UI/CharacterCreation/Registering'), localization.GetByLabel('UI/CharacterCreation/FailedForSomeReason'), 3, total)
   1233         else:
   1234             sm.GetService('loading').ProgressWnd(localization.GetByLabel('UI/CharacterCreation/Registering'), localization.GetByLabel('UI/CharacterCreation/InsertingRecord'), 2, total)
   1235             sm.GetService('photo').AddPortrait(self.portraitManager.GetPortraitSnapshotPath(portraitID), charID)
   1236             sm.GetService('loading').ProgressWnd(localization.GetByLabel('UI/CharacterCreation/Registering'), localization.GetByLabel('UI/Generic/Done'), 3, total)
   1237             return charID
   1238         finally:
   1239             self.sessionSounds = []
   1240 
   1241     @telemetry.ZONE_METHOD
   1242     def UpdateExistingCharacter(self, *args):
   1243         charID = self._activeCharID
   1244         activePortraitInfo = self.portraitManager.GetActivePortraitInfo()
   1245         if activePortraitInfo is None:
   1246             raise UserError('CharCreationNoPortrait')
   1247         dollExists = self._characterData[charID].dna is not None
   1248         dollInfo = self.characterSvc.GetCharacterAppearanceInfo(charID)
   1249         availableTypeIDs = sm.GetService('cc').GetMyApparel()
   1250         metadata = self.characterSvc.characterMetadata[charID]
   1251         typesInUse = metadata.types
   1252         for resourceID in typesInUse.itervalues():
   1253             if resourceID:
   1254                 info = cfg.paperdollResources.Get(resourceID)
   1255                 if info.typeID is not None and info.typeID not in availableTypeIDs:
   1256                     raise UserError('ItemNotAtStation', {'item': info.typeID})
   1257 
   1258         bgInfo = cfg.paperdollPortraitResources.Get(activePortraitInfo.backgroundID)
   1259         if bgInfo and bgInfo.typeID is not None and bgInfo.typeID not in availableTypeIDs:
   1260             raise UserError('ItemNotAtStation', {'item': bgInfo.typeID})
   1261         info = self.GetInfo()
   1262         if self.mode == ccConst.MODE_FULL_RECUSTOMIZATION:
   1263             sm.GetService('cc').UpdateExistingCharacterBloodline(charID, info.bloodlineID, False)
   1264             sm.GetService('cc').UpdateExistingCharacterFull(charID, dollInfo, activePortraitInfo, dollExists, flush=True)
   1265         elif self.mode == ccConst.MODE_LIMITED_RECUSTOMIZATION:
   1266             sm.GetService('cc').UpdateExistingCharacterLimited(charID, dollInfo, activePortraitInfo, dollExists)
   1267         elif self.mode == ccConst.MODE_FULL_BLOODLINECHANGE:
   1268             sm.GetService('cc').UpdateExistingCharacterBloodline(charID, info.bloodlineID, True)
   1269         sm.GetService('photo').AddPortrait(self.portraitManager.GetPortraitSnapshotPath(self.portraitManager.activePortraitIndex), charID)
   1270         self.characterSvc.CachePortraitInfo(self._activeCharID, self.portraitManager.GetActivePortraitInfo())
   1271         self.metrics.complete_cc(charid=charID)
   1272         if self.dollState != paperdoll.State.force_recustomize:
   1273             self.ExitToStation()
   1274         else:
   1275             uthread.pool('GameUI::ActivateView::charsel', sm.GetService('viewState').ActivateView, 'charsel')
   1276 
   1277     @telemetry.ZONE_METHOD
   1278     def StartEditMode(self, callback = None, **kwds):
   1279         if callback is None and kwds.get('mode', None) == 'sculpt':
   1280             callback = getattr(self.step, 'ChangeSculptingCursor', None)
   1281         info = self.GetInfo()
   1282         self.characterSvc.StartEditMode(info.charID, self.sceneManager.scene, self.sceneManager.camera, callback=callback, **kwds)
   1283 
   1284     @telemetry.ZONE_METHOD
   1285     def UpdateBackdropLite(self, raceID, mouseEnter = False, *args):
   1286         bdScene = self.sceneManagerSvc.Get2DBackdropScene()
   1287         if not bdScene:
   1288             return
   1289         blue.resMan.SetUrgentResourceLoads(True)
   1290         for each in bdScene.children:
   1291             each.display = False
   1292             if mouseEnter:
   1293                 if raceID:
   1294                     if each.name == 'mouseoverSprite_%d' % raceID:
   1295                         each.display = True
   1296                 else:
   1297                     each.display = True
   1298             elif each.name == 'backdropSprite':
   1299                 each.texturePrimary.resPath = 'res:/UI/Texture/CharacterCreation/bg/RACE_Background_%d.dds' % raceID
   1300                 each.display = True
   1301 
   1302         blue.resMan.SetUrgentResourceLoads(False)
   1303 
   1304     @telemetry.ZONE_METHOD
   1305     def CapturePortrait(self, portraitID, *args):
   1306         if self.sceneManager.camera is None:
   1307             return
   1308         poseData = self.characterSvc.GetPoseData()
   1309         if poseData is None:
   1310             return
   1311         return self.portraitManager.CapturePortrait(portraitID, self.sceneManager.camera, poseData, self.sceneManager.lightingID, self.sceneManager.lightColorID, self.sceneManager.GetLightIntensity())
   1312 
   1313     @telemetry.ZONE_METHOD
   1314     def OnHideUI(self, *args):
   1315         self.uiContainer.state = uiconst.UI_HIDDEN
   1316 
   1317     @telemetry.ZONE_METHOD
   1318     def OnShowUI(self, *args):
   1319         self.uiContainer.state = uiconst.UI_PICKCHILDREN
   1320 
   1321     def OnMapShortcut(self, *args):
   1322         uicore.cmd.commandMap.UnloadAllAccelerators()
   1323         uicore.cmd.commandMap.LoadAcceleratorsByCategory('general')
   1324 
   1325     @telemetry.ZONE_METHOD
   1326     def ShowLoading(self, why = '', top = 200, forceOn = 0, *args):
   1327         wheel = self.loadingWheel
   1328         wheel.top = top
   1329         wheel.hint = why
   1330         wheel.forcedOn = forceOn
   1331         wheel.Show()
   1332 
   1333     @telemetry.ZONE_METHOD
   1334     def HideLoading(self, why = '', forceOff = 0, *args):
   1335         wheel = self.loadingWheel
   1336         if not wheel.forcedOn or forceOff:
   1337             self.loadingWheel.Hide()
   1338             wheel.forcedOn = 0
   1339 
   1340     @telemetry.ZONE_METHOD
   1341     def OnCloseView(self):
   1342         uicore.cmd.LoadAllAccelerators()
   1343         self.TearDown()
   1344         self.audioService.SendUIEvent('ui_icc_sculpting_mouse_over_loop_stop')
   1345         if self.audioService.GetWorldVolume() == 0.0:
   1346             self.audioService.SetWorldVolume(self.worldLevel)
   1347         sm.GetService('cc').ClearMyAvailabelTypeIDs()
   1348         self.Flush()
   1349         self.step = None
   1350         if self.previewCharWindowWasOpenOn is not None:
   1351             charID = self.previewCharWindowWasOpenOn[0]
   1352             self.previewCharWindowWasOpenOn = None
   1353             sm.GetService('preview').PreviewCharacter(charID)
   1354         if self.previewWindowWasOpenOn is not None:
   1355             typeID = self.previewWindowWasOpenOn
   1356             sm.GetService('preview').PreviewType(typeID)
   1357 
   1358     @telemetry.ZONE_METHOD
   1359     def ExitToStation(self, updateDoll = True):
   1360         self.OnCloseView()
   1361         if session.structureid:
   1362             change = {'structureid': (None, session.structureid)}
   1363         else:
   1364             change = {'stationid': (None, session.stationid)}
   1365         sm.GetService('gameui').OnSessionChanged(isRemote=False, session=session, change=change)
   1366 
   1367     def RemoveBodyModifications(self, *args):
   1368         try:
   1369             if getattr(self, 'bodyModRemoved', 0):
   1370                 return
   1371             modifiersToRemove = [ccConst.p_earslow,
   1372              ccConst.p_earshigh,
   1373              ccConst.p_nose,
   1374              ccConst.p_nostril,
   1375              ccConst.p_brow,
   1376              ccConst.p_lips,
   1377              ccConst.p_chin,
   1378              ccConst.t_head,
   1379              ccConst.s_head]
   1380             character = self.characterSvc.GetSingleCharacter(self._activeCharID)
   1381             for mod in modifiersToRemove:
   1382                 modifiers = self.characterSvc.GetModifiersByCategory(self._activeCharID, mod)
   1383                 for m in modifiers:
   1384                     character.GetDoll(self._activeCharID).buildDataManager.RemoveModifier(m)
   1385                     self.characterSvc.RemoveFromCharacterMetadata(self._activeCharID, mod)
   1386 
   1387             self.bodyModRemoved = 1
   1388             sm.GetService('character').UpdateDollsAvatar(character)
   1389         except Exception:
   1390             pass
   1391 
   1392     def TryStoreDna(self, lastUpdateRedundant, fromWhere, sculpting = 0, force = 0, allowReduntant = 0, *args):
   1393         if not lastUpdateRedundant or fromWhere in ('RandomizeCharacterGroups', 'RandomizeCharacter', 'AddCharacter'):
   1394             if not self.parent.isopen:
   1395                 return
   1396             if self.stepID == ccConst.CUSTOMIZATIONSTEP:
   1397                 if self.step is None:
   1398                     return
   1399                 if not force and self.step.menuMode == self.step.TATTOOMENU:
   1400                     self.step.tattooChangeMade = 1
   1401                     return
   1402             try:
   1403                 currentIndex, _ = self.step.historySlider.GetCurrentIndexAndMaxIndex()
   1404                 self.dollManager.TryStoreDna(lastUpdateRedundant, currentIndex, self._activeCharID, force=force, allowRedundant=allowReduntant)
   1405             except:
   1406                 pass
   1407 
   1408     @telemetry.ZONE_METHOD
   1409     def PassMouseEventToSculpt(self, type, x, y):
   1410         if not hasattr(self, 'characterSvc'):
   1411             return
   1412         if self.dollManager.GetDoll(self._activeCharID) and self.dollManager.GetDoll(self._activeCharID).IsBusyUpdating():
   1413             return
   1414         sculpting = self.characterSvc.GetSculpting()
   1415         if not uicore.uilib.leftbtn and sculpting and sculpting.startXY is not None and self.characterSvc.GetSculptingActive():
   1416             type = 'LeftUp'
   1417         pickValue = None
   1418         if sculpting and self.characterSvc.GetSculptingActive():
   1419             if type == 'LeftDown':
   1420                 pickValue = sculpting.PickWrapper(x, y)
   1421             elif type == 'LeftUp':
   1422                 pickValue = sculpting.EndMotion(x, y)
   1423             elif type == 'Motion':
   1424                 pickValue = sculpting.MotionWrapper(x, y)
   1425         return pickValue
   1426 
   1427     def PickPortrait(self, newPortraitID):
   1428         if self.stepID != ccConst.PORTRAITSTEP:
   1429             return
   1430         self.step.PickPortrait(newPortraitID)
   1431 
   1432     def AnimateToStoredPortrait(self, newPortraitID):
   1433         if self.stepID == ccConst.PORTRAITSTEP:
   1434             info = self.GetInfo()
   1435             self.portraitManager.AnimateToStoredPortrait(newPortraitID, info.charID)
   1436 
   1437     def TryFreezeAnimation(self, *args):
   1438         if sm.GetService('machoNet').GetGlobalConfig().get('disableFreezeAnimationInNCC'):
   1439             return
   1440         if self.stepID != ccConst.CUSTOMIZATIONSTEP:
   1441             return
   1442         info = self.GetInfo()
   1443         avatar = self.characterSvc.GetSingleCharactersAvatar(info.charID)
   1444         if avatar is not None:
   1445             network = animparams.GetParamsPerAvatar(avatar, info.charID)
   1446             network.update = False
   1447             self.freezingAnimation = True
   1448 
   1449     def UnfreezeAnimationIfNeeded(self, *args):
   1450         if self.freezingAnimation:
   1451             info = self.GetInfo()
   1452             avatar = self.characterSvc.GetSingleCharactersAvatar(info.charID)
   1453             if avatar is not None:
   1454                 network = animparams.GetParamsPerAvatar(avatar, info.charID)
   1455                 network.update = True
   1456                 self.freezingAnimation = False