componentLicenseEntry.py (15625B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\cosmetics\ship\pages\studio\componentLicenseEntry.py 2 import blue 3 import carbonui.text.styles 4 import gametime 5 from carbonui.uiconst import OutputMode 6 import carbonui 7 import evetypes 8 from carbon.common.script.sys.serviceConst import ROLE_GML 9 from carbonui import Align, uiconst, PickState, SpriteEffect 10 from carbonui.control.contextMenu.menuData import MenuData 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.sprite import Sprite 17 from carbonui.uianimations import animations 18 from cosmetics.client.shipSkinComponentSvc import get_ship_skin_component_svc 19 from cosmetics.client.ships.skins.live_data import current_skin_design, current_skin_design_signals 20 from cosmetics.client.ships.skins.static_data import rarity_icon 21 from cosmetics.common.ships.skins.static_data.component import projection_type_to_fsd 22 from cosmetics.common.ships.skins.static_data.component_category import ComponentCategory 23 from cosmetics.common.ships.skins.static_data.slot_name import SlotID, PATTERN_MATERIAL_SLOT_ID_BY_PATTERN_ID, PATTERN_SLOT_IDS 24 from eve.client.script.ui import eveThemeColor, eveColor 25 from player_messaging.client.ui_message import message_player 26 from eve.client.script.ui.control.tooltips import TooltipPanel 27 from eve.client.script.ui.cosmetics.ship.controls.rarityIndicator import RarityIndicator 28 from eveservices.menu import GetMenuService 29 from localization import GetByLabel 30 from publicGateway.grpc.exceptions import GenericException 31 from stackless_response_router.exceptions import TimeoutException 32 ICON_SIZE = 48 33 TOOLTIP_ICON_SIZE = 128 34 PADDING = 14 35 36 def get_anim_time_offset(duration): 37 seconds = gametime.GetWallclockTimeNow() / float(gametime.SEC) 38 return duration - seconds % duration 39 40 41 brightness_blink_curve = ((0.0, 0.0), 42 (2 / 7.0, 1.5), 43 (4 / 7.0, 1.5), 44 (6 / 7.0, 0.0), 45 (1.0, 0.0)) 46 47 class ComponentEntry(Container): 48 default_width = ICON_SIZE + PADDING 49 default_height = ICON_SIZE + PADDING 50 default_state = uiconst.UI_NORMAL 51 52 def __init__(self, component_data, slot_id, is_selected, limited_license = None, unlimited_license = None, *args, **kwargs): 53 super(ComponentEntry, self).__init__(*args, **kwargs) 54 self.limited_license = limited_license 55 self.unlimited_license = unlimited_license 56 self.slot_id = slot_id 57 self._is_selected = is_selected 58 self.component_data = component_data 59 self.is_new = False 60 self.is_animating = False 61 self.construct_layout() 62 self.connect_signals() 63 64 def Close(self): 65 try: 66 self.disconnect_signals() 67 finally: 68 super(ComponentEntry, self).Close() 69 70 def connect_signals(self): 71 current_skin_design_signals.on_slot_fitting_changed.connect(self.on_slot_fitting_changed) 72 current_skin_design_signals.on_pattern_blend_mode_changed.connect(self.on_blend_mode_changed) 73 74 def disconnect_signals(self): 75 current_skin_design_signals.on_slot_fitting_changed.disconnect(self.on_slot_fitting_changed) 76 current_skin_design_signals.on_pattern_blend_mode_changed.disconnect(self.on_blend_mode_changed) 77 78 def on_blend_mode_changed(self, *args): 79 self._check_fit_default_pattern_material() 80 81 def construct_layout(self): 82 self.construct_icon() 83 self.construct_selection_frame() 84 self.check_construct_not_owned_frame() 85 self.update() 86 87 def check_construct_not_owned_frame(self): 88 if not self.limited_license and not self.unlimited_license: 89 self.not_owned_frame = Sprite(name='not_owned_frame', parent=self, texturePath='res:/UI/Texture/classes/Cosmetics/Ship/circle_frame_bg.png', color=eveColor.WHITE, opacity=0.2, align=Align.TOALL, pickState=PickState.OFF) 90 91 def construct_icon(self): 92 if self.is_pattern(): 93 size = ICON_SIZE - 2 94 else: 95 size = ICON_SIZE 96 self.icon = Sprite(name='icon', parent=self, align=Align.CENTER, state=uiconst.UI_DISABLED, texturePath=self.component_data.icon_file_path, textureSecondaryPath=self.icon_mask_texture, spriteEffect=SpriteEffect.MASK, pos=(0, 97 0, 98 size, 99 size)) 100 101 def construct_selection_frame(self): 102 self.selection_frame = Sprite(name='selection_frame', parent=self, texturePath='res:/UI/Texture/Classes/Cosmetics/Ship/circle_frame.png', align=Align.TOALL, pickState=PickState.OFF, glowBrightness=0.0) 103 104 def update(self): 105 self.is_new = get_ship_skin_component_svc().is_component_new(self.component_data.component_id) 106 if self.is_selected and self.is_new: 107 get_ship_skin_component_svc().set_component_is_new_flag(self.component_data.component_id, False) 108 self.is_new = False 109 self.selection_frame.display = self.is_selected or self.is_new 110 if self.is_new: 111 self.selection_frame.color = eveColor.DUSKY_ORANGE 112 self.selection_frame.outputMode = uiconst.OUTPUT_COLOR_AND_GLOW 113 else: 114 self.selection_frame.color = eveThemeColor.THEME_ACCENT 115 self.selection_frame.outputMode = self.selection_frame.default_outputMode 116 if self.is_new: 117 if not self.is_animating: 118 self.start_new_highlight_animation() 119 else: 120 self.stop_new_highlight_animation() 121 122 def start_new_highlight_animation(self): 123 self.is_animating = True 124 animations.MorphScalar(obj=self.selection_frame, attrName='glowBrightness', duration=2.5, loops=uiconst.ANIM_REPEAT, curveType=brightness_blink_curve, timeOffset=get_anim_time_offset(2.5)) 125 126 def on_new_highlight_animation_out_complete(self, *args): 127 if self.is_new: 128 self.start_new_highlight_animation() 129 130 def stop_new_highlight_animation(self): 131 animations.StopAnimation(self.selection_frame, 'glowBrightness') 132 self.selection_frame.glowBrightness = 0.0 133 self.is_animating = False 134 135 def on_slot_fitting_changed(self, slot_id, component_instance): 136 if slot_id != self.slot_id: 137 return 138 self.is_selected = component_instance and component_instance.component_id == self.component_data.component_id 139 140 def OnClick(self, *args): 141 get_ship_skin_component_svc().set_component_is_new_flag(self.component_data.component_id, False) 142 if self.slot_id in (SlotID.PATTERN_MATERIAL, SlotID.SECONDARY_PATTERN_MATERIAL): 143 if not self.is_selected: 144 self.set_selected() 145 current_skin_design.add_to_undo_history() 146 else: 147 self.toggle_selected() 148 current_skin_design.add_to_undo_history() 149 150 def is_pattern(self): 151 return self.slot_id in PATTERN_SLOT_IDS 152 153 def toggle_selected(self): 154 if self.is_selected: 155 self.set_deselected() 156 else: 157 self.set_selected() 158 159 def set_selected(self): 160 current_skin_design.get().fit_slot(self.slot_id, self.component_data.component_id, None) 161 if self.is_pattern(): 162 self._check_fit_default_pattern_material() 163 164 def set_deselected(self): 165 current_skin_design.get().fit_slot(self.slot_id, None) 166 167 def _check_fit_default_pattern_material(self): 168 material_slot_id = PATTERN_MATERIAL_SLOT_ID_BY_PATTERN_ID.get(self.slot_id, None) 169 if not material_slot_id or current_skin_design.get().get_fitted_component_instance(material_slot_id): 170 return 171 if not current_skin_design.get().get_fitted_component_instance(self.slot_id): 172 return 173 try: 174 component_license = get_ship_skin_component_svc().get_random_component_license(material_slot_id) 175 if component_license: 176 current_skin_design.get().fit_slot(material_slot_id, component_license.component_id, component_license) 177 except (GenericException, TimeoutException): 178 message_player(GetByLabel('UI/Common/CannotConnectToServer')) 179 180 @property 181 def is_selected(self): 182 return self._is_selected 183 184 @is_selected.setter 185 def is_selected(self, value): 186 self._is_selected = value 187 self.update() 188 189 def ConstructTooltipPanel(self): 190 return ComponentEntryTooltipPanel(self.component_data, self.icon_mask_texture) 191 192 @property 193 def icon_mask_texture(self): 194 if self.is_pattern(): 195 return 'res:/UI/Texture/circle_full.png' 196 else: 197 return 'res:/UI/Texture/classes/Cosmetics/Ship/pages/studio/nanocoating_mask.png' 198 199 def GetMenu(self): 200 m = MenuData() 201 for type_id in self.component_data.component_item_data_by_type_id.keys(): 202 m.AddEntry(evetypes.GetName(type_id), subMenuData=GetMenuService().GetMenuFromItemIDTypeID(itemID=None, typeID=type_id, includeMarketDetails=True)) 203 204 if session.role & ROLE_GML: 205 m.AddEntry('QA:', subMenuData=self.get_qa_menu()) 206 return m 207 208 def get_qa_menu(self): 209 m = MenuData() 210 m.AddEntry(text='component_id: {}'.format(self.component_data.component_id), func=lambda *x: blue.pyos.SetClipboardData(str(self.component_data.component_id))) 211 m.AddEntry(text='icon_path: {}'.format(self.component_data.icon_file_path), func=lambda *x: blue.pyos.SetClipboardData(str(self.component_data.icon_file_path))) 212 import webbrowser 213 m.AddEntry(text='Show in FSD editor', func=lambda : webbrowser.open_new('http://localhost:8000/cfsd/projects/cosmetic/ship_skin_design_components/ship_skin_design_components/{}/edit/'.format(self.component_data.component_id))) 214 for license in (self.limited_license, self.unlimited_license): 215 if not license: 216 continue 217 m.AddEntry(text='license_type: {}'.format(license.license_type), func=lambda *x: blue.pyos.SetClipboardData(str(license.license_type))) 218 component_type_id = None 219 for type_id, data in self.component_data.component_item_data_by_type_id.items(): 220 if data.license_type == license.license_type: 221 component_type_id = type_id 222 break 223 224 m.AddEntry(text='type_id: {}'.format(component_type_id), func=lambda *x: blue.pyos.SetClipboardData(str(component_type_id))) 225 226 m.AddEntry(text='Points Value: %s' % self.component_data.points_value) 227 if self.component_data.projection_type_uv: 228 u_fmt = projection_type_to_fsd(self.component_data.projection_type_uv[0]) 229 v_fmt = projection_type_to_fsd(self.component_data.projection_type_uv[1]) 230 m.AddEntry('Projection Type U: %s' % u_fmt) 231 m.AddEntry('Projection Type V: %s' % v_fmt) 232 return m 233 234 235 class TooltipCard(Container): 236 default_width = 130 237 default_height = 135 238 239 def __init__(self, component_data, **kw): 240 super(TooltipCard, self).__init__(**kw) 241 self.construct_icon(component_data) 242 self.construct_background() 243 244 def construct_icon(self, component_data): 245 icon_cont = Container(parent=self, align=Align.CENTERTOP, pos=(0, 12, 94, 94)) 246 if component_data.category == ComponentCategory.PATTERN: 247 Sprite(parent=icon_cont, align=Align.CENTER, state=uiconst.UI_DISABLED, texturePath=component_data.icon_file_path, pos=(0, 0, 80, 80)) 248 Fill(parent=icon_cont, align=Align.CENTER, state=uiconst.UI_DISABLED, color=eveThemeColor.THEME_TINT, opacity=0.5, pos=(0, 0, 92, 92)) 249 else: 250 Sprite(parent=icon_cont, align=Align.CENTER, state=uiconst.UI_DISABLED, texturePath=component_data.icon_file_path, textureSecondaryPath='res:/UI/Texture/classes/Cosmetics/Ship/pages/studio/nanocoating_mask.png', spriteEffect=SpriteEffect.MASK, pos=(0, 0, 80, 80)) 251 Sprite(parent=icon_cont, align=Align.CENTER, state=uiconst.UI_DISABLED, texturePath='res:/UI/Texture/circle_full.png', color=eveThemeColor.THEME_TINT, opacity=0.5, pos=(0, 0, 92, 92)) 252 253 def construct_background(self): 254 Sprite(parent=self, align=Align.HORIZONTALLY_CENTERED, width=120, texturePath='res:/UI/Texture/classes/Cosmetics/Ship/Cards/Tooltip/outline.png', color=eveColor.WHITE, opacity=0.2) 255 Sprite(parent=self, align=Align.HORIZONTALLY_CENTERED, width=130, texturePath='res:/UI/Texture/classes/Cosmetics/Ship/Cards/Tooltip/bg_overlay.png', color=eveThemeColor.THEME_FOCUSDARK, opacity=0.3) 256 Sprite(parent=self, align=Align.HORIZONTALLY_CENTERED, width=120, texturePath='res:/UI/Texture/classes/Cosmetics/Ship/Cards/Tooltip/bg_fill.png', color=eveColor.BLACK, opacity=0.9) 257 258 259 class ComponentEntryTooltipPanel(TooltipPanel): 260 default_margin = 0 261 default_cellPadding = 0 262 default_cellSpacing = (0, 4) 263 264 def __init__(self, component_data, icon_mask_texture, **kw): 265 super(ComponentEntryTooltipPanel, self).__init__(**kw) 266 self.columns = 1 267 self.construct_header(component_data) 268 TooltipCard(parent=self, component_data=component_data, align=Align.CENTER, pickState=PickState.OFF) 269 self.SetBackgroundAlpha(0.0) 270 271 def construct_header(self, component_data): 272 layout_grid = LayoutGrid(parent=self, columns=1, align=Align.CENTERTOP, cellSpacing=(8, 8), margin=(16, 16, 16, 8)) 273 license = get_ship_skin_component_svc().get_default_license_to_use(component_data.component_id, component_data.category) 274 is_new = get_ship_skin_component_svc().is_component_new(component_data.component_id) 275 if not license: 276 carbonui.text.styles.TextBody(parent=layout_grid, text=GetByLabel('UI/Personalization/ShipSkins/SKINR/NotInYourCollection'), color=eveColor.HOT_RED) 277 elif is_new: 278 carbonui.text.styles.TextBody(parent=layout_grid, text=GetByLabel('UI/Personalization/ShipSkins/SKINR/Studio/NewInYourCollection'), color=eveColor.DUSKY_ORANGE) 279 carbonui.text.styles.TextHeader(parent=layout_grid, text=component_data.name, maxWidth=160) 280 rarity_cont = ContainerAutoSize(parent=layout_grid) 281 RarityIndicator(parent=rarity_cont, rarity=component_data.rarity, align=Align.CENTERLEFT) 282 carbonui.text.styles.TextBody(parent=rarity_cont, align=Align.CENTERLEFT, left=40, text=GetByLabel('UI/Personalization/ShipSkins/SKINR/RarityName', rarity=GetByLabel(rarity_icon.get_name(component_data.rarity))), wrapWidth=300) 283 if is_new: 284 frame = Frame(bgParent=layout_grid, align=Align.HORIZONTALLY_CENTERED, width=120, texturePath='res:/UI/Texture/classes/Cosmetics/Ship/Cards/Tooltip/frame_textbox.png', color=eveColor.DUSKY_ORANGE, opacity=1.0, outputMode=OutputMode.COLOR_AND_GLOW, glowBrightness=0.0, cornerSize=16) 285 animations.MorphScalar(obj=frame, attrName='glowBrightness', duration=2.5, curveType=brightness_blink_curve, loops=uiconst.ANIM_REPEAT, timeOffset=get_anim_time_offset(2.5)) 286 else: 287 Frame(bgParent=layout_grid, align=Align.HORIZONTALLY_CENTERED, width=120, texturePath='res:/UI/Texture/classes/Cosmetics/Ship/Cards/Tooltip/frame_textbox.png', color=eveColor.WHITE, opacity=0.2, cornerSize=16) 288 Frame(bgParent=layout_grid, texturePath='res:/UI/Texture/classes/Cosmetics/Ship/Cards/Tooltip/bg_textbox.png', color=eveThemeColor.THEME_FOCUSDARK, opacity=0.2, cornerSize=16) 289 Frame(bgParent=layout_grid, texturePath='res:/UI/Texture/classes/Cosmetics/Ship/Cards/Tooltip/bg_textbox.png', color=eveColor.BLACK, opacity=0.9, cornerSize=16)