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

offer_card.py (18942B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\omega\upsell\components\offer_card.py
      2 import eveicon
      3 import uthread2
      4 from carbon.common.script.util.format import FmtAmt
      5 from carbonui import Align, Density, uiconst
      6 from carbonui import TextAlign, TextColor
      7 from carbonui.text.styles import TextBody
      8 from carbonui.text.custom import TextCustom
      9 from carbonui.button.group import ButtonGroup, ButtonSizeMode
     10 from carbonui.control.button import Button, ButtonFrameType, ButtonStyle, ButtonVariant
     11 from carbonui.primitives.container import Container
     12 from carbonui.primitives.containerAutoSize import ContainerAutoSize
     13 from carbonui.primitives.fill import Fill
     14 from carbonui.primitives.frame import Frame
     15 from carbonui.primitives.layoutGrid import LayoutGrid
     16 from carbonui.primitives.stretchspritehorizontal import StretchSpriteHorizontal
     17 from carbonui.primitives.sprite import Sprite
     18 from carbonui.uianimations import animations
     19 from clonegrade.const import ALPHA_TRAINING_MULTIPLIER
     20 from eve.client.script.ui import eveColor
     21 from eve.client.script.ui.omega.upsell import omegaUpsellSignals
     22 from eve.client.script.ui.omega.upsell.components.omega_frame import OmegaOfferFrame, OmegaOfferFrameBestDeal, OmegaFrameState
     23 from eve.client.script.ui.omega.upsell.components.buy_plex_tooltip import BuyPlexTooltip
     24 from eve.client.script.ui.omega.upsell.components.icons import PlexInfoIcon
     25 from eve.client.script.ui.omega.upsell.omegaUpsellConst import OMEGA_SKILL_POINTS_PER_SECOND
     26 from plex.client.api.repository import get_repository
     27 from plex.client.api.signal import on_balance_changed
     28 from vgs.client.currency import get_price_text, OFFER_CURRENCY_PLEX
     29 from eve.common.lib import appConst
     30 from localization import GetByLabel
     31 from store.client.const import OfferLabel
     32 
     33 class OfferCard(Container):
     34     default_state = uiconst.UI_NORMAL
     35     default_width = 280
     36     default_height = 528
     37     animation_duration = 0.4
     38 
     39     def __init__(self, offer_id, offer, *args, **kwargs):
     40         super(OfferCard, self).__init__(*args, **kwargs)
     41         self._update_state_thread = None
     42         self.offer_id = offer_id
     43         self.offer = offer
     44         self._show_price_in_plex = False
     45         self.construct_layout()
     46         self.update()
     47         self.connect_signals()
     48 
     49     def Close(self):
     50         try:
     51             self._kill_update_thread()
     52             self.disconnect_signals()
     53         finally:
     54             super(OfferCard, self).Close()
     55 
     56     def _kill_update_thread(self):
     57         if self._update_state_thread:
     58             self._update_state_thread.kill()
     59             self._update_state_thread = None
     60 
     61     def connect_signals(self):
     62         omegaUpsellSignals.on_show_price_in_plex_change.connect(self.on_show_price_in_plex_change)
     63         on_balance_changed.connect(self.on_plex_balance_changed)
     64 
     65     def disconnect_signals(self):
     66         omegaUpsellSignals.on_show_price_in_plex_change.disconnect(self.on_show_price_in_plex_change)
     67         on_balance_changed.disconnect(self.on_plex_balance_changed)
     68 
     69     def construct_layout(self):
     70         self.construct_containers()
     71         self.construct_frame()
     72         self.construct_label()
     73         self.construct_upgrade_button()
     74         Spacer(parent=self.content_container)
     75         self.construct_price_container()
     76         Spacer(parent=self.content_container)
     77         self.construct_savings_container()
     78         Spacer(parent=self.content_container)
     79         self.construct_skillpoints()
     80         self.construct_duration()
     81 
     82     def construct_containers(self):
     83         self.tag_container = Container(name='tag_container', parent=self)
     84         self.content_container = Container(name='content_container', parent=self, padding=(30, 0, 30, 36))
     85         self.frame_container = Container(name='frame_container', parent=self)
     86 
     87     def construct_frame(self):
     88         if self.ui_label == OfferLabel.UPSELL_BEST_DEAL:
     89             self.frame = OmegaOfferFrameBestDeal(name='frame', parent=self.frame_container)
     90         else:
     91             self.frame = OmegaOfferFrame(name='frame', parent=self.frame_container)
     92 
     93     def construct_label(self):
     94         label = self.ui_label
     95         if not label:
     96             return
     97         if label == OfferLabel.UPSELL_POPULAR_CHOICE:
     98             OfferLabelPopularChoice(parent=self.content_container, align=Align.CENTERTOP)
     99         elif label == OfferLabel.UPSELL_BEST_DEAL:
    100             OfferLabelBestDeal(parent=self.content_container, align=Align.CENTERTOP)
    101 
    102     def construct_duration(self):
    103         duration_container = Container(name='duration_container', parent=self.content_container)
    104         self.duration_label = TextCustom(name='duration_label', parent=duration_container, align=Align.CENTER, fontsize=40, color=eveColor.OMEGA_ACCENT)
    105 
    106     def construct_skillpoints(self):
    107         skillpoints_container = ContainerAutoSize(name='skillpoints_container', parent=self.content_container, align=Align.TOBOTTOM, width=220)
    108         Frame(bgParent=skillpoints_container, color=eveColor.WHITE, opacity=0.1, frameConst=uiconst.FRAME_FILLED_CORNER8)
    109         self.skillpoints_label = TextBody(name='skillpoints_label', parent=skillpoints_container, align=Align.TOTOP, textAlign=TextAlign.CENTER, bold=True, padding=(24, 8, 24, 8))
    110 
    111     def construct_savings_container(self):
    112         self.savings_container = Container(name='savings_container', parent=self.content_container, align=Align.TOBOTTOM, height=26)
    113 
    114     def construct_currency_savings(self):
    115         CurrencySavingsDivider(parent=self.savings_container, align=Align.CENTER, offer=self.offer)
    116 
    117     def construct_plex_savings(self):
    118         PlexSavingsDivider(parent=self.savings_container, align=Align.CENTER, offer=self.offer)
    119 
    120     def construct_no_savings(self):
    121         NoSavingsDivider(parent=self.savings_container, align=Align.CENTER)
    122 
    123     def construct_price_container(self):
    124         self.price_container = Container(name='price_container', parent=self.content_container, align=Align.TOBOTTOM, height=89)
    125 
    126     def construct_plex_price(self):
    127         price_month_wrapper = Container(name='price_month_wrapper', parent=self.price_container, align=Align.TOTOP, height=40)
    128         price_month_container = LayoutGrid(name='price_month_container', parent=price_month_wrapper, align=Align.CENTER, columns=2, contentSpacing=(8, 0))
    129         price_month_icon = PlexInfoIcon(name='plex_icon', parent=price_month_container, align=uiconst.CENTERLEFT, width=40, height=40)
    130         price_month_label = TextCustom(name='price_plex_label', parent=price_month_container, align=Align.CENTERLEFT, text=self.monthly_price_text, color=TextColor.HIGHLIGHT, fontsize=40)
    131         price_month_suffix_label = TextCustom(name='price_month_suffix_label', parent=self.price_container, align=Align.TOTOP, text=GetByLabel('UI/OmegaUpsell/PerMonth'), textAlign=TextAlign.CENTER, color=TextColor.NORMAL, fontsize=14, padTop=2)
    132         price_total_container = ContainerAutoSize(name='price_total_wrapper', parent=self.price_container, align=Align.CENTERBOTTOM, height=19, top=-2)
    133         price_total_icon = PlexInfoIcon(name='plex_icon', parent=price_total_container, align=uiconst.TOLEFT, width=14, height=14)
    134         price_total_label = TextBody(name='price_total_label', parent=price_total_container, align=Align.TOLEFT, text=self.total_price_text, color=eveColor.PLEX_YELLOW, padLeft=2)
    135         if not self.has_enough_plex:
    136             price_month_icon.icon_color = TextColor.DISABLED
    137             price_month_label.color = TextColor.DISABLED
    138             price_month_suffix_label.color = TextColor.DISABLED
    139             price_total_icon.icon_color = TextColor.DISABLED
    140             price_total_label.color = TextColor.DISABLED
    141 
    142     def construct_currency_price(self):
    143         price_month_container = ContainerAutoSize(name='price_month_container', parent=self.price_container, align=Align.TOTOP, padTop=-8)
    144         price_month_label = TextCustom(name='price_month_label', parent=price_month_container, align=Align.TOTOP, text=self.monthly_price_text, textAlign=TextAlign.CENTER, color=TextColor.HIGHLIGHT, fontsize=40)
    145         price_month_suffix_label = TextCustom(name='price_month_suffix_label', parent=price_month_container, align=Align.TOTOP, text=GetByLabel('UI/OmegaUpsell/PerMonth'), textAlign=TextAlign.CENTER, color=TextColor.NORMAL, fontsize=14, padTop=-2)
    146         price_total_label = TextBody(name='price_total_label', parent=self.price_container, align=Align.TOBOTTOM, textAlign=TextAlign.CENTER, text=self.total_price_text, color=eveColor.OMEGA_ACCENT, padBottom=-2)
    147 
    148     def construct_upgrade_button(self):
    149         button_group = ButtonGroup(name='button_group', parent=self.content_container, align=Align.TOBOTTOM, density=Density.EXPANDED, button_size_mode=ButtonSizeMode.STRETCH)
    150         self.upgrade_button = Button(name='upgrade_button', label=GetByLabel('UI/OmegaUpsell/Upgrade'), texturePath=eveicon.omega, style=ButtonStyle.MONETIZATION, variant=ButtonVariant.PRIMARY, frame_type=ButtonFrameType.CUT_BOTTOM_LEFT_RIGHT, func=self.on_upgrade_button_click)
    151         button_group.add_button(self.upgrade_button)
    152 
    153     def update(self):
    154         self.duration_label.text = '%s %s' % (self.month_duration_amount, self.duration_unit_text.capitalize())
    155         self.update_skillpoints()
    156         self.update_savings()
    157         self.update_price()
    158         self.update_button()
    159         self.update_tooltip()
    160 
    161     def update_skillpoints(self):
    162         duration_seconds = self.offer.schedule_data.cycle_duration
    163         extra_sp = FmtAmt(duration_seconds * OMEGA_SKILL_POINTS_PER_SECOND * ALPHA_TRAINING_MULTIPLIER)
    164         self.skillpoints_label.text = GetByLabel('UI/OmegaUpsell/OfferSkillPointsLabel', sp_color=eveColor.CRYO_BLUE_HEX, sp_amount=extra_sp)
    165 
    166     def update_savings(self):
    167         self.savings_container.Flush()
    168         if self.show_price_in_plex and self.offer.has_plex_discount:
    169             self.construct_plex_savings()
    170         elif not self.show_price_in_plex and self.offer.has_currency_discount:
    171             self.construct_currency_savings()
    172         else:
    173             self.construct_no_savings()
    174         animations.FadeTo(self.savings_container, 0.0, 1.0)
    175 
    176     def update_price(self):
    177         self.price_container.Flush()
    178         if self.show_price_in_plex:
    179             self.construct_plex_price()
    180         else:
    181             self.construct_currency_price()
    182         animations.FadeTo(self.price_container, 0.0, 1.0)
    183 
    184     def update_button(self):
    185         if self.show_price_in_plex and not self.has_enough_plex:
    186             self.upgrade_button.Disable()
    187         else:
    188             self.upgrade_button.Enable()
    189 
    190     def update_tooltip(self):
    191         if self.show_price_in_plex and not self.has_enough_plex:
    192             self.upgrade_button.tooltipPanelClassInfo = BuyPlexTooltip(required_amount=int(self.total_price))
    193         else:
    194             self.upgrade_button.tooltipPanelClassInfo = None
    195 
    196     def on_upgrade_button_click(self, *args):
    197         omegaUpsellSignals.upgrade_to_offer(self.offer_id, self.show_price_in_plex)
    198 
    199     def on_show_price_in_plex_change(self, value):
    200         self.show_price_in_plex = value
    201 
    202     def on_plex_balance_changed(self, *args):
    203         self.update()
    204 
    205     def OnMouseEnter(self, *args):
    206         self._kill_update_thread()
    207         self._update_state_thread = uthread2.start_tasklet(self._update_state)
    208 
    209     def OnMouseExit(self, *args):
    210         self._kill_update_thread()
    211         self.frame.set_state(OmegaFrameState.IDLE)
    212 
    213     def _update_state(self):
    214         if self.show_price_in_plex and not self.has_enough_plex:
    215             self.frame.set_state(OmegaFrameState.IDLE)
    216         else:
    217             self.frame.set_state(OmegaFrameState.HOVER)
    218 
    219     @property
    220     def month_duration_amount(self):
    221         return self.offer.schedule_data.cycle_duration * appConst.SEC / appConst.MONTH30
    222 
    223     @property
    224     def duration_unit_text(self):
    225         return GetByLabel('/Carbon/UI/Common/WrittenDateTimeQuantity/MonthText', months=self.month_duration_amount)
    226 
    227     @property
    228     def monthly_price_text(self):
    229         return get_price_text(price=self.total_price_in_cents / self.month_duration_amount / 100.0, currency=self.currency_code, useSpace=False)
    230 
    231     @property
    232     def total_price(self):
    233         return self.total_price_in_cents / 100.0
    234 
    235     @property
    236     def total_price_in_cents(self):
    237         if self.show_price_in_plex:
    238             return self.offer.price_before_tax.plex_price_in_cents
    239         else:
    240             return self.offer.price_before_tax.currency_price_in_cents
    241 
    242     @property
    243     def total_price_text(self):
    244         amount_text = get_price_text(price=self.total_price, currency=self.currency_code, useSpace=False)
    245         return '%s %s' % (amount_text, GetByLabel('UI/Common/Total').lower())
    246 
    247     @property
    248     def currency_code(self):
    249         if self.show_price_in_plex:
    250             return OFFER_CURRENCY_PLEX
    251         else:
    252             return self.offer.price_before_tax.currency_code
    253 
    254     @property
    255     def has_enough_plex(self):
    256         return get_repository().get_balance() >= self.total_price
    257 
    258     @property
    259     def ui_label(self):
    260         return self.offer.ui_annotations.get('label')
    261 
    262     @property
    263     def show_price_in_plex(self):
    264         return self._show_price_in_plex
    265 
    266     @show_price_in_plex.setter
    267     def show_price_in_plex(self, value):
    268         self._show_price_in_plex = value
    269         self.update()
    270 
    271 
    272 class HeaderFill(Fill):
    273     default_color = eveColor.OMEGA_ACCENT
    274     default_opacity = 0.1
    275 
    276     def show(self, animate = True):
    277         if animate:
    278             animations.FadeTo(self, self.opacity, self.default_opacity)
    279         else:
    280             self.opacity = self.default_opacity
    281 
    282     def hide(self, animate = True):
    283         if animate:
    284             animations.FadeTo(self, self.opacity, 0.0)
    285         else:
    286             self.opacity = 0.0
    287 
    288 
    289 class Spacer(Container):
    290     default_align = Align.TOBOTTOM
    291     default_height = 40
    292 
    293 
    294 class CurrencySavingsDivider(Container):
    295     default_width = 220
    296     default_height = 26
    297 
    298     def __init__(self, offer, *args, **kwargs):
    299         super(CurrencySavingsDivider, self).__init__(*args, **kwargs)
    300         self.offer = offer
    301         self.construct_layout()
    302         self.update()
    303 
    304     def construct_layout(self):
    305         self.left_container = Container(name='left_container', parent=self, align=Align.TOLEFT)
    306         Fill(parent=self.left_container, align=Align.VERTICALLY_CENTERED, height=1, color=eveColor.WHITE, opacity=0.4)
    307         self.center_container = Container(name='center_container', parent=self, align=Align.TOLEFT, padding=(8, 0, 8, 0))
    308         self.construct_label()
    309         self.right_container = Container(name='right_container', parent=self, align=Align.TOLEFT)
    310         Fill(parent=self.right_container, align=Align.VERTICALLY_CENTERED, height=1, color=eveColor.WHITE, opacity=0.4)
    311 
    312     def construct_label(self):
    313         self.label = CurrencySavingsLabel(parent=self.center_container, align=Align.TOTOP, offer=self.offer)
    314 
    315     def update(self):
    316         side_container_width = (self.default_width - self.label.component_width) * 0.5
    317         self.center_container.width = self.label.component_width
    318         self.left_container.width = side_container_width
    319         self.right_container.width = side_container_width
    320 
    321 
    322 class PlexSavingsDivider(CurrencySavingsDivider):
    323 
    324     def construct_label(self):
    325         self.label = PlexSavingsLabel(parent=self.center_container, align=Align.TOTOP, offer=self.offer)
    326 
    327 
    328 class NoSavingsDivider(Container):
    329     default_width = 220
    330     default_height = 26
    331 
    332     def __init__(self, *args, **kwargs):
    333         super(NoSavingsDivider, self).__init__(*args, **kwargs)
    334         self.construct_layout()
    335 
    336     def construct_layout(self):
    337         Sprite(parent=self, align=Align.CENTER, texturePath='res:/UI/Texture/classes/OmegaUpsell/divider.png', pos=(0, 0, 220, 26))
    338 
    339 
    340 class CurrencySavingsLabel(Container):
    341     default_height = 26
    342     label_padding = 8
    343     text_color = eveColor.OMEGA_TAG_TEXT_GREEN
    344 
    345     def __init__(self, offer, *args, **kwargs):
    346         super(CurrencySavingsLabel, self).__init__(*args, **kwargs)
    347         self.offer = offer
    348         self.construct_layout()
    349         self.update()
    350 
    351     def construct_layout(self):
    352         Frame(bgParent=self, color=eveColor.COPPER_OXIDE_GREEN, frameConst=uiconst.FRAME_FILLED_CORNER4)
    353         self.construct_label()
    354 
    355     def construct_label(self):
    356         self.label = TextCustom(name='label', parent=self, align=Align.TOPLEFT, color=self.text_color, fontSize=14, bold=True, padding=(self.label_padding,
    357          3,
    358          self.label_padding,
    359          0))
    360 
    361     def update(self):
    362         self.label.text = self.label_text
    363         self.width = self.component_width
    364 
    365     @property
    366     def label_text(self):
    367         return '%s %s%%' % (GetByLabel('UI/OmegaUpsell/Save'), FmtAmt(self.offer.currency_discount_percent))
    368 
    369     @property
    370     def component_width(self):
    371         w, _ = TextCustom.MeasureTextSize(self.label_text, fontSize=14, bold=True)
    372         return w + self.label_padding * 2
    373 
    374 
    375 class PlexSavingsLabel(CurrencySavingsLabel):
    376 
    377     @property
    378     def label_text(self):
    379         return '%s %s%%' % (GetByLabel('UI/OmegaUpsell/Save'), FmtAmt(self.offer.plex_discount_percent))
    380 
    381 
    382 class OfferLabelPopularChoice(Container):
    383     default_height = 24
    384     default_padding = (0, -12, 0, 0)
    385     edge_size = 14
    386 
    387     def __init__(self, *args, **kwargs):
    388         super(OfferLabelPopularChoice, self).__init__(*args, **kwargs)
    389         self.construct_layout()
    390         self.update()
    391 
    392     def construct_layout(self):
    393         self.label = TextCustom(parent=self, align=Align.CENTERTOP, color=eveColor.OMEGA_TAG_TEXT_RED, bold=True, padTop=2)
    394         StretchSpriteHorizontal(parent=self, align=Align.TOTOP_NOPUSH, texturePath='res:/UI/Texture/classes/OmegaUpsell/offer_label_red.png', leftEdgeSize=self.edge_size, rightEdgeSize=self.edge_size, height=self.default_height)
    395 
    396     def update(self):
    397         text = GetByLabel('UI/OmegaUpsell/Labels/PopularChoice')
    398         text_width, _ = TextCustom.MeasureTextSize(text, bold=True)
    399         self.label.text = text
    400         self.width = text_width + self.edge_size * 2
    401 
    402 
    403 class OfferLabelBestDeal(Container):
    404     default_height = 30
    405     default_padding = (0, -8, 0, 0)
    406     edge_size = 2
    407 
    408     def __init__(self, *args, **kwargs):
    409         super(OfferLabelBestDeal, self).__init__(*args, **kwargs)
    410         self.construct_layout()
    411         self.update()
    412 
    413     def construct_layout(self):
    414         self.label = TextCustom(parent=self, align=Align.CENTERTOP, color=eveColor.OMEGA_TAG_TEXT_BLUE, bold=True, padTop=5)
    415         StretchSpriteHorizontal(parent=self, align=Align.TOTOP_NOPUSH, texturePath='res:/UI/Texture/classes/OmegaUpsell/offer_label_blue.png', leftEdgeSize=self.edge_size, rightEdgeSize=self.edge_size, height=self.default_height)
    416 
    417     def update(self):
    418         text = GetByLabel('UI/OmegaUpsell/Labels/BestDeal')
    419         text_width, _ = TextCustom.MeasureTextSize(text, bold=True)
    420         self.label.text = text
    421         self.width = text_width + 12 + self.edge_size * 2