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

default_offer_card.py (18629B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\omega\upsell\components\default_offer_card.py
      2 import appConst
      3 import eveicon
      4 import evetypes
      5 import math
      6 import uthread2
      7 from carbonui import Align, Axis, AxisAlignment, ButtonVariant, ButtonStyle, Density, PickState, uiconst
      8 from carbonui import TextAlign, TextColor
      9 from carbonui.text.styles import TextBody
     10 from carbonui.text.custom import TextCustom
     11 from carbonui.button.group import ButtonGroup
     12 from carbonui.control.button import Button
     13 from carbonui.control.scrollContainer import ScrollContainer
     14 from carbonui.primitives.container import Container
     15 from carbonui.primitives.containerAutoSize import ContainerAutoSize
     16 from carbonui.primitives.flowcontainer import FlowContainer
     17 from carbonui.primitives.frame import Frame
     18 from carbonui.primitives.gradientSprite import GradientSprite
     19 from carbonui.primitives.sprite import Sprite
     20 from carbonui.uianimations import animations
     21 from eve.client.script.ui import eveColor
     22 from eve.client.script.ui.control.eveLoadingWheel import LoadingWheel
     23 from eve.client.script.ui.control.infoIcon import InfoIcon
     24 from eve.client.script.ui.control.itemIcon import ItemIcon
     25 from player_messaging.client.ui_message import message_player
     26 from eve.client.script.ui.omega.upsell import omegaUpsellSignals
     27 from eve.client.script.ui.omega.upsell.components.buy_plex_tooltip import BuyPlexTooltip
     28 from eve.client.script.ui.omega.upsell.components.currency_toggle import CurrencyToggle
     29 from eve.client.script.ui.omega.upsell.components.icons import PlexInfoIcon
     30 from eve.client.script.ui.omega.upsell.components.omega_frame import DefaultOfferBackgroundHighlight
     31 from eve.client.script.ui.shared.neocom.wallet import walletSignals
     32 from plex.client.api.repository import get_repository
     33 from vgs.client.currency import get_price_text, CURRENCY_SYMBOL, CURRENCY_TEXT, OFFER_CURRENCY_PLEX
     34 from localization import GetByLabel
     35 from plex.client.api.signal import on_balance_changed
     36 from stackless_response_router.exceptions import TimeoutException
     37 from store.client.const import StoreProductType
     38 from store.client.offers.offer_provider_svc import get_offer_provider_svc
     39 from utillib import KeyVal
     40 
     41 class DefaultOfferCard(Container):
     42     default_align = Align.CENTERTOP
     43     default_minWidth = 400
     44     default_maxWidth = 800
     45 
     46     def __init__(self, *args, **kwargs):
     47         super(DefaultOfferCard, self).__init__(*args, **kwargs)
     48         self.offer_id = None
     49         self.offer = None
     50         self.products = []
     51         self.show_price_in_plex = False
     52         self._update_thread = None
     53         self.construct_layout()
     54         self.update()
     55         on_balance_changed.connect(self.on_plex_balance_changed)
     56 
     57     def Close(self):
     58         try:
     59             self.kill_update_thread()
     60             self.disconnect_signals()
     61         finally:
     62             super(DefaultOfferCard, self).Close()
     63 
     64     def on_plex_balance_changed(self, *args):
     65         self.update()
     66 
     67     def disconnect_signals(self):
     68         on_balance_changed.disconnect(self.on_plex_balance_changed)
     69 
     70     def kill_update_thread(self):
     71         if self._update_thread is not None:
     72             self._update_thread.kill()
     73             self._update_thread = None
     74 
     75     def construct_layout(self):
     76         self.construct_containers()
     77         self.construct_loading_wheel()
     78         self.construct_background()
     79         self.construct_frame()
     80         self.construct_left_content()
     81         self.construct_center_content()
     82         self.construct_right_content()
     83 
     84     def construct_containers(self):
     85         self.loading_container = Container(name='loading_container', parent=self)
     86         self.frame_container = Container(name='frame_container', parent=self)
     87         self.content_container = Container(name='content_container', parent=self, opacity=0.0)
     88         self.background_container = Container(name='background_container', parent=self)
     89 
     90     def construct_loading_wheel(self):
     91         self.loading_wheel = LoadingWheel(name='loading_wheel', parent=self.loading_container, align=Align.CENTER)
     92 
     93     def construct_background(self):
     94         DefaultOfferBackgroundHighlight(name='highlight', parent=self.background_container)
     95         GradientSprite(name='background', parent=self.background_container, align=Align.TOALL, pickState=PickState.OFF, rgbData=eveColor.OMEGA_SECONDARY_GRADIENT_RGB_DATA, rotation=-math.pi / 2)
     96 
     97     def construct_frame(self):
     98         Frame(name='corners', parent=self.frame_container, texturePath='res:/UI/Texture/classes/OmegaUpsell/current_offer_frame.png', cornerSize=13)
     99         Frame(name='stroke', parent=self.frame_container, color=eveColor.OMEGA_TERTIARY)
    100 
    101     def construct_left_content(self):
    102         left_prop_container = Container(name='left_prop_container', parent=self.content_container, align=Align.TOLEFT_PROP, width=0.33, padLeft=32)
    103         self.left_content_container = ContainerAutoSize(name='left_content_container', parent=left_prop_container, align=Align.VERTICALLY_CENTERED)
    104         self.description_container = ContainerAutoSize(name='description_container', parent=self.left_content_container, align=Align.TOTOP, width=168)
    105         self.offer_name_label = TextBody(name='offer_name_label', parent=self.description_container, align=Align.TOTOP, color=eveColor.OMEGA_ACCENT, bold=True)
    106         self.price_container = ContainerAutoSize(name='price_container', parent=self.description_container, align=Align.TOTOP, height=48, padTop=5)
    107         self.construct_currency_toggle()
    108 
    109     def construct_currency_price(self):
    110         TextCustom(name='price_label', parent=self.price_container, align=Align.TOLEFT, text=self.total_price_text, color=TextColor.HIGHLIGHT, fontsize=40)
    111 
    112     def construct_plex_price(self):
    113         plex_icon = PlexInfoIcon(name='plex_icon', parent=self.price_container, align=Align.TOLEFT, width=40, height=40, padLeft=-8)
    114         price_plex_label = TextCustom(name='price_label', parent=self.price_container, align=Align.TOLEFT, text=self.total_price_text, color=TextColor.HIGHLIGHT, fontsize=40, padLeft=-4)
    115         if not self.has_enough_plex:
    116             plex_icon.icon_color = TextColor.DISABLED
    117             price_plex_label.color = TextColor.DISABLED
    118 
    119     def construct_currency_toggle(self):
    120         self.currency_container = ContainerAutoSize(name='currency_container', parent=self.left_content_container, align=Align.TOTOP, height=32, opacity=0.0)
    121         self.currency_label = TextBody(name='currency_label', parent=self.currency_container, align=Align.TOLEFT, color=TextColor.HIGHLIGHT, padTop=6)
    122         currency_toggle_container = Container(name='currency_toggle_container', parent=self.currency_container, align=Align.TOLEFT, width=38, padding=(8, 0, 8, 0))
    123         self.currency_toggle = CurrencyToggle(name='currency_toggle', parent=currency_toggle_container, align=Align.CENTER)
    124         self.currency_toggle.on_switch_changed.connect(self.on_show_price_in_plex_change)
    125         self.currency_toggle.Disable()
    126         self.plex_label = TextBody(name='plex_label', parent=self.currency_container, align=Align.TOLEFT, text=GetByLabel('UI/Common/PLEX'), color=eveColor.PLEX_YELLOW, padTop=6)
    127         plex_icon_container = Container(name='plex_icon_container', parent=self.currency_container, align=Align.TOLEFT, width=14)
    128         PlexInfoIcon(name='plex_icon', parent=plex_icon_container, align=Align.CENTER, width=14, height=14, padLeft=6)
    129 
    130     def construct_center_content(self):
    131         self.center_container = Container(name='center_container', parent=self.content_container, align=Align.TOLEFT_PROP, width=0.34, padding=(0, 8, 0, 8))
    132         self.products_label = TextBody(name='products_label', parent=self.center_container, align=Align.TOTOP, textAlign=TextAlign.CENTER, color=eveColor.OMEGA_ACCENT, opacity=0.6)
    133         self.products_scroll_container = ScrollContainer(name='products_scroll_container', parent=self.center_container, align=Align.TOTOP, height=92, padTop=12)
    134         self.product_container = FlowContainer(name='product_container', parent=self.products_scroll_container, align=Align.TOTOP, contentAlignment=AxisAlignment.CENTER, contentSpacing=(8, 8))
    135 
    136     def construct_right_content(self):
    137         button_group = ButtonGroup(name='button_group', parent=self.content_container, align=Align.CENTERRIGHT, orientation=Axis.VERTICAL, density=Density.EXPANDED, padRight=32)
    138         self.upgrade_button = Button(name='upgrade_button', parent=button_group, label=GetByLabel('UI/OmegaUpsell/Upgrade'), texturePath=eveicon.omega, variant=ButtonVariant.PRIMARY, style=ButtonStyle.MONETIZATION, func=self.on_upgrade_button)
    139         Button(name='more_offers_button', parent=button_group, label=GetByLabel('UI/OmegaUpsell/MoreOffers'), variant=ButtonVariant.GHOST, style=ButtonStyle.MONETIZATION, func=self.on_more_offers_button)
    140 
    141     def update(self):
    142         self.kill_update_thread()
    143         self._update_thread = uthread2.start_tasklet(self.update_async)
    144 
    145     def update_async(self):
    146         self.load_offer()
    147         if not self.offer:
    148             return
    149         self.offer_name_label.text = self.offer.localized_name
    150         self.update_price()
    151         self.update_currency()
    152         self.update_products()
    153         self.update_button()
    154         if self.is_omega_offer:
    155             self.products_label.text = None
    156         else:
    157             self.products_label.text = GetByLabel('UI/OmegaUpsell/PackContentsLabel')
    158         animations.FadeOut(self.loading_wheel)
    159         animations.FadeIn(self.content_container)
    160 
    161     def update_price(self):
    162         self.price_container.Flush()
    163         if self.show_price_in_plex:
    164             self.construct_plex_price()
    165         else:
    166             self.construct_currency_price()
    167         animations.FadeTo(self.price_container, 0.0, 1.0)
    168 
    169     def update_currency(self):
    170         if not self.offer.has_plex_price:
    171             self.currency_container.display = False
    172             self.description_container.align = Align.CENTER
    173             return
    174         currency_code = self.monetary_currency_code
    175         currency_symbol = CURRENCY_SYMBOL.get(currency_code, currency_code)
    176         currency_text = CURRENCY_TEXT.get(currency_code, currency_code)
    177         self.currency_label.text = '%s %s' % (currency_symbol, currency_text)
    178         self.currency_toggle.Enable()
    179         animations.FadeIn(self.currency_container)
    180 
    181     def update_products(self):
    182         self.product_container.Flush()
    183         if self.is_omega_offer:
    184             return
    185         for product, quantity in self.products:
    186             self.get_product_icon_class(product.product_type)(parent=self.product_container, product=product, quantity=quantity)
    187 
    188         self.products_label.top = 0 if len(self.products) > 5 else 24
    189 
    190     def get_product_icon_class(self, product_type):
    191         if product_type == StoreProductType.ITEM:
    192             return ProductItemIcon
    193         if product_type == StoreProductType.LICENSE:
    194             return ProductLicenseIcon
    195         if product_type == StoreProductType.BPC:
    196             return ProductBPCIcon
    197         if product_type == StoreProductType.PLEX:
    198             return ProductPlexIcon
    199         if product_type == StoreProductType.CHAR_TRANSFER:
    200             return ProductCharTransferIcon
    201         raise RuntimeError('Unsupported store product type: %s' % product_type)
    202 
    203     def update_button(self):
    204         if self.show_price_in_plex and not self.has_enough_plex:
    205             self.upgrade_button.Disable()
    206             self.upgrade_button.tooltipPanelClassInfo = BuyPlexTooltip(required_amount=int(self.total_price))
    207         else:
    208             self.upgrade_button.Enable()
    209             self.upgrade_button.tooltipPanelClassInfo = None
    210 
    211     def load_offer(self):
    212         if self.offer_id and self.offer:
    213             return
    214         try:
    215             self.offer_id = get_offer_provider_svc().get_default_omega_starter_pack_offer_id()
    216             if self.offer_id is None:
    217                 message_player(GetByLabel('UI/Common/CannotConnectToServer'))
    218                 raise RuntimeError('No default Omega Subscription offer provided')
    219             self.offer = get_offer_provider_svc().get_omega_subscription_offer(self.offer_id)
    220             self.load_products()
    221         except TimeoutException:
    222             message_player(GetByLabel('UI/Common/CannotConnectToServer'))
    223         except Exception:
    224             message_player(GetByLabel('UI/Common/CannotConnectToServer'))
    225             raise
    226 
    227     def load_products(self):
    228         self.products = []
    229         for product_id, quantity in self.offer.products:
    230             product = get_offer_provider_svc().get_product_definition(product_id)
    231             self.products.append((product, quantity))
    232 
    233     def on_show_price_in_plex_change(self, toggle, is_checked):
    234         self.show_price_in_plex = is_checked
    235         self.update()
    236 
    237     def on_upgrade_button(self, *args):
    238         if not self.offer_id:
    239             return
    240         omegaUpsellSignals.upgrade_to_offer(self.offer_id, self.show_price_in_plex)
    241 
    242     def on_more_offers_button(self, *args):
    243         omegaUpsellSignals.on_more_offers_button()
    244 
    245     @property
    246     def has_enough_plex(self):
    247         return get_repository().get_balance() >= self.total_price
    248 
    249     @property
    250     def total_price_text(self):
    251         return get_price_text(price=self.total_price, currency=self.currency_code, useSpace=False)
    252 
    253     @property
    254     def total_price(self):
    255         return self.total_price_in_cents / 100.0
    256 
    257     @property
    258     def total_price_in_cents(self):
    259         if self.show_price_in_plex:
    260             return self.offer.price_before_tax.plex_price_in_cents
    261         else:
    262             return self.offer.price_before_tax.currency_price_in_cents
    263 
    264     @property
    265     def currency_code(self):
    266         if self.show_price_in_plex:
    267             return OFFER_CURRENCY_PLEX
    268         else:
    269             return self.monetary_currency_code
    270 
    271     @property
    272     def monetary_currency_code(self):
    273         return self.offer.price_before_tax.currency_code
    274 
    275     @property
    276     def is_omega_offer(self):
    277         if len(self.products) == 1:
    278             product, quantity = self.products[0]
    279             return product.product_type == StoreProductType.LICENSE
    280         return False
    281 
    282 
    283 class ProductItemIcon(ItemIcon):
    284     default_align = Align.NOALIGN
    285     default_width = 40
    286     default_height = 40
    287 
    288     def __init__(self, product, quantity, *args, **kwargs):
    289         self.product = product
    290         self.quantity = quantity
    291         super(ProductItemIcon, self).__init__(typeID=self.product.type_id, **kwargs)
    292 
    293     def LoadTooltipPanel(self, tooltip_panel, *args):
    294         tooltip_panel.LoadGeneric2ColumnTemplate()
    295         tooltip_panel.state = uiconst.UI_NORMAL
    296         name = evetypes.GetName(self.typeID)
    297         tooltip_panel.AddLabelMedium(text='%s x %s' % (self.quantity, name) if self.quantity > 1 else name, align=Align.CENTERLEFT)
    298         tooltip_panel.AddInfoIcon(typeID=self.typeID, align=Align.CENTERRIGHT)
    299 
    300 
    301 class ProductLicenseIcon(Container):
    302     default_align = Align.NOALIGN
    303     default_state = uiconst.UI_NORMAL
    304     default_width = 40
    305     default_height = 40
    306 
    307     def __init__(self, product, quantity, *args, **kwargs):
    308         super(ProductLicenseIcon, self).__init__(*args, **kwargs)
    309         self.product = product
    310         self.quantity = quantity
    311         self.construct_layout()
    312 
    313     def construct_layout(self):
    314         Sprite(parent=self, align=Align.CENTER, state=uiconst.UI_DISABLED, texturePath=eveicon.omega, pos=(0,
    315          0,
    316          self.default_width,
    317          self.default_height))
    318 
    319     def LoadTooltipPanel(self, tooltip_panel, *args):
    320         tooltip_panel.LoadStandardSpacing()
    321         tooltip_panel.state = uiconst.UI_NORMAL
    322         tooltip_panel.AddLabelMedium(text=GetByLabel('UI/OmegaUpsell/LicenseProductTooltip', amount=self.product.duration * appConst.SEC / appConst.DAY), align=Align.CENTERLEFT)
    323 
    324 
    325 class BPCItemIcon(ItemIcon):
    326 
    327     def __init__(self, product, quantity, *args, **kwargs):
    328         self.product = product
    329         self.quantity = quantity
    330         super(BPCItemIcon, self).__init__(*args, **kwargs)
    331 
    332     def LoadTooltipPanel(self, tooltip_panel, *args):
    333         tooltip_panel.LoadGeneric2ColumnTemplate()
    334         tooltip_panel.state = uiconst.UI_NORMAL
    335         tooltip_panel.AddLabelMedium(text=GetByLabel('UI/OmegaUpsell/BPCProductTooltip', name=evetypes.GetName(self.typeID), amount=self.product.nb_runs), align=Align.CENTERLEFT)
    336         bp_data = sm.GetService('blueprintSvc').GetBlueprintTypeCopy(typeID=self.typeID, original=False)
    337         infoIcon = InfoIcon(typeID=self.typeID, itemID=self.itemID, align=Align.CENTERRIGHT, left=0, top=0, abstractinfo=KeyVal(fullBlueprintData=bp_data))
    338         tooltip_panel.AddCell(infoIcon)
    339 
    340 
    341 class ProductBPCIcon(Container):
    342     default_align = Align.NOALIGN
    343     default_width = 40
    344     default_height = 40
    345 
    346     def __init__(self, product, quantity, *args, **kwargs):
    347         super(ProductBPCIcon, self).__init__(*args, **kwargs)
    348         self.product = product
    349         self.quantity = quantity
    350         self.construct_layout()
    351 
    352     def construct_layout(self):
    353         icon = BPCItemIcon(parent=self, align=Align.CENTER, product=self.product, typeID=self.product.type_id, isCopy=True, pos=(0,
    354          0,
    355          self.default_width,
    356          self.default_height))
    357 
    358 
    359 class ProductPlexIcon(Container):
    360     default_align = Align.NOALIGN
    361     default_width = 40
    362     default_height = 40
    363 
    364     def __init__(self, product, quantity, *args, **kwargs):
    365         super(ProductPlexIcon, self).__init__(*args, **kwargs)
    366         self.product = product
    367         self.quantity = quantity
    368         self.construct_layout()
    369 
    370     def construct_layout(self):
    371         PlexInfoIcon(parent=self, align=Align.CENTER, amount=self.product.plex, pos=(0,
    372          0,
    373          self.default_width,
    374          self.default_height))
    375 
    376 
    377 class ProductCharTransferIcon(Container):
    378     default_align = Align.NOALIGN
    379     default_state = uiconst.UI_NORMAL
    380     default_width = 40
    381     default_height = 40
    382 
    383     def __init__(self, product, quantity, *args, **kwargs):
    384         super(ProductCharTransferIcon, self).__init__(*args, **kwargs)
    385         self.product = product
    386         self.quantity = quantity
    387         self.construct_layout()
    388 
    389     def construct_layout(self):
    390         Sprite(parent=self, align=Align.CENTER, state=uiconst.UI_DISABLED, texturePath='res:/UI/Texture/classes/CharacterSelection/silhuette.png', color=eveColor.WHITE, pos=(0,
    391          0,
    392          self.default_width,
    393          self.default_height))
    394 
    395     def LoadTooltipPanel(self, tooltip_panel, *args):
    396         tooltip_panel.LoadStandardSpacing()
    397         tooltip_panel.state = uiconst.UI_NORMAL
    398         tooltip_panel.AddLabelMedium(text=self.product.localized_name, align=Align.CENTERLEFT)