skin_design.py (13638B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\cosmetics\common\ships\skins\live_data\skin_design.py 2 import datetime 3 from carbon.common.script.sys.serviceConst import ROLE_QA 4 from collections import defaultdict 5 from cosmetics.common.ships.skins.static_data.pattern_blend_mode import PatternBlendMode, NO_SECONDARY_COLOR_BLEND_MODES 6 from cosmetics.common.ships.skins.static_data.slot_configuration import SlotConfigurationsDataLoader 7 from cosmetics.common.ships.skins.static_data.skin_tier import ShipSkinTierTable 8 from cosmetics.common.ships.skins.static_data.slot_name import SlotID 9 from google.protobuf.timestamp_pb2 import Timestamp 10 from signals import Signal 11 12 class BaseSkinDesign(object): 13 SLOT_LAYOUT_CLASS = None 14 CHARACTER_PROTO = None 15 SKIN_ATTRIBUTES_PROTO = None 16 SKIN_TIER_PROTO = None 17 18 def __init__(self, creator_character_id = None, tier_table = None): 19 self.construct_signals() 20 self._ship_type_id = None 21 self._slot_layout = None 22 self.clear() 23 self.creator_character_id = creator_character_id 24 self._tier_table = tier_table if tier_table is not None else ShipSkinTierTable() 25 26 def construct_signals(self): 27 self.on_name_changed = Signal('on_name_changed') 28 self.on_line_name_changed = Signal('on_line_name_changed') 29 self.on_slot_fitting_changed = Signal('on_slot_fitting_changed') 30 self.on_ship_type_id_changed = Signal('on_ship_type_id_changed') 31 self.on_tier_level_changed = Signal('on_tier_level_changed') 32 self.on_component_instance_license_to_use_changed = Signal('on_component_instance_license_to_use_changed') 33 self.on_pattern_blend_mode_changed = Signal('on_pattern_blend_mode_changed') 34 self.on_component_attribute_changed = Signal('on_component_attribute_changed') 35 36 @property 37 def name(self): 38 return self._name 39 40 @name.setter 41 def name(self, value): 42 self._name = u'{}'.format(value) if value else '' 43 self.on_name_changed(self.name) 44 45 @property 46 def full_name(self): 47 return self._name 48 49 @property 50 def line_name(self): 51 return self._line_name 52 53 @line_name.setter 54 def line_name(self, value): 55 self._line_name = u'{}'.format(value) if value else '' 56 self.on_line_name_changed(self.line_name) 57 58 @property 59 def creator_character_id(self): 60 return self._creator_character_id 61 62 @creator_character_id.setter 63 def creator_character_id(self, value): 64 self._creator_character_id = value 65 66 @property 67 def ship_type_id(self): 68 return self._ship_type_id 69 70 @ship_type_id.setter 71 def ship_type_id(self, value): 72 if self.ship_type_id != value: 73 self._ship_type_id = value 74 old_layout = None 75 if self._slot_layout is not None: 76 old_layout = self._slot_layout 77 self._initialize_slot_layout() 78 if old_layout is not None: 79 self._slot_layout.copy_slots_from_other(old_layout) 80 self._compute_tier_level() 81 self.on_ship_type_id_changed(self.ship_type_id) 82 self._connect_component_instance_signals() 83 self._apply_type_id_to_components(value) 84 85 def _apply_type_id_to_components(self, value): 86 pass 87 88 @property 89 def saved_skin_design_id(self): 90 return self._saved_skin_design_id 91 92 @saved_skin_design_id.setter 93 def saved_skin_design_id(self, value): 94 self._saved_skin_design_id = value 95 96 @property 97 def design_hex(self): 98 return self._design_hex 99 100 @design_hex.setter 101 def design_hex(self, value): 102 self._design_hex = value 103 104 def copy_ship_type_and_layout(self, ship_type_id, layout): 105 self._slot_layout = layout 106 self.ship_type_id = ship_type_id 107 108 @property 109 def tier_level(self): 110 return self._tier_level 111 112 @tier_level.setter 113 def tier_level(self, value): 114 self._tier_level = value 115 116 @property 117 def slot_layout(self): 118 if self._slot_layout is None: 119 self._initialize_slot_layout() 120 return self._slot_layout 121 122 @property 123 def sequenced(self): 124 return self._sequenced 125 126 @sequenced.setter 127 def sequenced(self, value): 128 self._sequenced = value 129 130 @property 131 def tier_points(self): 132 return self.compute_current_points_value() 133 134 @property 135 def tier_thresholds(self): 136 return self._tier_table.get_thresholds_for_ship_type(self.ship_type_id) 137 138 def compute_current_points_value(self): 139 total_points = 0 140 for slot_id, component_instance in self.slot_layout.fitted_slots.iteritems(): 141 if component_instance.get_component_data().points_value: 142 if self.slot_layout.pattern_blend_mode in NO_SECONDARY_COLOR_BLEND_MODES and slot_id == SlotID.SECONDARY_PATTERN_MATERIAL: 143 pass 144 else: 145 total_points += component_instance.get_component_data().points_value 146 147 if session and session.role & ROLE_QA: 148 total_points += self._qa_tier_points 149 return total_points 150 151 def get_sequence_binder_amounts_required(self, number_of_runs): 152 amount_by_type_id = defaultdict(int) 153 for instance in self.slot_layout.fitted_slots.values(): 154 amount_required = instance.sequence_binder_amount_required 155 if amount_required > 0: 156 type_id = instance.sequence_binder_type_id 157 amount_by_type_id[type_id] += amount_required * number_of_runs 158 159 return amount_by_type_id 160 161 def fit_slot(self, slot_id, component_id, licence = None): 162 if component_id: 163 component_instance = self.create_component_instance(component_id) 164 component_instance.component_license_to_use = licence 165 component_instance.on_licence_to_use_changed.connect(self.on_component_instance_license_to_use_changed) 166 component_instance.on_attribute_changed.connect(self._on_component_instance_attribute_changed) 167 else: 168 component_instance = None 169 self.slot_layout.fit_slot(slot_id, component_instance) 170 return component_instance 171 172 def swap_slots(self, slot_id_1, slot_id_2): 173 self.slot_layout.swap_slots(slot_id_1, slot_id_2) 174 175 def unfit_all(self): 176 self.slot_layout.unfit_all() 177 178 def _on_component_instance_attribute_changed(self, component_instance, attribute_id, value): 179 slot_id = self.slot_layout.get_slot_id(component_instance) 180 if slot_id: 181 self.on_component_attribute_changed(slot_id, attribute_id, value) 182 183 def get_fitted_component_instance(self, slot_id): 184 return self.slot_layout.slots.get(slot_id, None) 185 186 def has_fitted_components(self): 187 return bool(self.get_fitted_components()) 188 189 def get_fitted_components(self): 190 return [ component_instance for component_instance in self.slot_layout.slots.itervalues() if component_instance ] 191 192 def _initialize_slot_layout(self): 193 config = SlotConfigurationsDataLoader.get_for_ship(self._ship_type_id) 194 if config: 195 self._slot_layout = self.SLOT_LAYOUT_CLASS(config) 196 self._slot_layout.on_slot_fitting_changed.connect(self._on_slot_fitting_changed) 197 self._slot_layout.on_pattern_blend_mode_changed.connect(self._on_pattern_blend_mode_changed) 198 else: 199 self._slot_layout = None 200 201 def _on_slot_fitting_changed(self, slot_id, component_instance): 202 self._compute_tier_level() 203 self.on_slot_fitting_changed(slot_id, component_instance) 204 205 def _on_pattern_blend_mode_changed(self, value): 206 self._compute_tier_level() 207 self.on_pattern_blend_mode_changed(value) 208 209 def clear(self): 210 self._name = None 211 self._line_name = None 212 self._creator_character_id = None 213 self._tier_level = 1 214 if self.slot_layout: 215 for component_instance in self.slot_layout.fitted_slots.values(): 216 component_instance.on_licence_to_use_changed.disconnect(self.on_component_instance_license_to_use_changed) 217 component_instance.on_attribute_changed.disconnect(self._on_component_instance_attribute_changed) 218 219 self._slot_layout = None 220 self._sequenced = None 221 self._saved_skin_design_id = None 222 self._design_hex = None 223 self._qa_tier_points = 0 224 225 def _compute_tier_level(self): 226 new_tier_level = self._tier_table.get_tier_level_for_design(self) 227 if new_tier_level == self.tier_level: 228 return 229 self.tier_level = new_tier_level 230 self.on_tier_level_changed(new_tier_level) 231 232 def __eq__(self, other): 233 if other is None: 234 return False 235 return self.name == other.name and self.line_name == other.line_name and self.creator_character_id == other.creator_character_id and self.ship_type_id == other.ship_type_id and self.slot_layout == other.slot_layout and self.tier_level == other.tier_level 236 237 def __ne__(self, other): 238 return not self.__eq__(other) 239 240 def __str__(self): 241 design_str = 'Name: {name}\nLine:\n{line}\nCreated by {char_id} for ship type {ship_type}\nTier level {tier}\n'.format(name=self.name.encode('utf-8'), line=self.line_name.encode('utf-8') if self.line_name is not None else 'No line name', char_id=self.creator_character_id, ship_type=self.ship_type_id, tier=self.tier_level) 242 design_str += 'Layout:\n %s' % self.slot_layout 243 return design_str 244 245 def update_from_other(self, other_design, saved_skin_design_id = None): 246 self.clear() 247 self.creator_character_id = other_design.creator_character_id 248 self._name = other_design.name 249 self._line_name = other_design.line_name 250 self.slot_layout.copy_slots_from_other(other_design.slot_layout, should_copy_components_to_use=True) 251 self.ship_type_id = other_design.ship_type_id 252 self.sequenced = other_design.sequenced 253 self.saved_skin_design_id = saved_skin_design_id 254 self.design_hex = other_design.design_hex 255 self._compute_tier_level() 256 257 def clean_up_skin(self): 258 if self.slot_layout is None: 259 return 260 if self.slot_layout.pattern_blend_mode in NO_SECONDARY_COLOR_BLEND_MODES or self.slot_layout.slots[SlotID.SECONDARY_PATTERN] is None: 261 self.slot_layout.unfit_slot(SlotID.SECONDARY_PATTERN_MATERIAL, True) 262 263 def _connect_component_instance_signals(self): 264 for component_instance in self.slot_layout.fitted_slots.values(): 265 component_instance.on_licence_to_use_changed.connect(self.on_component_instance_license_to_use_changed) 266 component_instance.on_attribute_changed.connect(self._on_component_instance_attribute_changed) 267 268 @classmethod 269 def copy_from_other(cls, other_design, new_creator_character_id = None, should_copy_components_to_use = False): 270 if other_design is not None: 271 if new_creator_character_id is not None: 272 design_copy = cls(new_creator_character_id) 273 else: 274 design_copy = cls(other_design.creator_character_id) 275 design_copy.name = other_design.name 276 design_copy.line_name = other_design.line_name 277 design_copy.ship_type_id = other_design.ship_type_id 278 design_copy.slot_layout.copy_slots_from_other(other_design.slot_layout, should_copy_components_to_use=should_copy_components_to_use) 279 design_copy.sequenced = other_design.sequenced 280 design_copy.saved_skin_design_id = other_design.saved_skin_design_id 281 design_copy.design_hex = other_design.design_hex 282 design_copy.tier_level = other_design.tier_level 283 return design_copy 284 285 @classmethod 286 def build_from_proto(cls, payload): 287 design = cls(payload.creator.sequential) 288 design.name = payload.name 289 design.line_name = payload.line_name if len(payload.line_name) > 0 else None 290 design.ship_type_id = payload.layout.ship_type.sequential 291 design.slot_layout.fit_slots_from_proto(payload.layout) 292 design.tier_level = payload.tier.level 293 if payload.HasField('first_sequenced'): 294 design.sequenced = datetime.datetime.fromtimestamp(payload.first_sequenced.seconds) 295 return design 296 297 @classmethod 298 def build_proto_from_design(cls, skin_design): 299 attributes = cls.SKIN_ATTRIBUTES_PROTO() 300 attributes.name = skin_design.name 301 attributes.line_name = skin_design.line_name if skin_design.line_name is not None else '' 302 attributes.creator.CopyFrom(cls.CHARACTER_PROTO(sequential=skin_design.creator_character_id)) 303 attributes.layout.CopyFrom(cls.SLOT_LAYOUT_CLASS.build_proto_from_layout(skin_design.ship_type_id, skin_design.slot_layout)) 304 attributes.tier.CopyFrom(cls.SKIN_TIER_PROTO(level=skin_design.tier_level)) 305 if skin_design.sequenced is None: 306 attributes.not_sequenced = True 307 else: 308 attributes.sequenced = Timestamp().FromDatetime(skin_design.sequenced) 309 return attributes 310 311 def create_component_instance(self, component_id, **kwargs): 312 raise NotImplementedError 313 314 @property 315 def qa_tier_points(self): 316 if session and session.role & ROLE_QA: 317 return self._qa_tier_points 318 return 0 319 320 def set_qa_tier_points(self, value): 321 if session and session.role & ROLE_QA: 322 self._qa_tier_points = value 323 self._compute_tier_level() 324 self.on_tier_level_changed(self.tier_level)