attributes.py (19448B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\shared\neocom\attributes.py 2 import blue 3 from carbon.common.script.util.format import FmtDate 4 from carbonui.control.scrollentries import SE_BaseClassCore 5 from carbonui.primitives.containerAutoSize import ContainerAutoSize 6 from characterskills.attribute import ATTRIBUTEBONUS_BY_ATTRIBUTEID 7 from carbonui.primitives.container import Container 8 from carbonui.primitives.layoutGrid import LayoutGrid 9 from carbonui.primitives.line import Line 10 from dogma.effects import IsBoosterSkillAccelerator 11 from eve.client.script.ui.control import eveIcon, eveLabel 12 from carbonui.button.group import ButtonGroup 13 from carbonui.control.button import Button 14 from eve.client.script.ui.control.clickableboxbar import ClickableBoxBar 15 from eve.client.script.ui.control.eveLabel import EveLabelMedium 16 from carbonui.control.window import Window 17 from eve.client.script.ui.control.infoIcon import MoreInfoIcon 18 from eve.client.script.ui.control.themeColored import SpriteThemeColored 19 from eve.common.lib import appConst as const 20 from eveexceptions import UserError 21 from fsdBuiltData.common.iconIDs import GetIconFile 22 import uthread 23 import carbonui.const as uiconst 24 import localization 25 import evetypes 26 from player_messaging.client.ui_message import prompt_player_fsd_dialog 27 28 class AttributeRespecWindow(Window): 29 __guid__ = 'form.attributeRespecWindow' 30 default_windowID = 'attributerespecification' 31 default_iconNum = 'res:/ui/Texture/WindowIcons/attributes.png' 32 default_minSize = (450, 450) 33 default_captionLabelPath = 'UI/CharacterSheet/CharacterSheetWindow/Attributes/NeuralRemapping' 34 35 def ApplyAttributes(self, attributes): 36 super(AttributeRespecWindow, self).ApplyAttributes(attributes) 37 self.readOnly = attributes.readOnly 38 self.MakeUnResizeable() 39 self.godma = sm.StartService('godma') 40 self.skillHandler = sm.StartService('skills').GetSkillHandler() 41 self.main_cont = ContainerAutoSize(parent=self.content, align=uiconst.TOTOP, callback=self._on_main_cont_size_changed, only_use_callback_when_size_changes=True) 42 self.attributes = [const.attributePerception, 43 const.attributeMemory, 44 const.attributeWillpower, 45 const.attributeIntelligence, 46 const.attributeCharisma] 47 self.implantTypes = [19540, 48 19551, 49 19553, 50 19554, 51 19555] 52 self.attributeIcons = ['ui_22_32_5', 53 'ui_22_32_4', 54 'ui_22_32_2', 55 'ui_22_32_3', 56 'ui_22_32_1'] 57 self.attributeLabels = [localization.GetByLabel('UI/CharacterSheet/CharacterSheetWindow/Attributes/AttributePerception'), 58 localization.GetByLabel('UI/CharacterSheet/CharacterSheetWindow/Attributes/AttributeMemory'), 59 localization.GetByLabel('UI/CharacterSheet/CharacterSheetWindow/Attributes/AttributeWillpower'), 60 localization.GetByLabel('UI/CharacterSheet/CharacterSheetWindow/Attributes/AttributeIntelligence'), 61 localization.GetByLabel('UI/CharacterSheet/CharacterSheetWindow/Attributes/AttributeCharisma')] 62 self.currentAttributes = {} 63 self.implantModifier = {} 64 self.unspentPts = 0 65 self.ConstructLayout() 66 self.Load() 67 68 def Load(self, *args): 69 if not session.charid or self.destroyed: 70 return 71 dogmaLM = sm.GetService('clientDogmaIM').GetDogmaLocation() 72 skillSvc = sm.StartService('skills') 73 dogmaStaticMgr = dogmaLM.dogmaStaticMgr 74 attrDict = skillSvc.GetCharacterAttributes() 75 unspentPts = const.respecTotalRespecPoints 76 for x in xrange(0, 5): 77 attr = self.attributes[x] 78 if attr in attrDict: 79 attrValue = attrDict[attr] 80 implantBonus = 0 81 implants = skillSvc.GetImplants() 82 boosters = skillSvc.GetBoosters() 83 for implantSlot, implant in implants.iteritems(): 84 implantBonus += dogmaStaticMgr.GetTypeAttribute2(implant.typeID, ATTRIBUTEBONUS_BY_ATTRIBUTEID[attr]) 85 86 boosterBonus = 0 87 for boosterTypeID, boosterRecord in boosters.iteritems(): 88 if IsBoosterSkillAccelerator(dogmaStaticMgr, boosterRecord): 89 boosterBonus += dogmaStaticMgr.GetTypeAttribute2(boosterRecord.boosterTypeID, ATTRIBUTEBONUS_BY_ATTRIBUTEID[attr]) 90 91 attrValue -= implantBonus 92 attrValue -= boosterBonus 93 if attrValue > const.respecMaximumAttributeValue: 94 attrValue = const.respecMaximumAttributeValue 95 if attrValue < const.respecMinimumAttributeValue: 96 attrValue = const.respecMinimumAttributeValue 97 self.currentAttributes[attr] = attrValue 98 self.respecBar[x].SetValue(attrValue - const.respecMinimumAttributeValue) 99 unspentPts -= attrValue 100 totalAttributesText = localization.formatters.FormatNumeric(int(self.currentAttributes[attr]) + implantBonus, decimalPlaces=0) 101 self.totalLabels[x].text = totalAttributesText 102 self.implantModifier[x] = implantBonus 103 label, icon = self.implantLabels[x] 104 if implantBonus == 0: 105 icon.SetAlpha(0.5) 106 label.text = localization.GetByLabel('UI/CharacterSheet/CharacterSheetWindow/Attributes/ImplantBonusZero') 107 label.SetAlpha(0.5) 108 else: 109 label.text = localization.GetByLabel('UI/CharacterSheet/CharacterSheetWindow/Attributes/ImplantBonus', implantBonus=int(implantBonus)) 110 111 if not self.readOnly: 112 self.unspentPts = unspentPts 113 self.sr.unassignedBar.SetValue(unspentPts) 114 unspentPtsText = localization.formatters.FormatNumeric(self.unspentPts, decimalPlaces=0) 115 self.availableLabel.text = unspentPtsText 116 if self.unspentPts <= 0: 117 self.sr.saveWarningText.state = uiconst.UI_HIDDEN 118 else: 119 self.sr.saveWarningText.state = uiconst.UI_DISABLED 120 121 def ConstructLayout(self): 122 self.implantLabels = [] 123 self.respecBar = [] 124 self.totalLabels = [] 125 iconsize = 32 126 buttonSize = 24 127 boxWidth = 6 128 boxHeight = 12 129 boxMargin = 1 130 boxSpacing = 1 131 numBoxes = const.respecMaximumAttributeValue - const.respecMinimumAttributeValue 132 barWidth = numBoxes * boxSpacing + 2 * boxMargin + numBoxes * boxWidth - 1 133 barHeight = boxHeight + 2 * boxMargin 134 backgroundColor = (0.0, 0.0, 0.0, 0.0) 135 colorDict = {ClickableBoxBar.COLOR_UNSELECTED: (0.2, 0.2, 0.2, 1.0), 136 ClickableBoxBar.COLOR_SELECTED: (0.2, 0.8, 0.2, 1.0)} 137 MoreInfoIcon(parent=self.header.extra_content, align=uiconst.CENTERLEFT, hint=localization.GetByLabel('UI/CharacterSheet/CharacterSheetWindow/Attributes/CharacterRespecMessage')) 138 if self.readOnly: 139 columns = 7 140 else: 141 columns = 9 142 gridParent = ContainerAutoSize(parent=self.main_cont, align=uiconst.TOTOP) 143 self.mainGrid = LayoutGrid(parent=gridParent, align=uiconst.CENTERTOP, columns=columns, cellPadding=4, OnGridSizeChanged=self._on_main_grid_size_changed) 144 for labelPath, colSpan in (('UI/CharacterSheet/CharacterSheetWindow/NavScroll/Attributes', 2), 145 ('UI/CharacterSheet/CharacterSheetWindow/Attributes/BaseStatPoints', 1), 146 ('UI/CharacterSheet/CharacterSheetWindow/Attributes/CharacterImplants', 2), 147 ('UI/CharacterSheet/CharacterSheetWindow/Attributes/RemappableStat', 1 if self.readOnly else 3), 148 ('UI/CharacterSheet/CharacterSheetWindow/Attributes/StatTotal', 1)): 149 label = EveLabelMedium(text=localization.GetByLabel(labelPath), align=uiconst.CENTER) 150 self.mainGrid.AddCell(cellObject=label, colSpan=colSpan, cellPadding=(10, 2, 10, 2)) 151 152 line = Line(align=uiconst.TOTOP) 153 self.mainGrid.AddCell(cellObject=line, colSpan=self.mainGrid.columns) 154 for x in xrange(5): 155 eveIcon.Icon(parent=self.mainGrid, width=iconsize, height=iconsize, size=iconsize, icon=self.attributeIcons[x], align=uiconst.TOPLEFT) 156 EveLabelMedium(text=self.attributeLabels[x], parent=self.mainGrid, state=uiconst.UI_DISABLED, align=uiconst.CENTERLEFT) 157 minText = localization.formatters.FormatNumeric(const.respecMinimumAttributeValue, decimalPlaces=0) 158 EveLabelMedium(text=minText, parent=self.mainGrid, state=uiconst.UI_DISABLED, align=uiconst.CENTER, bold=True) 159 icon = eveIcon.Icon(parent=self.mainGrid, width=32, height=32, size=32, icon=GetIconFile(evetypes.GetIconID(self.implantTypes[x])), align=uiconst.TOPLEFT, ignoreSize=True) 160 implantLabel = EveLabelMedium(text='0', parent=self.mainGrid, align=uiconst.CENTERLEFT) 161 self.implantLabels.append((implantLabel, icon)) 162 if not self.readOnly: 163 minusText = localization.GetByLabel('UI/Common/Buttons/Minus') 164 Button(parent=self.mainGrid, label=minusText, fixedwidth=buttonSize, func=self.DecreaseAttribute, args=(x,), align=uiconst.CENTERRIGHT) 165 bar = Container(parent=self.mainGrid, align=uiconst.CENTER, width=barWidth, height=barHeight, state=uiconst.UI_PICKCHILDREN) 166 bar = ClickableBoxBar(parent=bar, numBoxes=numBoxes, boxWidth=boxWidth, boxHeight=boxHeight, boxMargin=boxMargin, boxSpacing=boxSpacing, backgroundColor=backgroundColor, colorDict=colorDict) 167 bar.OnValueChanged = self.OnMemberBoxClick 168 bar.OnAttemptBoxClicked = self.ValidateBoxClick 169 self.respecBar.append(bar) 170 if not self.readOnly: 171 plusText = localization.GetByLabel('UI/Common/Buttons/Plus') 172 Button(parent=self.mainGrid, label=plusText, fixedwidth=buttonSize, func=self.IncreaseAttribute, args=(x,), align=uiconst.CENTERLEFT) 173 totalLabel = EveLabelMedium(text='0', parent=self.mainGrid, left=8, align=uiconst.CENTERRIGHT, bold=True) 174 self.totalLabels.append(totalLabel) 175 176 if not self.readOnly: 177 line = Line(align=uiconst.TOTOP) 178 self.mainGrid.AddCell(cellObject=line, colSpan=self.mainGrid.columns) 179 textObj = EveLabelMedium(text=localization.GetByLabel('UI/CharacterSheet/CharacterSheetWindow/Attributes/UnassignedAttributePoints')) 180 self.mainGrid.AddCell(cellObject=textObj, colSpan=6) 181 numBoxes = const.respecTotalRespecPoints - const.respecMinimumAttributeValue * 5 182 barWidth = numBoxes * boxSpacing + 2 * boxMargin + numBoxes * boxWidth - 1 183 unassignedBarParent = Container(align=uiconst.TOPLEFT, width=barWidth, height=barHeight, state=uiconst.UI_PICKCHILDREN) 184 self.mainGrid.AddCell(cellObject=unassignedBarParent, colSpan=2) 185 self.sr.unassignedBar = ClickableBoxBar(parent=unassignedBarParent, numBoxes=numBoxes, boxWidth=boxWidth, boxHeight=boxHeight, boxMargin=boxMargin, boxSpacing=boxSpacing, backgroundColor=backgroundColor, colorDict=colorDict, readonly=True, hintFormat='UI/CharacterSheet/CharacterSheetWindow/Attributes/UnassignedPointsHint') 186 self.availableLabel = EveLabelMedium(parent=self.mainGrid, align=uiconst.CENTERRIGHT, left=8) 187 self.mainGrid.FillRow() 188 self.sr.saveWarningText = EveLabelMedium(text=localization.GetByLabel('UI/CharacterSheet/CharacterSheetWindow/Attributes/CannotSaveUnassignedPoints'), color=(1.0, 0.0, 0.0, 0.9)) 189 self.mainGrid.AddCell(cellObject=self.sr.saveWarningText, colSpan=self.mainGrid.columns) 190 if not self.readOnly: 191 ButtonGroup(btns=[[localization.GetByLabel('UI/CharacterSheet/CharacterSheetWindow/Attributes/SaveStatChanges'), 192 self.SaveChanges, 193 (), 194 None], [localization.GetByLabel('UI/Common/Buttons/Cancel'), 195 self.CloseByUser, 196 (), 197 None]], parent=self.main_cont, align=uiconst.TOTOP) 198 199 def OnMainGridSizeChanged(self, width, height, *args, **kwds): 200 self.SetMinSize([width + 12, height + self.headerText.height + 110], refresh=1) 201 202 def _on_main_cont_size_changed(self): 203 _, height = self.GetWindowSizeForContentSize(height=self.main_cont.height) 204 self.SetMinSize(size=(self.GetMinWidth(), height), refresh=True) 205 206 def _on_main_grid_size_changed(self, width, height): 207 width, _ = self.GetWindowSizeForContentSize(width=width) 208 self.SetMinSize(size=(width, self.GetMinHeight()), refresh=True) 209 210 def SaveChanges(self, *args): 211 totalAttrs = 0 212 newAttributes = {} 213 for x in xrange(0, 5): 214 newAttributes[self.attributes[x]] = const.respecMinimumAttributeValue + self.respecBar[x].GetValue() 215 216 for attrValue in newAttributes.itervalues(): 217 if attrValue < const.respecMinimumAttributeValue: 218 raise UserError('RespecAttributesTooLow') 219 elif attrValue > const.respecMaximumAttributeValue: 220 raise UserError('RespecAttributesTooHigh') 221 totalAttrs += attrValue 222 223 if totalAttrs != const.respecTotalRespecPoints or self.sr.unassignedBar.GetValue() > 0: 224 self.sr.saveWarningText.state = uiconst.UI_DISABLED 225 raise UserError('RespecAttributesMisallocated') 226 allSame = True 227 for attr in self.attributes: 228 if int(self.currentAttributes[attr]) != int(newAttributes[attr]): 229 allSame = False 230 break 231 232 if not allSame: 233 respecInfo = sm.GetService('skills').GetRespecInfo() 234 freeRespecs = respecInfo['freeRespecs'] 235 if respecInfo['nextTimedRespec'] is None or respecInfo['nextTimedRespec'] <= blue.os.GetWallclockTime(): 236 if prompt_player_fsd_dialog('ConfirmRespec2', {'months': int(const.respecTimeInterval / const.MONTH30)}, uiconst.YESNO) != uiconst.ID_YES: 237 return 238 elif freeRespecs > 0: 239 if prompt_player_fsd_dialog('ConfirmRespecFree', {'freerespecs': int(respecInfo['freeRespecs']) - 1}, uiconst.YESNO) != uiconst.ID_YES: 240 return 241 else: 242 raise UserError('RespecTooSoon', {'nextTime': respecInfo['nextTimedRespec']}) 243 self.skillHandler.RespecCharacter(newAttributes) 244 self.CloseByUser() 245 246 def IncreaseAttribute(self, attribute, *args): 247 if self.respecBar[attribute].GetValue() >= const.respecMaximumAttributeValue - const.respecMinimumAttributeValue: 248 return 249 if self.unspentPts <= 0: 250 raise UserError('RespecCannotIncrementNotEnoughPoints') 251 if not self.respecBar[attribute].Increment(): 252 raise UserError('RespecAttributesTooHigh') 253 254 def DecreaseAttribute(self, attribute, *args): 255 if self.respecBar[attribute].GetValue() <= 0: 256 return 257 if not self.respecBar[attribute].Decrement(): 258 raise UserError('RespecAttributesTooLow') 259 260 def ValidateBoxClick(self, oldValue, newValue): 261 if self.readOnly: 262 return False 263 if oldValue >= newValue: 264 return True 265 if self.unspentPts < newValue - oldValue: 266 return False 267 return True 268 269 def OnMemberBoxClick(self, oldValue, newValue): 270 if oldValue is None or oldValue == newValue: 271 return 272 if self.readOnly: 273 return 274 self.unspentPts -= newValue - oldValue 275 self.sr.unassignedBar.SetValue(self.unspentPts) 276 unspentPtsText = localization.formatters.FormatNumeric(self.unspentPts, decimalPlaces=0) 277 self.availableLabel.text = unspentPtsText 278 for x in xrange(0, 5): 279 totalPts = const.respecMinimumAttributeValue + self.respecBar[x].GetValue() + self.implantModifier[x] 280 totalPtsText = localization.formatters.FormatNumeric(int(totalPts), decimalPlaces=0) 281 self.totalLabels[x].text = totalPtsText 282 283 if self.unspentPts <= 0: 284 self.sr.saveWarningText.state = uiconst.UI_HIDDEN 285 286 287 class AttributeRespecEntry(SE_BaseClassCore): 288 __guid__ = 'listentry.AttributeRespec' 289 default_showHilite = False 290 291 def Startup(self, *args): 292 self.OnSelectCallback = None 293 self.sr.label = eveLabel.EveLabelSmall(parent=self, left=8, top=4, text=localization.GetByLabel('UI/Neocom/NextDNAModification'), maxLines=1) 294 self.sr.respecTime = eveLabel.EveLabelMedium(parent=self, left=8, top=18, text='', maxLines=1) 295 self.sr.numberOfRemaps = eveLabel.EveLabelMedium(parent=self, state=uiconst.UI_HIDDEN, left=8, top=38, text='', maxLines=1) 296 self.sr.respecButton = Button(parent=self, label=localization.GetByLabel('UI/CharacterSheet/CharacterSheetWindow/Attributes/RemapStatsNow'), align=uiconst.TOPRIGHT, func=self.OpenRespecWindow, args=(False,)) 297 self.hint = localization.GetByLabel('UI/CharacterSheet/CharacterSheetWindow/Attributes/CharacterSheetHint') 298 299 def Load(self, node): 300 self.sr.node = node 301 freeRespecs = node.Get('freeRespecs', 0) 302 nextRespecTime = node.Get('nextTimedRespec', None) 303 canRemap = False 304 if nextRespecTime is None or nextRespecTime <= blue.os.GetWallclockTime(): 305 self.sr.respecTime.text = localization.GetByLabel('UI/Generic/Now') 306 canRemap = True 307 else: 308 self.sr.respecTime.text = FmtDate(node.nextTimedRespec) 309 self.refreshThread = uthread.new(self.RefreshThread) 310 if freeRespecs > 0: 311 canRemap = True 312 lbl = localization.GetByLabel('UI/CharacterSheet/CharacterSheetWindow/Attributes/BonusRemapsAvailable', remapsAvailable=freeRespecs) 313 self.sr.numberOfRemaps.text = lbl 314 if nextRespecTime is not None and nextRespecTime > blue.os.GetWallclockTime(): 315 self.hint = localization.GetByLabel('UI/CharacterSheet/CharacterSheetWindow/Attributes/CharacterSheetHintFree') 316 else: 317 self.hint = localization.GetByLabel('UI/CharacterSheet/CharacterSheetWindow/Attributes/CharacterSheetHintFreeTimed') 318 self.sr.numberOfRemaps.state = uiconst.UI_DISABLED 319 if not canRemap: 320 self.sr.respecButton.SetLabel(localization.GetByLabel('UI/CharacterSheet/CharacterSheetWindow/Attributes/AttributesOverview')) 321 self.sr.respecButton.args = (True,) 322 323 def OpenRespecWindow(self, readOnly, *args): 324 wnd = AttributeRespecWindow.GetIfOpen() 325 if wnd is not None and not wnd.destroyed: 326 wnd.Maximize() 327 else: 328 AttributeRespecWindow.Open(readOnly=readOnly) 329 330 def RefreshThread(self): 331 if not self or self.destroyed: 332 return 333 sleepMsec = max(self.sr.node.nextTimedRespec - blue.os.GetWallclockTime(), 0) / 10000 334 sleepMsec = min(sleepMsec, 60000) 335 while sleepMsec > 0: 336 blue.pyos.synchro.SleepWallclock(sleepMsec) 337 if not self or self.destroyed: 338 return 339 sleepMsec = max(self.sr.node.nextTimedRespec - blue.os.GetWallclockTime(), 0) / 10000 340 sleepMsec = min(sleepMsec, 60000) 341 342 if not self or self.destroyed: 343 return 344 self.sr.respecButton.state = uiconst.UI_NORMAL 345 self.sr.respecTime.text = localization.GetByLabel('UI/Generic/Now')