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

vgsOffer.py (18728B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\view\aurumstore\vgsOffer.py
      2 import blue
      3 import industry
      4 import itertools
      5 import logging
      6 import math
      7 import signals
      8 import uthread
      9 from carbon.common.script.sys.serviceConst import ROLE_PROGRAMMER
     10 from carbonui import const as uiconst
     11 from carbonui.fontconst import STYLE_HEADER
     12 from carbonui.primitives.container import Container
     13 from carbonui.primitives.containerAutoSize import ContainerAutoSize
     14 from carbonui.primitives.fill import Fill
     15 from carbonui.primitives.frame import Frame
     16 from carbonui.primitives.gradientSprite import GradientSprite
     17 from carbonui.primitives.sprite import Sprite
     18 from carbonui.primitives.transform import Transform
     19 from carbonui.uicore import uicore
     20 from carbonui.util.color import Color
     21 from eve.client.script.ui.control.eveLabel import Label
     22 from eve.client.script.ui.control.eveLoadingWheel import LoadingWheel
     23 from eve.client.script.ui.shared.preview import PreviewContainer
     24 from eve.client.script.ui.shared.vgs.priceTag import PriceTagMedium
     25 from eve.client.script.ui.util.uiComponents import Component, ButtonEffect, RadioButtonEffect, ToggleButtonEffect
     26 from eve.client.script.ui.view.aurumstore.shared.offerpricing import iter_currencies
     27 from eve.client.script.ui.view.aurumstore import vgsUiConst
     28 from eve.client.script.ui.view.aurumstore.shared.const import STORE_PREVIEW_OVERLAY_HEIGHT
     29 from eve.client.script.ui.view.aurumstore.vgsUiPrimitives import LazyUrlSprite, VgsLabelRibbon, VgsLabelRibbonLarge
     30 from eve.common.lib import appConst
     31 from eve.common.script.sys.eveCfg import IsApparel, IsBlueprint, IsPreviewable
     32 logger = logging.getLogger(__name__)
     33 
     34 class Ribbon(Container):
     35     ID_SALE = 'Sale'
     36     ID_NEW = 'New'
     37     ID_HOT = 'Hot'
     38     ID_BUNDLE = 'Bundle'
     39     ID_FREE = 'Free'
     40     SORTING = [ID_FREE,
     41      ID_SALE,
     42      ID_NEW,
     43      ID_HOT,
     44      ID_BUNDLE]
     45     default_name = 'Ribbon'
     46     default_state = uiconst.UI_DISABLED
     47     default_align = uiconst.TOPLEFT
     48 
     49     def __init__(self, text, texture_path, is_big = False, *args, **kwargs):
     50         super(Ribbon, self).__init__(*args, **kwargs)
     51         self.text = text
     52         self.texture_path = texture_path
     53         self.is_big = is_big
     54         self.construct_layout()
     55 
     56     def construct_layout(self):
     57         if self.is_big:
     58             self.left = vgsUiConst.OFFER_INFO_PADDING_BIG
     59             label = VgsLabelRibbonLarge(parent=self, align=uiconst.CENTER, text=self.text, padding=(-20, 10, 0, 0), color=Color.WHITE)
     60         else:
     61             label = VgsLabelRibbon(parent=self, align=uiconst.CENTER, text=self.text, padding=(-10, 19, 0, 0), color=Color.WHITE)
     62         self.bgSprite = Frame(parent=self, texturePath=self.texture_path, align=uiconst.TOPLEFT, height=50, cornerSize=20, uiScaleVertices=True)
     63         self.width = label.textwidth + 70
     64         self.height = label.textheight + 2
     65         self.bgSprite.width = self.width
     66 
     67 
     68 def CreateFill(parent, _):
     69     return Fill(name='highlight', bgParent=parent.imageLayer, color=vgsUiConst.OFFER_BACKGROUND_COLOR, idx=0)
     70 
     71 
     72 @Component(ButtonEffect(bgElementFunc=CreateFill, idx=0, opacityIdle=0.0, opacityHover=0.5, opacityMouseDown=0.85, audioOnEntry='store_hover'))
     73 
     74 class VgsOffer(Container):
     75     default_name = 'Offer'
     76     default_align = uiconst.TOPLEFT
     77     default_clipChildren = False
     78     default_state = uiconst.UI_NORMAL
     79 
     80     def ApplyAttributes(self, attributes):
     81         Container.ApplyAttributes(self, attributes)
     82         self.offer = attributes.offer
     83         self.name = 'offer_{}'.format(self.offer.id)
     84         if self.offer:
     85             self.CreateInfoBox(attributes.upperText, attributes.upperSize or vgsUiConst.VGS_FONTSIZE_OFFER)
     86         self.imageLayer = Transform(name='imageLayer', parent=self, align=uiconst.TOALL, scalingCenter=(0.5, 0.5), bgColor=vgsUiConst.OFFER_BACKGROUND_COLOR)
     87         if self.offer and self.offer.label:
     88             Ribbon(parent=self, align=uiconst.TOPLEFT, text=self.offer.label.description, texture_path=self.offer.label.url, state=uiconst.UI_DISABLED, idx=0, padding=(-10, -10, 0, 0))
     89         if self.offer:
     90             self.lazySprite = LazyUrlSprite(parent=self.imageLayer, align=uiconst.TOALL, imageUrl=self.offer.imageUrl)
     91         GradientSprite(name='OfferGradient', align=uiconst.TOALL, bgParent=self.imageLayer, rgbData=((1.0, vgsUiConst.OFFER_RADIAL_SHADOW),), alphaData=((0.0, 0.0), (1.0, 1.0)), radial=True, idx=0)
     92 
     93     def OnClick(self):
     94         if self.offer:
     95             sm.GetService('vgsService').ShowOffer(self.offer.id)
     96 
     97     def CreateInfoBox(self, upperText, upperSize):
     98         textLayer = Container(name='TextLayer', parent=self, align=uiconst.TOALL)
     99         infoBox = ContainerAutoSize(name='InfoBox', parent=textLayer, align=uiconst.TOBOTTOM, height=vgsUiConst.OFFER_INFO_BOX_HEIGHT, state=uiconst.UI_DISABLED, bgColor=vgsUiConst.OFFER_TEXT_BOX_COLOR)
    100         Label(parent=infoBox, align=uiconst.TOTOP, text=upperText, fontsize=upperSize, padding=(vgsUiConst.OFFER_INFO_PADDING,
    101          2,
    102          vgsUiConst.OFFER_INFO_PADDING,
    103          -2), fontStyle=STYLE_HEADER, uppercase=True, lineSpacing=-0.15, color=Color.WHITE)
    104         priceCont = Container(parent=infoBox, align=uiconst.TOTOP, height=len(self.offer.offerPricings) * 24, padding=(8, 2, 8, 2))
    105         for currency, price, basePrice in iter_currencies(self.offer):
    106             PriceTagMedium(parent=priceCont, align=uiconst.TOTOP, currency=currency, price=price, basePrice=basePrice)
    107 
    108     def OnMouseEnter(self, *args):
    109         uicore.animations.Tr2DScaleTo(self.imageLayer, startScale=self.imageLayer.scale, endScale=(1.02, 1.02), duration=0.2)
    110 
    111     def OnMouseExit(self, *args):
    112         uicore.animations.Tr2DScaleTo(self.imageLayer, self.imageLayer.scale, endScale=(1.0, 1.0), duration=0.2)
    113 
    114     def GetMenu(self, *args):
    115         if session.role & ROLE_PROGRAMMER:
    116             return [('offerid: %s' % self.offer.id, lambda : blue.pyos.SetClipboardData(str(self.offer.id)))]
    117 
    118 
    119 class VgsOfferPreview(Container):
    120     default_name = 'OfferPreview'
    121     default_align = uiconst.TOPLEFT
    122     default_state = uiconst.UI_PICKCHILDREN
    123     default_clipChildren = True
    124     charID = industry.Property('_charID', 'on_charid')
    125     typeID = industry.Property('_typeID', 'on_typeid')
    126 
    127     def ApplyAttributes(self, attributes):
    128         Container.ApplyAttributes(self, attributes)
    129         self.offer = attributes.offer
    130         self._charID = None
    131         self._typeID = None
    132         self.on_charid = signals.Signal(signalName='on_charid')
    133         self.on_typeid = signals.Signal(signalName='on_typeid')
    134         self.on_charid.connect(self.OnPickCharacter)
    135         self.on_typeid.connect(self.OnPickType)
    136         self.charButtons = []
    137         self._charButtonsDisplayed = False
    138         self.isPreviewOffer = False
    139         for product in self.offer.productQuantities.itervalues():
    140             if IsPreviewable(GetPreviewType(product.typeId)):
    141                 self.typeID = product.typeId
    142                 self.isPreviewOffer = True
    143 
    144         if not self.isPreviewOffer:
    145             self._openServiceOfferCard()
    146         elif not IsApparel(self.typeID):
    147             self._openSkinOfferCard()
    148         else:
    149             self._openApperalOfferCard()
    150 
    151     def _createOfferCard(self):
    152         textLayer = Container(name='TextLayer', parent=self)
    153         descriptionBox = ContainerAutoSize(parent=textLayer, align=uiconst.TOBOTTOM, bgColor=vgsUiConst.OFFER_TEXT_BOX_COLOR, clipChildren=True, name='DescriptionBox')
    154         descriptionText = Label(parent=descriptionBox, align=uiconst.TOTOP, text=self.offer.description, fontsize=vgsUiConst.VGS_FONTSIZE_SMALL, padding=vgsUiConst.STORE_OFFER_CARD_DESCRIPTION_PADDING, name='DescriptionText')
    155         titleBox = ContainerAutoSize(name='InfoBox', parent=textLayer, align=uiconst.TOBOTTOM, bgColor=vgsUiConst.OFFER_TEXT_BOX_COLOR)
    156         titleText = Label(parent=titleBox, align=uiconst.TOTOP, text=self.offer.name, fontsize=vgsUiConst.VGS_FONTSIZE_LARGE, padding=vgsUiConst.STORE_OFFER_CARD_TITLE_PADDING, fontStyle=STYLE_HEADER, uppercase=True, lineSpacing=-0.15, name='TitleText')
    157         textLayerScreenHeight = textLayer.GetAbsoluteSize()[1]
    158         descriptionScreenHeight = descriptionText.GetAbsoluteSize()[1]
    159         titleBoxScreenHeight = titleBox.GetAbsoluteSize()[1]
    160         titleTextScreenHeight = titleText.GetAbsoluteSize()[1]
    161         descriptionMaxHeight = textLayerScreenHeight - (titleBoxScreenHeight + titleTextScreenHeight) - STORE_PREVIEW_OVERLAY_HEIGHT
    162         if descriptionScreenHeight > 50:
    163             descriptionBox.maxHeight = 50
    164             self._makeCollapsable(parent=textLayer, target=descriptionBox, contentMaxHeight=descriptionMaxHeight, contentMinHeight=50)
    165 
    166     def _addLoading(self):
    167         self.loadingLayer = Container(parent=self, align=uiconst.TOALL, state=uiconst.UI_DISABLED)
    168         self.loadingWheel = LoadingWheel(parent=self.loadingLayer, align=uiconst.CENTER, state=uiconst.UI_DISABLED, opacity=0.0)
    169         self.cover = Sprite(parent=self.loadingLayer, align=uiconst.TOALL, texturePath='res:/UI/Texture/preview/asset_preview_background.png')
    170 
    171     def _addPreviewImageLayer(self):
    172         self.imageLayer = Transform(name='imageLayer', parent=self, align=uiconst.TOALL, scalingCenter=(0.5, 0.5), bgColor=vgsUiConst.OFFER_BACKGROUND_COLOR)
    173         self.previewContainer = PreviewContainer(parent=self.imageLayer, OnStartLoading=self.OnStartLoading, OnStopLoading=self.OnStopLoading)
    174         self.lazySprite = LazyUrlSprite(parent=self.imageLayer, align=uiconst.TOALL, imageUrl=self.offer.imageUrl, state=uiconst.UI_DISABLED)
    175         GradientSprite(name='OfferGradient', align=uiconst.TOALL, bgParent=self.imageLayer, rgbData=((1.0, vgsUiConst.OFFER_RADIAL_SHADOW),), alphaData=((0.0, 0.0), (1.0, 1.0)), radial=True, idx=0)
    176 
    177     def _addCharacterPicker(self):
    178         self.characterPickerLayer = ContainerAutoSize(parent=self, align=uiconst.TOPLEFT, state=uiconst.UI_PICKCHILDREN, top=50 if self.offer.label else 10, left=10)
    179         self.CreateCharacterButtons()
    180 
    181     def _openServiceOfferCard(self):
    182         self._createOfferCard()
    183         self._addLoading()
    184         self.imageLayer = Transform(name='imageLayer', parent=self, align=uiconst.TOALL, scalingCenter=(0.5, 0.5), bgColor=vgsUiConst.OFFER_BACKGROUND_COLOR)
    185         self.lazySprite = LazyUrlSprite(parent=self.imageLayer, align=uiconst.TOALL, imageUrl=self.offer.imageUrl, state=uiconst.UI_DISABLED)
    186         GradientSprite(name='OfferGradient', align=uiconst.TOALL, bgParent=self.imageLayer, rgbData=((1.0, vgsUiConst.OFFER_RADIAL_SHADOW),), alphaData=((0.0, 0.0), (1.0, 1.0)), radial=True, idx=0)
    187         uicore.animations.SpMaskOut(self.cover, duration=0.4)
    188 
    189     def _openSkinOfferCard(self):
    190         self._createOfferCard()
    191         self._addLoading()
    192         self._addPreviewImageLayer()
    193 
    194     def _openApperalOfferCard(self):
    195         self._createOfferCard()
    196         self._addCharacterPicker()
    197         self._addLoading()
    198         self._addPreviewImageLayer()
    199 
    200     def _makeCollapsable(self, parent, target, contentMaxHeight, contentMinHeight):
    201         collapseBox = ContainerAutoSize(parent=parent, align=uiconst.TOBOTTOM_NOPUSH, top=-36, name='CollapseBox')
    202         CollapseButton(parent=collapseBox, align=uiconst.TOPRIGHT, target=target, contentMaxHeight=contentMaxHeight, contentCollapsedHeight=contentMinHeight, name='CollapseButton')
    203 
    204     def CreateCharacterButtons(self):
    205         characters = sm.GetService('cc').GetCharactersToSelect()
    206         charIDList = itertools.chain([None], map(lambda c: c.characterID, characters))
    207         for i, charID in enumerate(charIDList):
    208             button = CharacterButton(parent=self.characterPickerLayer, align=uiconst.RELATIVE, pos=(0,
    209              i * 43,
    210              38,
    211              38), charID=charID, onClick=lambda charID: setattr(self, 'charID', charID), isActive=charID == self.charID, opacity=0.0, state=uiconst.UI_DISABLED)
    212             self.on_charid.connect(button.OnCharID)
    213             self.on_typeid.connect(button.OnTypeID)
    214             self.charButtons.append(button)
    215 
    216     def ShowCharacterButtons(self):
    217         if self._charButtonsDisplayed:
    218             return
    219         for i, button in enumerate(self.charButtons):
    220             uicore.animations.MoveInFromLeft(button, amount=10, duration=0.2, timeOffset=i * 0.1)
    221             uicore.animations.FadeIn(button, duration=0.4, timeOffset=i * 0.1)
    222             button.state = uiconst.UI_NORMAL
    223 
    224         self._charButtonsDisplayed = True
    225 
    226     def HideCharacterButtons(self):
    227         if not self._charButtonsDisplayed:
    228             return
    229         for button in self.charButtons:
    230             uicore.animations.FadeOut(button, duration=0.2)
    231             button.state = uiconst.UI_DISABLED
    232 
    233         self._charButtonsDisplayed = False
    234 
    235     def OnPickCharacter(self, _):
    236         uthread.new(self.ShowPreview)
    237 
    238     def OnPickType(self, _):
    239         if IsPreviewable(self.typeID) and IsApparel(self.typeID) and not IsWearableBy(self.typeID, self.charID):
    240             self.charID = None
    241             return
    242         uthread.new(self.ShowPreview)
    243 
    244     def OnStartLoading(self, _):
    245         if not IsApparel(self.typeID):
    246             self.HideCharacterButtons()
    247         else:
    248             self.lazySprite.SetOpacity(0)
    249         if self.isPreviewOffer:
    250             uicore.animations.FadeIn(self.loadingWheel, duration=0.3, timeOffset=0.2)
    251             uicore.animations.SpMaskIn(self.cover, duration=0.5, sleep=True)
    252             self.previewContainer.Show()
    253 
    254     def OnStopLoading(self, preview, success):
    255         if IsApparel(self.typeID):
    256             self.ShowCharacterButtons()
    257         else:
    258             self.lazySprite.SetOpacity(1)
    259         if self.isPreviewOffer:
    260             uicore.animations.FadeOut(self.loadingWheel, duration=0.3)
    261             uicore.animations.SpMaskOut(self.cover, duration=0.5)
    262 
    263     def ShowPreview(self):
    264         typeID = GetPreviewType(self.typeID)
    265         if not IsPreviewable(typeID):
    266             return
    267         if IsApparel(self.typeID) and self.charID:
    268             self.previewContainer.PreviewCharacter(self.charID, apparel=[self.typeID])
    269         else:
    270             self.previewContainer.PreviewType(typeID, scenePath='res:/dx9/scene/fitting/fitting.red')
    271         if IsApparel(typeID):
    272             self.previewContainer.AnimEntry(-0.1 + math.pi, 0.0, 0.5 + math.pi, -0.2)
    273         else:
    274             self.previewContainer.AnimEntry(0.3 + math.pi, 0.2, 0.6 + math.pi, -0.3)
    275 
    276     def PrepareClose(self):
    277         uicore.animations.SpMaskIn(self.cover, duration=0.2, sleep=True)
    278         if self.isPreviewOffer:
    279             self.previewContainer.SetState(uiconst.UI_HIDDEN)
    280 
    281 
    282 @Component(ToggleButtonEffect(bgElementFunc=lambda parent, _: parent.arrow, opacityIdle=0.5, opacityHover=0.8, opacityMouseDown=1.0, audioOnEntry='store_hover'))
    283 
    284 class CollapseButton(Container):
    285     default_state = uiconst.UI_NORMAL
    286     default_width = 32
    287     default_height = 32
    288 
    289     def ApplyAttributes(self, attributes):
    290         Container.ApplyAttributes(self, attributes)
    291         self.target = attributes.target
    292         self.contentMaxHeight = attributes.contentMaxHeight
    293         self.contentCollapsedHeight = attributes.contentCollapsedHeight
    294         self.arrow = Sprite(parent=self, align=uiconst.TOALL, state=uiconst.UI_DISABLED, texturePath='res:/UI/Texture/Icons/105_32_21.png', rotation=math.pi)
    295 
    296     def Expand(self):
    297         self.target.maxHeight = self.contentMaxHeight
    298         self.Animate(0, self.target.height, 0)
    299 
    300     def Collapse(self):
    301         self.target.maxHeight = self.contentCollapsedHeight
    302         self.Animate(self.target.height, 0, math.pi)
    303 
    304     def Animate(self, startHeight, endHeight, rotation):
    305         uicore.animations.MorphScalar(self.target, 'height', startVal=startHeight, endVal=endHeight, duration=0.4, curveType=uiconst.ANIM_OVERSHOT)
    306         uicore.animations.MorphScalar(self.arrow, 'rotation', startVal=self.arrow.rotation, endVal=rotation, duration=0.2)
    307 
    308     def OnClick(self, *args):
    309         if self.isActive:
    310             self.Collapse()
    311         else:
    312             self.Expand()
    313 
    314 
    315 @Component(RadioButtonEffect(bgElementFunc=lambda parent, _: parent.highlight, idx=0, opacityIdle=0.0, opacityHover=0.5, opacityMouseDown=0.85, audioOnEntry='store_hover'))
    316 
    317 class CharacterButton(Container):
    318     default_state = uiconst.UI_NORMAL
    319 
    320     def ApplyAttributes(self, attributes):
    321         Container.ApplyAttributes(self, attributes)
    322         self.charID = attributes.get('charID', None)
    323         self.onClick = attributes.onClick
    324         self.gender = None
    325         if self.charID:
    326             self.gender = cfg.eveowners.Get(self.charID).gender
    327         self.highlight = Fill(bgParent=self, color=(0.8, 0.8, 0.8, 1.0), padding=[-3,
    328          -3,
    329          -2,
    330          -2], fillCenter=True)
    331         self.portrait = Sprite(parent=self, name='portraitSprite', align=uiconst.TOALL, state=uiconst.UI_DISABLED)
    332         if self.charID:
    333             sm.GetService('photo').GetPortrait(self.charID, 38, self.portrait)
    334         else:
    335             self.portrait.texturePath = 'res:/UI/Texture/Vgs/mannequin.png'
    336 
    337     def OnClick(self, *args):
    338         if not self.disabled:
    339             self.onClick(self.charID)
    340 
    341     def OnCharID(self, container):
    342         self.SetActive(container.charID == self.charID)
    343 
    344     def OnTypeID(self, container):
    345         if self.gender is None or not IsApparel(container.typeID):
    346             return
    347         gender = GetApparelGender(container.typeID)
    348         if gender is None or gender == self.gender:
    349             self.disabled = False
    350             uicore.animations.SpColorMorphTo(self.portrait, endColor=(1.0, 1.0, 1.0), duration=0.4)
    351         else:
    352             self.disabled = True
    353             uicore.animations.SpColorMorphTo(self.portrait, endColor=(0.4, 0.4, 0.4), duration=0.4)
    354 
    355 
    356 GENDER_BY_APPAREL_GENDER = {1: appConst.MALE,
    357  2: None,
    358  3: appConst.FEMALE,
    359  4: 'Missing Gender'}
    360 
    361 def GetApparelGender(typeID):
    362     apparelGender = None
    363     for resource in cfg.paperdollResources:
    364         if resource.typeID == typeID:
    365             if resource.resGender is None:
    366                 apparelGender = None
    367             elif resource.resGender:
    368                 apparelGender = appConst.MALE
    369             elif not resource.resGender:
    370                 apparelGender = appConst.FEMALE
    371 
    372     return apparelGender
    373 
    374 
    375 def IsWearableBy(typeID, charID):
    376     if not IsApparel(typeID):
    377         return False
    378     if charID is None:
    379         return True
    380     apparelGender = GetApparelGender(typeID)
    381     characterGender = cfg.eveowners.Get(charID).gender
    382     return apparelGender is None or apparelGender == characterGender
    383 
    384 
    385 def GetPreviewType(typeID):
    386     if IsBlueprint(typeID):
    387         blueprint = sm.GetService('blueprintSvc').GetBlueprintType(typeID)
    388         return blueprint.productTypeID
    389     return typeID