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

characterCreationContainer.py (117540B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\login\characterCreationContainer.py
      2 import copy
      3 import math
      4 import random
      5 import time
      6 import types
      7 import blue
      8 import geo2
      9 import mathext
     10 import telemetry
     11 import carbonui.const as uiconst
     12 import charactercreator.client.animparams as animparams
     13 import charactercreator.client.characterCreationMetrics as ccm
     14 import charactercreator.client.scalingUtils as ccScalingUtils
     15 import charactercreator.const as ccConst
     16 import eve.client.script.ui.login.charcreation.ccUtil as ccUtil
     17 import eve.common.lib.appConst as const
     18 import evegraphics.settings as gfxsettings
     19 import everesourceprefetch
     20 import gatekeeper
     21 import localization
     22 import log
     23 import paperdoll
     24 import utillib
     25 from carbonui import Density
     26 from carbonui.control.button import Button
     27 from carbonui.primitives.fill import Fill
     28 from carbonui.util.bunch import Bunch
     29 from eve.client.script.ui.control.eveLoadingWheel import LoadingWheel
     30 from eve.common.script.paperDoll.paperDollDefinitions import BODY_CATEGORIES, DEFAULT_NUDE_PARTS, DESIRED_ORDER, LOD_SKIN
     31 from eve.common.script.util import paperDollUtil
     32 import trinity
     33 import uthread
     34 from carbon.client.script.environment.AudioUtil import PlaySound
     35 from carbonui.primitives.container import Container
     36 from carbonui.primitives.gradientSprite import GradientSprite
     37 from carbonui.primitives.sprite import Sprite
     38 from carbonui.uianimations import animations
     39 from carbonui.uicore import uicore
     40 from charactercreator.client.characterCreationSteps import ModeStorage, GetStepOrder, GetStepsUpToAndIncluding, STEP_ORDER_LIST
     41 from charactercreator.client.logging.stepLogger import StepLogger
     42 from eve.client.script.ui.camera.charCreationCamera import CharCreationCamera
     43 from carbonui.control.buttonIcon import ButtonIcon
     44 from eve.client.script.ui.login.charcreation.charCreationButtons import EXIT_BUTTON_SIZE
     45 from eve.client.script.ui.login.charcreation.empireStepContainer import EmpireStepContainer
     46 from eve.client.script.ui.login.charcreation.portraitMaker import PortraitMaker
     47 from eve.client.script.ui.login.charcreation.charCreationNavigation import CharCreationNavigation
     48 from eve.client.script.ui.login.charcreation.steps.bloodLineStep import BloodlineStep
     49 from eve.client.script.ui.login.charcreation.steps.characterCustomization import CharacterCustomization
     50 from eve.client.script.ui.login.charcreation.steps.characterNaming import CharacterNaming
     51 from eve.client.script.ui.login.charcreation.steps.characterPortrait import CharacterPortrait
     52 from eve.client.script.ui.login.charcreation.steps.empireStep import EmpireStep
     53 from eve.client.script.ui.login.charcreation.steps.technologyStep import TechnologyStep, GetEmpireNavigationWidth, GetEmpireNavigationHeight
     54 from eve.client.script.ui.login.charcreation.technologyViewUtils import EmpireNavigation
     55 from eve.client.script.ui.login.charcreation.technologyViewsTracker import TechnologyViewsTracker
     56 from eve.client.script.ui.login.charcreation.assetMenu import femaleModifierDisplayNames, maleModifierDisplayNames
     57 from eve.client.script.ui.shared.colorThemes import DEFAULT_COLORTHEMEID, COLOR_THEME_ID_BY_RACE
     58 from eve.client.script.ui.util.disconnectNotice import DisconnectNotice
     59 from eveprefs import prefs
     60 from fsdBuiltData.common.graphicIDs import GetGraphicFile
     61 from locks import TempLock
     62 from eveexceptions import UserError
     63 from player_messaging.client.ui_message import prompt_player_fsd_dialog
     64 from storylines.client.airnpe import is_air_npe_enabled
     65 NETWORKMODE_BASIC = 1
     66 NETWORKMODE_PORTRAIT = 2
     67 LEFT_SIDE_WITH = 180
     68 LEFT_SIDE_HEIGHT = 400
     69 RIGHT_SIDE_WIDTH = 350
     70 RIGHT_SIDE_HEIGHT = ccConst.BUTTON_AREA_HEIGHT
     71 BOTTOM_LEFT_SIDE_WIDTH = 220
     72 BOTTOM_LEFT_SIDE_HEIGHT = ccConst.BUTTON_AREA_HEIGHT
     73 BUTTON_WIDTH = 145
     74 BUTTON_HEIGHT = 36
     75 BUTTON_FONT_SIZE = 18
     76 BACKGROUND_TRANSITION_SECONDS = 2.5
     77 RESOURCE_WHEEL_SEPARATION = 8
     78 DOLLSTATES_TO_RETURN_TO_CC = [paperdoll.State.no_existing_customization, paperdoll.State.force_recustomize]
     79 CLOTHING_ITEMS = (ccConst.topouter,
     80  ccConst.topmiddle,
     81  ccConst.bottomouter,
     82  ccConst.outer,
     83  ccConst.feet,
     84  ccConst.glasses,
     85  ccConst.bottommiddle,
     86  ccConst.mask)
     87 RACE_TO_RESOURCE_NAME = {const.raceCaldari: 'caldari',
     88  const.raceAmarr: 'amarr',
     89  const.raceMinmatar: 'minmatar',
     90  const.raceGallente: 'gallente'}
     91 TOP_NAVIGATION_LINE_HEIGHT = 1
     92 BOTTOM_NAVIGATION_LINE_HEIGHT = 1
     93 TOP_NAVIGATION_LINE_OPACITY = 0.25
     94 BOTTOM_NAVIGATION_LINE_OPACITY = 0.25
     95 EXIT_BUTTON_MARGIN = 10
     96 BG_OPACITY = 1.0
     97 BG_WIDTH = 2117
     98 BG_HEIGHT = 1200
     99 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)))
    100 VIGNETTE_ALPHA_DATA = ((0.0, 0.0), (0.8, 0.5), (1.0, 1.0))
    101 VIGNETTE_OPACITY = 0.7
    102 STEPS_WITH_VIGNETTE_EFFECT = [ccConst.BLOODLINESTEP]
    103 
    104 class CharacterCreationContainer(Container):
    105     __notifyevents__ = ['OnSetDevice',
    106      'OnGraphicSettingsChanged',
    107      'OnHideUI',
    108      'OnShowUI',
    109      'OnDollUpdated',
    110      'OnMapShortcut',
    111      'OnUIRefresh',
    112      'OnDisconnect',
    113      'OnUIScalingChange']
    114     __update_on_reload__ = 1
    115 
    116     def ApplyAttributes(self, attributes):
    117         super(CharacterCreationContainer, self).ApplyAttributes(attributes)
    118         sm.RegisterNotify(self)
    119         self.needSculptingTutorial = True
    120         self.needNavigationTutorial = True
    121         self.step = None
    122         self.mainNav = None
    123         self.vignette = None
    124         self.backgroundGradient = None
    125         self.backgroundSprite = None
    126         self.topNavigationWhiteLineContainer = None
    127         self.bottomNavigationWhiteLineContainer = None
    128         self.exitButton = None
    129         self.empireNavigationContainer = None
    130         self.gender_changed = False
    131 
    132     def SetNameCallback(self, name):
    133         self.charName = name
    134 
    135     @telemetry.ZONE_METHOD
    136     def OnSetDevice(self, *args):
    137         self.UpdateLayout()
    138         self.UpdateBackdrop()
    139 
    140     def OnDisconnect(self, reason = 0, msg = ''):
    141         disconnectNotice = DisconnectNotice(logProvider=self)
    142         disconnectNotice.OnDisconnect(reason, msg)
    143 
    144     @telemetry.ZONE_METHOD
    145     def UpdateLayout(self):
    146         try:
    147             self.LockEverything()
    148             with TempLock('CharacterCreationLayerUpdate'):
    149                 for each in self.mainCont.children:
    150                     if hasattr(each, 'UpdateLayout'):
    151                         each.UpdateLayout()
    152 
    153                 desktopWidth = uicore.desktop.width
    154                 self.empireStepContainer.UpdateLayout()
    155                 self.topNavigationContainer.width = desktopWidth
    156                 self.bottomNavigationContainer.width = desktopWidth
    157                 self.topNavigationContainer.height = ccScalingUtils.GetTopNavHeight()
    158                 self.bottomNavigationContainer.height = ccScalingUtils.GetBottomNavHeight()
    159                 self.empireNavigationButtonContainer.width = ccScalingUtils.GetMainPanelWidth()
    160                 self.empireNavigationButtonContainer.height = self.bottomNavigationContainer.height
    161                 if self.empireNavigationContainer:
    162                     self.empireNavigationContainer.Close()
    163                     self._AddEmpireNavigation(self.stepID)
    164                 self.AddWhiteLines()
    165                 self.AddBackgroundGradient()
    166                 self.AddVignetteEffect()
    167                 self.UpdateVisualArtifacts(self.stepID)
    168         finally:
    169             self.UnlockEverything()
    170 
    171     def UpdateVisualArtifacts(self, stepID):
    172         if stepID in [ccConst.EMPIRESTEP, ccConst.TECHNOLOGYSTEP]:
    173             self.ShowWhiteLines()
    174             self.ShowBackgroundGradient()
    175         elif stepID == ccConst.BLOODLINESTEP:
    176             self.ShowWhiteLines()
    177             self.HideBackgroundGradient()
    178         else:
    179             self.HideWhiteLines()
    180             self.HideBackgroundGradient()
    181         if stepID in STEPS_WITH_VIGNETTE_EFFECT:
    182             self.ShowVignetteEffect()
    183         else:
    184             self.HideVignetteEffect()
    185 
    186     def ShowVignetteEffect(self):
    187         if self.vignette and not self.vignette.destroyed:
    188             self.vignette.opacity = VIGNETTE_OPACITY
    189 
    190     def HideVignetteEffect(self):
    191         if self.vignette and not self.vignette.destroyed:
    192             self.vignette.opacity = 0.0
    193 
    194     def AddVignetteEffect(self):
    195         width, height = self._GetBackdropSpriteSizes()
    196         if self.vignette and not self.vignette.destroyed:
    197             self.vignette.width = width
    198             self.vignette.height = height
    199             return
    200         self.vignette = GradientSprite(name='vignetteGradientSprite', align=uiconst.CENTER, parent=self, width=width, height=height, rgbData=VIGNETTE_RGB_DATA, alphaData=VIGNETTE_ALPHA_DATA, radial=True, opacity=0.0)
    201 
    202     def ShowBackgroundGradient(self):
    203         if self.backgroundGradient and not self.backgroundGradient.destroyed:
    204             self.backgroundGradient.opacity = 1.0
    205 
    206     def HideBackgroundGradient(self):
    207         if self.backgroundGradient and not self.backgroundGradient.destroyed:
    208             self.backgroundGradient.opacity = 0.0
    209 
    210     def AddBackgroundGradient(self):
    211         if self.backgroundGradient and not self.backgroundGradient.destroyed:
    212             self.backgroundGradient.Close()
    213         start = min(0.4, (uicore.desktop.width - ccScalingUtils.GetMainPanelWidth()) / (2 * uicore.desktop.width))
    214         self.backgroundGradient = GradientSprite(name='backgroundGradient', parent=self, align=uiconst.TOALL, rgbData=[(0, (0.0, 0.0, 0.0))], alphaData=[(0, 0.0),
    215          (start, 0.0),
    216          (0.4, 0.5),
    217          (0.6, 0.5),
    218          (1.0 - start, 0.0),
    219          (1.0, 0.0)], padTop=self.topNavigationContainer.height, padBottom=self.bottomNavigationContainer.height)
    220 
    221     def ShowWhiteLines(self):
    222         if self.topNavigationWhiteLineContainer and not self.topNavigationWhiteLineContainer.destroyed:
    223             self.topNavigationWhiteLineContainer.opacity = TOP_NAVIGATION_LINE_OPACITY
    224         if self.bottomNavigationWhiteLineContainer and not self.bottomNavigationWhiteLineContainer.destroyed:
    225             self.bottomNavigationWhiteLineContainer.opacity = BOTTOM_NAVIGATION_LINE_OPACITY
    226 
    227     def HideWhiteLines(self):
    228         if self.topNavigationWhiteLineContainer and not self.topNavigationWhiteLineContainer.destroyed:
    229             self.topNavigationWhiteLineContainer.opacity = 0.0
    230         if self.bottomNavigationWhiteLineContainer and not self.bottomNavigationWhiteLineContainer.destroyed:
    231             self.bottomNavigationWhiteLineContainer.opacity = 0.0
    232 
    233     def AddWhiteLines(self):
    234         if self.topNavigationWhiteLineContainer and not self.topNavigationWhiteLineContainer.destroyed:
    235             self.topNavigationWhiteLineContainer.Close()
    236         self.topNavigationWhiteLineContainer = Container(name='topNavigationWhiteLineContainer', parent=self.topNavigationContainer, align=uiconst.TOTOP_NOPUSH, width=self.uiContainer.width, height=self.topNavigationContainer.height, opacity=0.0)
    237         GradientSprite(name='topNavigationWhiteLine', parent=self.topNavigationWhiteLineContainer, align=uiconst.TOBOTTOM, width=self.uiContainer.width, height=TOP_NAVIGATION_LINE_HEIGHT, rgbData=[(0, (1.0, 1.0, 1.0))], alphaData=[(0, 0.0),
    238          (0.4, 1.0),
    239          (0.6, 1.0),
    240          (1.0, 0.0)])
    241         if self.bottomNavigationWhiteLineContainer and not self.bottomNavigationWhiteLineContainer.destroyed:
    242             self.bottomNavigationWhiteLineContainer.Close()
    243         self.bottomNavigationWhiteLineContainer = Container(name='bottomNavigationWhiteLineContainer', parent=self.bottomNavigationContainer, align=uiconst.TOBOTTOM_NOPUSH, width=self.uiContainer.width, height=self.bottomNavigationContainer.height, opacity=0.0)
    244         GradientSprite(name='bottomNavigationWhiteLine', parent=self.bottomNavigationWhiteLineContainer, align=uiconst.TOTOP, width=self.uiContainer.width, height=BOTTOM_NAVIGATION_LINE_HEIGHT, rgbData=[(0, (1.0, 1.0, 1.0))], alphaData=[(0, 0.0),
    245          (0.4, 1.0),
    246          (0.6, 1.0),
    247          (1.0, 0.0)])
    248 
    249     @telemetry.ZONE_METHOD
    250     def OnOpenView(self, **kwargs):
    251         self.startTime = blue.os.GetWallclockTime()
    252         self.backgroundGradient = None
    253         self.topNavigationWhiteLineContainer = None
    254         self.bottomNavigationWhiteLineContainer = None
    255         self.vignette = None
    256         uicore.cmd.commandMap.UnloadAllAccelerators()
    257         uicore.cmd.commandMap.LoadAcceleratorsByCategory('general')
    258         uicore.cmd.commandMap.LoadAcceleratorsByCategory('charactercreator')
    259         self.freezingAnimation = False
    260         from eve.client.script.ui.shared.preview import PreviewCharacterWnd, PreviewWnd
    261         previewCharWnd = PreviewCharacterWnd.GetIfOpen()
    262         if previewCharWnd:
    263             self.previewCharWindowWasOpenOn = (previewCharWnd.charID, previewCharWnd.dna)
    264             previewCharWnd.CloseByUser()
    265         else:
    266             self.previewCharWindowWasOpenOn = None
    267         previewWnd = PreviewWnd.GetIfOpen()
    268         if previewWnd:
    269             self.previewWindowWasOpenOn = previewWnd.typeID
    270             previewWnd.CloseByUser()
    271         else:
    272             self.previewWindowWasOpenOn = None
    273         self._setColorsByCategory = {}
    274         self._setSpecularityByCategory = {}
    275         self._setIntensityByCategory = {}
    276         self.characterSvc = sm.GetService('character')
    277         self.sceneManager = sm.GetService('sceneManager')
    278         self.alreadyLoadedOldPortraitData = False
    279         self.backdropPath = None
    280         self.colorCodedBackDrop = None
    281         self.posePath = None
    282         self.lightingID = ccConst.LIGHT_SETTINGS_ID[0]
    283         self.lightColorID = ccConst.LIGHT_COLOR_SETTINGS_ID[0]
    284         self.lightIntensity = 0.5
    285         self.camera = None
    286         self.cameraUpdateJob = None
    287         self.fastCharacterCreation = gfxsettings.Get(gfxsettings.GFX_CHAR_FAST_CHARACTER_CREATION)
    288         self.bloodlineSelector = None
    289         self.raceMusicStarted = False
    290         self.jukeboxWasPlaying = False
    291         self.mode = ccConst.MODE_FULLINITIAL_CUSTOMIZATION
    292         self.modeStorage = ModeStorage()
    293         self.modeObject = self.modeStorage.GetModeFor(self.mode)
    294         self.charID = None
    295         self.raceID = None
    296         self.bloodlineID = None
    297         self.genderID = None
    298         self.schoolID = None
    299         self.charName = ''
    300         self.clothesOff = 0
    301         self.clothesStorage = {}
    302         self.clothesMetaStorage = {}
    303         self.dnaList = None
    304         self.dna = None
    305         self.lastLitHistoryBit = None
    306         sm.GetService('cc').ClearMyAvailabelTypeIDs()
    307         self.backgroundSprite = None
    308         self.stepID = None
    309         self.floor = None
    310         self.showingHelp = 0
    311         self.empireStepContainer = None
    312         self.topNavigationContainer = None
    313         self.bottomNavigationContainer = None
    314         self.empireNavigationContainer = None
    315         self.CreateUI()
    316         self.avatarScene = None
    317         self.animatingToPortraitID = None
    318         self.animateToStoredPortraitThread = None
    319         self.technologyViewsTracker = None
    320         self.audioService = sm.GetService('audio')
    321         self.worldLevel = self.audioService.GetWorldVolume()
    322         self.audioService.SetWorldVolume(0.0)
    323         self.stepLogger = StepLogger(blue.os.GetWallclockTime)
    324         self.finalStepLogger = None
    325         self.metrics = None
    326         self.approve_in_progress = False
    327         self.save_in_progress = False
    328 
    329     def Finalize(self):
    330         self.Save()
    331 
    332     def CreateLoadingWheel(self):
    333         self.loadingWheel = LoadingWheel(name='loadingWheel', parent=self, align=uiconst.CENTER, state=uiconst.UI_NORMAL)
    334         self.loadingWheel.forcedOn = 0
    335 
    336     def CreateUI(self):
    337         self.uiContainer = Container(name='uiContainer', parent=self, align=uiconst.TOALL)
    338         self.CreateLoadingWheel()
    339         self.leftSide = Container(name='leftSide', parent=self.uiContainer, align=uiconst.TOPLEFT, pos=(0,
    340          0,
    341          LEFT_SIDE_WITH,
    342          LEFT_SIDE_HEIGHT))
    343         self.rightSide = Container(name='rightSide', parent=self.uiContainer, align=uiconst.BOTTOMRIGHT, pos=(0,
    344          0,
    345          RIGHT_SIDE_WIDTH,
    346          RIGHT_SIDE_HEIGHT))
    347         self.bottomLeftSide = Container(name='bottomLeftSide', parent=self.uiContainer, align=uiconst.BOTTOMLEFT, pos=(0,
    348          0,
    349          BOTTOM_LEFT_SIDE_WIDTH,
    350          BOTTOM_LEFT_SIDE_HEIGHT))
    351         self.buttonNavLeft = Container(name='buttonNavLeft', parent=self.bottomLeftSide, align=uiconst.TOTOP, height=BUTTON_HEIGHT, padLeft=10)
    352         self.buttonNavRight = Container(name='buttonNavRight', parent=self.rightSide, align=uiconst.TOTOP, height=BUTTON_HEIGHT, padRight=10)
    353         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, density=Density.EXPANDED)
    354         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, density=Density.EXPANDED)
    355         self.approveBtn = Button(name='nextButtonCC', parent=self.buttonNavRight, align=uiconst.TORIGHT, label=localization.GetByLabel('UI/Generic/Next'), func=self.Approve, left=10, args=(), density=Density.EXPANDED)
    356         self.backBtn = Button(name='backButtonCC', parent=self.buttonNavLeft, align=uiconst.TOLEFT, label=localization.GetByLabel('UI/Commands/Back'), func=self.Back, args=(), left=10, density=Density.EXPANDED)
    357         self.blackOut = Fill(name='blackOutFill', parent=self, color=(0.0, 0.0, 0.0, 0.0))
    358         self.mainCont = Container(name='mainCont', parent=self, align=uiconst.TOALL)
    359         desktopHeight = uicore.desktop.height
    360         topNavHeight = ccScalingUtils.GetTopNavHeight()
    361         bottomNavHeight = ccScalingUtils.GetBottomNavHeight()
    362         width = ccScalingUtils.GetMainPanelWidth()
    363         height = desktopHeight - topNavHeight - bottomNavHeight
    364         self.empireStepContainer = EmpireStepContainer(name='empireStepContainer', parent=self.uiContainer, align=uiconst.CENTER, width=width, height=height, state=uiconst.UI_NORMAL, step=self)
    365         self.empireStepContainer.SetOrder(0)
    366         self.topNavigationContainer = Container(name='topNavigationContainer', parent=self.uiContainer, align=uiconst.TOTOP_NOPUSH, width=self.uiContainer.width, height=topNavHeight, bgColor=(0.0, 0.0, 0.0, 1.0))
    367         self.topNavigationContainer.SetOrder(1)
    368         self.bottomNavigationContainer = Container(name='bottomNavigationContainer', parent=self.uiContainer, align=uiconst.TOBOTTOM_NOPUSH, width=self.uiContainer.width, height=bottomNavHeight, bgColor=(0.0, 0.0, 0.0, 1.0))
    369         self.bottomNavigationContainer.SetOrder(2)
    370         self.empireNavigationButtonContainer = Container(parent=self.bottomNavigationContainer, align=uiconst.CENTER, width=width, height=self.bottomNavigationContainer.height)
    371         self.UpdateLayout()
    372 
    373     def OnAvailabilityCheck(self, name):
    374         if self.finalStepLogger:
    375             self.finalStepLogger.AddTriedName(name)
    376 
    377     def OnUIRefresh(self):
    378         while getattr(self, 'doll', None) and self.doll.IsBusyUpdating():
    379             blue.synchro.Yield()
    380 
    381         self.Flush()
    382         self.CreateUI()
    383         self.SwitchStep(self.stepID)
    384 
    385     def OnUIScalingChange(self, *args):
    386         if not self.destroyed and self.stepID in ccConst.EMPIRE_SELECTION_STEPS:
    387             self.UpdateLayout()
    388             self.UpdateBackdrop()
    389 
    390     def OnHelpMouseEnter(self, btn, *args):
    391         PlaySound('ui_icc_button_mouse_over_play')
    392         btn.sr.icon.SetAlpha(1.0)
    393 
    394     def OnHelpMouseExit(self, btn, *args):
    395         if not self.showingHelp:
    396             btn.sr.icon.SetAlpha(0.5)
    397 
    398     def SetHintText(self, modifier, hintText = ''):
    399         if self.stepID in (ccConst.CUSTOMIZATIONSTEP, ccConst.PORTRAITSTEP):
    400             self.step.SetHintText(modifier, hintText)
    401 
    402     def SetMode(self, modeID):
    403         self.mode = modeID
    404         self.modeObject = self.modeStorage.GetModeFor(modeID)
    405 
    406     def GetModeIDForDollState(self, dollState):
    407         mode = ccConst.MODE_LIMITED_RECUSTOMIZATION
    408         if dollState == paperdoll.State.full_recustomizing:
    409             mode = ccConst.MODE_FULL_BLOODLINECHANGE
    410         elif dollState in (paperdoll.State.resculpting, paperdoll.State.no_existing_customization, paperdoll.State.force_recustomize):
    411             mode = ccConst.MODE_FULL_RECUSTOMIZATION
    412         return mode
    413 
    414     @telemetry.ZONE_METHOD
    415     def SetCharDetails(self, charID = None, gender = None, raceID = None, bloodlineID = None, dollState = None):
    416         self.ClearFacePortrait()
    417         self.dollState = dollState
    418         self.dna = None
    419         self.charID = 0
    420         self.raceID = raceID
    421         if charID:
    422             self.bloodlineID = bloodlineID
    423             self.genderID = int(gender)
    424             self.charID = charID
    425             if dollState not in (paperdoll.State.force_recustomize, paperdoll.State.no_existing_customization):
    426                 self.dna = sm.GetService('paperdoll').GetMyPaperDollData(self.charID)
    427             mode = self.GetModeIDForDollState(dollState)
    428         else:
    429             mode = ccConst.MODE_FULLINITIAL_CUSTOMIZATION
    430             if self._IsUserInSkipTechnologyStepCohort():
    431                 self._DeleteTechStep()
    432         self.SetMode(mode)
    433         self.availableSteps = self.modeObject.GetSteps()
    434         stepID = self.availableSteps[0]
    435         self.stepsUsed = {stepID}
    436         self.mainCont.Flush()
    437         self.stepLogger.Start()
    438         if dollState in (paperdoll.State.force_recustomize, paperdoll.State.no_existing_customization) or mode == ccConst.MODE_FULLINITIAL_CUSTOMIZATION:
    439             self.metrics = ccm.CharacterCreationMetrics(mode, ccConst.LOGIN)
    440         else:
    441             self.metrics = ccm.CharacterCreationMetrics(mode, ccConst.HANGAR)
    442         self.needSculptingTutorial = self.CanChangeBaseAppearance()
    443         self.SwitchStep(stepID)
    444 
    445     @telemetry.ZONE_METHOD
    446     def _IsUserInSkipTechnologyStepCohort(self):
    447         if gatekeeper.user.IsInCohort(gatekeeper.cohortSkipTechScreen):
    448             return True
    449         else:
    450             return False
    451 
    452     def _DeleteTechStep(self):
    453         steps = self.modeStorage.GetModeFor(ccConst.MODE_FULLINITIAL_CUSTOMIZATION)
    454         if ccConst.TECHNOLOGYSTEP in steps.GetSteps():
    455             steps.GetSteps().remove(ccConst.TECHNOLOGYSTEP)
    456             STEP_ORDER_LIST.remove(ccConst.TECHNOLOGYSTEP)
    457 
    458     @telemetry.ZONE_METHOD
    459     def GetInfo(self):
    460         return Bunch(charID=self.charID, raceID=self.raceID, bloodlineID=self.bloodlineID, genderID=self.genderID, dna=self.dna, schoolID=self.schoolID, stepID=self.stepID, charName=self.charName)
    461 
    462     def CanChangeBaseAppearance(self, *args):
    463         return self.modeObject.CanChangeBaseAppearance()
    464 
    465     def GetMode(self, *args):
    466         return self.mode
    467 
    468     def CanChangeBloodline(self, *args):
    469         return self.modeObject.CanChangeBloodLine()
    470 
    471     def CanChangeGender(self, *args):
    472         return self.modeObject.CanChangeGender()
    473 
    474     def CanChangeName(self, *args):
    475         return self.modeObject.CanChangeName()
    476 
    477     def _AddExitButton(self, toStep):
    478         if self.exitButton and not self.exitButton.destroyed:
    479             self.exitButton.Close()
    480         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)
    481 
    482     def ConstructNavigationIfNeeded(self, toStep):
    483         self._AddExitButton(toStep)
    484         self._AddEmpireNavigation(toStep)
    485         if toStep == ccConst.EMPIRESTEP:
    486             return
    487         if not self.mainNav or self.mainNav.destroyed:
    488             navSteps = copy.copy(self.availableSteps)
    489             if ccConst.EMPIRESTEP in navSteps:
    490                 navSteps.remove(ccConst.EMPIRESTEP)
    491             self.mainNav = CharCreationNavigation(name='navigation', align=uiconst.CENTERTOP, parent=self.leftSide, pos=(12, 12, 0, 60), stepID=toStep, func=self.SwitchStepIfDifferent, stepsUsed=self.stepsUsed, availableSteps=navSteps)
    492         if toStep == ccConst.EMPIRESTEP:
    493             self.mainNav.Hide()
    494         else:
    495             self.mainNav.Show()
    496         if toStep in ccConst.EMPIRE_SELECTION_STEPS:
    497             self.mainNav.SetParent(self.topNavigationContainer)
    498             self.mainNav.align = uiconst.TOLEFT
    499             self.topNavigationContainer.Show()
    500             self.bottomNavigationContainer.Show()
    501             sm.GetService('gameui').MoveResourceLoadingIndicator(newAlignment=uiconst.BOTTOMLEFT, newLeft=10, newTop=10)
    502         else:
    503             self.mainNav.SetParent(self.leftSide, 0)
    504             self.mainNav.align = uiconst.TOLEFT
    505             self.topNavigationContainer.Hide()
    506             self.bottomNavigationContainer.Hide()
    507             sm.GetService('gameui').MoveResourceLoadingIndicator(newAlignment=uiconst.BOTTOMLEFT, newLeft=20, newTop=RESOURCE_WHEEL_SEPARATION + ccConst.BUTTON_AREA_HEIGHT)
    508 
    509     def _ShowOrHideCharacterScene(self, toStep):
    510         if toStep == ccConst.BLOODLINESTEP:
    511             self.sceneManager.characterRenderJob.DisableStep('RENDER_SCENE')
    512         elif self.stepID == ccConst.BLOODLINESTEP or self.stepID is None:
    513             self.sceneManager.characterRenderJob.EnableStep('RENDER_SCENE')
    514 
    515     @telemetry.ZONE_METHOD
    516     def SwitchStepIfDifferent(self, toStep, *args):
    517         if self.stepID == toStep:
    518             return
    519         if self.stepID == ccConst.TECHNOLOGYSTEP and toStep == ccConst.BLOODLINESTEP:
    520             self.step.techView.Enlist()
    521             return
    522         self.SwitchStep(toStep, args)
    523 
    524     @telemetry.ZONE_METHOD
    525     def SwitchStep(self, toStep, *args):
    526         if GetStepOrder(toStep) > GetStepOrder(self.stepID):
    527             if not self.PassedStepCheck(toStep):
    528                 return
    529         try:
    530             self.LockEverything()
    531             isStepOutOfEmpireSelection = self.stepID is not None and self.stepID not in ccConst.EMPIRE_SELECTION_STEPS
    532             isToStepOutOfEmpireSelection = toStep not in ccConst.EMPIRE_SELECTION_STEPS
    533             isBlackScreenTransition = isStepOutOfEmpireSelection or isToStepOutOfEmpireSelection
    534             if isBlackScreenTransition:
    535                 self.FadeToBlack(why=localization.GetByLabel('UI/Generic/Loading'))
    536             with TempLock('CharacterCreationLayerUpdate'):
    537                 self.UnfreezeAnimationIfNeeded()
    538                 self.BlockOnUpdate()
    539                 self.ConstructNavigationIfNeeded(toStep)
    540                 self.empireNavigationButtonContainer.Flush()
    541                 if self.stepID == ccConst.CUSTOMIZATIONSTEP:
    542                     sm.StartService('audio').SendUIEvent(unicode('ui_icc_sculpting_mouse_over_loop_stop'))
    543                     self.step.StoreHistorySliderPosition()
    544                 elif self.stepID == ccConst.PORTRAITSTEP:
    545                     sm.StartService('audio').SendUIEvent(unicode('ui_icc_sculpting_mouse_over_loop_stop'))
    546                     self.StorePortraitCameraSettings()
    547                 self.step = None
    548                 raceTheme = COLOR_THEME_ID_BY_RACE.get(self.raceID, DEFAULT_COLORTHEMEID)
    549                 sm.GetService('uiColor').SetNoCharTheme(raceTheme)
    550                 if self.mainNav:
    551                     stepsUsed = self.stepsUsed.copy()
    552                     uthread.new(self.mainNav.PerformStepChange, toStep, stepsUsed)
    553                 self._ShowOrHideCharacterScene(toStep)
    554                 self.BlockOnUpdate()
    555                 self._UpdateNavigationButtons(toStep)
    556                 if isBlackScreenTransition:
    557                     self.mainCont.Flush()
    558                 if toStep in ccConst.EMPIRE_SELECTION_STEPS:
    559                     self.empireStepContainer.Show()
    560                 else:
    561                     self.empireStepContainer.Hide()
    562                 self.UpdateVisualArtifacts(toStep)
    563                 if toStep != ccConst.EMPIRESTEP:
    564                     self.empireStepContainer.ShowBackgroundBanner(self.raceID)
    565                 if toStep == ccConst.EMPIRESTEP:
    566                     self.empireStepContainer.ShowViewCaps()
    567                     self.empireStepContainer.HideBannerCaps()
    568                 elif toStep == ccConst.TECHNOLOGYSTEP:
    569                     self.empireStepContainer.HideViewCaps()
    570                     self.empireStepContainer.SetBannerCapsForMenu()
    571                 elif toStep == ccConst.BLOODLINESTEP:
    572                     self.empireStepContainer.HideViewCaps()
    573                     self.empireStepContainer.SetBannerCapsForView()
    574                 self.Cleanup()
    575                 self.BlockOnUpdate()
    576                 self.metrics.new_page(toStep)
    577                 self.StartStep(toStep)
    578                 self.stepID = toStep
    579                 self.stepsUsed.add(toStep)
    580                 self.UpdateBackdrop()
    581         finally:
    582             self.UnlockEverything()
    583             self.setupDone = 1
    584             self.FadeFromBlack()
    585             self.UnfreezeAnimationIfNeeded()
    586             sm.ScatterEvent('OnCharacterCreationStep', self.stepID, toStep)
    587 
    588     def BlockOnUpdate(self):
    589         info = self.GetInfo()
    590         characters = self.characterSvc.characters
    591         if info.charID in characters and self.characterSvc.GetSingleCharactersDoll(info.charID).busyUpdating:
    592             start_time = time.clock()
    593             while self.characterSvc.GetSingleCharactersDoll(info.charID).busyUpdating and time.clock() - start_time < 60:
    594                 blue.synchro.Yield()
    595 
    596             if self.characterSvc.GetSingleCharactersDoll(info.charID).busyUpdating:
    597                 raise UserError('uiwarning01')
    598 
    599     def _UpdateNavigationButtons(self, toStep):
    600         if toStep in ccConst.EMPIRE_SELECTION_STEPS:
    601             self.backBtn.Hide()
    602             self.approveBtn.Hide()
    603             self.finalizeBtn.Hide()
    604             self.saveBtn.Hide()
    605             return
    606         self.approveBtn.Show()
    607         self.backBtn.Show()
    608         isGoingToFinalStep = toStep == self.availableSteps[-1]
    609         if isGoingToFinalStep:
    610             self.approveBtn.state = uiconst.UI_HIDDEN
    611             isNewChar = not self.charID
    612             if isNewChar:
    613                 self.finalizeBtn.state = uiconst.UI_NORMAL
    614                 self.saveBtn.state = uiconst.UI_HIDDEN
    615             else:
    616                 self.finalizeBtn.state = uiconst.UI_HIDDEN
    617                 self.saveBtn.state = uiconst.UI_NORMAL
    618         else:
    619             self.approveBtn.state = uiconst.UI_NORMAL
    620             self.finalizeBtn.state = uiconst.UI_HIDDEN
    621             self.saveBtn.state = uiconst.UI_HIDDEN
    622 
    623     def _SetUpSceneAndCamera(self, scene):
    624         self.sceneManager.Show2DBackdropScene()
    625         self.SetupScene(scene)
    626         self.camera = CharCreationCamera(None)
    627         self.camera.fieldOfView = 0.5
    628         self.camera.distance = 7.0
    629         self.camera.SetPointOfInterest((0.0, 1.3, 0.0))
    630         self.camera.frontClip = 3.5
    631         self.camera.backclip = 100.0
    632         self.SetupCameraUpdateJob()
    633 
    634     def _HideBackButtonIfNoCharacters(self):
    635         if not sm.GetService('cc').GetCharactersToSelect():
    636             self.backBtn.Hide()
    637 
    638     def StartEmpireStep(self):
    639         self.step = EmpireStep(parent=self.empireStepContainer, stepContainer=self.empireStepContainer)
    640         self._SetUpSceneAndCamera(ccConst.SCENE_PATH_RACE_SELECTION)
    641         self.camera.ToggleMode(ccConst.CAMERA_MODE_DEFAULT, avatar=None)
    642 
    643     def _AddEmpireNavigation(self, stepID):
    644         if stepID not in (ccConst.TECHNOLOGYSTEP, ccConst.BLOODLINESTEP):
    645             if self.empireNavigationContainer is not None and not self.empireNavigationContainer.destroyed:
    646                 self.empireNavigationContainer.Close()
    647             return
    648         if self.empireNavigationContainer is not None and not self.empireNavigationContainer.destroyed:
    649             return
    650         width = GetEmpireNavigationWidth()
    651         height = GetEmpireNavigationHeight()
    652         self.empireNavigationContainer = Container(name='empireNavigationContainer', align=uiconst.CENTER, parent=self.topNavigationContainer, width=width, height=self.topNavigationContainer.height, idx=1)
    653         rightAlignedContainer = Container(name='rightAlignedContainer', align=uiconst.TORIGHT, parent=self.empireNavigationContainer, width=width, height=self.empireNavigationContainer.height)
    654         empireNavigationWrapper = Container(name='empireNavigationWrapper', align=uiconst.CENTERBOTTOM, parent=rightAlignedContainer, width=width, height=height)
    655         EmpireNavigation(name='empireNavigationContainer', align=uiconst.TOLEFT, parent=empireNavigationWrapper, width=width, height=height, raceSetter=self.SelectRaceIfDifferent, activeRace=self.raceID)
    656 
    657     def StartTechnologyStep(self):
    658         if self.technologyViewsTracker is None:
    659             self.technologyViewsTracker = TechnologyViewsTracker()
    660         self._SetUpSceneAndCamera(ccConst.SCENE_PATH_RACE_SELECTION)
    661         self.camera.ToggleMode(ccConst.CAMERA_MODE_DEFAULT, avatar=None)
    662         isStepChange = self.stepID != ccConst.TECHNOLOGYSTEP
    663         self.step = TechnologyStep(parent=self.empireStepContainer, shouldGoBackToStart=isStepChange, technologyViewsTracker=self.technologyViewsTracker)
    664         self.step.SetOrder(0)
    665 
    666     def StartNewBloodlineStep(self):
    667         try:
    668             self.ShowLoading()
    669             self.LoadBloodlineStep()
    670         finally:
    671             self.HideLoading()
    672 
    673     def _ShouldUseVideosInBloodlines(self):
    674         return not self.IsSlowMachine()
    675 
    676     def LoadBloodlineStep(self):
    677         self.step = BloodlineStep(parent=self.empireStepContainer, buttonContainer=uicore.layer.charactercreation.controller.empireNavigationButtonContainer, useVideos=self._ShouldUseVideosInBloodlines())
    678         self.step.SetOrder(0)
    679         self.step.ShowBloodlineInfo()
    680         self.step.ShowBoundingBoxes()
    681         self.step.ShowGenders()
    682         self.step.ShowNote()
    683 
    684     def ReloadBloodlineStep(self, updateRace = False):
    685         try:
    686             self.LockEverything()
    687             self.HideContent(updateRace)
    688             self.ShowLoading()
    689             if self.step and not self.step.destroyed:
    690                 self.step.Close()
    691             self.LoadBloodlineStep()
    692             if updateRace:
    693                 raceTheme = COLOR_THEME_ID_BY_RACE.get(self.raceID, DEFAULT_COLORTHEMEID)
    694                 sm.GetService('uiColor').SetNoCharTheme(raceTheme)
    695                 self.UpdateBackdrop()
    696             self.ShowContent(updateRace)
    697         finally:
    698             self.HideLoading()
    699             self.UnlockEverything()
    700 
    701     def ShowContent(self, updateRace):
    702         if self.empireStepContainer and not self.empireStepContainer.destroyed:
    703             self.empireStepContainer.Show()
    704             if updateRace:
    705                 self.empireStepContainer.ExpandRace(self.raceID)
    706         if not updateRace and self.backgroundSprite and not self.backgroundSprite.destroyed:
    707             self.backgroundSprite.Show()
    708 
    709     def HideContent(self, updateRace):
    710         if self.empireStepContainer and not self.empireStepContainer.destroyed:
    711             self.empireStepContainer.Hide()
    712         if not updateRace and self.backgroundSprite and not self.backgroundSprite.destroyed:
    713             self.backgroundSprite.Hide()
    714 
    715     def StartStep(self, toStep):
    716         self.stepLogger.SetStep(toStep)
    717         start_method = {ccConst.CUSTOMIZATIONSTEP: self.StartCustomizationStep,
    718          ccConst.PORTRAITSTEP: self.StartPortraitStep,
    719          ccConst.NAMINGSTEP: self.StartNamingStep,
    720          ccConst.EMPIRESTEP: self.StartEmpireStep,
    721          ccConst.TECHNOLOGYSTEP: self.StartTechnologyStep,
    722          ccConst.BLOODLINESTEP: self.StartNewBloodlineStep}.get(toStep, None)
    723         if start_method is None:
    724             raise NotImplementedError
    725         start_method()
    726 
    727     def IsNamingStep(self, step):
    728         return step == ccConst.NAMINGSTEP
    729 
    730     @telemetry.ZONE_METHOD
    731     def StorePortraitCameraSettings(self):
    732         if self.camera is not None:
    733             self.storedPortraitCameraSettings = {'poi': self.camera.poi,
    734              'pitch': self.camera.pitch,
    735              'yaw': self.camera.yaw,
    736              'distance': self.camera.distance,
    737              'xFactor': self.camera.xFactor,
    738              'yFactor': self.camera.yFactor,
    739              'fieldOfView': self.camera.fieldOfView}
    740 
    741     @telemetry.ZONE_METHOD
    742     def FadeToBlack(self, why = ''):
    743         self.ShowLoading(why=why, forceOn=True)
    744         animations.FadeIn(self.blackOut, duration=0.5, sleep=True)
    745 
    746     @telemetry.ZONE_METHOD
    747     def FadeFromBlack(self):
    748         animations.FadeOut(self.blackOut, duration=0.5)
    749         self.HideLoading(forceOff=1)
    750 
    751     @telemetry.ZONE_METHOD
    752     def PassedStepCheck(self, toStep, *args):
    753         if toStep not in self.availableSteps:
    754             raise UserError('CCStepUnavailable')
    755         if toStep == ccConst.BLOODLINESTEP and self.raceID is None:
    756             raise UserError('CCMustSelectRace')
    757         if toStep == ccConst.CUSTOMIZATIONSTEP and (self.raceID is None or self.bloodlineID is None or self.genderID is None):
    758             raise UserError('CCMustSelectRaceAndBloodline')
    759         isNamingStep = self.IsNamingStep(toStep)
    760         if (toStep == ccConst.PORTRAITSTEP or isNamingStep) and not prefs.GetValue('ignoreCCValidation', False):
    761             info = self.GetInfo()
    762             self.ToggleClothes(forcedValue=0)
    763             self.characterSvc.ValidateDollCustomizationComplete(info.charID)
    764         currentStepID = None
    765         if self.step:
    766             currentStepID = self.step.stepID
    767         if self.step:
    768             self.step.ValidateStepComplete()
    769         return True
    770 
    771     @telemetry.ZONE_METHOD
    772     def Approve(self, *args):
    773         if not self.approve_in_progress:
    774             try:
    775                 self.approve_in_progress = True
    776                 self.stepLogger.IncrementNextTryCount()
    777                 idx = self.availableSteps.index(self.stepID)
    778                 if len(self.availableSteps) > idx + 1:
    779                     nextStep = self.availableSteps[idx + 1]
    780                     self.SwitchStep(nextStep)
    781                     uicore.registry.SetFocus(self)
    782             finally:
    783                 self.approve_in_progress = False
    784 
    785     def CheckAndGetName(self):
    786         isAvailable = self.step.CheckAvailability()
    787         if isAvailable.charName is None:
    788             prompt_player_fsd_dialog('CustomInfo', {'info': isAvailable.reason})
    789         return isAvailable.charName
    790 
    791     @telemetry.ZONE_METHOD
    792     def Save(self, *args):
    793         if not self.save_in_progress:
    794             self.save_in_progress = True
    795             self.BlockOnUpdate()
    796             if self.finalStepLogger:
    797                 self.finalStepLogger.Finalize()
    798             self.stepLogger.IncrementNextTryCount()
    799             try:
    800                 selectedPortraitInfo = self.portraitInfo[self.activePortraitIndex]
    801                 if selectedPortraitInfo and selectedPortraitInfo.backgroundID < const.NCC_MIN_NORMAL_BACKGROUND_ID:
    802                     prompt_player_fsd_dialog('CustomInfo', {'info': localization.GetByLabel('UI/CharacterCreation/CannotSaveWithThisBackground')})
    803                     return
    804                 self.LockEverything()
    805                 if self.step:
    806                     self.step.ValidateStepComplete()
    807                 if self.stepID != self.availableSteps[-1]:
    808                     raise UserError('CCCannotSave')
    809                 if self.charID and self.modeObject.AskForPortraitConfirmation():
    810                     self.UpdateExistingCharacter()
    811                     return
    812                 if self.bloodlineID is None or self.genderID is None:
    813                     raise UserError('CCCannotSave2')
    814                 else:
    815                     characterName = self.CheckAndGetName()
    816                     if characterName is None:
    817                         return
    818                     self.SaveAndEnterCurrentCharacter(characterName)
    819             finally:
    820                 if self and not self.destroyed:
    821                     self.UnlockEverything()
    822                     self.save_in_progress = False
    823 
    824     def SaveAndEnterCurrentCharacter(self, characterName):
    825         self.audioService.SendUIEvent('ui_es_button_mouse_down_finalize_play')
    826         charID = self.SaveCurrentCharacter(characterName, self.bloodlineID, self.genderID, self.activePortraitIndex)
    827         self.metrics.complete_cc(charid=charID)
    828         if charID:
    829             self.characterSvc.CachePortraitInfo(charID, self.portraitInfo[self.activePortraitIndex])
    830             self.tryLoginElseCharacterSelection(charID)
    831 
    832     def tryLoginElseCharacterSelection(self, charID):
    833         try:
    834             if gatekeeper.user.IsInCohort(gatekeeper.cohortToCharacterSelectionTest):
    835                 sm.GetService('cc').GetCharactersToSelect(force=True)
    836                 uthread.pool('GameUI::ActivateView::charsel', sm.GetService('viewState').ActivateView, 'charsel')
    837             else:
    838                 skipTutorial = self.CheckSkipTutorial() if is_air_npe_enabled() else True
    839                 sm.GetService('cc').FinishCharacterCreation(charID, skipTutorial)
    840         except:
    841             cc = sm.GetService('cc')
    842             cc.StopIntro()
    843             cc.GetCharactersToSelect(force=True)
    844             sm.GetService('loading').ProgressWnd(localization.GetByLabel('UI/CharacterCreation/CharacterSelection'), localization.GetByLabel('UI/CharacterCreation/FailedToEnterGame'), 2, 2)
    845             uthread.pool('GameUI::ActivateView::charsel', sm.GetService('viewState').ActivateView, 'charsel')
    846             raise
    847 
    848     def CheckSkipTutorial(self):
    849         if sm.RemoteSvc('charUnboundMgr').GetNumCharacters() > 1:
    850             if prompt_player_fsd_dialog('AskStartWithTutorial', {}, uiconst.YESNO, default=uiconst.ID_NO) == uiconst.ID_NO:
    851                 return True
    852         return False
    853 
    854     def OnEsc(self):
    855         self.Back()
    856 
    857     def CancelCharacterCreation(self):
    858         if self.modeObject.ExitToStation():
    859             msg = 'AskCancelCharCustomization'
    860         else:
    861             msg = 'AskCancelCharCreation'
    862         self.audioService.SendUIEvent('ui_es_pop_up_play')
    863         popUp = prompt_player_fsd_dialog(msg, {}, uiconst.YESNO)
    864         self.audioService.SendUIEvent('ui_es_pop_up_stop')
    865         if popUp != uiconst.ID_YES:
    866             return
    867         self.needNavigationTutorial = True
    868         self.needSculptingTutorial = True
    869         self.audioService.SendUIEvent('ui_es_stop')
    870         self.metrics.cancel_cc()
    871         if self.modeObject.ExitToStation():
    872             self.ExitToStation(updateDoll=False)
    873         else:
    874             sm.StartService('cc').GoBack()
    875         self.stepID = None
    876 
    877     @telemetry.ZONE_METHOD
    878     def Back(self, *args):
    879         idx = self.availableSteps.index(self.stepID)
    880         if idx == 0:
    881             self.CancelCharacterCreation()
    882         else:
    883             nextStep = self.availableSteps[idx - 1]
    884             self.SwitchStep(nextStep)
    885             uicore.registry.SetFocus(self)
    886 
    887     @telemetry.ZONE_METHOD
    888     def LockEverything(self, *args):
    889         if not getattr(self, 'setupDone', 0):
    890             return
    891         containers = [self,
    892          self.topNavigationContainer,
    893          self.bottomNavigationContainer,
    894          self.empireStepContainer]
    895         for container in containers:
    896             container.pickState = uiconst.TR2_SPS_OFF
    897 
    898         self.LockNavigation()
    899 
    900     @telemetry.ZONE_METHOD
    901     def LockNavigation(self, *args):
    902         if not getattr(self, 'setupDone', 0):
    903             return
    904         self.buttonNavRight.state = uiconst.UI_DISABLED
    905         self.buttonNavLeft.state = uiconst.UI_DISABLED
    906         if self.mainNav:
    907             self.mainNav.state = uiconst.UI_DISABLED
    908 
    909     @telemetry.ZONE_METHOD
    910     def UnlockEverything(self, *args):
    911         containers = [self,
    912          self.topNavigationContainer,
    913          self.bottomNavigationContainer,
    914          self.empireStepContainer]
    915         for container in containers:
    916             container.pickState = uiconst.TR2_SPS_CHILDREN
    917 
    918         self.UnlockNavigation()
    919 
    920     @telemetry.ZONE_METHOD
    921     def UnlockNavigation(self, *args):
    922         if not self.parent.isopen:
    923             return
    924         self.buttonNavRight.state = uiconst.UI_PICKCHILDREN
    925         self.buttonNavLeft.state = uiconst.UI_PICKCHILDREN
    926         if self.mainNav:
    927             self.mainNav.state = uiconst.UI_PICKCHILDREN
    928 
    929     @telemetry.ZONE_METHOD
    930     def SetActivePortrait(self, portraitNo, *args):
    931         self.activePortraitIndex = portraitNo
    932         self.activePortrait = self.facePortraits[portraitNo]
    933 
    934     @telemetry.ZONE_METHOD
    935     def GetActivePortrait(self):
    936         return self.activePortrait
    937 
    938     @telemetry.ZONE_METHOD
    939     def SetFacePortrait(self, photo, portraitNo, *args):
    940         self.facePortraits[portraitNo] = photo
    941         self.SetActivePortrait(portraitNo)
    942 
    943     @telemetry.ZONE_METHOD
    944     def ClearFacePortrait(self, *args):
    945         self.facePortraits = [None] * ccConst.NUM_PORTRAITS
    946         self.ClearPortraitInfo()
    947         self.activePortraitIndex = 0
    948         self.activePortrait = None
    949 
    950     @telemetry.ZONE_METHOD
    951     def SelectRaceIfDifferent(self, raceID, check = 1):
    952         if self.raceID != raceID:
    953             self._ChangeRace(raceID, check)
    954         self._UpdateResourcePriority(raceID)
    955 
    956     @telemetry.ZONE_METHOD
    957     def SelectRace(self, raceID, check = 1):
    958         self._ChangeRace(raceID, check)
    959         self._UpdateResourcePriority(raceID)
    960 
    961     def _ChangeRace(self, raceID, check = 1):
    962         oldRaceID = self.raceID
    963         bloodLineStepIsUsed = ccConst.BLOODLINESTEP in self.stepsUsed
    964         if check and oldRaceID is not None and bloodLineStepIsUsed:
    965             if self.stepID == ccConst.CUSTOMIZATIONSTEP:
    966                 dnaLog = self.GetDollDNAHistory()
    967                 if dnaLog and len(dnaLog) > 1:
    968                     if prompt_player_fsd_dialog('CharCreationLoseChangesRace', {}, uiconst.YESNO) != uiconst.ID_YES:
    969                         return
    970         self.ClearSteps(what='race')
    971         self.ResetClothesStorage()
    972         self.raceID = raceID
    973         if self.stepID == ccConst.BLOODLINESTEP:
    974             self.SelectBloodlineAndGender(None, None, check)
    975         else:
    976             self.SelectBloodline(None, check=check)
    977         if hasattr(self.step, 'OnRaceSelected'):
    978             self.step.OnRaceSelected(raceID)
    979         self.UpdateBackdrop()
    980 
    981     def _UpdateResourcePriority(self, raceID):
    982         raceAsString = RACE_TO_RESOURCE_NAME.get(raceID, None)
    983         if raceAsString:
    984             everesourceprefetch.ScheduleFront('interior_' + raceAsString)
    985             everesourceprefetch.ScheduleFront('bloodline_select_' + raceAsString)
    986 
    987     @telemetry.ZONE_METHOD
    988     def SelectBloodline(self, bloodlineID, check = 1):
    989         if self.bloodlineID != bloodlineID:
    990             oldBloodlineID = self.bloodlineID
    991             if check and oldBloodlineID is not None and ccConst.CUSTOMIZATIONSTEP in self.stepsUsed:
    992                 if self.stepID == ccConst.CUSTOMIZATIONSTEP:
    993                     dnaLog = self.GetDollDNAHistory()
    994                     if dnaLog and len(dnaLog) > 1:
    995                         if prompt_player_fsd_dialog('CharCreationLoseChangeBloodline', {}, uiconst.YESNO) != uiconst.ID_YES:
    996                             return
    997             self.ClearSteps(what='bloodline')
    998             self.ResetClothesStorage()
    999             self.bloodlineID = bloodlineID
   1000             if hasattr(self.step, 'OnBloodlineSelected'):
   1001                 self.step.OnBloodlineSelected(bloodlineID, oldBloodlineID)
   1002             if self.bloodlineSelector is not None:
   1003                 self.bloodlineSelector.SelectBloodline(bloodlineID)
   1004 
   1005     @telemetry.ZONE_METHOD
   1006     def SelectGender(self, genderID, check = 1):
   1007         if not self.CanChangeGender():
   1008             return
   1009         if check and self.genderID not in [None, genderID] and ccConst.CUSTOMIZATIONSTEP in self.stepsUsed:
   1010             if self.stepID == ccConst.CUSTOMIZATIONSTEP:
   1011                 dnaLog = self.GetDollDNAHistory()
   1012                 if dnaLog and len(dnaLog) > 1:
   1013                     if prompt_player_fsd_dialog('CharCreationLoseChangeGender', {}, uiconst.YESNO) != uiconst.ID_YES:
   1014                         return
   1015         self.genderID = genderID
   1016         self.ClearSteps(what='gender')
   1017         self.ResetClothesStorage()
   1018         if hasattr(self.step, 'OnGenderSelected'):
   1019             self.step.OnGenderSelected(genderID)
   1020         if getattr(self.step.sr, 'historySlider'):
   1021             self.step.sr.historySlider.LoadHistory(0)
   1022 
   1023     @telemetry.ZONE_METHOD
   1024     def SelectBloodlineAndGender(self, bloodlineID, genderID, check = 1):
   1025         isBloodlineChanged = self.bloodlineID != bloodlineID
   1026         isGenderChanged = self.genderID != genderID
   1027         self.WarnOfUnsavedChanges(check, isBloodlineChanged, isGenderChanged)
   1028         if isBloodlineChanged or isGenderChanged:
   1029             self.ClearSteps(what='bloodline')
   1030         self.bloodlineID = bloodlineID
   1031         self.genderID = genderID
   1032         self.ResetClothesStorage()
   1033         if getattr(self.step, 'sr') and getattr(self.step.sr, 'historySlider'):
   1034             self.step.sr.historySlider.LoadHistory(0)
   1035 
   1036     def WarnOfUnsavedChanges(self, check, isBloodlineChanged, isGenderChanged):
   1037         isBloodlineUnsaved = isBloodlineChanged and self.bloodlineID is not None
   1038         isGenderUnsaved = isGenderChanged and self.genderID is not None
   1039         areThereUnsavedChanges = isBloodlineUnsaved or isGenderUnsaved
   1040         isCustomized = self.stepID == ccConst.CUSTOMIZATIONSTEP and ccConst.CUSTOMIZATIONSTEP in self.stepsUsed
   1041         shouldWarn = check and areThereUnsavedChanges and isCustomized
   1042         if shouldWarn:
   1043             dnaLog = self.GetDollDNAHistory()
   1044             if dnaLog and len(dnaLog) > 1:
   1045                 message = 'CharCreationLoseChangeBloodline' if isBloodlineUnsaved else 'CharCreationLoseChangeGender'
   1046                 if prompt_player_fsd_dialog(message, {}, uiconst.YESNO) != uiconst.ID_YES:
   1047                     return
   1048 
   1049     @telemetry.ZONE_METHOD
   1050     def SelectSchool(self, schoolID):
   1051         if self.finalStepLogger:
   1052             self.finalStepLogger.SetSchool(schoolID)
   1053         self.schoolID = schoolID
   1054         if hasattr(self.step, 'OnSchoolSelected'):
   1055             self.step.OnSchoolSelected(schoolID)
   1056 
   1057     @telemetry.ZONE_METHOD
   1058     def OnDollUpdated(self, charID, redundantUpdate, fromWhere, *args):
   1059         if fromWhere in ('AddCharacter', 'OnSetDevice'):
   1060             return
   1061         self.ClearSteps(what='dollUpdated')
   1062 
   1063     @telemetry.ZONE_METHOD
   1064     def IsSlowMachine(self):
   1065         if gfxsettings.Get(gfxsettings.GFX_CHAR_FAST_CHARACTER_CREATION):
   1066             return True
   1067         if gfxsettings.Get(gfxsettings.GFX_SHADER_QUALITY) < 2:
   1068             return True
   1069         return False
   1070 
   1071     @telemetry.ZONE_METHOD
   1072     def OnGraphicSettingsChanged(self, changes):
   1073         shouldGoBack = False
   1074         if getattr(uicore.layer.charactercreation, 'isopen', 0):
   1075             if gfxsettings.GFX_CHAR_FAST_CHARACTER_CREATION in changes:
   1076                 header = localization.GetByLabel('UI/CharacterCreation/LoseChangesHeader')
   1077                 text = localization.GetByLabel('UI/CharacterCreation/LoseChanges')
   1078                 if prompt_player_fsd_dialog('CustomQuestion', {'header': header,
   1079                  'question': text}, uiconst.YESNO) == uiconst.ID_YES:
   1080                     self.avatarScene = None
   1081                     shouldGoBack = True
   1082                 else:
   1083                     gfxsettings.Set(gfxsettings.GFX_CHAR_FAST_CHARACTER_CREATION, 0, pending=False)
   1084         if shouldGoBack:
   1085             if self.charID:
   1086                 trinity.WaitForResourceLoads()
   1087                 self.ExitToStation()
   1088             else:
   1089                 sm.GetService('cc').GoBack()
   1090         else:
   1091             self.bloodlineSelector = None
   1092             if self.stepID in ccConst.EMPIRE_SELECTION_STEPS:
   1093                 self.ReloadStep()
   1094         if gfxsettings.UI_NCC_GREEN_SCREEN in changes:
   1095             self.UpdateBackdrop()
   1096             if self.floor:
   1097                 self.floor.display = not gfxsettings.Get(gfxsettings.UI_NCC_GREEN_SCREEN)
   1098 
   1099     def ReloadStep(self):
   1100         self.ShowLoading()
   1101         try:
   1102             self.SwitchStep(self.stepID)
   1103         finally:
   1104             self.HideLoading()
   1105 
   1106     @telemetry.ZONE_METHOD
   1107     def TearDown(self):
   1108         self.Cleanup()
   1109         self.characterSvc.TearDown()
   1110         self.floor = None
   1111         self.doll = None
   1112         self.scene = None
   1113         self.avatarScene = None
   1114         self.ClearCamera()
   1115         self.cameraUpdateJob = None
   1116         if self.bloodlineSelector is not None:
   1117             self.bloodlineSelector.TearDown()
   1118         self.bloodlineSelector = None
   1119         self.animateToStoredPortraitThread = None
   1120         self.animatingToPortraitID = None
   1121         self.freezingAnimation = False
   1122         self.colorCodedBackDrop = None
   1123 
   1124     @telemetry.ZONE_METHOD
   1125     def SetAvatarScene(self, skipAddCharacter = False):
   1126         info = self.GetInfo()
   1127         if self.avatarScene is None:
   1128             self.SetupScene(ccConst.SCENE_PATH_CUSTOMIZATION)
   1129             self.avatarScene = self.scene
   1130             if not skipAddCharacter:
   1131                 self.AddCharacter(info.charID, info.bloodlineID, info.raceID, info.genderID, dna=info.dna)
   1132             self.floor = trinity.Load(ccConst.CUSTOMIZATION_FLOOR)
   1133             self.scene.dynamics.append(self.floor)
   1134         else:
   1135             self.scene = self.avatarScene
   1136         self.sceneManager.SetActiveScene(self.scene, sceneKey='characterCreation')
   1137         self.BlockOnUpdate()
   1138 
   1139     @telemetry.ZONE_METHOD
   1140     def StartCustomizationStep(self, *args):
   1141         info = self.GetInfo()
   1142         self.sceneManager.Show2DBackdropScene()
   1143         self.SetAvatarScene()
   1144         avatar = self.characterSvc.GetSingleCharactersAvatar(info.charID)
   1145         if avatar is not None:
   1146             network = animparams.GetParamsPerAvatar(avatar, info.charID)
   1147             network.SetControlParameter('ControlParameters|NetworkMode', NETWORKMODE_BASIC)
   1148             self.camera = CharCreationCamera(avatar, ccConst.CAMERA_MODE_FACE)
   1149             self.camera.frontClip = 0.1
   1150             self.camera.backclip = 100.0
   1151             self.SetupCameraUpdateJob()
   1152             self.camera.SetMoveCallback(self.CameraMoveCB)
   1153         self.SetDefaultLighting()
   1154         self.step = CharacterCustomization(parent=self.mainCont)
   1155         if self.CanChangeBaseAppearance():
   1156             self.StartEditMode(showPreview=True, callback=self.step.ChangeSculptingCursor)
   1157         self.camera.ToggleMode(ccConst.CAMERA_MODE_FACE, avatar=avatar, transformTime=500.0)
   1158         self.scene.SetupShadowMaps()
   1159 
   1160     def CameraMoveCB(self, viewMatrix):
   1161         info = self.GetInfo()
   1162         if info.charID not in self.characterSvc.characters:
   1163             return
   1164         if not len(self.characterSvc.characters):
   1165             return
   1166         avatar = self.characterSvc.GetSingleCharactersAvatar(info.charID)
   1167         matrix = geo2.MatrixInverse(viewMatrix.transform)
   1168         view = matrix[3][:3]
   1169         if info.genderID:
   1170             height = 1.7
   1171         else:
   1172             height = 1.6
   1173         head = (0, height, 0)
   1174         rot = avatar.rotation
   1175         vec = (view[0] - head[0], view[1] - head[1], view[2] - head[2])
   1176         vecLength = geo2.Vec3Distance((0, 0, 0), vec)
   1177         vec = geo2.QuaternionTransformVector(geo2.QuaternionInverse(rot), vec)
   1178         norm = 1.0 / math.sqrt(vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2])
   1179         vec = (vec[0] * norm, vec[1] * norm, vec[2] * norm)
   1180         leftright = math.atan2(vec[2], vec[0])
   1181         updown = math.asin(vec[1])
   1182         leftright = 1 - abs(leftright) / 3.1415927 * 2
   1183         updown = 2 * updown / 3.1415927
   1184         dist = (vecLength - 1.0) / 7.0
   1185         network = animparams.GetParamsPerAvatar(avatar, info.charID)
   1186         network.SetControlParameter('ControlParameters|HeadLookLeftRight', leftright)
   1187         network.SetControlParameter('ControlParameters|HeadLookUpDown', updown)
   1188         network.SetControlParameter('ControlParameters|CameraDistance', dist)
   1189 
   1190     @telemetry.ZONE_METHOD
   1191     def FetchOldPortraitData(self, charID):
   1192         PREFIX = 'ControlParameters|'
   1193         cache = self.characterSvc.GetCachedPortraitInfo(charID)
   1194         if cache is not None:
   1195             self.lightingID = cache.lightID
   1196             self.lightColorID = cache.lightColorID
   1197             self.lightIntensity = cache.lightIntensity
   1198             path = self.GetBackgroundPathFromID(cache.backgroundID)
   1199             if path in self.GetAvailableBackgroundsPaths():
   1200                 self.backdropPath = path
   1201             self.poseID = cache.poseData['PortraitPoseNumber']
   1202             self.cameraPos = cache.cameraPosition
   1203             self.cameraPoi = cache.cameraPoi
   1204             self.cameraFov = cache.cameraFieldOfView
   1205             params = []
   1206             for key in cache.poseData:
   1207                 params.append((PREFIX + key, cache.poseData[key]))
   1208 
   1209             if len(params):
   1210                 self.characterSvc.SetControlParametersFromList(params, charID)
   1211         else:
   1212             portraitData = sm.GetService('cc').GetPortraitData(charID)
   1213             if portraitData is not None:
   1214                 self.lightingID = portraitData.lightID
   1215                 self.lightColorID = portraitData.lightColorID
   1216                 self.lightIntensity = portraitData.lightIntensity
   1217                 path = self.GetBackgroundPathFromID(portraitData.backgroundID)
   1218                 if path in self.GetAvailableBackgroundsPaths():
   1219                     self.backdropPath = path
   1220                 self.poseID = portraitData.portraitPoseNumber
   1221                 self.cameraPos = (portraitData.cameraX, portraitData.cameraY, portraitData.cameraZ)
   1222                 self.cameraPoi = (portraitData.cameraPoiX, portraitData.cameraPoiY, portraitData.cameraPoiZ)
   1223                 self.cameraFov = portraitData.cameraFieldOfView
   1224                 params = self.GetControlParametersFromPoseData(portraitData, fromDB=True).values()
   1225                 self.characterSvc.SetControlParametersFromList(params, charID)
   1226         self.alreadyLoadedOldPortraitData = True
   1227 
   1228     @telemetry.ZONE_METHOD
   1229     def StartPortraitStep(self):
   1230         info = self.GetInfo()
   1231         if not getattr(self, 'alreadyLoadedOldPortraitData', False):
   1232             if self.modeObject.GetOldPortraitData():
   1233                 self.FetchOldPortraitData(info.charID)
   1234         self.step = CharacterPortrait(parent=self.mainCont)
   1235         self.SetAvatarScene()
   1236         avatar = self.characterSvc.GetSingleCharactersAvatar(info.charID)
   1237         if avatar is not None and avatar.animationUpdater is not None:
   1238             network = animparams.GetParamsPerAvatar(avatar, info.charID)
   1239             network.SetControlParameter('ControlParameters|NetworkMode', NETWORKMODE_PORTRAIT)
   1240         if self.camera is None:
   1241             self.camera = CharCreationCamera(avatar, ccConst.CAMERA_MODE_PORTRAIT)
   1242             self.SetupCameraUpdateJob()
   1243         else:
   1244             self.camera.ToggleMode(ccConst.CAMERA_MODE_PORTRAIT, avatar=avatar, transformTime=500.0)
   1245         if hasattr(self, 'cameraPos'):
   1246             if self.gender_changed:
   1247                 if info.genderID:
   1248                     camDelta = 0.1
   1249                 else:
   1250                     camDelta = -0.1
   1251                 self.cameraPos = (self.cameraPos[0], self.cameraPos[1] + camDelta, self.cameraPos[2])
   1252                 self.cameraPoi = (self.cameraPoi[0], self.cameraPoi[1] + camDelta, self.cameraPoi[2])
   1253             self.camera.PlacePortraitCamera(self.cameraPos, self.cameraPoi)
   1254             xFactor, yFactor = self.camera.GetCorrectCameraXandYFactors(self.cameraPos, self.cameraPoi)
   1255             self.camera.xFactor = self.camera.xTarget = xFactor
   1256             self.camera.yFactor = self.camera.yTarget = yFactor
   1257         if hasattr(self, 'storedPortraitCameraSettings'):
   1258             self.camera.SetPointOfInterest(self.storedPortraitCameraSettings['poi'])
   1259             self.camera.pitch = self.storedPortraitCameraSettings['pitch']
   1260             self.camera.yaw = self.storedPortraitCameraSettings['yaw']
   1261             self.camera.distance = self.storedPortraitCameraSettings['distance']
   1262             self.camera.xFactor = self.storedPortraitCameraSettings['xFactor']
   1263             self.camera.yFactor = self.storedPortraitCameraSettings['yFactor']
   1264             self.camera.fieldOfView = self.storedPortraitCameraSettings['fieldOfView']
   1265         self.UpdateLights()
   1266         self.characterSvc.StartPosing(charID=info.charID, callback=self.step.ChangeSculptingCursor)
   1267 
   1268     @telemetry.ZONE_METHOD
   1269     def StartNamingStep(self, *args):
   1270         self.SetAvatarScene()
   1271         self.SetDefaultLighting()
   1272         self.CaptureAndSetActivePortrait()
   1273         self.step = CharacterNaming(parent=self.mainCont)
   1274         self.SetUpNamingStepCamera()
   1275         self.SetAvatarAnimationNetworkMode(NETWORKMODE_BASIC)
   1276 
   1277     def CaptureAndSetActivePortrait(self):
   1278         self.SetAvatarAnimationNetworkMode(NETWORKMODE_PORTRAIT)
   1279         while not self.characterSvc.sculpting:
   1280             blue.synchro.Yield()
   1281 
   1282         info = self.GetInfo()
   1283         avatar = self.characterSvc.GetSingleCharactersAvatar(info.charID)
   1284         self.camera = CharCreationCamera(avatar, ccConst.CAMERA_MODE_PORTRAIT)
   1285         self.camera.SetMoveCallback(self.CameraMoveCB)
   1286         self.camera.Update()
   1287         self.backdropPath = random.choice(ccConst.defaultBackgroundOptions)
   1288         self.activePortrait = self.CapturePortrait(0)
   1289 
   1290     def SetUpNamingStepCamera(self):
   1291         info = self.GetInfo()
   1292         avatar = self.characterSvc.GetSingleCharactersAvatar(info.charID)
   1293         self.camera = CharCreationCamera(avatar=avatar)
   1294         self.camera.fieldOfView = 0.3
   1295         self.camera.distance = 8.0
   1296         self.camera.frontClip = 3.5
   1297         self.camera.backclip = 100.0
   1298         self.SetupCameraUpdateJob()
   1299         self.camera.SetPointOfInterest((0.0, self.camera.avatarEyeHeight / 2.0, 0.0))
   1300 
   1301     def SetAvatarAnimationNetworkMode(self, networkMode):
   1302         info = self.GetInfo()
   1303         avatar = self.characterSvc.GetSingleCharactersAvatar(info.charID)
   1304         if avatar is not None:
   1305             if info.charID in self.characterSvc.characters:
   1306                 network = animparams.GetParamsPerAvatar(avatar, info.charID)
   1307                 network.SetControlParameter('ControlParameters|NetworkMode', networkMode)
   1308                 while network.rebuilding:
   1309                     blue.synchro.Yield()
   1310 
   1311     @telemetry.ZONE_METHOD
   1312     def ClearSteps(self, what = None, force = 0, *args):
   1313         stepAlreadyCleared = getattr(self.step, 'stepAlreadyCleared', 0)
   1314         if not force and stepAlreadyCleared or not getattr(self.mainNav, 'ResetToStep', None):
   1315             return
   1316         currentStepID = self.stepID
   1317         currentStepOrder = GetStepOrder(currentStepID)
   1318         if currentStepOrder <= GetStepOrder(ccConst.CUSTOMIZATIONSTEP):
   1319             self.ClearFacePortrait()
   1320             self.ClearPortraitInfo()
   1321             self.schoolID = None
   1322             if what in ('bloodline', 'race'):
   1323                 self.schoolID = None
   1324         if currentStepOrder <= GetStepOrder(ccConst.BLOODLINESTEP):
   1325             self.avatarScene = None
   1326             self.characterSvc.TearDown()
   1327             self.schoolID = None
   1328         if currentStepOrder <= GetStepOrder(ccConst.EMPIRESTEP):
   1329             self.TearDown()
   1330         firstStep = self.availableSteps[0]
   1331         self.stepsUsed = GetStepsUpToAndIncluding(currentStepID) or {firstStep}
   1332         self.mainNav.ResetToStep(currentStepID, stepsUsed=self.stepsUsed)
   1333         if self.step:
   1334             self.step.stepAlreadyCleared = 1
   1335 
   1336     @telemetry.ZONE_METHOD
   1337     def Cleanup(self):
   1338         self.characterSvc.StopEditing()
   1339         info = self.GetInfo()
   1340         charID = info.charID
   1341         if charID in self.characterSvc.characters:
   1342             self.characterSvc.StopPosing(charID)
   1343         self.scene = None
   1344         self.ClearCamera()
   1345         self.sceneManager.UnregisterScene('characterCreation')
   1346 
   1347     @telemetry.ZONE_METHOD
   1348     def SetupScene(self, path):
   1349         scene = trinity.Load(path)
   1350         blue.resMan.Wait()
   1351         if self.IsSlowMachine():
   1352             if hasattr(scene, 'shadowCubeMap'):
   1353                 scene.shadowCubeMap.enabled = False
   1354             if hasattr(scene, 'ssao') and hasattr(scene.ssao, 'enable'):
   1355                 scene.ssao.enable = False
   1356             if hasattr(scene, 'ambientColor'):
   1357                 scene.ambientColor = (0.25, 0.25, 0.25)
   1358         elif scene:
   1359             if hasattr(scene, 'shadowCubeMap'):
   1360                 scene.shadowCubeMap.enabled = False
   1361         self.scene = scene
   1362         self.sceneManager.RegisterScene(scene, 'characterCreation')
   1363         self.sceneManager.SetRegisteredScenes('characterCreation')
   1364 
   1365     @telemetry.ZONE_METHOD
   1366     def ReduceLights(self):
   1367         scene = self.scene
   1368         if hasattr(scene, 'lights'):
   1369             lightsToRemove = []
   1370             for each in scene.lights:
   1371                 if each.name != 'FrontMain':
   1372                     lightsToRemove.append(each)
   1373 
   1374             for each in lightsToRemove:
   1375                 scene.lights.remove(each)
   1376 
   1377     def ApplyGraphicsSettingsToDoll(self, doll):
   1378         doll.overrideLod = LOD_SKIN
   1379         textureQuality = gfxsettings.Get(gfxsettings.GFX_CHAR_TEXTURE_QUALITY)
   1380         doll.textureResolution = ccConst.TEXTURE_RESOLUTIONS[textureQuality]
   1381         if self.IsSlowMachine():
   1382             doll.useFastShader = True
   1383         else:
   1384             doll.useFastShader = False
   1385 
   1386     @telemetry.ZONE_METHOD
   1387     def AddCharacter(self, charID, bloodlineID, raceID, genderID, scene = None, dna = None, validateColors = True, noRandomize = False):
   1388         self.ResetDna()
   1389         self.characterSvc.AddCharacterToScene(charID, scene or self.scene, ccUtil.GenderIDToPaperDollGender(genderID), dna=dna, bloodlineID=bloodlineID, raceID=raceID, updateDoll=False, noRandomize=noRandomize)
   1390         self.doll = self.characterSvc.GetSingleCharactersDoll(charID)
   1391         while self.doll.IsBusyUpdating():
   1392             blue.synchro.Yield()
   1393 
   1394         self.ApplyGraphicsSettingsToDoll(self.doll)
   1395         self.bloodlineID = bloodlineID
   1396         self.characterSvc.SetDollBloodline(charID, bloodlineID)
   1397         if validateColors:
   1398             for categoryName in ccConst.COLORMAPPING.keys():
   1399                 self.UpdateColorSelectionFromDoll(categoryName)
   1400                 self.ValidateColors(categoryName)
   1401 
   1402         self.StartDnaLogging()
   1403         self.characterSvc.UpdateDoll(charID, fromWhere='AddCharacter')
   1404 
   1405     def GetAvailableBackgroundsPaths(self):
   1406         bgOptions = []
   1407         if gfxsettings.Get(gfxsettings.UI_NCC_GREEN_SCREEN):
   1408             bgOptions += ccConst.greenscreenBackgroundOptions
   1409         authoredBackgrounds = self.characterSvc.GetAvailableBackgrounds()
   1410         for eachResource in authoredBackgrounds:
   1411             path = eachResource.resPath
   1412             bgOptions.append(path)
   1413 
   1414         return bgOptions
   1415 
   1416     @telemetry.ZONE_METHOD
   1417     def GetAvailableStyles(self, modifier):
   1418         info = self.GetInfo()
   1419         gender = info.genderID
   1420         bloodline = info.bloodlineID
   1421         currentModifier = self.characterSvc.GetModifierByCategory(info.charID, modifier)
   1422         itemTypes = self.characterSvc.GetAvailableTypesByCategory(modifier, gender, info.raceID)
   1423         activeIndex = None
   1424         if currentModifier:
   1425             currentType = currentModifier.GetTypeData()
   1426             for i, each in enumerate(itemTypes):
   1427                 if each[1][0] == currentType[0] and each[1][1] == currentType[1] and each[1][2] == currentType[2]:
   1428                     activeIndex = i
   1429 
   1430         return (itemTypes, activeIndex)
   1431 
   1432     def GetModifierIntensity(self, modifierPath):
   1433         info = self.GetInfo()
   1434         modifier = self.characterSvc.GetModifiersByCategory(info.charID, modifierPath)
   1435         if modifier:
   1436             return modifier[0].weight
   1437         return 0.0
   1438 
   1439     @telemetry.ZONE_METHOD
   1440     def GetAvailableColors(self, modifier):
   1441         info = self.GetInfo()
   1442         colors, activeColorIndex = self.characterSvc.GetCharacterColorVariations(info.charID, modifier)
   1443         colors = tuple(colors)
   1444         retColors = []
   1445         for name, color in colors:
   1446             if color and type(color[0]) == types.TupleType:
   1447                 r = g = b = 0
   1448                 for _r, _g, _b, _a in color:
   1449                     r += _r
   1450                     g += _g
   1451                     b += _b
   1452 
   1453                 r = r / float(len(color))
   1454                 g = g / float(len(color))
   1455                 b = b / float(len(color))
   1456                 retColors.append((name, (r,
   1457                   g,
   1458                   b,
   1459                   1.0), color))
   1460             else:
   1461                 retColors.append((name, color, color))
   1462 
   1463         return (retColors, activeColorIndex)
   1464 
   1465     @telemetry.ZONE_METHOD
   1466     def SetColorValue(self, modifier, primaryColor, secondaryColor = None, doUpdate = True, ignoreValidate = False):
   1467         self._setColorsByCategory[modifier] = (primaryColor, secondaryColor)
   1468         info = self.GetInfo()
   1469         self.characterSvc.SetColorValueByCategory(info.charID, modifier, primaryColor, secondaryColor, doUpdate=False)
   1470         if ccUtil.HasUserDefinedSpecularity(modifier):
   1471             specValue = self._setSpecularityByCategory.setdefault(modifier, 0.5)
   1472             self.SetColorSpecularity(modifier, specValue, doUpdate=False)
   1473         if ccUtil.HasUserDefinedWeight(modifier):
   1474             defaultIntensity = ccConst.defaultIntensity.get(modifier, 0.5)
   1475             intensityValue = self._setIntensityByCategory.setdefault(modifier, defaultIntensity)
   1476             self.SetIntensity(modifier, intensityValue, doUpdate=False)
   1477         if not ignoreValidate:
   1478             self.ValidateColors(modifier)
   1479         if doUpdate:
   1480             self.characterSvc.UpdateDoll(info.charID, fromWhere='SetColorValue')
   1481 
   1482     def GetSpecularityByCategory(self, category):
   1483         return self._setSpecularityByCategory.get(category, 0.5)
   1484 
   1485     def GetIntensityByCategory(self, category):
   1486         return self._setIntensityByCategory.get(category, 0.5)
   1487 
   1488     @telemetry.ZONE_METHOD
   1489     def SetRandomColorSpecularity(self, modifier, doUpdate = True):
   1490         self.SetColorSpecularity(modifier, random.random(), doUpdate=doUpdate)
   1491 
   1492     @telemetry.ZONE_METHOD
   1493     def SetColorSpecularity(self, modifier, specularity, doUpdate = True):
   1494         self._setSpecularityByCategory[modifier] = specularity
   1495         info = self.GetInfo()
   1496         self.characterSvc.SetColorSpecularityByCategory(info.charID, modifier, specularity, doUpdate=doUpdate)
   1497 
   1498     @telemetry.ZONE_METHOD
   1499     def SetRandomHairDarkness(self, doUpdate = True):
   1500         self.SetHairDarkness(random.random(), doUpdate=doUpdate)
   1501 
   1502     @telemetry.ZONE_METHOD
   1503     def SetHairDarkness(self, darkness, doUpdate = True):
   1504         info = self.GetInfo()
   1505         self.characterSvc.SetHairDarkness(info.charID, darkness)
   1506         if doUpdate:
   1507             sm.GetService('character').UpdateDoll(info.charID, fromWhere='SetHairDarkness')
   1508 
   1509     @telemetry.ZONE_METHOD
   1510     def SetRandomIntensity(self, modifier, doUpdate = True):
   1511         self.SetIntensity(modifier, random.random(), doUpdate=doUpdate)
   1512 
   1513     @telemetry.ZONE_METHOD
   1514     def SetIntensity(self, modifier, value, doUpdate = True):
   1515         info = self.GetInfo()
   1516         if modifier == ccConst.muscle:
   1517             self.characterSvc.SetCharacterMuscularity(info.charID, value, doUpdate=doUpdate)
   1518         elif modifier == ccConst.weight:
   1519             self.characterSvc.SetCharacterWeight(info.charID, value, doUpdate=doUpdate)
   1520         else:
   1521             self._setIntensityByCategory[modifier] = value
   1522             self.characterSvc.SetWeightByCategory(info.charID, modifier, value, doUpdate=doUpdate)
   1523 
   1524     @telemetry.ZONE_METHOD
   1525     def SetItemType(self, itemType, weight = 1.0, doUpdate = True):
   1526         info = self.GetInfo()
   1527         category = self.characterSvc.GetCategoryFromResPath(itemType[1][0])
   1528         if category in CLOTHING_ITEMS:
   1529             if category in self.clothesStorage:
   1530                 self.clothesStorage.pop(category)
   1531                 self.clothesMetaStorage.pop(category)
   1532             self.ToggleClothes(forcedValue=0, doUpdate=False)
   1533         self.characterSvc.ApplyTypeToDoll(info.charID, itemType, weight=weight, doUpdate=False)
   1534         if category in self._setColorsByCategory:
   1535             var1, var2 = self._setColorsByCategory[category]
   1536             self.SetColorValue(category, var1, var2, doUpdate=False)
   1537         self.ValidateColors(category)
   1538         if doUpdate:
   1539             self.characterSvc.UpdateDoll(info.charID, fromWhere='SetItemType')
   1540 
   1541     @telemetry.ZONE_METHOD
   1542     def SetStyle(self, category, style, variation = None, doUpdate = True):
   1543         info = self.GetInfo()
   1544         if style or variation or category in CLOTHING_ITEMS:
   1545             self.ToggleClothes(forcedValue=0, doUpdate=False)
   1546         self.characterSvc.ApplyItemToDoll(info.charID, category, style, removeFirst=True, variation=variation, doUpdate=False)
   1547         if style:
   1548             if category in self._setColorsByCategory:
   1549                 var1, var2 = self._setColorsByCategory[category]
   1550                 self.SetColorValue(category, var1, var2, doUpdate=False)
   1551             self.ValidateColors(category)
   1552         if doUpdate:
   1553             self.characterSvc.UpdateDoll(info.charID, fromWhere='SetStyle')
   1554 
   1555     @telemetry.ZONE_METHOD
   1556     def ValidateColors(self, category):
   1557         if category not in ccConst.COLORMAPPING:
   1558             return
   1559         info = self.GetInfo()
   1560         categoryColors = self.characterSvc.GetAvailableColorsForCategory(category, info.genderID, info.raceID)
   1561         if not categoryColors:
   1562             return
   1563         primary, secondary = categoryColors
   1564         hasValidColor = False
   1565         modifier = self.characterSvc.GetModifiersByCategory(info.charID, category)
   1566         if modifier:
   1567             currentColor = modifier[0].GetColorizeData()
   1568             if secondary:
   1569                 if modifier[0].metaData.numColorAreas > 1:
   1570                     for primaryColorTuple in primary:
   1571                         primaryColorName, primaryDisplayColor, primaryColorValue = primaryColorTuple
   1572                         pA, pB, pC = primaryColorValue['colors']
   1573                         for secondaryColorTuple in secondary:
   1574                             secondaryColorName, secondaryDisplayColor, secondaryColorValue = secondaryColorTuple
   1575                             srA, srB, srC = secondaryColorValue['colors']
   1576                             if pA == currentColor[0] and srB == currentColor[1] and srC == currentColor[2]:
   1577                                 hasValidColor = True
   1578                                 if category not in self._setColorsByCategory or self._setColorsByCategory[category][1] == None:
   1579                                     self.SetColorValue(category, primaryColorTuple, secondaryColorTuple, doUpdate=False, ignoreValidate=True)
   1580                                 break
   1581 
   1582                         if hasValidColor:
   1583                             break
   1584 
   1585                     if not hasValidColor:
   1586                         for primaryColorTuple in primary:
   1587                             primaryColorName, primaryDisplayColor, primaryColorValue = primaryColorTuple
   1588                             if primaryColorValue['colors'] == currentColor:
   1589                                 hasValidColor = True
   1590                                 self.SetColorValue(category, primaryColorTuple, secondary[0], doUpdate=False, ignoreValidate=True)
   1591                                 break
   1592 
   1593                 else:
   1594                     for primaryColorTuple in primary:
   1595                         primaryColorName, primaryDisplayColor, primaryColorValue = primaryColorTuple
   1596                         if primaryColorValue['colors'] == currentColor:
   1597                             hasValidColor = True
   1598                             if category not in self._setColorsByCategory:
   1599                                 self.SetColorValue(category, primaryColorTuple, None, doUpdate=False, ignoreValidate=True)
   1600                             break
   1601                     else:
   1602                         if category in self._setColorsByCategory:
   1603                             hasValidColor = True
   1604             else:
   1605                 for primaryColorTuple in primary:
   1606                     primaryColorName, primaryDisplayColor, primaryColorValue = primaryColorTuple
   1607                     if primaryColorValue['colors'] == currentColor:
   1608                         hasValidColor = True
   1609                         if category not in self._setColorsByCategory:
   1610                             self.SetColorValue(category, primaryColorTuple, None, doUpdate=False, ignoreValidate=True)
   1611                         break
   1612 
   1613             if not hasValidColor and primary:
   1614                 if secondary:
   1615                     var2 = secondary[0]
   1616                 else:
   1617                     var2 = None
   1618                 self.SetColorValue(category, primary[0], var2, doUpdate=False, ignoreValidate=True)
   1619 
   1620     def UpdateColorSelectionFromDoll(self, category):
   1621         if category not in ccConst.COLORMAPPING:
   1622             return
   1623         info = self.GetInfo()
   1624         categoryColors = self.characterSvc.GetAvailableColorsForCategory(category, info.genderID, info.raceID)
   1625         if not categoryColors:
   1626             return
   1627         primary, secondary = categoryColors
   1628         modifier = self.characterSvc.GetModifiersByCategory(info.charID, category)
   1629         if modifier:
   1630             corPrimary = None
   1631             corSecondary = None
   1632             try:
   1633                 chosenPrimary, chosenSecondary = self.characterSvc.GetSingleCharactersMetadata(info.charID).typeColors[category]
   1634                 for primaryColorTuple in primary:
   1635                     if primaryColorTuple[0] == chosenPrimary:
   1636                         corPrimary = primaryColorTuple
   1637                         break
   1638 
   1639                 if secondary and chosenSecondary:
   1640                     for secondaryColorTuple in secondary:
   1641                         if secondaryColorTuple[0] == chosenSecondary:
   1642                             corSecondary = secondaryColorTuple
   1643                             break
   1644 
   1645             except KeyError:
   1646                 log.LogWarn('KeyError when getting Metadata for a single character in characterCreationContainer.UpdateColorSelectionFromDoll', info.charID, category)
   1647 
   1648             if corPrimary is not None:
   1649                 self._setColorsByCategory[category] = (corPrimary, corSecondary)
   1650             if category in self.characterSvc.characterMetadata[info.charID].typeWeights:
   1651                 self._setIntensityByCategory[category] = self.characterSvc.characterMetadata[info.charID].typeWeights[category]
   1652             if category in self.characterSvc.characterMetadata[info.charID].typeSpecularity:
   1653                 self._setSpecularityByCategory[category] = self.characterSvc.characterMetadata[info.charID].typeSpecularity[category]
   1654 
   1655     @telemetry.ZONE_METHOD
   1656     def ClearCategory(self, category, doUpdate = True):
   1657         self.SetStyle(category, style=None, doUpdate=doUpdate)
   1658 
   1659     @telemetry.ZONE_METHOD
   1660     def CheckDnaLog(self, trigger = None):
   1661         if self.step and self.step.sr.historySlider:
   1662             currentIndex, maxIndex = self.step.sr.historySlider.GetCurrentIndexAndMaxIndex()
   1663             if currentIndex != maxIndex:
   1664                 self.ClearDnaLogFromIndex(currentIndex)
   1665 
   1666     @telemetry.ZONE_METHOD
   1667     def ClearCamera(self):
   1668         if self.camera is not None:
   1669             for priority, behavior in self.camera.cameraBehaviors:
   1670                 behavior.camera = None
   1671 
   1672             del self.camera.cameraBehaviors[:]
   1673             self.camera.avatar = None
   1674             self.camera = None
   1675 
   1676     @telemetry.ZONE_METHOD
   1677     def SetDefaultLighting(self):
   1678         self.SetLightScene('res:/Graphics/Character/Global/PaperdollSettings/LightSettings/Normal.red')
   1679         if self.IsSlowMachine():
   1680             self.ReduceLights()
   1681 
   1682     @telemetry.ZONE_METHOD
   1683     def SetupCameraUpdateJob(self):
   1684         self.sceneManager.RefreshJob(self.camera)
   1685         if self.cameraUpdateJob is None:
   1686             self.cameraUpdateJob = trinity.renderJob.CreateRenderJob('cameraUpdate')
   1687             r = trinity.TriStepPythonCB()
   1688             r.SetCallback(self.UpdateCamera)
   1689             self.cameraUpdateJob.steps.append(r)
   1690         self.sceneManager.characterRenderJob.SetCameraUpdate(self.cameraUpdateJob)
   1691 
   1692     @telemetry.ZONE_METHOD
   1693     def UpdateCamera(self):
   1694         if self.camera is not None:
   1695             self.camera.Update()
   1696 
   1697     @telemetry.ZONE_METHOD
   1698     def PickObjectUV(self, pos):
   1699         return self.scene.PickObjectUV(pos[0], pos[1], self.camera.projectionMatrix, self.camera.viewMatrix, trinity.device.viewport)
   1700 
   1701     @telemetry.ZONE_METHOD
   1702     def PickObjectAndArea(self, pos):
   1703         return self.scene.PickObjectAndArea(pos[0], pos[1], self.camera.projectionMatrix, self.camera.viewMatrix, trinity.device.viewport)
   1704 
   1705     @telemetry.ZONE_METHOD
   1706     def PickObject(self, pos):
   1707         if not self.step:
   1708             return
   1709         if self.scene is not None:
   1710             return self.scene.PickObject(pos[0], pos[1], self.camera.projectionMatrix, self.camera.viewMatrix, self.step.GetViewportForPicking())
   1711 
   1712     @telemetry.ZONE_METHOD
   1713     def SaveCurrentCharacter(self, charactername, bloodlineID, genderID, portraitID):
   1714         total = 3
   1715         sm.GetService('loading').ProgressWnd(localization.GetByLabel('UI/CharacterCreation/Registering'), localization.GetByLabel('UI/CharacterCreation/CompilePrefs'), 1, total)
   1716         try:
   1717             if self.portraitInfo[portraitID] is None:
   1718                 raise UserError('CharCreationNoPortrait')
   1719             info = self.GetInfo()
   1720             charInfo = self.characterSvc.GetCharacterAppearanceInfo(info.charID)
   1721             charID = sm.GetService('cc').CreateCharacterWithDoll(charactername, bloodlineID, genderID, const.ancestryUndefined, charInfo, self.portraitInfo[portraitID], info.schoolID, info.raceID)
   1722         except UserError as what:
   1723             if not what.msg.startswith('CharNameInvalid'):
   1724                 prompt_player_fsd_dialog(*what.args)
   1725                 return
   1726             sm.GetService('loading').ProgressWnd(localization.GetByLabel('UI/CharacterCreation/Registering'), localization.GetByLabel('UI/CharacterCreation/FailedForSomeReason'), 3, total)
   1727         else:
   1728             sm.GetService('loading').ProgressWnd(localization.GetByLabel('UI/CharacterCreation/Registering'), localization.GetByLabel('UI/CharacterCreation/InsertingRecord'), 2, total)
   1729             sm.GetService('photo').AddPortrait(self.GetPortraitSnapshotPath(portraitID), charID)
   1730             sm.GetService('loading').ProgressWnd(localization.GetByLabel('UI/CharacterCreation/Registering'), localization.GetByLabel('UI/Generic/Done'), 3, total)
   1731             return charID
   1732         finally:
   1733             self.sessionSounds = []
   1734 
   1735     @telemetry.ZONE_METHOD
   1736     def UpdateExistingCharacter(self, *args):
   1737         portraitID = self.activePortraitIndex
   1738         charID = self.charID
   1739         activePortraitInfo = self.portraitInfo[portraitID]
   1740         if activePortraitInfo is None:
   1741             raise UserError('CharCreationNoPortrait')
   1742         dollExists = self.dna is not None
   1743         dollInfo = self.characterSvc.GetCharacterAppearanceInfo(charID)
   1744         availableTypeIDs = sm.GetService('cc').GetMyApparel()
   1745         metadata = self.characterSvc.characterMetadata[charID]
   1746         typesInUse = metadata.types
   1747         for resourceID in typesInUse.itervalues():
   1748             if resourceID:
   1749                 info = cfg.paperdollResources.Get(resourceID)
   1750                 if info.typeID is not None and info.typeID not in availableTypeIDs:
   1751                     raise UserError('ItemNotAtStation', {'item': info.typeID})
   1752 
   1753         bgInfo = cfg.paperdollPortraitResources.Get(activePortraitInfo.backgroundID)
   1754         if bgInfo and bgInfo.typeID is not None and bgInfo.typeID not in availableTypeIDs:
   1755             raise UserError('ItemNotAtStation', {'item': bgInfo.typeID})
   1756         try:
   1757             if self.mode == ccConst.MODE_FULL_RECUSTOMIZATION:
   1758                 sm.GetService('cc').UpdateExistingCharacterFull(charID, dollInfo, activePortraitInfo, dollExists, metadata['genderID'], flush=True)
   1759             elif self.mode == ccConst.MODE_LIMITED_RECUSTOMIZATION:
   1760                 sm.GetService('cc').UpdateExistingCharacterLimited(charID, dollInfo, activePortraitInfo, dollExists)
   1761             elif self.mode == ccConst.MODE_FULL_BLOODLINECHANGE:
   1762                 info = self.GetInfo()
   1763                 sm.GetService('cc').UpdateExistingCharacterBloodline(charID, info.bloodlineID, True)
   1764         except UserError as e:
   1765             if e.msg == 'CCInvalidClothing':
   1766                 if e.dict['genderID'] == 0:
   1767                     modifierNames = femaleModifierDisplayNames
   1768                 else:
   1769                     modifierNames = maleModifierDisplayNames
   1770                 modifierName = modifierNames.get(e.dict['modifierKey'], '')
   1771                 prompt_player_fsd_dialog('CustomInfo', {'info': localization.GetByLabel('UI/Login/CharacterCreation/InvalidApparelAssets', modifierName=localization.GetByLabel(modifierName))})
   1772                 return
   1773 
   1774         sm.GetService('photo').AddPortrait(self.GetPortraitSnapshotPath(portraitID), charID)
   1775         self.characterSvc.CachePortraitInfo(self.charID, self.portraitInfo[self.activePortraitIndex])
   1776         self.metrics.complete_cc(charid=charID)
   1777         if self.dollState != paperdoll.State.force_recustomize:
   1778             self.ExitToStation()
   1779         else:
   1780             uthread.pool('GameUI::ActivateView::charsel', sm.GetService('viewState').ActivateView, 'charsel')
   1781 
   1782     @telemetry.ZONE_METHOD
   1783     def SetBackdrop(self, backdropPath):
   1784         self.backdropPath = backdropPath
   1785 
   1786     @telemetry.ZONE_METHOD
   1787     def SetPoseID(self, poseID):
   1788         self.poseID = poseID
   1789 
   1790     @telemetry.ZONE_METHOD
   1791     def SetLightScene(self, lightPath, scene = None):
   1792         scene = scene or self.scene
   1793         lightScene = trinity.Load(lightPath)
   1794         if scene:
   1795             lightList = []
   1796             for l in scene.lights:
   1797                 lightList.append(l)
   1798 
   1799             for l in lightList:
   1800                 scene.RemoveLightSource(l)
   1801 
   1802             for l in lightScene.lights:
   1803                 scene.AddLightSource(l)
   1804 
   1805     @telemetry.ZONE_METHOD
   1806     def SetLights(self, lightID):
   1807         self.lightingID = lightID
   1808         self.UpdateLights()
   1809 
   1810     @telemetry.ZONE_METHOD
   1811     def GetLight(self):
   1812         return self.lightingID
   1813 
   1814     @telemetry.ZONE_METHOD
   1815     def SetLightColor(self, lightID):
   1816         self.lightColorID = lightID
   1817         self.UpdateLights()
   1818 
   1819     @telemetry.ZONE_METHOD
   1820     def GetLightColor(self):
   1821         return self.lightColorID
   1822 
   1823     @telemetry.ZONE_METHOD
   1824     def SetLightsAndColor(self, lightID, colorID):
   1825         self.lightingID = lightID
   1826         self.lightColorID = colorID
   1827         self.UpdateLights()
   1828 
   1829     @telemetry.ZONE_METHOD
   1830     def SetLightIntensity(self, intensity):
   1831         self.lightIntensity = intensity
   1832         self.UpdateLights()
   1833 
   1834     @telemetry.ZONE_METHOD
   1835     def GetLightIntensity(self):
   1836         return self.lightIntensity
   1837 
   1838     @telemetry.ZONE_METHOD
   1839     def UpdateLights(self):
   1840         lightsPath = GetGraphicFile(self.lightingID)
   1841         lightColorPath = GetGraphicFile(self.lightColorID)
   1842         lightScene = trinity.Load(lightsPath)
   1843         lightColorScene = trinity.Load(lightColorPath)
   1844         ccUtil.SetupLighting(self.scene, lightScene, lightColorScene, self.lightIntensity)
   1845         if self.IsSlowMachine():
   1846             self.ReduceLights()
   1847 
   1848     @telemetry.ZONE_METHOD
   1849     def GetBackdrop(self):
   1850         return self.backdropPath
   1851 
   1852     @telemetry.ZONE_METHOD
   1853     def GetPoseId(self):
   1854         return getattr(self, 'poseID', 0)
   1855 
   1856     @telemetry.ZONE_METHOD
   1857     def StartEditMode(self, callback = None, **kwds):
   1858         if callback is None and kwds.get('mode', None) == 'sculpt':
   1859             callback = getattr(self.step, 'ChangeSculptingCursor', None)
   1860         info = self.GetInfo()
   1861         self.characterSvc.StartEditMode(info.charID, self.scene, self.camera, callback=callback, **kwds)
   1862 
   1863     @telemetry.ZONE_METHOD
   1864     def UpdateBackdropLite(self, raceID, mouseEnter = False, *args):
   1865         bdScene = self.sceneManager.Get2DBackdropScene()
   1866         if not bdScene:
   1867             return
   1868         blue.resMan.SetUrgentResourceLoads(True)
   1869         for each in bdScene.children:
   1870             each.display = False
   1871             if mouseEnter:
   1872                 if raceID:
   1873                     if each.name == 'mouseoverSprite_%d' % raceID:
   1874                         each.display = True
   1875                 else:
   1876                     each.display = True
   1877             elif each.name == 'backdropSprite':
   1878                 each.texturePrimary.resPath = 'res:/UI/Texture/CharacterCreation/bg/RACE_Background_%d.dds' % raceID
   1879                 each.display = True
   1880 
   1881         blue.resMan.SetUrgentResourceLoads(False)
   1882 
   1883     def _GetDpiScaledWidthAndHeight(self):
   1884         desktopWidth = int(uicore.desktop.width * uicore.desktop.dpiScaling)
   1885         desktopHeight = int(uicore.desktop.height * uicore.desktop.dpiScaling)
   1886         return (desktopWidth, desktopHeight)
   1887 
   1888     def _AssembleBackDropSprite(self, name, res_path, bgSize = None):
   1889         desktopWidth, desktopHeight = self._GetDpiScaledWidthAndHeight()
   1890         if bgSize is None:
   1891             bgSize = max(desktopWidth, desktopHeight)
   1892         sprite = trinity.Tr2Sprite2d()
   1893         sprite.name = name
   1894         sprite.displayWidth = bgSize
   1895         sprite.displayHeight = bgSize
   1896         sprite.displayY = (desktopHeight - bgSize) / 2
   1897         sprite.displayX = (desktopWidth - bgSize) / 2
   1898         sprite.texturePrimary = trinity.Tr2Sprite2dTexture()
   1899         sprite.texturePrimary.resPath = res_path
   1900         sprite.display = True
   1901         return sprite
   1902 
   1903     def _GetCurrentSpritePath(self):
   1904         if self.stepID == ccConst.EMPIRESTEP:
   1905             return ccConst.EMPIRE_PANELS_BACKGROUND
   1906         if self.stepID in ccConst.EMPIRE_SELECTION_STEPS:
   1907             return ccConst.EMPIRE_BACKGROUND_BY_RACE.get(self.raceID, None)
   1908 
   1909     def _GetBackdropSpriteSizes(self):
   1910         clientHeight = uicore.desktop.height
   1911         percentOfClientHeight = float(clientHeight) / BG_HEIGHT
   1912         height = clientHeight
   1913         width = int(percentOfClientHeight * BG_WIDTH)
   1914         return (width, height)
   1915 
   1916     def _CreateNewBackdropSprite(self, path):
   1917         spriteID = const.races[self.raceID] if self.raceID else 'Global'
   1918         width, height = self._GetBackdropSpriteSizes()
   1919         return Sprite(name='charCreationBackground_%s' % spriteID, parent=self, texturePath=path, align=uiconst.CENTER, width=width, height=height, opacity=BG_OPACITY)
   1920 
   1921     def UpdateBackdropSprite(self):
   1922         spritePath = self._GetCurrentSpritePath()
   1923         if spritePath is None:
   1924             if self.backgroundSprite and not self.backgroundSprite.destroyed:
   1925                 self.backgroundSprite.Close()
   1926             return
   1927         if self.backgroundSprite and not self.backgroundSprite.destroyed:
   1928             if spritePath == self.backgroundSprite.texturePath:
   1929                 width, height = self._GetBackdropSpriteSizes()
   1930                 self.backgroundSprite.width = width
   1931                 self.backgroundSprite.height = height
   1932                 return
   1933             oldSprite = self.backgroundSprite
   1934             oldSpriteOrder = oldSprite.GetOrder()
   1935             self.backgroundSprite = self._CreateNewBackdropSprite(spritePath)
   1936             self.backgroundSprite.SetOrder(oldSpriteOrder)
   1937             uicore.animations.SpMaskIn(self.backgroundSprite, duration=BACKGROUND_TRANSITION_SECONDS, callback=oldSprite.Close)
   1938         elif self.stepID in ccConst.EMPIRE_SELECTION_STEPS:
   1939             self.backgroundSprite = self._CreateNewBackdropSprite(spritePath)
   1940 
   1941     @telemetry.ZONE_METHOD
   1942     def UpdateBackdrop(self, *args):
   1943         self.UpdateBackdropSprite()
   1944         bdScene = self.sceneManager.Get2DBackdropScene()
   1945         if not bdScene:
   1946             return
   1947         bdScene.clearBackground = True
   1948         for each in bdScene.children[:]:
   1949             bdScene.children.remove(each)
   1950 
   1951         for each in bdScene.curveSets[:]:
   1952             bdScene.curveSets.remove(each)
   1953 
   1954         self.colorCodedBackDrop = None
   1955         desktopWidth, desktopHeight = self._GetDpiScaledWidthAndHeight()
   1956         size = min(desktopHeight, desktopWidth)
   1957         margin = -200
   1958         info = self.GetInfo()
   1959         if gfxsettings.Get(gfxsettings.UI_NCC_GREEN_SCREEN):
   1960             backdropSprite = self._AssembleBackDropSprite(u'greenscreen', 'res:/UI/Texture/CharacterCreation/backdrops/Background_1001_thumb.dds')
   1961             bdScene.children.append(backdropSprite)
   1962         else:
   1963             if self.stepID in ccConst.EMPIRE_SELECTION_STEPS:
   1964                 return
   1965             if info.raceID == const.raceCaldari:
   1966                 rn = 'caldari'
   1967                 bgcolor = (74 / 255.0, 87 / 255.0, 97 / 255.0)
   1968             elif info.raceID == const.raceAmarr:
   1969                 rn = 'amarr'
   1970                 bgcolor = (93 / 255.0, 89 / 255.0, 74 / 255.0)
   1971             elif info.raceID == const.raceMinmatar:
   1972                 rn = 'minmatar'
   1973                 bgcolor = (92 / 255.0, 81 / 255.0, 80 / 255.0)
   1974             elif info.raceID == const.raceGallente:
   1975                 rn = 'gallente'
   1976                 bgcolor = (77 / 255.0, 94 / 255.0, 93 / 255.0)
   1977             else:
   1978                 rn = 'caldari'
   1979                 bgcolor = (74 / 255.0, 87 / 255.0, 97 / 255.0)
   1980                 log.LogWarn('Unknown raceID in characterCreationContainer.UpdateBackground', info.raceID)
   1981             mainHalo = trinity.Tr2Sprite2d()
   1982             mainHalo.name = u'mainHalo'
   1983             mainHalo.texturePrimary = trinity.Tr2Sprite2dTexture()
   1984             mainHalo.blendMode = trinity.TR2_SBM_ADD
   1985             r, g, b = bgcolor
   1986             mainHalo.color = (r * 0.75,
   1987              g * 0.75,
   1988              b * 0.75,
   1989              1.0)
   1990             mainHalo.displayWidth = mainHalo.displayHeight = max(desktopWidth, desktopHeight) * 1.5
   1991             mainHalo.displayX = (desktopWidth - mainHalo.displayWidth) / 2
   1992             mainHalo.displayY = (desktopHeight - mainHalo.displayHeight) / 2
   1993             mainHalo.display = True
   1994             mainHalo.texturePrimary.resPath = 'res:/UI/Texture/CharacterCreation/mainCenterHalo.dds'
   1995             bdScene.children.append(mainHalo)
   1996             if self.stepID == ccConst.PORTRAITSTEP:
   1997                 activeBackdrop = self.GetBackdrop()
   1998                 if activeBackdrop:
   1999                     portraitBackground = trinity.Tr2Sprite2d()
   2000                     portraitBackground.name = u'portraitBackground'
   2001                     bdScene.children.insert(0, portraitBackground)
   2002                     portraitBackground.displayX = (desktopWidth - size) * 0.5
   2003                     portraitBackground.displayY = (desktopHeight - size) * 0.5
   2004                     portraitBackground.displayWidth = size
   2005                     portraitBackground.displayHeight = size
   2006                     if not portraitBackground.texturePrimary:
   2007                         portraitBackground.texturePrimary = trinity.Tr2Sprite2dTexture()
   2008                         portraitBackground.texturePrimary.resPath = activeBackdrop
   2009                     portraitBackground.color = (1, 1, 1, 1)
   2010                     portraitBackground.display = True
   2011                     portraitBackground.blendMode = trinity.TR2_SBM_BLEND
   2012             else:
   2013                 if gfxsettings.Get(gfxsettings.UI_NCC_GREEN_SCREEN):
   2014                     return
   2015                 mainSize = size - margin
   2016                 cs = trinity.TriCurveSet()
   2017                 cs.name = 'RotationCurveSet'
   2018                 bdScene.curveSets.append(cs)
   2019                 for textureNo, textureSize in ((1, 468 / 1024.0),
   2020                  (2, 580 / 1024.0),
   2021                  (3, 1.0),
   2022                  (4, 1.0)):
   2023                     tf = trinity.Tr2Sprite2dTransform()
   2024                     tf.name = u'tf'
   2025                     tf.displayX = desktopWidth * 0.5
   2026                     tf.displayY = desktopHeight * 0.5
   2027                     bdScene.children.append(tf)
   2028                     circleBG = trinity.Tr2Sprite2d()
   2029                     circleBG.name = u'circleBG'
   2030                     circleBG.texturePrimary = trinity.Tr2Sprite2dTexture()
   2031                     circleBG.texturePrimary.resPath = 'res:/UI/Texture/CharacterCreation/circularRaceBgs/%s_%s.dds' % (rn, textureNo)
   2032                     circleBG.color = (0.025, 0.025, 0.025, 1.0)
   2033                     circleBG.blendMode = trinity.TR2_SBM_BLEND
   2034                     circleBG.displayWidth = mainSize * textureSize
   2035                     circleBG.displayHeight = mainSize * textureSize
   2036                     circleBG.displayX = -circleBG.displayWidth * 0.5
   2037                     circleBG.displayY = -circleBG.displayHeight * 0.5
   2038                     circleBG.display = True
   2039                     tf.children.append(circleBG)
   2040                     rotationCurve = self.CreatePerlinCurve(cs, scale=16.0, offset=10.0, speed=0.001, alpha=0.9, beta=1.0 + random.random())
   2041                     self.CreateBinding(cs, rotationCurve, tf, 'rotation', 'value')
   2042 
   2043                 cs.Play()
   2044 
   2045     @telemetry.ZONE_METHOD
   2046     def CreateBinding(self, curveSet, curve, destinationObject, attribute, sourceAttribute = 'currentValue'):
   2047         binding = trinity.TriValueBinding()
   2048         curveSet.bindings.append(binding)
   2049         binding.destinationObject = destinationObject
   2050         binding.destinationAttribute = attribute
   2051         binding.sourceObject = curve
   2052         binding.sourceAttribute = sourceAttribute
   2053         return binding
   2054 
   2055     @telemetry.ZONE_METHOD
   2056     def CreatePerlinCurve(self, curveSet, scale = 1.0, offset = 6.0, speed = 1.1, alpha = 1.0, beta = 1.1):
   2057         curve = trinity.TriPerlinCurve()
   2058         curve.scale = scale
   2059         curve.offset = offset
   2060         curve.speed = speed
   2061         curve.alpha = alpha
   2062         curve.beta = beta
   2063         curveSet.curves.append(curve)
   2064         return curve
   2065 
   2066     @telemetry.ZONE_METHOD
   2067     def GetLightID(self):
   2068         path = self.lightingID
   2069         files = ccConst.LIGHT_SETTINGS_ID
   2070         for i, light in enumerate(files):
   2071             if path == light:
   2072                 return i
   2073 
   2074         return 1
   2075 
   2076     @telemetry.ZONE_METHOD
   2077     def GetLightColorID(self):
   2078         path = self.lightColorID
   2079         files = ccConst.LIGHT_COLOR_SETTINGS_ID
   2080         for i, light in enumerate(files):
   2081             if path == light:
   2082                 return i
   2083 
   2084         return 1
   2085 
   2086     @telemetry.ZONE_METHOD
   2087     def GetCurrentBackgroundID(self):
   2088         path = self.backdropPath
   2089         return self.GetBackgroundID(path)
   2090 
   2091     def GetBackgroundID(self, path):
   2092         authoredBackground = self.characterSvc.GetPortraitResourceByPath(path)
   2093         if authoredBackground:
   2094             return authoredBackground.portraitResourceID
   2095         ID = path.split('_')[-1].split('.')[0]
   2096         try:
   2097             ID = int(ID)
   2098         except ValueError:
   2099             return None
   2100 
   2101         return ID
   2102 
   2103     @telemetry.ZONE_METHOD
   2104     def GetBackgroundPathFromID(self, bgID):
   2105         if bgID < const.NCC_MIN_NORMAL_BACKGROUND_ID:
   2106             return 'res:/UI/Texture/CharacterCreation/backdrops/Background_' + str(bgID) + '.dds'
   2107         resourceRow = cfg.paperdollPortraitResources.Get(bgID)
   2108         if resourceRow:
   2109             return resourceRow.resPath
   2110 
   2111     @telemetry.ZONE_METHOD
   2112     def CapturePortrait(self, portraitID, *args):
   2113         if self.camera is None:
   2114             return
   2115         poseData = self.characterSvc.GetPoseData()
   2116         if poseData is None:
   2117             return
   2118         self.portraitInfo[portraitID] = utillib.KeyVal(cameraPosition=self.camera.GetPosition(), cameraFieldOfView=self.camera.fieldOfView, cameraPoi=self.camera.GetPointOfInterest(), backgroundID=self.GetCurrentBackgroundID(), lightID=self.lightingID, lightColorID=self.lightColorID, lightIntensity=self.GetLightIntensity(), poseData=poseData)
   2119         maker = PortraitMaker(self.camera, self.backdropPath)
   2120         return maker.GetPortraitTexture(portraitID)
   2121 
   2122     @telemetry.ZONE_METHOD
   2123     def GetPortraitSnapshotPath(self, portraitID):
   2124         return blue.paths.ResolvePathForWriting(u'cache:/Pictures/Portraits/PortraitSnapshot_%s_%s.jpg' % (portraitID, session.userid))
   2125 
   2126     @telemetry.ZONE_METHOD
   2127     def ClearPortraitInfo(self):
   2128         self.portraitInfo = [None] * ccConst.NUM_PORTRAITS
   2129 
   2130     @telemetry.ZONE_METHOD
   2131     def GetPortraitInfo(self, portraitID):
   2132         return self.portraitInfo[portraitID]
   2133 
   2134     @telemetry.ZONE_METHOD
   2135     def GetDNA(self, getHiddenModifiers = False, getWeightless = False):
   2136         return self.doll.GetDNA(getHiddenModifiers=getHiddenModifiers, getWeightless=getWeightless)
   2137 
   2138     @telemetry.ZONE_METHOD
   2139     def OnHideUI(self, *args):
   2140         self.uiContainer.state = uiconst.UI_HIDDEN
   2141 
   2142     @telemetry.ZONE_METHOD
   2143     def OnShowUI(self, *args):
   2144         self.uiContainer.state = uiconst.UI_PICKCHILDREN
   2145 
   2146     def OnMapShortcut(self, *args):
   2147         uicore.cmd.commandMap.UnloadAllAccelerators()
   2148         uicore.cmd.commandMap.LoadAcceleratorsByCategory('general')
   2149 
   2150     @telemetry.ZONE_METHOD
   2151     def ShowLoading(self, why = '', top = 200, forceOn = 0, *args):
   2152         wheel = self.loadingWheel
   2153         wheel.top = top
   2154         wheel.hint = why
   2155         wheel.forcedOn = forceOn
   2156         wheel.Show()
   2157 
   2158     @telemetry.ZONE_METHOD
   2159     def HideLoading(self, why = '', forceOff = 0, *args):
   2160         wheel = self.loadingWheel
   2161         if not wheel.forcedOn or forceOff:
   2162             self.loadingWheel.Hide()
   2163             wheel.forcedOn = 0
   2164 
   2165     @telemetry.ZONE_METHOD
   2166     def OnCloseView(self):
   2167         uicore.cmd.LoadAllAccelerators()
   2168         self.TearDown()
   2169         self.audioService.SendUIEvent('ui_icc_sculpting_mouse_over_loop_stop')
   2170         if self.audioService.GetWorldVolume() == 0.0:
   2171             self.audioService.SetWorldVolume(self.worldLevel)
   2172         sm.GetService('cc').ClearMyAvailabelTypeIDs()
   2173         self.Flush()
   2174         self.step = None
   2175         if self.previewCharWindowWasOpenOn is not None:
   2176             charID = self.previewCharWindowWasOpenOn[0]
   2177             self.previewCharWindowWasOpenOn = None
   2178             sm.GetService('preview').PreviewCharacter(charID)
   2179         if self.previewWindowWasOpenOn is not None:
   2180             typeID = self.previewWindowWasOpenOn
   2181             sm.GetService('preview').PreviewType(typeID)
   2182 
   2183     @telemetry.ZONE_METHOD
   2184     def ExitToStation(self, updateDoll = True):
   2185         self.OnCloseView()
   2186         if session.structureid:
   2187             change = {'structureid': (None, session.structureid)}
   2188         else:
   2189             change = {'stationid': (None, session.stationid)}
   2190         sm.GetService('gameui').OnSessionChanged(isRemote=False, session=session, change=change)
   2191 
   2192     def ToggleClothes(self, forcedValue = None, doUpdate = True, *args):
   2193         valueBefore = self.clothesOff
   2194         if forcedValue is None:
   2195             self.clothesOff = not self.clothesOff
   2196         else:
   2197             self.clothesOff = forcedValue
   2198         if valueBefore == self.clothesOff:
   2199             return
   2200         info = self.GetInfo()
   2201         if info.charID in self.characterSvc.characters:
   2202             character = self.characterSvc.GetSingleCharacter(info.charID)
   2203             if self.clothesOff:
   2204                 self.RemoveClothes(character, doUpdate=doUpdate)
   2205             else:
   2206                 self.ReApplyClothes(character, doUpdate=doUpdate)
   2207 
   2208     @telemetry.ZONE_METHOD
   2209     def ReApplyClothes(self, character, doUpdate = True):
   2210         if not self.clothesStorage or character is None:
   2211             return
   2212         doll = character.doll
   2213         bdm = doll.buildDataManager
   2214         for category in self.clothesMetaStorage:
   2215             self.characterSvc.SetCharacterMetadataByCategory(self.charID, category, self.clothesMetaStorage[category])
   2216 
   2217         modifiers = doll.SortModifiersForBatchAdding(self.clothesStorage.values())
   2218         for modifier in modifiers:
   2219             bdm.AddModifier(modifier)
   2220 
   2221         self.ResetClothesStorage()
   2222         if doUpdate:
   2223             sm.GetService('character').UpdateDollsAvatar(character)
   2224 
   2225     @telemetry.ZONE_METHOD
   2226     def RemoveClothes(self, character, doUpdate = True):
   2227         if self.clothesStorage or character is None:
   2228             return
   2229         categoriesToRemove = BODY_CATEGORIES - (BODY_CATEGORIES.SKIN,
   2230          BODY_CATEGORIES.TATTOO,
   2231          BODY_CATEGORIES.TOPUNDERWEAR,
   2232          BODY_CATEGORIES.BOTTOMUNDERWEAR,
   2233          BODY_CATEGORIES.SKINTONE,
   2234          BODY_CATEGORIES.SKINTYPE,
   2235          BODY_CATEGORIES.SCARS)
   2236         categoriesToRemove = list(categoriesToRemove)
   2237         categoriesToRemove.sort(key=lambda x: -DESIRED_ORDER.index(x))
   2238         self.ResetClothesStorage()
   2239         bdm = character.doll.buildDataManager
   2240         for category in categoriesToRemove:
   2241             categoryModifiers = bdm.GetModifiersByCategory(category)
   2242             self.StoreCharacterMetadata(category)
   2243             self.characterSvc.RemoveFromCharacterMetadata(self.charID, category)
   2244             for modifier in categoryModifiers:
   2245                 if modifier.respath not in DEFAULT_NUDE_PARTS:
   2246                     self.clothesStorage[category] = modifier
   2247                     bdm.RemoveModifier(modifier)
   2248 
   2249         modifier = self.characterSvc.GetModifierByCategory(self.charID, ccConst.glasses)
   2250         if modifier:
   2251             self.StoreCharacterMetadata(ccConst.glasses)
   2252             self.characterSvc.RemoveFromCharacterMetadata(self.charID, ccConst.glasses)
   2253             self.clothesStorage[ccConst.glasses] = modifier
   2254             bdm.RemoveModifier(modifier)
   2255         modifier = self.characterSvc.GetModifierByCategory(self.charID, ccConst.mask)
   2256         if modifier:
   2257             self.StoreCharacterMetadata(ccConst.mask)
   2258             self.characterSvc.RemoveFromCharacterMetadata(self.charID, ccConst.mask)
   2259             self.clothesStorage[ccConst.mask] = modifier
   2260             bdm.RemoveModifier(modifier)
   2261         if doUpdate:
   2262             sm.GetService('character').UpdateDollsAvatar(character)
   2263 
   2264     def StoreCharacterMetadata(self, category):
   2265         self.clothesMetaStorage[category] = self.characterSvc.GetCharacterMetadataByCategory(self.charID, category)
   2266 
   2267     def ResetClothesStorage(self, *args):
   2268         self.clothesStorage.clear()
   2269         self.clothesMetaStorage.clear()
   2270 
   2271     def RemoveBodyModifications(self, *args):
   2272         try:
   2273             if getattr(self, 'bodyModRemoved', 0):
   2274                 return
   2275             modifiersToRemove = [ccConst.p_earslow,
   2276              ccConst.p_earshigh,
   2277              ccConst.p_nose,
   2278              ccConst.p_nostril,
   2279              ccConst.p_brow,
   2280              ccConst.p_lips,
   2281              ccConst.p_chin,
   2282              ccConst.t_head,
   2283              ccConst.s_head]
   2284             character = self.characterSvc.GetSingleCharacter(self.charID)
   2285             for mod in modifiersToRemove:
   2286                 modifiers = self.characterSvc.GetModifiersByCategory(self.charID, mod)
   2287                 for m in modifiers:
   2288                     character.doll.buildDataManager.RemoveModifier(m)
   2289                     self.characterSvc.RemoveFromCharacterMetadata(self.charID, mod)
   2290 
   2291             self.bodyModRemoved = 1
   2292             sm.GetService('character').UpdateDollsAvatar(character)
   2293         except Exception:
   2294             pass
   2295 
   2296     @telemetry.ZONE_METHOD
   2297     def StartDnaLogging(self):
   2298         self.dnaList = []
   2299         self.lastLitHistoryBit = None
   2300 
   2301     def ResetDna(self, *args):
   2302         self.dnaList = None
   2303         self.lastLitHistoryBit = None
   2304 
   2305     @telemetry.ZONE_METHOD
   2306     def ClearDnaLogFromIndex(self, fromIndex):
   2307         if self.dnaList:
   2308             to = fromIndex + 1
   2309             if to > len(self.dnaList):
   2310                 to = len(self.dnaList)
   2311             self.dnaList = self.dnaList[:to]
   2312 
   2313     @telemetry.ZONE_METHOD
   2314     def GetDollDNAHistory(self):
   2315         return self.dnaList
   2316 
   2317     def TryStoreDna(self, lastUpdateRedundant, fromWhere, sculpting = 0, force = 0, allowReduntant = 0, *args):
   2318         if not lastUpdateRedundant or fromWhere in ('RandomizeCharacterGroups', 'RandomizeCharacter', 'AddCharacter'):
   2319             if not self.parent.isopen:
   2320                 return
   2321             if self.stepID == ccConst.CUSTOMIZATIONSTEP:
   2322                 if self.step is None:
   2323                     return
   2324                 if not force and self.step.menuMode == self.step.TATTOOMENU:
   2325                     self.step.tattooChangeMade = 1
   2326                     return
   2327             if self.dnaList is not None:
   2328                 self.CheckDnaLog('UpdateDoll')
   2329                 dna = self.GetDNA(getHiddenModifiers=False, getWeightless=True)
   2330                 if not allowReduntant:
   2331                     try:
   2332                         currentIndex, maxIndex = self.step.sr.historySlider.GetCurrentIndexAndMaxIndex()
   2333                         if dna == self.dnaList[currentIndex][0]:
   2334                             return
   2335                     except Exception:
   2336                         pass
   2337 
   2338                 currMetadata = copy.deepcopy(self.characterSvc.characterMetadata[self.charID])
   2339                 self.dnaList.append((dna, currMetadata))
   2340                 if lastUpdateRedundant or force:
   2341                     sm.ScatterEvent('OnHistoryUpdated')
   2342 
   2343     @telemetry.ZONE_METHOD
   2344     def LoadDnaFromHistory(self, historyIndex):
   2345         if len(self.characterSvc.characters) > 0:
   2346             character = self.characterSvc.GetSingleCharacter(self.charID)
   2347             if character:
   2348                 historyIndex = max(0, min(len(self.dnaList) - 1, historyIndex))
   2349                 dna, metadata = self.dnaList[historyIndex]
   2350                 metadata = copy.deepcopy(metadata)
   2351                 self.ToggleClothes(forcedValue=0, doUpdate=False)
   2352                 self.characterSvc.MatchDNA(character, dna)
   2353                 self.characterSvc.characterMetadata[self.charID] = metadata
   2354                 if self.characterSvc.GetSculptingActive():
   2355                     sculpting = self.characterSvc.GetSculpting()
   2356                     sculpting.UpdateFieldsBasedOnExistingValues(character.doll)
   2357                 self.characterSvc.UpdateDoll(self.charID, fromWhere='LoadDnaFromHistory', registerDna=False)
   2358                 self.characterSvc.SynchronizeHairColors(self.charID)
   2359 
   2360     @telemetry.ZONE_METHOD
   2361     def PassMouseEventToSculpt(self, type, x, y):
   2362         if not hasattr(self, 'characterSvc'):
   2363             return
   2364         if getattr(self, 'doll', None) is None:
   2365             return
   2366         if self.doll.IsBusyUpdating():
   2367             return
   2368         sculpting = self.characterSvc.GetSculpting()
   2369         if not uicore.uilib.leftbtn and sculpting and sculpting.startXY is not None and self.characterSvc.GetSculptingActive():
   2370             type = 'LeftUp'
   2371         pickValue = None
   2372         if sculpting and self.characterSvc.GetSculptingActive():
   2373             if type == 'LeftDown':
   2374                 pickValue = sculpting.PickWrapper(x, y)
   2375             elif type == 'LeftUp':
   2376                 pickValue = sculpting.EndMotion(x, y)
   2377             elif type == 'Motion':
   2378                 pickValue = sculpting.MotionWrapper(x, y)
   2379         return pickValue
   2380 
   2381     def PickPortrait(self, newPortraitID):
   2382         if self.stepID != ccConst.PORTRAITSTEP:
   2383             return
   2384         self.step.PickPortrait(newPortraitID)
   2385 
   2386     def AnimateToStoredPortrait(self, newPortraitID):
   2387         if self.stepID != ccConst.PORTRAITSTEP:
   2388             return
   2389         if self.portraitInfo[newPortraitID] is None:
   2390             return
   2391         if self.animateToStoredPortraitThread and self.animateToStoredPortraitThread.alive:
   2392             if self.animatingToPortraitID == newPortraitID:
   2393                 return
   2394             self.animateToStoredPortraitThread.kill()
   2395         self.animateToStoredPortraitThread = uthread.new(self.AnimateToStoredPortrait_thread, newPortraitID)
   2396 
   2397     def AnimateToStoredPortrait_thread(self, newPortraitID):
   2398         portraitInfo = self.portraitInfo[newPortraitID]
   2399         if portraitInfo is None:
   2400             return
   2401         newParams = self.GetControlParametersFromPortraitID(newPortraitID)
   2402         if newParams is None:
   2403             return
   2404         oldParams = self.GetControlParametersFromPortraitID(None)
   2405         if len(oldParams) < 1 or len(newParams) < 1:
   2406             return
   2407         self.animatingToPortraitID = newPortraitID
   2408         thereIsCamera = self.camera is not None
   2409         if thereIsCamera:
   2410             oldCameraPos = self.camera.cameraPosition
   2411             oldCameraPoi = self.camera.poi
   2412             oldCameraFov = self.camera.fieldOfView
   2413         info = self.GetInfo()
   2414         start, ndt = blue.os.GetWallclockTime(), 0.0
   2415         moveCamera = self.ShouldMoveCamera(portraitInfo.cameraPosition, portraitInfo.cameraPoi)
   2416         while ndt != 1.0:
   2417             timeValue = min(blue.os.TimeDiffInMs(start, blue.os.GetWallclockTime()) / 250.0, 1.0)
   2418             ndt = math.sin(timeValue * math.pi - math.pi / 2.0) / 2.0 + 0.5
   2419             params = []
   2420             for shortKey, keyAndValue in oldParams.iteritems():
   2421                 longKey, value = keyAndValue
   2422                 if shortKey == 'HeadLookTarget':
   2423                     lerpedValue = geo2.Lerp(value, newParams[shortKey][1], ndt)
   2424                 elif shortKey == 'PortraitPoseNumber':
   2425                     continue
   2426                 else:
   2427                     lerpedValue = mathext.lerp(value, newParams[shortKey][1], ndt)
   2428                 params.append([longKey, lerpedValue])
   2429 
   2430             sm.GetService('character').SetControlParametersFromList(params, info.charID)
   2431             if thereIsCamera and moveCamera:
   2432                 posValue = geo2.Lerp(oldCameraPos, portraitInfo.cameraPosition, ndt)
   2433                 poiValue = geo2.Lerp(oldCameraPoi, portraitInfo.cameraPoi, ndt)
   2434                 self.cameraPos = posValue
   2435                 self.cameraPoi = poiValue
   2436                 self.camera.PlacePortraitCamera(self.cameraPos, self.cameraPoi)
   2437             blue.pyos.synchro.Yield()
   2438 
   2439         xFactor, yFactor = self.camera.GetCorrectCameraXandYFactors(portraitInfo.cameraPosition, portraitInfo.cameraPoi)
   2440         self.camera.xFactor = self.camera.xTarget = xFactor
   2441         self.camera.yFactor = self.camera.yTarget = yFactor
   2442         self.lightingID = portraitInfo.lightID
   2443         self.lightIntensity = portraitInfo.lightIntensity
   2444         self.lightColorID = portraitInfo.lightColorID
   2445         shouldSnapPortrait = False
   2446         path = self.GetBackgroundPathFromID(portraitInfo.backgroundID)
   2447         if path in self.GetAvailableBackgroundsPaths():
   2448             self.backdropPath = path
   2449         elif not gfxsettings.Get(gfxsettings.UI_NCC_GREEN_SCREEN):
   2450             shouldSnapPortrait = True
   2451         self.poseID = int(portraitInfo.poseData['PortraitPoseNumber'])
   2452         sm.GetService('character').SetControlParametersFromList([['ControlParameters|PortraitPoseNumber', float(self.poseID)]], info.charID)
   2453         uicore.layer.charactercreation.controller.UpdateBackdrop()
   2454         self.UpdateLights()
   2455         sm.ScatterEvent('OnPortraitPicked')
   2456         if shouldSnapPortrait and self.step:
   2457             self.step.CapturePortrait(newPortraitID)
   2458 
   2459     def ShouldMoveCamera(self, newPos, newPoi):
   2460         newDirection = geo2.Subtract(newPos, newPoi)
   2461         distanceDiff = abs(self.camera.distance - geo2.Vec3Length(newDirection))
   2462         direction2 = geo2.Vec3Normalize(newDirection)
   2463         yaw = math.acos(direction2[0])
   2464         yawDiff = abs(self.camera.yaw - yaw)
   2465         pitch = math.asin(direction2[1]) + math.pi / 2.0
   2466         pitchDiff = math.sqrt(math.pow(self.camera.pitch - pitch, 2))
   2467         diffPos = geo2.Vec3Distance(self.camera.GetPosition(), newPos)
   2468         if distanceDiff < 5e-07 and yawDiff < 5e-05 and pitchDiff < 5e-05 and diffPos < 0.05:
   2469             return False
   2470         return True
   2471 
   2472     def GetControlParametersFromPortraitID(self, portraitID, *args):
   2473         PREFIX = 'ControlParameters|'
   2474         params = {}
   2475         if portraitID is not None:
   2476             portraitInfo = uicore.layer.charactercreation.controller.portraitInfo[portraitID]
   2477             if portraitInfo is None:
   2478                 return {}
   2479             return self.GetControlParametersFromPoseData(portraitInfo.poseData)
   2480         info = self.GetInfo()
   2481         avatar = self.characterSvc.GetSingleCharactersAvatar(info.charID)
   2482         if avatar is None:
   2483             return {}
   2484         for controlParameter in paperDollUtil.FACIAL_POSE_PARAMETERS.__dict__.iterkeys():
   2485             if controlParameter.startswith('_'):
   2486                 continue
   2487             longKey = PREFIX + controlParameter
   2488             network = animparams.GetParamsPerAvatar(avatar, info.charID)
   2489             value = network.GetControlParameterValue(longKey)
   2490             params[controlParameter] = (longKey, value)
   2491 
   2492         return params
   2493 
   2494     def GetControlParametersFromPoseData(self, poseData, fromDB = False):
   2495         if poseData is None:
   2496             return {}
   2497         if fromDB:
   2498             allParameterKeys = poseData.__keys__
   2499         else:
   2500             allParameterKeys = poseData.keys()
   2501         params = {}
   2502         PREFIX = 'ControlParameters|'
   2503         for key in allParameterKeys:
   2504             if key in ('headLookTargetX', 'headLookTargetY', 'headLookTargetZ', 'cameraX', 'cameraY', 'cameraZ'):
   2505                 continue
   2506             value = poseData[key]
   2507             if fromDB:
   2508                 key = key.replace(key[0], key[0].upper(), 1)
   2509             params[key] = (PREFIX + key, value)
   2510 
   2511         if fromDB:
   2512             params['HeadLookTarget'] = (PREFIX + 'HeadLookTarget', (poseData['headLookTargetX'], poseData['headLookTargetY'], poseData['headLookTargetZ']))
   2513         return params
   2514 
   2515     def TryFreezeAnimation(self, *args):
   2516         if sm.GetService('machoNet').GetGlobalConfig().get('disableFreezeAnimationInNCC'):
   2517             return
   2518         if self.stepID != ccConst.CUSTOMIZATIONSTEP:
   2519             return
   2520         info = self.GetInfo()
   2521         avatar = self.characterSvc.GetSingleCharactersAvatar(info.charID)
   2522         if avatar is not None:
   2523             network = animparams.GetParamsPerAvatar(avatar, info.charID)
   2524             network.update = False
   2525             self.freezingAnimation = True
   2526 
   2527     def UnfreezeAnimationIfNeeded(self, *args):
   2528         if self.freezingAnimation:
   2529             info = self.GetInfo()
   2530             avatar = self.characterSvc.GetSingleCharactersAvatar(info.charID)
   2531             if avatar is not None:
   2532                 network = animparams.GetParamsPerAvatar(avatar, info.charID)
   2533                 network.update = True
   2534                 self.freezingAnimation = False