entry.py (14966B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\achievement_system\client\ui\window\page\rewards\entry.py 2 import datetimeutils 3 import eveicon 4 import eveui 5 import gametime 6 import math 7 import threadutils 8 import uthread2 9 from achievement_system.client.model.definition.reward.type import RewardType 10 from carbonui import Align, ButtonStyle, Density, PickState, TextColor, uiconst 11 from carbonui.control.button import Button 12 from carbonui.primitives.container import Container 13 from carbonui.primitives.containerAutoSize import ContainerAutoSize 14 from carbonui.primitives.frame import Frame 15 from carbonui.primitives.line import Line 16 from carbonui.primitives.sprite import Sprite 17 from carbonui.text.styles import TextBody 18 from carbonui.util.color import Color 19 from eve.client.script.ui import eveColor 20 from eve.client.script.ui.control.gauge import Gauge 21 from eve.client.script.ui.control.tooltips import TooltipPanel 22 from eveicon import IconData 23 from eveui.animation.curve import CurveType 24 from localization import GetByLabel 25 from menucheckers import SessionChecker 26 GLOW_IDLE = 0.5 27 GLOW_HOVER = 1.0 28 OPACITY_IDLE = 0.1 29 OPACITY_HOVER = 0.2 30 FLAIR_SIZE = 300 31 DEFAULT_FLAIR_TEXTURE = 'res:/UI/Texture/Shared/CareerPaths/unclassified_flair.png' 32 REWARD_WIDTH_RATIO = 0.45 33 34 class RewardClaimButton(Button): 35 default_display = True 36 default_style = ButtonStyle.SUCCESS 37 38 def __init__(self, func = None, label_claim = None, label_claimed = None, *args, **kwargs): 39 self._label_claim = label_claim or GetByLabel('UI/Generic/Claim') 40 self._label_claimed = label_claimed or GetByLabel('UI/Generic/Claimed') 41 kwargs.setdefault('func', func) 42 super(RewardClaimButton, self).__init__(*args, **kwargs) 43 self.set_claimable() 44 45 def set_claimable(self): 46 self.Show() 47 self.enable() 48 self.busy = False 49 self.hint = None 50 self.label = self._label_claim 51 52 def set_claimed(self): 53 self.disable() 54 self.busy = False 55 self.label = self._label_claimed 56 57 def start_claim_sequence(self): 58 self.busy = True 59 60 def flag_error(self, error_text = None): 61 self.disable() 62 self.busy = False 63 self.label = error_text or GetByLabel('UI/Inflight/JumpThroughError') 64 65 66 def _get_hours_until(timestamp): 67 timestamp_datetime = datetimeutils.filetime_to_datetime(timestamp) 68 remaining_time_delta = timestamp_datetime - gametime.now() 69 remaining_seconds = remaining_time_delta.total_seconds() 70 return int(float(remaining_seconds) / 3600) 71 72 73 def _get_time_color_icon(timestamp): 74 if timestamp is None: 75 return TextColor.DISABLED 76 remaining_hours = _get_hours_until(timestamp) 77 if remaining_hours < 6: 78 return eveColor.CHERRY_RED 79 if remaining_hours < 24: 80 return eveColor.DUSKY_ORANGE 81 return TextColor.DISABLED 82 83 84 def _get_time_color_text(timestamp): 85 if timestamp is None: 86 return TextColor.SECONDARY 87 remaining_hours = _get_hours_until(timestamp) 88 if remaining_hours < 6: 89 return eveColor.CHERRY_RED 90 if remaining_hours < 24: 91 return eveColor.DUSKY_ORANGE 92 return TextColor.SECONDARY 93 94 95 def _get_time_color_time(timestamp): 96 if timestamp is None: 97 return Color.RGBtoHex(*TextColor.NORMAL) 98 remaining_hours = _get_hours_until(timestamp) 99 if remaining_hours < 6: 100 return eveColor.DANGER_RED_HEX 101 if remaining_hours < 24: 102 return eveColor.WARNING_ORANGE_HEX 103 return Color.RGBtoHex(*TextColor.NORMAL) 104 105 106 def _get_default_expiration_text(expiration_time): 107 time_now = gametime.GetWallclockTime() 108 time_remaining = expiration_time - time_now 109 if time_remaining <= 0: 110 return GetByLabel('UI/Generic/Expired') 111 else: 112 return GetByLabel('UI/Generic/ExpiresIn', color=_get_time_color_time(expiration_time), remaining=time_remaining) 113 114 115 class ExpirationTooltip(TooltipPanel): 116 117 def __init__(self, expiration_time, get_text = None): 118 super(ExpirationTooltip, self).__init__() 119 self._expiration_time = expiration_time 120 self._get_text = get_text or _get_default_expiration_text 121 122 def ApplyAttributes(self, attributes): 123 super(ExpirationTooltip, self).ApplyAttributes(attributes) 124 self.LoadGeneric1ColumnTemplate() 125 self._timer_label = self.AddLabelMedium() 126 uthread2.start_tasklet(self._time_remaining_routine) 127 128 def _time_remaining_routine(self): 129 while not self.destroyed: 130 self._timer_label.color = _get_time_color_text(self._expiration_time) 131 self._timer_label.text = self._get_text(self._expiration_time) 132 remaining = self._expiration_time - gametime.GetWallclockTime() 133 if remaining <= 0: 134 break 135 uthread2.sleep(1) 136 137 138 class ExpirationIcon(ContainerAutoSize): 139 default_align = Align.TORIGHT 140 default_state = uiconst.UI_NORMAL 141 142 def __init__(self, expiration_time, icon_size = 16, get_text = None, *args, **kwargs): 143 super(ExpirationIcon, self).__init__(*args, **kwargs) 144 self._expiration_time = expiration_time 145 self._get_text = get_text or _get_default_expiration_text 146 self._expiration_icon = Sprite(name='expiration_icon', parent=self, state=uiconst.UI_DISABLED, align=Align.CENTER, width=icon_size, height=icon_size, texturePath=eveicon.hourglass, color=_get_time_color_icon(self._expiration_time), opacity=TextColor.NORMAL.opacity) 147 uthread2.start_tasklet(self._time_remaining_routine) 148 149 def _time_remaining_routine(self): 150 while not self.destroyed: 151 self._refresh_color() 152 remaining = self._expiration_time - gametime.GetWallclockTime() 153 if remaining <= 0: 154 break 155 uthread2.sleep(5) 156 157 def _refresh_color(self): 158 self._expiration_icon.color = _get_time_color_icon(self._expiration_time) 159 160 def OnMouseEnter(self, *args): 161 super(ExpirationIcon, self).OnMouseEnter(*args) 162 self._refresh_color() 163 164 def ConstructTooltipPanel(self): 165 return ExpirationTooltip(self._expiration_time, self._get_text) 166 167 168 class AchievementRewardEntry(Container): 169 default_width = 300 170 default_height = 46 171 default_state = uiconst.UI_NORMAL 172 default_clipChildren = True 173 default_padding = (0, 4, 0, 4) 174 175 def __init__(self, controller, progress = 1.0, theme_color = None, flair_texture = None, on_claim = None, expiration_time = None, *args, **kwargs): 176 self.controller = controller 177 self._progress = progress 178 self._theme_color = theme_color or eveColor.SUCCESS_GREEN[:3] 179 self._flair_texture = flair_texture or DEFAULT_FLAIR_TEXTURE 180 self._on_claim = on_claim 181 self._expiration_time = expiration_time 182 super(AchievementRewardEntry, self).__init__(*args, **kwargs) 183 self.construct_layout() 184 self.update() 185 186 def construct_layout(self): 187 self.construct_base_layout() 188 self.construct_right_wrapper() 189 self.construct_left_layout() 190 self.construct_left_side() 191 self.construct_right_layout() 192 self.construct_right_side() 193 self.construct_underlay() 194 195 def construct_base_layout(self): 196 self._left_line = Line(name='leftLine', parent=self, align=Align.TOLEFT, color=eveColor.PLATINUM_GREY, outputMode=uiconst.OUTPUT_COLOR_AND_GLOW, opacity=0.6, glowBrightness=GLOW_IDLE) 197 self._left_line.rgb = self._theme_color 198 self.content = Container(name='content', parent=self) 199 self.flair_cont = Container(name='flairCont', parent=self, pickState=PickState.OFF, clipChildren=True) 200 201 def construct_right_wrapper(self): 202 self._right_wrapper = Container(name='right_wrapper', parent=self.content, align=Align.TORIGHT_PROP, width=REWARD_WIDTH_RATIO) 203 204 def construct_left_layout(self): 205 self.left_container_outer = Container(name='left_container_outer', parent=self.content, align=Align.TOALL, clipChildren=True) 206 self.left_container = Container(name='left_container', parent=self.left_container_outer, align=Align.TOALL, padLeft=16, padRight=16, clipChildren=True) 207 208 def construct_left_side(self): 209 self.construct_gauge(self.left_container) 210 self._construct_title_icons(self.left_container) 211 title_container = Container(parent=self.left_container, align=Align.TOALL, clipChildren=True) 212 self._title_label = TextBody(parent=title_container, align=Align.CENTERLEFT, maxLines=1, bold=True, autoFadeSides=16) 213 214 def _construct_title_icons(self, container): 215 icon = self.controller.icon 216 if icon is None: 217 return 218 container = Container(name='title_icons_container', parent=container, align=Align.TOLEFT, padRight=8, width=16, height=16, pickState=PickState.ON) 219 Sprite(parent=container, texturePath=icon, align=Align.CENTER, width=16, height=16, color=TextColor.SECONDARY, pickState=PickState.OFF) 220 container.OnClick = self.OnClick 221 222 def construct_gauge(self, parent): 223 gauge_container = ContainerAutoSize(name='gauge_container', parent=parent, align=Align.TORIGHT, height=self.height) 224 self._gauge = Gauge(name='gauge', parent=gauge_container, align=Align.CENTERRIGHT, gaugeHeight=2, width=37) 225 self._gauge_label = TextBody(name='gauge_label', parent=gauge_container, align=Align.CENTERRIGHT, padRight=45) 226 227 def construct_right_layout(self): 228 self.right_container = Container(name='right_container', parent=self._right_wrapper, align=Align.TOALL, padLeft=16, padRight=16) 229 230 def construct_right_side(self): 231 cta_cont = ContainerAutoSize(name='cta_cont', parent=self.right_container, align=Align.TORIGHT, padLeft=8) 232 self._claim_button = RewardClaimButton(name='claim_button', parent=cta_cont, align=Align.CENTERLEFT, density=Density.COMPACT, func=lambda *_: self._on_claim(entry=self)) 233 self._claim_button.set_claimable() 234 if self._expiration_time is not None: 235 ExpirationIcon(name='expiration_icon', parent=self.right_container, align=Align.TORIGHT, expiration_time=self._expiration_time, padLeft=4) 236 self.right_content_container = Container(name='right_content_container', parent=self.right_container, align=Align.TOALL, clipChildren=True) 237 self.rewards_container = ContainerAutoSize(name='rewards_container', parent=self.right_content_container, align=Align.TOLEFT) 238 239 def construct_underlay(self): 240 Frame(bgParent=self, texturePath='res:/UI/Texture/classes/Opportunities/card_mask.png', cornerSize=16, color=(0, 0, 0, 0.9)) 241 flair_pos = (-FLAIR_SIZE / 2 + 25, 242 -FLAIR_SIZE / 2 + 25, 243 FLAIR_SIZE, 244 FLAIR_SIZE) 245 self._bg_flair = Sprite(name='bgFlair', parent=self.flair_cont, texturePath=self._flair_texture, color=eveColor.SILVER_GREY, align=Align.TOPLEFT, outputMode=uiconst.OUTPUT_COLOR_AND_GLOW, glowBrightness=0.0, opacity=0.7, pos=flair_pos) 246 self._bg_flair.rgb = self._theme_color 247 self._hover_frame = Frame(parent=self, texturePath='res:/UI/Texture/classes/Opportunities/card_mask.png', cornerSize=16, color=eveColor.PLATINUM_GREY, opacity=OPACITY_IDLE) 248 self._hover_frame.rgb = self._theme_color 249 self._right_frame = Frame(bgParent=self._right_wrapper, texturePath='res:/UI/Texture/classes/Opportunities/list_entry_mask.png', cornerSize=16, color=eveColor.WHITE, opacity=0.05) 250 251 @threadutils.highlander_threaded 252 def update(self): 253 self._title_label.text = self.controller.name 254 self.update_rewards() 255 self.update_gauge(animate=False) 256 257 def update_rewards(self): 258 self.rewards_container.Flush() 259 reward_data = self.controller.reward_data 260 has_multiple_rewards = len(reward_data) > 1 261 has_item_reward = False 262 for reward in reward_data: 263 self.add_reward(reward) 264 if reward.is_item: 265 has_item_reward = True 266 267 if has_multiple_rewards or has_item_reward: 268 self.rewards_container.state = uiconst.UI_NORMAL 269 self.rewards_container.LoadTooltipPanel = lambda tooltipPanel, *args: RewardsTooltip(tooltipPanel=tooltipPanel, reward_data=reward_data) 270 if has_item_reward: 271 self._claim_button.GetHint = self.claim_button_item_hint 272 else: 273 self._claim_button.GetHint = lambda : None 274 275 def update_gauge(self, animate = True): 276 color = eveColor.SUCCESS_GREEN if self._progress >= 1.0 else eveColor.CRYO_BLUE 277 self._gauge.SetColor(color) 278 self._gauge.SetValue(self._progress, animate=animate) 279 self._gauge_label.text = '{:.0%}'.format(self._progress) 280 self._gauge_label.color = color 281 282 def add_reward(self, reward): 283 cont = ContainerAutoSize(parent=self.rewards_container, align=Align.TOLEFT, padRight=8) 284 Sprite(parent=cont, align=Align.CENTERLEFT, state=uiconst.UI_DISABLED, pos=(0, 0, 16, 16), texturePath=reward.icon) 285 self.reward_label = TextBody(parent=cont, align=Align.CENTERLEFT, state=uiconst.UI_DISABLED if reward.is_item else uiconst.UI_PICKCHILDREN, left=20, maxLines=1, autoFadeSides=16, text=reward.hint if reward.is_title else reward.text, color=TextColor.NORMAL) 286 287 def claim_button_item_hint(self): 288 if SessionChecker(session, sm).IsPilotDocked(): 289 return GetByLabel('UI/Achievements/Window/Rewards/ClaimItemHintDocked') 290 else: 291 return GetByLabel('UI/Achievements/Window/Rewards/ClaimItemHintUndocked') 292 293 def OnMouseEnter(self, *args): 294 eveui.Sound.entry_hover.play() 295 eveui.fade_in(self._hover_frame, end_value=OPACITY_HOVER, duration=0.2) 296 eveui.animate(self._left_line, 'glowBrightness', GLOW_HOVER, duration=0.2) 297 eveui.animate(self._bg_flair, 'glowBrightness', 0.3, duration=0.2) 298 eveui.animate(self._bg_flair, 'rotation', end_value=self._bg_flair.rotation + 2 * math.pi, duration=80.0, curve_type=CurveType.linear) 299 300 def OnMouseExit(self, *args): 301 eveui.fade(self._hover_frame, end_value=OPACITY_IDLE, duration=0.2) 302 eveui.animate(self._left_line, 'glowBrightness', GLOW_IDLE, duration=0.2) 303 eveui.animate(self._bg_flair, 'glowBrightness', 0.0, duration=0.2) 304 eveui.animate(self._bg_flair, 'rotation', end_value=self._bg_flair.rotation + 0.025, duration=0.2) 305 306 307 def RewardsTooltip(tooltipPanel, reward_data): 308 tooltipPanel.LoadStandardSpacing() 309 tooltipPanel.columns = 2 310 for reward in reward_data: 311 label_text = reward.hint 312 if label_text is None: 313 continue 314 if isinstance(reward.icon, IconData): 315 icon = reward.icon.resolve(16) 316 else: 317 icon = reward.icon 318 _, labelObj = tooltipPanel.AddIconLabel(icon=icon, iconSize=20 if reward.reward_type == RewardType.SP else 16, label=label_text) 319 if reward.is_item: 320 tooltipPanel.state = uiconst.UI_NORMAL 321 labelObj.state = uiconst.UI_NORMAL