craftingWindow.py (55194B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\shared\dynamicItem\craftingWindow.py 2 from __future__ import absolute_import 3 import __builtin__ 4 import math 5 import dogma.data as dogma_data 6 from carbon.client.script.environment.AudioUtil import PlaySound 7 from carbonui import uiconst 8 from carbonui.control.contextMenu.menuData import MenuData 9 from carbonui.control.scrollContainer import ScrollContainer 10 from carbonui.primitives.container import Container 11 from carbonui.primitives.containerAutoSize import ContainerAutoSize 12 from carbonui.primitives.fill import Fill 13 from carbonui.primitives.sprite import Sprite 14 from clientsettings.setting import CharSettingBool 15 from carbonui.uianimations import animations 16 from carbonui.uicore import uicore 17 from carbonui.util.color import Color 18 from carbonui.util.various_unsorted import IsUnder 19 from eve.client.script.ui.control import eveLabel 20 from eve.client.script.ui.control.entries import item as item_entry 21 from eve.client.script.ui.control.entries.space import Space 22 from eve.client.script.ui.control.entries.text import Text 23 from eve.client.script.ui.control.entries.util import GetFromClass 24 from eve.client.script.ui.control.eveIcon import Icon 25 from eve.client.script.ui.control.eveScroll import Scroll 26 from carbonui.control.window import Window 27 from eve.client.script.ui.control.itemIcon import ItemIcon 28 from eve.client.script.ui.control.themeColored import FillThemeColored, SpriteThemeColored 29 from eve.client.script.ui.control.utilMenu import UtilMenu 30 from eve.client.script.ui.shared.dynamicItem.const import COLOR_DECAYED, COLOR_GRAVID, COLOR_NEGATIVE, COLOR_POSITIVE, COLOR_UNSTABLE, GetAbnormalTypes, GetDecayedTypes, GetGravidTypes, GetUnstableTypes 31 from eve.client.script.ui.shared.dynamicItem.info import FormatAttributeBonusRange 32 from eve.client.script.ui.shared.industry.submitButton import PrimaryButton 33 from eve.client.script.ui.shared.industry.views.errorFrame import ErrorFrame 34 from eve.client.script.ui.shared.info.attribute import Attribute, MutatedAttribute 35 from eve.client.script.ui.util import uix 36 from eve.common.lib import appConst as const 37 import blue 38 import dynamicitemattributes as dynamicitems 39 import eveexceptions 40 import evetypes 41 import localization 42 import log 43 import menucheckers 44 import signals 45 import trinity 46 import uthread 47 import utillib 48 from localization import GetByLabel 49 MUTATOR_FAST_ANIMATIONS_SETTING = CharSettingBool('MutatorFastAnimations', False) 50 RES_BAR_FILL = 'res:/UI/Texture/Classes/DynamicItem/arrows.png' 51 RES_ERROR_ICON = 'res:/UI/Texture/Icons/icons111_07.png' 52 RES_INPUT_BACKGROUND = 'res:/UI/Texture/classes/DynamicItem/inputBackground.png' 53 RES_INPUT_FRAME = 'res:/UI/Texture/classes/DynamicItem/inputFrame.png' 54 RES_INPUT_RING = 'res:/UI/Texture/classes/DynamicItem/bgCircle.png' 55 RES_RING_GLOW_MASK = 'res:/UI/Texture/classes/DynamicItem/bgGlowRing.png' 56 RES_SWIRL = 'res:/UI/Texture/classes/DynamicItem/swirl.png' 57 RES_WINDOW_ICON = 'res:/ui/Texture/WindowIcons/mutation.png' 58 59 def Item(typeID, itemID = None, locationID = None, flagID = None, ownerID = None): 60 return utillib.KeyVal(typeID=typeID, itemID=itemID, locationID=locationID, flagID=flagID, ownerID=ownerID) 61 62 63 class CraftingController(object): 64 __notifyevents__ = ['OnItemChange', 'OnSessionChanged'] 65 66 def __init__(self, mutator, source = None): 67 self.onChange = signals.Signal(signalName='onChange') 68 self.onDragEnter = signals.Signal(signalName='onDragEnter') 69 self.onDragExit = signals.Signal(signalName='onDragExit') 70 self.onError = signals.Signal(signalName='onError') 71 self._craftingResult = None 72 self._isCrafting = False 73 self._isResultPresented = False 74 self._mutator = mutator 75 self._mutatorTypes = None 76 self._resultItemID = None 77 self._settings = __builtin__.settings 78 self._source = source 79 sm.RegisterNotify(self) 80 81 @property 82 def isFastAnimations(self): 83 return MUTATOR_FAST_ANIMATIONS_SETTING.is_enabled() 84 85 @isFastAnimations.setter 86 def isFastAnimations(self, isFastAnimations): 87 MUTATOR_FAST_ANIMATIONS_SETTING.set(isFastAnimations) 88 89 @property 90 def isResultPresented(self): 91 return self._isResultPresented 92 93 @property 94 def mutatorItemID(self): 95 return self._mutator.itemID 96 97 @property 98 def mutatorTypeID(self): 99 return self._mutator.typeID 100 101 @property 102 def resultItemID(self): 103 return self._resultItemID 104 105 @property 106 def sourceTypeID(self): 107 if self._source is None: 108 return 109 return self._source.typeID 110 111 @property 112 def sourceItemID(self): 113 if self._source is None: 114 return 115 return self._source.itemID 116 117 @property 118 def resultTypeID(self): 119 if self._source is None: 120 return 121 return dynamicitems.GetResultType(self._mutator.typeID, self._source.typeID) 122 123 def GetAttribute(self, attributeID): 124 if not self.IsSourceItemSelected(): 125 return Attribute(attributeID, None) 126 value = None 127 if self.IsCraftingResultAvailable(): 128 value = self._craftingResult[attributeID] 129 static = sm.GetService('clientDogmaStaticSvc') 130 base = static.GetTypeAttribute(self._source.typeID, attributeID) 131 bonusLow, bonusHigh = self.GetAttributeBonusRange(attributeID) 132 return MutatedAttribute(attribute=Attribute(attributeID, value), sourceValue=base, minValue=base * bonusLow, maxValue=base * bonusHigh, highIsGood=self._GetHighIsGood(attributeID)) 133 134 def _GetHighIsGood(self, attributeID): 135 attributes = dynamicitems.GetMutatorAttributes(self.mutatorTypeID) 136 attribute = attributes[attributeID] 137 return getattr(attribute, 'highIsGood', None) 138 139 def GetApplicableTypes(self): 140 return dynamicitems.GetApplicableTypes(self.mutatorTypeID) 141 142 def GetRelatedMutatorTypes(self): 143 if self._mutatorTypes is None: 144 resultTypes = dynamicitems.GetResultTypes(self.mutatorTypeID) 145 mutatorTypes = dynamicitems.GetMutatorTypesByResultTypes(resultTypes) 146 self._mutatorTypes = mutatorTypes 147 return self._mutatorTypes 148 149 def GetModifiedAttributeIDs(self): 150 return dynamicitems.GetMutatorAttributes(self.mutatorTypeID).keys() 151 152 def GetAttributeBonusRange(self, attributeID): 153 attributes = dynamicitems.GetMutatorAttributes(self.mutatorTypeID) 154 attribute = attributes[attributeID] 155 return (attribute.min, attribute.max) 156 157 def SelectSourceItem(self, item): 158 if self._isCrafting or self.IsCraftingResultAvailable(): 159 return 160 if item is None and self._source is not None: 161 self._source = None 162 self.onChange() 163 return 164 if item.typeID not in self.GetApplicableTypes(): 165 raise RuntimeError('Invalid source item type') 166 if item.itemID is None: 167 item = self._FindItem(item.typeID) 168 if self.sourceItemID is not None and self.sourceItemID == item.itemID: 169 return 170 self._source = item 171 self.onChange() 172 173 def SelectMutator(self, item): 174 if self._isCrafting or self.IsCraftingResultAvailable(): 175 return 176 if item is None: 177 return 178 if item.typeID not in dynamicitems.GetAllMutatorTypes(): 179 raise RuntimeError('Invalid mutator item type') 180 if item.itemID is None: 181 item = self._FindItem(item.typeID) 182 if item.itemID is not None and self._mutator.itemID == item.itemID: 183 return 184 if self.mutatorTypeID == item.typeID and self.mutatorItemID == item.itemID: 185 return 186 if item.typeID not in self.GetRelatedMutatorTypes(): 187 self._source = None 188 self._mutatorTypes = None 189 self._mutator = item 190 if self.IsSourceItemSelected() and not self.IsSourceItemAvailable(): 191 self._source = self._FindItem(self._source.typeID) 192 self.onChange() 193 194 def IsItemAvailable(self, typeID): 195 item = self._FindItem(typeID) 196 return item.itemID is not None 197 198 def _FindItem(self, typeID): 199 checker = menucheckers.ItemChecker(self._mutator) 200 if not checker.IsInPilotLocation(): 201 return Item(typeID=typeID, locationID=self._mutator.locationID, flagID=self._mutator.flagID) 202 invCache = sm.GetService('invCache') 203 if self._mutator.locationID == session.stationid: 204 inventory = invCache.GetInventory(const.containerHangar) 205 else: 206 inventory = invCache.GetInventoryFromId(self._mutator.locationID) 207 items = inventory.List(self._mutator.flagID) 208 for item in items: 209 if item.typeID == typeID: 210 return Item(typeID=typeID, itemID=item.itemID, locationID=self._mutator.locationID, flagID=self._mutator.flagID, ownerID=item.ownerID) 211 212 return Item(typeID=typeID, locationID=self._mutator.locationID, flagID=self._mutator.flagID) 213 214 def IsSourceItemSelected(self): 215 return self._source is not None 216 217 def IsSourceItemAvailable(self): 218 if not self.IsSourceItemSelected(): 219 return False 220 checker = menucheckers.ItemChecker(self._source) 221 return self._source.itemID is not None and checker.IsInPilotLocation() 222 223 def IsMutatorAvailable(self): 224 checker = menucheckers.ItemChecker(self._mutator) 225 return self._mutator.itemID is not None and checker.IsInPilotLocation() 226 227 def ExecuteCrafting(self): 228 self._isCrafting = True 229 try: 230 if not self.IsMutatorAvailable(): 231 raise RuntimeError('The selected mutator is not available') 232 if not self.IsSourceItemAvailable(): 233 raise RuntimeError('Invalid source item selected') 234 dynamicItemSvc = sm.GetService('dynamicItemSvc') 235 try: 236 itemID = dynamicItemSvc.CreateDynamicItem(self.mutatorItemID, self._source.itemID) 237 except eveexceptions.UserError: 238 raise 239 except Exception: 240 log.LogException() 241 message = localization.GetByLabel('UI/DynamicItem/UnknownErrorBody') 242 self.onError(message) 243 else: 244 self._craftingResult = dynamicItemSvc.GetMutatedAttributes(itemID) 245 self._resultItemID = itemID 246 self.onChange() 247 248 finally: 249 self._isCrafting = False 250 251 def SetResultPresented(self): 252 self._isResultPresented = True 253 self.onChange() 254 255 def IsCraftingResultAvailable(self): 256 return self._craftingResult is not None 257 258 def Close(self): 259 sm.UnregisterNotify(self) 260 self.onChange.clear() 261 self.onDragEnter.clear() 262 self.onDragExit.clear() 263 self.onError.clear() 264 265 def AcceptItemDropData(self, dropData): 266 for entry in dropData: 267 item = getattr(entry, 'item', None) 268 if item is None: 269 continue 270 if self.IsAcceptedSourceItem(item): 271 self.SelectSourceItem(item) 272 break 273 if self.IsAcceptedMutatorItem(item): 274 self.SelectMutator(item) 275 break 276 277 def IsAcceptedSourceItem(self, item): 278 if self._isCrafting or self.IsCraftingResultAvailable(): 279 return False 280 if not self._IsItemReachable(item): 281 return False 282 if item.typeID not in self.GetApplicableTypes(): 283 return False 284 if self._source is None: 285 return True 286 if self._source.itemID is None and item.itemID is not None: 287 return True 288 if self._source.itemID != item.itemID: 289 return True 290 if self._source.typeID != item.typeID: 291 return True 292 return False 293 294 def IsAcceptedMutatorItem(self, item): 295 if self._isCrafting or self.IsCraftingResultAvailable(): 296 return False 297 if not self._IsItemReachable(item): 298 return False 299 if item.typeID not in dynamicitems.GetAllMutatorTypes(): 300 return False 301 if self._mutator.itemID is None and item.itemID is not None: 302 return True 303 if self._mutator.itemID != item.itemID: 304 return True 305 if self._mutator.typeID != item.typeID: 306 return True 307 return False 308 309 def _IsItemReachable(self, item): 310 if item.ownerID != session.charid: 311 return False 312 if item.flagID in const.fittingFlags: 313 return False 314 return True 315 316 def GetResultDragData(self): 317 item = utillib.KeyVal(typeID=self.resultTypeID, itemID=self._resultItemID, locationID=self._mutator.locationID, flagID=self._mutator.flagID, ownerID=self._mutator.ownerID, stacksize=1, categoryID=evetypes.GetCategoryID(self.resultTypeID), groupID=evetypes.GetGroupID(self.resultTypeID), singleton=True) 318 return utillib.KeyVal(__guid__='listentry.InvItem', typeID=self.resultTypeID, item=item, rec=item, label=evetypes.GetName(self.resultTypeID), name=evetypes.GetName(self.resultTypeID)) 319 320 def Reset(self): 321 self._resultItemID = None 322 self._craftingResult = None 323 self._isResultPresented = False 324 self._mutator = self._FindItem(self._mutator.typeID) 325 if self.IsSourceItemSelected(): 326 self._source = self._FindItem(self._source.typeID) 327 self.onChange() 328 329 def OnItemChange(self, item, change, location): 330 itemMovedKeys = (const.ixLocationID, const.ixOwnerID, const.ixFlag) 331 if not any((key in change for key in itemMovedKeys)): 332 return 333 if item.itemID is None: 334 return 335 if item.itemID == self.resultItemID: 336 self.Reset() 337 elif item.itemID == self.mutatorItemID: 338 self.SelectMutator(Item(typeID=item.typeID)) 339 elif item.typeID == self.mutatorTypeID and not self.IsMutatorAvailable(): 340 self.SelectMutator(Item(typeID=item.typeID)) 341 elif item.itemID == self.sourceItemID or item.typeID == self.sourceTypeID and not self.IsSourceItemAvailable(): 342 self.SelectSourceItem(Item(typeID=item.typeID)) 343 344 def OnSessionChanged(self, isRemote, session, changes): 345 monitoredChanges = ('locationid', 'stationid', 'structureid', 'shipid') 346 if any((key in changes for key in monitoredChanges)): 347 self.Reset() 348 349 def OnDragEnter(self, item): 350 self.onDragEnter(item) 351 352 def OnDragExit(self): 353 self.onDragExit() 354 355 356 class CraftingWindow(Window): 357 default_fixedWidth = 750 358 default_fixedHeight = 478 359 default_windowID = 'CraftingWindow' 360 default_caption = GetByLabel('UI/DynamicItem/MutateItem') 361 default_isCollapseable = False 362 default_isLightBackgroundConfigurable = False 363 default_isLockable = False 364 default_isOverlayable = False 365 default_isStackable = False 366 default_iconNum = RES_WINDOW_ICON 367 368 @classmethod 369 def Open(cls, **attributes): 370 window = cls.GetIfOpen() 371 if not window: 372 return super(CraftingWindow, cls).Open(**attributes) 373 mutator = attributes.get('mutator', None) 374 if mutator and mutator.itemID == window.controller.mutatorItemID: 375 window.Maximize() 376 return window 377 window.CloseByUser() 378 return super(CraftingWindow, cls).Open(**attributes) 379 380 def ApplyAttributes(self, attributes): 381 super(CraftingWindow, self).ApplyAttributes(attributes) 382 self.controller = CraftingController(attributes.mutator) 383 self._dragCookie = None 384 self._isDragHintActive = False 385 self.Layout() 386 self.controller.onDragEnter.connect(self.OnItemDragEnter) 387 self.controller.onDragExit.connect(self.OnItemDragExit) 388 self.controller.onError.connect(self.OnError) 389 390 def Layout(self): 391 buildCont = Container(name='buildCont', parent=self.GetMainArea(), align=uiconst.TOLEFT, width=400) 392 buttonCont = ContainerAutoSize(name='buttonCont', parent=buildCont, align=uiconst.TOBOTTOM) 393 BuildButton(parent=buttonCont, align=uiconst.CENTERTOP, controller=self.controller) 394 inputCont = Container(name='inputCont', parent=buildCont, align=uiconst.TOALL) 395 self.sourceItemSlot = SourceItemSlot(parent=inputCont, align=uiconst.CENTER, top=-10, controller=self.controller) 396 self.sourceItemSlot.OnDropData = self.OnDropData 397 attributeCont = Container(name='attributeCont', parent=self.GetMainArea(), align=uiconst.TOALL) 398 mutatorItemSlot = MutatorItemSlot(parent=attributeCont, align=uiconst.TOTOP, controller=self.controller) 399 mutatorItemSlot.OnDropData = self.OnDropData 400 attributeScroll = ScrollContainer(parent=attributeCont, align=uiconst.TOALL, padTop=5, padBottom=5) 401 attributeListCont = ContainerAutoSize(parent=attributeScroll, align=uiconst.TOPLEFT) 402 AttributeList(parent=attributeListCont, align=uiconst.TOPLEFT, controller=self.controller, width=300) 403 404 def UpdateState(self): 405 name = evetypes.GetName(self.controller.mutatorTypeID) 406 self.SetCaption(name) 407 408 def Close(self, *args, **kwargs): 409 self.controller.Close() 410 if self._dragCookie is not None: 411 uicore.uilib.UnregisterForTriuiEvents(self._dragCookie) 412 self._dragCookie = None 413 super(CraftingWindow, self).Close(*args, **kwargs) 414 415 def GetUtilMenu(self, menu): 416 menu.AddCheckBox(text=localization.GetByLabel('UI/DynamicItem/OptionFastAnimations'), checked=MUTATOR_FAST_ANIMATIONS_SETTING.is_enabled(), callback=MUTATOR_FAST_ANIMATIONS_SETTING.toggle) 417 418 def GetMenuMoreOptions(self): 419 m = MenuData() 420 m.AddCheckbox(localization.GetByLabel('UI/DynamicItem/OptionFastAnimations'), setting=MUTATOR_FAST_ANIMATIONS_SETTING) 421 return m 422 423 def OnItemDragEnter(self, item): 424 if self.controller.IsAcceptedSourceItem(item): 425 self._isDragHintActive = True 426 uthread.new(self.AnimDragEnter) 427 428 def OnItemDragExit(self): 429 if self._isDragHintActive: 430 uthread.new(self.AnimDragExit) 431 432 def AnimDragEnter(self): 433 self.sourceItemSlot.AnimShowDragHint() 434 435 def AnimDragExit(self): 436 self.sourceItemSlot.AnimHideDragHint() 437 438 def OnError(self, message): 439 error = ErrorMessage(parent=self.GetMainArea(), align=uiconst.CENTER, width=750, height=450, idx=0) 440 error.DelegateEvents(self) 441 error.SetMessage(message) 442 uthread.new(error.AnimShow) 443 self.controller.Close() 444 445 def OnDropData(self, source, data): 446 self.controller.AcceptItemDropData(data) 447 if self._dragCookie is not None: 448 uicore.uilib.UnregisterForTriuiEvents(self._dragCookie) 449 self._dragCookie = None 450 uthread.new(self.AnimDragExit) 451 452 def OnGlobalDrag(self, *args): 453 target = uicore.uilib.mouseOver 454 if uicore.IsDragging() and (IsUnder(target, self) or target == self): 455 return True 456 else: 457 self.controller.OnDragExit() 458 self._dragCookie = None 459 return False 460 461 def OnDragEnter(self, source, data): 462 if self._dragCookie is not None: 463 return 464 for entry in data: 465 item = getattr(entry, 'item', None) 466 if item is None: 467 continue 468 self._dragCookie = uicore.uilib.RegisterForTriuiEvents(uiconst.UI_MOUSEMOVEDRAG, self.OnGlobalDrag) 469 self.controller.OnDragEnter(item) 470 break 471 472 473 class ErrorMessage(Container): 474 default_state = uiconst.UI_NORMAL 475 476 def ApplyAttributes(self, attributes): 477 super(ErrorMessage, self).ApplyAttributes(attributes) 478 self.Layout() 479 480 def Layout(self): 481 Fill(bgParent=self, color=Color.BLACK, opacity=0.75) 482 dialogCont = ContainerAutoSize(parent=self, align=uiconst.CENTER, alignMode=uiconst.TOPLEFT, width=440) 483 Sprite(parent=dialogCont, align=uiconst.TOPLEFT, state=uiconst.UI_DISABLED, pos=(0, 0, 64, 64), texturePath=RES_ERROR_ICON) 484 messageCont = ContainerAutoSize(parent=dialogCont, align=uiconst.TOPLEFT, left=80, width=360) 485 FillThemeColored(bgParent=messageCont, colorType=uiconst.COLORTYPE_UIBASECONTRAST, opacity=0.9) 486 eveLabel.EveCaptionLarge(parent=messageCont, align=uiconst.TOTOP, padding=(16, 16, 16, 8), text=localization.GetByLabel('UI/DynamicItem/UnknownErrorHeader')) 487 self.label = eveLabel.EveLabelLarge(parent=messageCont, align=uiconst.TOTOP, padding=(16, 0, 16, 16)) 488 489 def SetMessage(self, message): 490 self.label.SetText(message) 491 492 def AnimShow(self): 493 animations.FadeTo(self, duration=0.8, curveType=uiconst.ANIM_OVERSHOT) 494 495 496 class AttributeList(ContainerAutoSize): 497 498 def ApplyAttributes(self, attributes): 499 super(AttributeList, self).ApplyAttributes(attributes) 500 self.controller = attributes.controller 501 self._mutatorTypes = self.controller.GetRelatedMutatorTypes() 502 uthread.new(self.Load) 503 self.controller.onChange.connect(self.OnChange) 504 505 def Load(self): 506 entries = [] 507 for attributeID in self.controller.GetModifiedAttributeIDs(): 508 entry = AttributeEntry(align=uiconst.TOTOP, top=8, controller=self.controller, attributeID=attributeID) 509 entries.append(entry) 510 511 sortedEntries = sorted(entries, key=lambda e: e.label) 512 for i, entry in enumerate(sortedEntries): 513 entry.SetParent(self) 514 entry.SetAnimationIndex(i) 515 516 def Reload(self): 517 self.Flush() 518 self.Load() 519 for child in self.children: 520 uthread.new(child.AnimEnter) 521 522 def OnChange(self): 523 if self.controller.mutatorTypeID not in self._mutatorTypes: 524 self._mutatorTypes = self.controller.GetRelatedMutatorTypes() 525 uthread.new(self.Reload) 526 else: 527 for child in self.children: 528 child.OnChange() 529 530 531 class MutatorItemSlot(Container): 532 default_height = 80 533 default_state = uiconst.UI_NORMAL 534 535 def ApplyAttributes(self, attributes): 536 super(MutatorItemSlot, self).ApplyAttributes(attributes) 537 self.controller = attributes.controller 538 self._selectedTypeID = self.controller.mutatorTypeID 539 self._isAvailable = self.controller.IsMutatorAvailable() 540 self.Layout() 541 self.UpdateState() 542 self.controller.onChange.connect(self.OnChange) 543 self.controller.onDragEnter.connect(self.OnItemDragEnter) 544 self.controller.onDragExit.connect(self.OnItemDragExit) 545 546 def Layout(self): 547 self.bgFill = FillThemeColored(bgParent=self, colorType=uiconst.COLORTYPE_UIHILIGHT) 548 self.errorFrame = ErrorFrame(bgParent=self, opacity=0.0, opacityHigh=0.2, opacityLow=0.1, color=COLOR_NEGATIVE) 549 mutatorIconCont = Container(parent=self, align=uiconst.TOLEFT, width=80) 550 self.mutatorIcon = ItemIcon(parent=mutatorIconCont, align=uiconst.CENTER, state=uiconst.UI_DISABLED, width=64, height=64, showOmegaOverlay=False) 551 mutatorLabelCont = Container(parent=self, align=uiconst.TOALL) 552 floatLabelCont = ContainerAutoSize(parent=mutatorLabelCont, align=uiconst.CENTERLEFT, width=206) 553 self.mutatorLabel = eveLabel.EveLabelLargeBold(parent=floatLabelCont, align=uiconst.TOTOP) 554 eveLabel.EveLabelMedium(parent=floatLabelCont, align=uiconst.TOTOP, text=localization.GetByLabel('UI/DynamicItem/MutationEffects'), opacity=0.6) 555 556 def UpdateState(self): 557 self._selectedTypeID = self.controller.mutatorTypeID 558 self._isAvailable = self.controller.IsMutatorAvailable() 559 name = evetypes.GetName(self._selectedTypeID) 560 self.mutatorLabel.SetText(name) 561 self.mutatorIcon.SetTypeID(self._selectedTypeID) 562 if self._isAvailable: 563 self.errorFrame.Hide() 564 self.mutatorIcon.opacity = 1.0 565 self.mutatorLabel.opacity = 1.0 566 else: 567 self.errorFrame.Show() 568 self.mutatorIcon.opacity = 0.5 569 self.mutatorLabel.opacity = 0.5 570 571 def AnimUpdateItemIcon(self): 572 animations.FadeTo(self.mutatorIcon, endVal=self.mutatorIcon.opacity, duration=0.3, curveType=uiconst.ANIM_OVERSHOT, timeOffset=0.1) 573 animations.Tr2DScaleIn(self.mutatorIcon.icon, scaleCenter=(0.5, 0.5), duration=0.2) 574 animations.FadeIn(self.mutatorLabel, endVal=self.mutatorLabel.opacity, duration=0.3) 575 if uicore.uilib.mouseOver != self: 576 self.AnimDeactivate() 577 578 def LoadTooltipPanel(self, panel, owner): 579 if not self.controller.IsCraftingResultAvailable(): 580 panel.SetState(uiconst.UI_NORMAL) 581 panel.margin = (8, 8, 8, 8) 582 MutatorItemScrollList(parent=panel, align=uiconst.TOPLEFT, width=284, height=110, controller=self.controller) 583 584 def GetTooltipPositionFallbacks(self): 585 return [uiconst.POINT_BOTTOM_2, 586 uiconst.POINT_TOP_2, 587 uiconst.POINT_LEFT_2, 588 uiconst.POINT_RIGHT_2] 589 590 def AnimActivate(self): 591 animations.FadeTo(self.bgFill, startVal=self.bgFill.opacity, endVal=0.35, duration=0.1) 592 593 def AnimDeactivate(self): 594 animations.FadeTo(self.bgFill, startVal=self.bgFill.opacity, endVal=0.25, duration=0.3) 595 596 def OnMouseEnter(self, *args): 597 if self.controller.IsCraftingResultAvailable(): 598 return 599 PlaySound(uiconst.SOUND_BUTTON_HOVER) 600 uthread.new(self.AnimActivate) 601 602 def OnMouseExit(self, *args): 603 uthread.new(self.AnimDeactivate) 604 605 def OnItemDragEnter(self, item): 606 if self.controller.IsAcceptedMutatorItem(item): 607 uthread.new(self.AnimActivate) 608 609 def OnItemDragExit(self): 610 uthread.new(self.AnimDeactivate) 611 612 def OnChange(self): 613 if self._selectedTypeID != self.controller.mutatorTypeID or self._isAvailable != self.controller.IsMutatorAvailable(): 614 self.UpdateState() 615 uthread.new(self.AnimUpdateItemIcon) 616 617 618 class MutatorItemScrollList(Scroll): 619 default_multiSelect = False 620 621 def ApplyAttributes(self, attributes): 622 super(MutatorItemScrollList, self).ApplyAttributes(attributes) 623 self.controller = attributes.controller 624 uthread.new(self.LoadMutatorItemList) 625 626 def LoadMutatorItemList(self): 627 self.ShowLoading() 628 try: 629 entries = {} 630 for typeID in self.controller.GetRelatedMutatorTypes(): 631 key = evetypes.GetName(typeID) 632 entry = GetFromClass(SourceItemEntry, {'typeID': typeID, 633 'label': evetypes.GetName(typeID), 634 'getIcon': True, 635 'isSelected': typeID == self.controller.sourceTypeID, 636 'isItemOwned': self.controller.IsItemAvailable(typeID)}) 637 entries[key] = entry 638 639 finally: 640 self.HideLoading() 641 642 sortedEntries = [] 643 if entries: 644 text = localization.GetByLabel('UI/DynamicItem/MutaplasmidListHeader') 645 entry = GetFromClass(HeaderEntry, {'text': text}) 646 sortedEntries.append(entry) 647 sortedEntries.extend(sortedValuesByKey(entries)) 648 self.Load(contentList=sortedEntries) 649 650 def OnSelectionChange(self, nodes): 651 if not nodes: 652 return 653 item = Item(typeID=nodes[0].typeID, itemID=None) 654 self.controller.SelectMutator(item) 655 656 def Prepare_Underlay_(self): 657 pass 658 659 660 class SourceItemSlot(Container): 661 default_width = 70 662 default_height = 70 663 default_state = uiconst.UI_NORMAL 664 default_clipChildren = False 665 isDragObject = True 666 667 def ApplyAttributes(self, attributes): 668 super(SourceItemSlot, self).ApplyAttributes(attributes) 669 self.controller = attributes.controller 670 self._selectedMutatorID = self.controller.mutatorTypeID 671 self._selectedTypeID = self.controller.sourceTypeID 672 self._isAvailable = self.controller.IsSourceItemAvailable() 673 self.Layout() 674 self.Load() 675 self.controller.onChange.connect(self.OnChange) 676 677 def Layout(self): 678 swirlCont = Container(parent=self, align=uiconst.CENTER, state=uiconst.UI_DISABLED, pos=(0, 0, 64, 64), clipChildren=False) 679 self.swirlPrimary = Sprite(parent=swirlCont, align=uiconst.CENTER, pos=(0, 0, 500, 500), texturePath=RES_SWIRL, color=self.GetSwirlColor()) 680 self.swirlSecondary = Sprite(parent=swirlCont, align=uiconst.CENTER, pos=(0, 0, 500, 500), texturePath=RES_SWIRL, textureSecondaryPath=RES_SWIRL, spriteEffect=trinity.TR2_SFX_MODULATE, blendMode=trinity.TR2_SBM_ADDX2, color=self.GetSwirlColor(), rotation=math.pi) 681 self.swirlTiny = Sprite(parent=swirlCont, align=uiconst.CENTER, pos=(0, 0, 200, 200), texturePath=RES_SWIRL, textureSecondaryPath=RES_SWIRL, spriteEffect=trinity.TR2_SFX_MODULATE, blendMode=trinity.TR2_SBM_ADDX2, color=self.GetSwirlColor(), opacity=0.0) 682 animations.MorphScalar(self.swirlPrimary, 'rotation', startVal=0.0, endVal=-2.0 * math.pi, duration=80.0, curveType=uiconst.ANIM_LINEAR, loops=uiconst.ANIM_REPEAT) 683 animations.SpSecondaryTextureRotate(self.swirlSecondary, endVal=-2.0 * math.pi, duration=40.0, curveType=uiconst.ANIM_LINEAR, loops=uiconst.ANIM_REPEAT) 684 animations.SpSecondaryTextureScale(self.swirlSecondary, startVal=(0.8, 0.8), endVal=(1.2, 1.2), duration=35.0, curveType=uiconst.ANIM_WAVE, loops=uiconst.ANIM_REPEAT) 685 self.itemIcon = ItemIcon(parent=self, align=uiconst.CENTER, state=uiconst.UI_DISABLED, width=64, height=64, showOmegaOverlay=False) 686 self.box = SpriteThemeColored(parent=self, align=uiconst.CENTER, state=uiconst.UI_DISABLED, pos=(0, 0, 70, 70), texturePath=RES_INPUT_FRAME, opacity=0.5, colorType=uiconst.COLORTYPE_UIHILIGHTGLOW) 687 self.errorFrame = ErrorFrame(parent=self, align=uiconst.CENTER, state=uiconst.UI_DISABLED, pos=(0, 0, 64, 64), opacity=0.0, opacityHigh=0.2, opacityLow=0.1, color=COLOR_NEGATIVE) 688 self.bgGrid = Sprite(parent=self, align=uiconst.CENTER, state=uiconst.UI_DISABLED, pos=(0, 0, 64, 64), texturePath=RES_INPUT_BACKGROUND, opacity=0.0) 689 Fill(parent=self, align=uiconst.CENTER, pos=(0, 0, 64, 64), color=Color.BLACK, opacity=0.2) 690 self.bgCircle = SpriteThemeColored(parent=self, align=uiconst.CENTER, state=uiconst.UI_DISABLED, width=400, height=400, texturePath=RES_INPUT_RING, textureSecondaryPath=RES_RING_GLOW_MASK, spriteEffect=trinity.TR2_SFX_MODULATE, opacity=0.0, colorType=uiconst.COLORTYPE_UIHILIGHTGLOW) 691 self.bgCircleUnderlay = SpriteThemeColored(parent=self, align=uiconst.CENTER, state=uiconst.UI_DISABLED, width=400, height=400, texturePath=RES_INPUT_RING, opacity=0.6, colorType=uiconst.COLORTYPE_UIHILIGHTGLOW) 692 693 def Load(self): 694 if self._selectedTypeID is not None: 695 self.itemIcon.SetTypeID(self._selectedTypeID) 696 if self._isAvailable: 697 self.itemIcon.opacity = 1.0 698 self.errorFrame.Hide() 699 else: 700 self.itemIcon.opacity = 0.5 701 self.errorFrame.Show() 702 703 def AnimUpdateItemIcon(self): 704 animations.FadeOut(self.bgCircle, duration=0.3, callback=self.bgCircle.StopAnimations) 705 if self._selectedTypeID is None: 706 self.itemIcon.opacity = 0.0 707 self.errorFrame.Hide() 708 animations.FadeOut(self.bgGrid, duration=0.3) 709 else: 710 self.itemIcon.SetTypeID(self._selectedTypeID) 711 if self._isAvailable: 712 opacity = 1.0 713 self.errorFrame.Hide() 714 else: 715 opacity = 0.5 716 self.errorFrame.Show() 717 animations.FadeTo(self.itemIcon, endVal=opacity, duration=0.3, curveType=uiconst.ANIM_OVERSHOT5, timeOffset=0.1) 718 animations.Tr2DScaleIn(self.itemIcon.icon, scaleCenter=(0.5, 0.5), duration=0.2) 719 animations.FadeTo(self.bgGrid, startVal=self.bgGrid.opacity, endVal=0.8, duration=0.2) 720 721 def AnimShowDragHint(self): 722 animations.FadeTo(self.box, startVal=self.box.opacity, endVal=1.0, duration=0.3) 723 animations.FadeTo(self.bgCircle, startVal=0.0, endVal=10.0, duration=1.6, curveType=uiconst.ANIM_WAVE, loops=uiconst.ANIM_REPEAT) 724 animations.MorphVector2(self.bgCircle, 'scaleSecondary', startVal=(1.1, 1.1), endVal=(2.5, 2.5), duration=1.6, curveType=uiconst.ANIM_LINEAR, loops=uiconst.ANIM_REPEAT) 725 726 def AnimHideDragHint(self): 727 self.bgCircle.StopAnimations() 728 self.box.StopAnimations() 729 animations.FadeTo(self.bgCircle, startVal=self.bgCircle.opacity, endVal=0.0, duration=0.6) 730 animations.FadeTo(self.box, startVal=self.box.opacity, endVal=0.5, duration=0.3) 731 732 def AnimCraft(self): 733 self.Disable() 734 animations.FadeOut(self.itemIcon, duration=0.3) 735 animations.MorphScalar(self.itemIcon, 'width', startVal=64, endVal=55, duration=0.5) 736 animations.MorphScalar(self.itemIcon, 'height', startVal=64, endVal=55, duration=0.5) 737 animations.FadeTo(self.swirlPrimary, startVal=self.swirlPrimary.opacity, endVal=1.6, duration=2.0, curveType=uiconst.ANIM_WAVE, loops=uiconst.ANIM_REPEAT) 738 animations.FadeTo(self.swirlSecondary, startVal=self.swirlSecondary.opacity, endVal=4.0, duration=1.0) 739 animations.SpSecondaryTextureRotate(self.swirlSecondary, startVal=self.swirlSecondary.rotationSecondary, endVal=self.swirlSecondary.rotationSecondary - 2.0 * math.pi, duration=22.0, curveType=uiconst.ANIM_LINEAR, loops=uiconst.ANIM_REPEAT) 740 animations.FadeIn(self.swirlTiny, duration=0.8, curveType=uiconst.ANIM_OVERSHOT5) 741 animations.SpSecondaryTextureRotate(self.swirlTiny, endVal=-2.0 * math.pi, duration=7.0, curveType=uiconst.ANIM_LINEAR, loops=uiconst.ANIM_REPEAT) 742 attributeCount = len(self.controller.GetModifiedAttributeIDs()) 743 animationFactor = 1.0 if not MUTATOR_FAST_ANIMATIONS_SETTING.is_enabled() else 10.0 744 duration = 1.5 + (attributeCount - 1) * 1.2 / animationFactor 745 blue.pyos.synchro.SleepWallclock(duration * 1000) 746 if self.destroyed: 747 return 748 self.itemIcon.SetTypeID(self.controller.resultTypeID) 749 self.Enable() 750 animations.FadeTo(self.bgCircle, startVal=0.0, endVal=10.0, duration=1.6, curveType=uiconst.ANIM_WAVE, loops=uiconst.ANIM_REPEAT) 751 animations.MorphVector2(self.bgCircle, 'scaleSecondary', startVal=(2.5, 2.5), endVal=(1.1, 1.1), duration=1.6, curveType=uiconst.ANIM_LINEAR, loops=uiconst.ANIM_REPEAT) 752 animations.FadeTo(self.itemIcon, duration=0.3, curveType=uiconst.ANIM_OVERSHOT5, timeOffset=0.1) 753 animations.MorphScalar(self.itemIcon, 'width', startVal=55, endVal=64, duration=0.4) 754 animations.MorphScalar(self.itemIcon, 'height', startVal=55, endVal=64, duration=0.4, callback=self.controller.SetResultPresented) 755 animations.FadeOut(self.swirlTiny, duration=0.5) 756 animations.FadeTo(self.swirlPrimary, startVal=self.swirlPrimary.opacity, endVal=1.0, duration=3.0) 757 animations.FadeTo(self.swirlSecondary, startVal=self.swirlSecondary.opacity, endVal=0.0, duration=3.0, sleep=True) 758 if self.destroyed: 759 return 760 animations.SpSecondaryTextureRotate(self.swirlSecondary, startVal=self.swirlSecondary.rotationSecondary, endVal=self.swirlSecondary.rotationSecondary - 2.0 * math.pi, duration=40.0, curveType=uiconst.ANIM_LINEAR, loops=uiconst.ANIM_REPEAT) 761 animations.FadeIn(self.swirlSecondary, duration=3.0) 762 763 def AnimUpdateSwirl(self): 764 newColor = self.GetSwirlColor() 765 animations.SpColorMorphTo(self.swirlPrimary, endColor=newColor, duration=0.5) 766 animations.SpColorMorphTo(self.swirlSecondary, endColor=newColor, duration=0.5) 767 animations.SpColorMorphTo(self.swirlTiny, endColor=newColor, duration=0.5) 768 769 def LoadTooltipPanel(self, panel, owner): 770 panel.SetState(uiconst.UI_NORMAL) 771 panel.margin = (8, 8, 8, 8) 772 if self.controller.IsCraftingResultAvailable(): 773 name = evetypes.GetName(self.controller.resultTypeID) 774 panel.AddLabelMedium(text=name) 775 else: 776 SourceItemScrollList(parent=panel, align=uiconst.TOPLEFT, width=250, height=200, controller=self.controller) 777 778 def OnChange(self): 779 if self.controller.isResultPresented: 780 return 781 if self.controller.IsCraftingResultAvailable(): 782 self._selectedTypeID = self.controller.resultTypeID 783 self._isAvailable = True 784 uthread.new(self.AnimCraft) 785 elif self._selectedTypeID != self.controller.sourceTypeID or self._isAvailable != self.controller.IsSourceItemAvailable(): 786 self._selectedTypeID = self.controller.sourceTypeID 787 self._isAvailable = self.controller.IsSourceItemAvailable() 788 uthread.new(self.AnimUpdateItemIcon) 789 if self._selectedMutatorID != self.controller.mutatorTypeID: 790 self._selectedMutatorID = self.controller.mutatorTypeID 791 uthread.new(self.AnimUpdateSwirl) 792 793 def OnMouseEnter(self, *args): 794 PlaySound(uiconst.SOUND_BUTTON_HOVER) 795 animations.FadeTo(self.box, startVal=self.box.opacity, endVal=1.0, duration=0.2) 796 797 def OnMouseExit(self, *args): 798 animations.FadeTo(self.box, startVal=self.box.opacity, endVal=0.5, duration=0.3) 799 800 def GetDragData(self, *args): 801 if self.controller.IsCraftingResultAvailable(): 802 return [self.controller.GetResultDragData()] 803 else: 804 return [] 805 806 def GetSwirlColor(self): 807 typeID = self.controller.mutatorTypeID 808 if typeID in GetDecayedTypes(): 809 return COLOR_DECAYED 810 elif typeID in GetGravidTypes(): 811 return COLOR_GRAVID 812 elif typeID in GetUnstableTypes() or typeID in GetAbnormalTypes(): 813 return COLOR_UNSTABLE 814 else: 815 return Color.WHITE 816 817 818 class SourceItemScrollList(Scroll): 819 default_multiSelect = False 820 821 def ApplyAttributes(self, attributes): 822 super(SourceItemScrollList, self).ApplyAttributes(attributes) 823 self.controller = attributes.controller 824 uthread.new(self.LoadSourceItemList) 825 826 def LoadSourceItemList(self): 827 self.ShowLoading() 828 try: 829 owned = {} 830 unowned = {} 831 for typeID in self.controller.GetApplicableTypes(): 832 if typeID == self.controller.sourceTypeID: 833 isOwned = self.controller.IsSourceItemAvailable() 834 itemID = self.controller.sourceItemID 835 else: 836 isOwned = self.controller.IsItemAvailable(typeID) 837 itemID = None 838 key = (evetypes.GetMetaLevel(typeID), evetypes.GetName(typeID)) 839 entry = GetFromClass(SourceItemEntry, {'typeID': typeID, 840 'itemID': itemID, 841 'label': evetypes.GetName(typeID), 842 'getIcon': True, 843 'isSelected': typeID == self.controller.sourceTypeID, 844 'isItemOwned': isOwned}) 845 if isOwned: 846 owned[key] = entry 847 else: 848 unowned[key] = entry 849 850 finally: 851 self.HideLoading() 852 853 entries = [] 854 if owned or unowned: 855 text = localization.GetByLabel('UI/DynamicItem/SourceItemListHeader') 856 entry = GetFromClass(HeaderEntry, {'text': text}) 857 entries.append(entry) 858 if owned: 859 entries.extend(sortedValuesByKey(owned)) 860 if unowned: 861 if owned: 862 entries.append(GetFromClass(Space, {'height': 8})) 863 entries.extend(sortedValuesByKey(unowned)) 864 self.Load(contentList=entries) 865 self.ScrollToSelectedNode() 866 867 def OnSelectionChange(self, nodes): 868 if not nodes: 869 return 870 data = nodes[0] 871 item = Item(typeID=data.typeID, itemID=data.itemID) 872 self.controller.SelectSourceItem(item) 873 874 def Prepare_Underlay_(self): 875 pass 876 877 878 def sortedValuesByKey(dictionary): 879 for _, entry in sorted(dictionary.items(), key=lambda x: x[0]): 880 yield entry 881 882 883 class HeaderEntry(Text): 884 885 def Startup(self, *args): 886 super(HeaderEntry, self).Startup(*args) 887 self.sr.text.align = uiconst.TOPLEFT 888 self.sr.text.padLeft = -8 889 self.sr.text.state = uiconst.UI_DISABLED 890 891 def GetHeight(self, *args): 892 node, width = args 893 textHeight = uix.GetTextHeight(node.text, maxLines=1) 894 node.height = 4 + textHeight 895 return node.height 896 897 def GetMenu(self): 898 return [] 899 900 901 class SourceItemEntry(item_entry.Item): 902 903 def Load(self, node): 904 super(SourceItemEntry, self).Load(node) 905 if not node.isItemOwned: 906 self.sr.icon.opacity = self.sr.label.opacity = 0.25 907 908 def UpdateAlignment(self, *args): 909 budget = super(SourceItemEntry, self).UpdateAlignment(*args) 910 budgetLeft, budgetTop, budgetWidth, budgetHeight, sizeChange = budget 911 self.sr.label.SetRightAlphaFade(budgetWidth - 54, 16) 912 return budget 913 914 915 class AttributeEntry(Container): 916 default_clipChildren = False 917 default_height = 48 918 919 def ApplyAttributes(self, attributes): 920 super(AttributeEntry, self).ApplyAttributes(attributes) 921 self.attributeID = attributes.attributeID 922 self.controller = attributes.controller 923 self._animIndex = attributes.get('animationIndex', 0) 924 self._lastSourceTypeID = self.controller.sourceTypeID 925 self._lastMutatorTypeID = self.controller.mutatorTypeID 926 self.Layout() 927 uthread.new(self.Load) 928 929 @property 930 def label(self): 931 return dogma_data.get_attribute_display_name(self.attributeID) 932 933 def Layout(self): 934 self.valueLabel = None 935 self.bgFill = FillThemeColored(bgParent=self, colorType=uiconst.COLORTYPE_UIHILIGHT) 936 iconCont = Container(parent=self, align=uiconst.TOLEFT, width=46) 937 self.icon = Icon(parent=iconCont, align=uiconst.CENTER, state=uiconst.UI_DISABLED, size=32) 938 self.nameLabel = eveLabel.EveLabelMedium(parent=self, align=uiconst.TOTOP, state=uiconst.UI_NORMAL, top=6, singleline=True, opacity=self.GetNameLabelOpacity()) 939 self.nameLabel.SetRightAlphaFade(fadeEnd=200, maxFadeWidth=10) 940 self.mutationBar = MutationBar(parent=self, align=uiconst.TOBOTTOM, idx=0, controller=self.controller, attributeID=self.attributeID, animationIndex=self._animIndex) 941 942 def Load(self): 943 self.icon.LoadIcon(dogma_data.get_attribute_icon_id(self.attributeID)) 944 self.nameLabel.SetText(dogma_data.get_attribute_display_name(self.attributeID)) 945 self.nameLabel.SetHint(dogma_data.get_attribute_display_name(self.attributeID)) 946 self.UpdateAttributeValue() 947 948 def SetAnimationIndex(self, index): 949 self._animIndex = index 950 self.mutationBar.SetAnimationIndex(index) 951 952 def ConstructValueLabel(self): 953 attribute = self.controller.GetAttribute(self.attributeID) 954 if self.controller.IsCraftingResultAvailable(): 955 return FinalAttributeValueLabel(parent=self, align=uiconst.TOTOP_NOPUSH, height=16, controller=self.controller, attributeID=self.attributeID, value=attribute.value) 956 if self.controller.IsSourceItemSelected(): 957 text = self._FormatAttributeRange(attribute) 958 else: 959 low, high = self.controller.GetAttributeBonusRange(self.attributeID) 960 text = FormatAttributeBonusRange(self.attributeID, low, high) 961 return eveLabel.EveLabelMedium(parent=self, align=uiconst.TOTOP_NOPUSH, text=text) 962 963 def GetNameLabelOpacity(self): 964 if self.controller.IsCraftingResultAvailable(): 965 return 0.6 966 else: 967 return 1.0 968 969 def UpdateAttributeValue(self): 970 if self.valueLabel: 971 self.valueLabel.Close() 972 self.valueLabel = self.ConstructValueLabel() 973 974 def AnimEnter(self): 975 animations.FadeTo(self, duration=0.3, timeOffset=self._animIndex * 0.05) 976 977 def AnimUpdateAttributeValue(self): 978 if self.valueLabel: 979 animations.FadeOut(self.valueLabel, duration=0.1, sleep=True) 980 if self.destroyed: 981 return 982 self.UpdateAttributeValue() 983 animations.FadeTo(self.valueLabel, duration=0.25, curveType=uiconst.ANIM_OVERSHOT2, timeOffset=self._animIndex * 0.05) 984 animations.FadeTo(self.nameLabel, startVal=self.nameLabel.opacity, endVal=self.GetNameLabelOpacity(), duration=0.3) 985 986 def AnimShowFinalValue(self): 987 animations.FadeOut(self.valueLabel, duration=0.05, sleep=True) 988 if self.destroyed: 989 return 990 self.UpdateAttributeValue() 991 attribute = self.controller.GetAttribute(self.attributeID) 992 self.valueLabel.value = attribute.sourceValue 993 animationFactor = 1.0 if not MUTATOR_FAST_ANIMATIONS_SETTING.is_enabled() else 10.0 994 uthread.new(self.valueLabel.AnimSetValue, attribute.value, duration=1.5, timeOffset=self._animIndex * 1.2 / animationFactor) 995 animations.FadeTo(self.nameLabel, startVal=self.nameLabel.opacity, endVal=self.GetNameLabelOpacity(), duration=0.3, timeOffset=self._animIndex * 1.2 / animationFactor) 996 animations.FadeTo(self.valueLabel, startVal=0.0, endVal=1.0, duration=1.5, curveType=uiconst.ANIM_OVERSHOT2, timeOffset=self._animIndex * 1.2 / animationFactor) 997 animations.MorphScalar(self.valueLabel, 'left', startVal=-10, endVal=0, duration=0.2, timeOffset=self._animIndex * 1.2 / animationFactor) 998 animations.FadeTo(self.bgFill, startVal=self.bgFill.opacity, endVal=self.bgFill.opacity + 0.2, duration=0.25, curveType=uiconst.ANIM_WAVE, timeOffset=1.5 + self._animIndex * 1.2 / animationFactor) 999 1000 def _FormatAttributeRange(self, attribute): 1001 negativeColor = Color(*COLOR_NEGATIVE).GetHex() 1002 positiveColor = Color(*COLOR_POSITIVE).GetHex() 1003 if not attribute.highIsGood: 1004 negativeColor, positiveColor = positiveColor, negativeColor 1005 if attribute.mutationLow < attribute.sourceValue: 1006 lowColor = negativeColor 1007 else: 1008 lowColor = positiveColor 1009 if attribute.mutationHigh < attribute.sourceValue: 1010 highColor = negativeColor 1011 else: 1012 highColor = positiveColor 1013 template = u'<color={colorLow}>{low}</color> - <color={colorHigh}>{high}</color>' 1014 return template.format(low=attribute.displayMutationLow, high=attribute.displayMutationHigh, colorLow=lowColor, colorHigh=highColor) 1015 1016 def OnChange(self): 1017 if self.controller.isResultPresented: 1018 return 1019 if self.controller.IsCraftingResultAvailable(): 1020 self._lastSourceTypeID = None 1021 self._lastMutatorTypeID = None 1022 uthread.new(self.AnimShowFinalValue) 1023 elif self._lastSourceTypeID != self.controller.sourceTypeID or self._lastMutatorTypeID != self.controller.mutatorTypeID: 1024 uthread.new(self.AnimUpdateAttributeValue) 1025 self._lastSourceTypeID = self.controller.sourceTypeID 1026 self._lastMutatorTypeID = self.controller.mutatorTypeID 1027 1028 1029 class FinalAttributeValueLabel(Container): 1030 default_clipChildren = True 1031 1032 def ApplyAttributes(self, attributes): 1033 super(FinalAttributeValueLabel, self).ApplyAttributes(attributes) 1034 self.controller = attributes.controller 1035 self.attributeID = attributes.attributeID 1036 self._value = attributes.value 1037 self.Layout() 1038 self.UpdateValueLabel() 1039 1040 def Layout(self): 1041 self.valueLabel = eveLabel.EveLabelLargeBold(parent=self, align=uiconst.BOTTOMLEFT, autoFitToText=True) 1042 self.diffLabel = eveLabel.EveLabelSmall(parent=self, align=uiconst.BOTTOMLEFT, top=1, opacity=0.0) 1043 1044 @property 1045 def value(self): 1046 return self._value 1047 1048 @value.setter 1049 def value(self, value): 1050 self._value = value 1051 self.UpdateValueLabel() 1052 1053 def AnimSetValue(self, value, duration = 0.5, timeOffset = 0.0): 1054 base = self.value or 0.0 1055 animations.FadeOut(self.diffLabel, duration=0.05) 1056 animations.MorphScalar(self, 'value', startVal=base, endVal=value, duration=duration, timeOffset=timeOffset, sleep=True) 1057 if self.destroyed: 1058 return 1059 self.value = value 1060 self.SetDiff(value - base) 1061 animations.FadeIn(self.diffLabel, duration=0.2) 1062 1063 def SetDiff(self, diff): 1064 attribute = self.controller.GetAttribute(self.attributeID) 1065 diffStr = attribute.FormatDiff(diff) 1066 if attribute.isMutationPositive: 1067 color = Color(*COLOR_POSITIVE).GetHex() 1068 else: 1069 color = Color(*COLOR_NEGATIVE).GetHex() 1070 text = u'<color={color}>({diff})</color>'.format(color=color, diff=diffStr) 1071 self.diffLabel.SetText(text) 1072 self.diffLabel.left = self.valueLabel.textwidth + 8 1073 1074 def UpdateValueLabel(self): 1075 attribute = self.controller.GetAttribute(self.attributeID) 1076 self.valueLabel.SetText(attribute.Format(self.value)) 1077 1078 1079 class MutationBar(Container): 1080 default_height = 4 1081 default_clipChildren = False 1082 1083 def ApplyAttributes(self, attributes): 1084 super(MutationBar, self).ApplyAttributes(attributes) 1085 self.controller = attributes.controller 1086 self.attributeID = attributes.attributeID 1087 self._lastAttribute = self.controller.GetAttribute(self.attributeID) 1088 self._animationIndex = attributes.get('animationIndex', 0) 1089 self.Layout() 1090 self.controller.onChange.connect(self.OnChange) 1091 1092 def Layout(self): 1093 FillThemeColored(bgParent=self, colorType=uiconst.COLORTYPE_UIHILIGHT) 1094 self.barPin = Container(parent=self, align=uiconst.TOPLEFT_PROP, left=0.5, height=1.0, width=1.0, opacity=0.0) 1095 self.barPip = Fill(parent=self.barPin, align=uiconst.TOPLEFT, height=self.height, width=1, color=(0.7, 0.7, 0.7, 1.0)) 1096 self.barFill = Sprite(parent=self, align=uiconst.TOLEFT_PROP, left=0.5, width=0.0, texturePath=RES_BAR_FILL, tileX=True) 1097 1098 def SetAnimationIndex(self, index): 1099 self._animationIndex = index 1100 1101 def AnimUpdateBar(self): 1102 attribute = self.controller.GetAttribute(self.attributeID) 1103 attributeValuesDiffer = self._lastAttribute is not None and self._lastAttribute.value is not None and self._lastAttribute.value != attribute.value 1104 if attributeValuesDiffer: 1105 self.AnimHideBar() 1106 if self.destroyed: 1107 return 1108 if attribute is not None and attribute.value is not None: 1109 self.AnimShowBar() 1110 self._lastAttribute = attribute 1111 1112 def AnimHideBar(self): 1113 animations.MorphScalar(self.barFill, 'width', startVal=self.barFill.width, endVal=0.0, duration=0.1) 1114 animations.MorphScalar(self.barPin, 'left', startVal=self.barPin.left, endVal=0.5, duration=0.1) 1115 animations.FadeOut(self.barPin, duration=0.05, sleep=True, timeOffset=0.1) 1116 1117 def AnimShowBar(self): 1118 attribute = self.controller.GetAttribute(self.attributeID) 1119 trueSource = max(attribute.sourceValue, attribute.mutationMin) 1120 if attribute.isMutationPositive: 1121 self.barFill.align = uiconst.TOLEFT_PROP 1122 self.barFill.SetRGB(*COLOR_POSITIVE) 1123 self.barFill.rotation = 0.0 1124 rangeWidth = abs(attribute.mutationHigh - trueSource) 1125 else: 1126 self.barFill.align = uiconst.TORIGHT_PROP 1127 self.barFill.SetRGB(*COLOR_NEGATIVE) 1128 self.barFill.rotation = math.pi 1129 rangeWidth = abs(attribute.mutationLow - trueSource) 1130 animationFactor = 1.0 if not MUTATOR_FAST_ANIMATIONS_SETTING.is_enabled() else 10.0 1131 animations.FadeTo(self.barPin, startVal=0.0, endVal=1.0, duration=0.05, timeOffset=self._animationIndex * 1.2 / animationFactor) 1132 animations.FadeTo(self.barPip, startVal=self.barPip.opacity, endVal=2.0, duration=0.2, curveType=uiconst.ANIM_WAVE, timeOffset=1.5 + self._animationIndex * 1.2 / animationFactor) 1133 if rangeWidth > 0.0: 1134 width = abs(attribute.value - trueSource) / rangeWidth 1135 else: 1136 width = 0.0 1137 sign = 1.0 if attribute.isMutationPositive else -1.0 1138 animations.MorphScalar(self.barPin, 'left', startVal=0.5, endVal=0.5 + sign * (width / 2.0), duration=1.5, timeOffset=self._animationIndex * 1.2 / animationFactor) 1139 animations.MorphScalar(self.barFill, 'width', startVal=0.0, endVal=width / 2.0, duration=1.5, timeOffset=self._animationIndex * 1.2 / animationFactor) 1140 1141 def OnChange(self): 1142 if self.controller.isResultPresented: 1143 return 1144 uthread.new(self.AnimUpdateBar) 1145 1146 1147 class BuildButton(PrimaryButton): 1148 1149 def ApplyAttributes(self, attributes): 1150 super(BuildButton, self).ApplyAttributes(attributes) 1151 self.controller = attributes.controller 1152 self.UpdateState() 1153 self.controller.onChange.connect(self.UpdateState) 1154 1155 def ClickFunc(self, *args): 1156 if self.controller.isResultPresented: 1157 self.controller.Reset() 1158 else: 1159 self.controller.ExecuteCrafting() 1160 1161 def GetColor(self): 1162 return sm.GetService('uiColor').GetUIColor(uiconst.COLORTYPE_UIHILIGHT) 1163 1164 def GetText(self): 1165 if self.controller.isResultPresented: 1166 return localization.GetByLabel('UI/DynamicItem/Continue') 1167 else: 1168 return localization.GetByLabel('UI/DynamicItem/Mutate') 1169 1170 def IsActive(self): 1171 if self.controller.isResultPresented: 1172 return True 1173 elif self.controller.IsCraftingResultAvailable(): 1174 return False 1175 elif not self.controller.IsMutatorAvailable(): 1176 return False 1177 elif self.controller.IsSourceItemAvailable(): 1178 return True 1179 else: 1180 return False 1181 1182 def IsBlinking(self): 1183 return False 1184 1185 def IsErrorPresent(self): 1186 if not self.controller.IsMutatorAvailable(): 1187 return True 1188 if not self.controller.IsSourceItemSelected(): 1189 return False 1190 if not self.controller.IsSourceItemAvailable(): 1191 return True 1192 return False 1193 1194 def UpdateIsEnabledByState(self): 1195 if self.IsActive(): 1196 self.Enable() 1197 else: 1198 self.Disable() 1199 1200 def UpdateBlinkByState(self): 1201 pass 1202 1203 def OnColorThemeChanged(self): 1204 self.UpdateState() 1205 1206 def LoadTooltipPanel(self, panel, owner): 1207 panel.margin = (8, 8, 8, 0) 1208 if not self.controller.IsMutatorAvailable(): 1209 panel.AddLabelMedium(text=localization.GetByLabel('UI/DynamicItem/ErrorMutatorNotAvailable'), width=200, padBottom=8) 1210 if not self.controller.IsSourceItemSelected(): 1211 panel.AddLabelMedium(text=localization.GetByLabel('UI/DynamicItem/ErrorSourceItemNotSelected'), width=200, padBottom=8) 1212 elif not self.controller.IsSourceItemAvailable(): 1213 panel.AddLabelMedium(text=localization.GetByLabel('UI/DynamicItem/ErrorSourceItemNotAvailable'), width=200, padBottom=8) 1214 1215 1216 def __SakeReloadHook(): 1217 try: 1218 instance = CraftingWindow.GetIfOpen() 1219 if instance: 1220 CraftingWindow.Reload(instance) 1221 except Exception: 1222 import log 1223 log.LogException()