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

slot_layout.py (13533B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\cosmetics\common\ships\skins\live_data\slot_layout.py
      2 import logging
      3 from collections import defaultdict
      4 from cosmetics.common.ships.skins.errors import SlotConfigurationUnavailable, ComponentsUnavailable, SlotDataUnavailable, ComponentDataUnavailable, ComponentNotAllowedInSlot, SlotDisallowedForComponent, InvalidBlendMode, UnusedComponentsFitted, MissingPatternColor
      5 from cosmetics.common.ships.skins.live_data.component_instance import BasePatternComponentInstance
      6 from cosmetics.common.ships.skins.live_data.component_license_type import ComponentLicenseType
      7 from cosmetics.common.ships.skins.sequencing_util import build_component_licenses_per_type_dict
      8 from cosmetics.common.ships.skins.static_data.pattern_blend_mode import PatternBlendMode, NO_SECONDARY_COLOR_BLEND_MODES
      9 from cosmetics.common.ships.skins.static_data.slot import SlotsDataLoader
     10 from cosmetics.common.ships.skins.static_data.slot_configuration import SlotConfiguration, SlotConfigurationsDataLoader
     11 from cosmetics.common.ships.skins.static_data.slot_name import SlotID
     12 from signals import Signal
     13 log = logging.getLogger(__name__)
     14 DEFAULT_BLEND_MODE = PatternBlendMode.OVERLAY
     15 
     16 class BaseSlotLayout(object):
     17     LAYOUT_PROTO = None
     18     SLOT_IDENTIFIER_PROTO = None
     19     SHIP_TYPE_PROTO = None
     20 
     21     def __init__(self, slot_config):
     22         self._slot_config = slot_config
     23         self._slots = {}
     24         self._pattern_blend_mode = DEFAULT_BLEND_MODE
     25         self._init_slots()
     26         self.on_slot_fitting_changed = Signal('on_slot_fitting_changed')
     27         self.on_pattern_blend_mode_changed = Signal('on_pattern_blend_mode_changed')
     28 
     29     def _init_slots(self):
     30         for slot_id in self._slot_config.slots:
     31             self._slots[slot_id] = None
     32 
     33     @property
     34     def slot_config(self):
     35         return self._slot_config
     36 
     37     @property
     38     def slots(self):
     39         return self._slots
     40 
     41     @property
     42     def pattern_blend_mode(self):
     43         return self._pattern_blend_mode
     44 
     45     @pattern_blend_mode.setter
     46     def pattern_blend_mode(self, value):
     47         if value == PatternBlendMode.UNSPECIFIED:
     48             value = DEFAULT_BLEND_MODE
     49         self._pattern_blend_mode = value
     50         self.on_pattern_blend_mode_changed(value)
     51 
     52     def get_component(self, slot_id):
     53         return self.slots[slot_id]
     54 
     55     def get_slot_id(self, component_instance):
     56         for slot_id, _component in self._slots.items():
     57             if component_instance == _component:
     58                 return slot_id
     59 
     60     @property
     61     def fitted_slots(self):
     62         return {slot_id:instance for slot_id, instance in self.slots.items() if instance}
     63 
     64     def fit_slot(self, slot_id, component_instance = None, notify = True):
     65         self._fit_slot(slot_id, component_instance, notify, check_license=True)
     66         if component_instance:
     67             component_instance.apply_user_authored_values()
     68 
     69     def _fit_slot(self, slot_id, component_instance = None, notify = True, check_license = False):
     70         if slot_id not in self._slots:
     71             return
     72         if component_instance is None:
     73             self.unfit_slot(slot_id, notify)
     74         elif self._validate(slot_id, component_instance):
     75             previous_component = self._slots[slot_id]
     76             if previous_component is not None:
     77                 component_instance.copy_params_from_other(previous_component)
     78             self._slots[slot_id] = component_instance
     79             if check_license:
     80                 self._check_select_default_component_instance_license(component_instance)
     81             if notify:
     82                 self.on_slot_fitting_changed(slot_id, component_instance)
     83 
     84     def swap_slots(self, slot_id_1, slot_id_2):
     85         instance_1 = self._slots[slot_id_1]
     86         instance_2 = self._slots[slot_id_2]
     87         self._slots[slot_id_1] = instance_2
     88         self._slots[slot_id_2] = instance_1
     89         self.on_slot_fitting_changed(slot_id_1, instance_2)
     90         self.on_slot_fitting_changed(slot_id_2, instance_1)
     91 
     92     def unfit_slot(self, slot_id, notify = True):
     93         self._slots[slot_id] = None
     94         if notify:
     95             self.on_slot_fitting_changed(slot_id, None)
     96         if slot_id == SlotID.PATTERN:
     97             self.unfit_slot(SlotID.PATTERN_MATERIAL, notify)
     98             self.unfit_slot(SlotID.SECONDARY_PATTERN, notify)
     99             self.pattern_blend_mode = DEFAULT_BLEND_MODE
    100         elif slot_id == SlotID.SECONDARY_PATTERN:
    101             self.unfit_slot(SlotID.SECONDARY_PATTERN_MATERIAL, notify)
    102             self.pattern_blend_mode = DEFAULT_BLEND_MODE
    103 
    104     def unfit_all(self):
    105         for slot_id in self._slots.keys():
    106             self.unfit_slot(slot_id)
    107 
    108     def _check_select_default_component_instance_license(self, component_instance):
    109         if not component_instance.component_license_to_use:
    110             component_type = component_instance.get_component_data().category
    111             license = self.get_component_license(component_instance.component_id, component_type)
    112             if license:
    113                 component_instance.component_license_to_use = license
    114 
    115     def copy_slots_from_other(self, other_layout, should_copy_components_to_use = False):
    116         for slot_id, component_instance in other_layout.slots.items():
    117             if component_instance:
    118                 component_instance_copy = self.create_component_instance(component_instance.component_id)
    119                 component_instance_copy.copy_params_from_other(component_instance, should_copy_components_to_use)
    120                 self._fit_slot(slot_id, component_instance_copy, notify=False, check_license=True)
    121             else:
    122                 self._fit_slot(slot_id, None, notify=False, check_license=False)
    123 
    124         self.pattern_blend_mode = other_layout.pattern_blend_mode
    125 
    126     def _validate(self, slot_id, component_instance):
    127         slot_data = SlotsDataLoader.get_slot_data(slot_id)
    128         if not slot_data:
    129             raise SlotDataUnavailable()
    130         component_data = component_instance.get_component_data()
    131         if not component_data:
    132             raise ComponentDataUnavailable()
    133         if component_data.category not in slot_data.allowed_component_categories:
    134             raise ComponentNotAllowedInSlot()
    135         if slot_id in component_data.disallowed_slot_ids:
    136             raise SlotDisallowedForComponent()
    137         return True
    138 
    139     def validate_layout(self):
    140         try:
    141             for slot_id, component_instance in self.fitted_slots.items():
    142                 self._validate(slot_id, component_instance)
    143 
    144         except (SlotDataUnavailable,
    145          ComponentDataUnavailable,
    146          ComponentNotAllowedInSlot,
    147          SlotDisallowedForComponent) as e:
    148             log.exception(e)
    149             return False
    150 
    151         try:
    152             self.validate_patterns()
    153         except (MissingPatternColor, InvalidBlendMode, UnusedComponentsFitted) as e:
    154             log.exception(e)
    155             return False
    156 
    157         return True
    158 
    159     def validate_patterns(self):
    160         nb_patterns = len([ x for x, y in self.fitted_slots.iteritems() if isinstance(y, BasePatternComponentInstance) ])
    161         if nb_patterns > 1 and self.pattern_blend_mode == PatternBlendMode.UNSPECIFIED:
    162             raise InvalidBlendMode()
    163         if self.pattern_blend_mode in NO_SECONDARY_COLOR_BLEND_MODES:
    164             secondary_color = self.get_component(SlotID.SECONDARY_PATTERN_MATERIAL)
    165             if secondary_color is not None:
    166                 raise UnusedComponentsFitted()
    167         else:
    168             nb_pattern_colors = len([ x for x, y in self.fitted_slots.iteritems() if x in [SlotID.SECONDARY_PATTERN_MATERIAL, SlotID.PATTERN_MATERIAL] ])
    169             if nb_pattern_colors != nb_patterns:
    170                 raise MissingPatternColor()
    171 
    172     def validate_licenses(self, number_of_runs, licenses, exact_match = False):
    173         if any([ x is None for x in licenses ]):
    174             return False
    175         licenses_available = build_component_licenses_per_type_dict(licenses)
    176         component_required_amounts = defaultdict(int)
    177         for component_instance in self.fitted_slots.values():
    178             component_required_amounts[component_instance.component_id] += number_of_runs
    179 
    180         for component_id, nb_required in component_required_amounts.iteritems():
    181             if component_id not in licenses_available:
    182                 return False
    183             if ComponentLicenseType.LIMITED in licenses_available[component_id]:
    184                 if exact_match:
    185                     if nb_required < licenses_available[component_id][ComponentLicenseType.LIMITED].remaining_license_uses:
    186                         return False
    187                     if nb_required != licenses_available[component_id][ComponentLicenseType.LIMITED].remaining_license_uses:
    188                         if ComponentLicenseType.UNLIMITED not in licenses_available[component_id]:
    189                             return False
    190                     elif ComponentLicenseType.UNLIMITED in licenses_available[component_id]:
    191                         return False
    192                 elif nb_required > licenses_available[component_id][ComponentLicenseType.LIMITED].remaining_license_uses:
    193                     if ComponentLicenseType.UNLIMITED not in licenses_available[component_id]:
    194                         return False
    195             elif ComponentLicenseType.UNLIMITED not in licenses_available[component_id]:
    196                 return False
    197 
    198         return True
    199 
    200     def get_amount_of_components_with_license_type(self, component_id, license_type = None):
    201         component_amount = 0
    202         for component_instance in self.fitted_slots.values():
    203             if component_instance.component_id == component_id:
    204                 license = component_instance.component_license_to_use
    205                 if license_type is None or license is None:
    206                     component_amount += 1
    207                 elif license.license_type == license_type:
    208                     component_amount += 1
    209 
    210         return component_amount
    211 
    212     def __eq__(self, other):
    213         if other is None:
    214             return False
    215         if len(self.slots) != len(other.slots):
    216             return False
    217         for slot_id, slot_data in self.slots.items():
    218             if slot_id not in other.slots:
    219                 return False
    220             if slot_data != other.slots[slot_id]:
    221                 return False
    222 
    223         if self.pattern_blend_mode != other.pattern_blend_mode:
    224             return False
    225         return True
    226 
    227     def __ne__(self, other):
    228         return not self.__eq__(other)
    229 
    230     def __str__(self):
    231         result = ''
    232         for slot_id, slot_data in self._slots.iteritems():
    233             result += 'slot %s: %s\n' % (slot_id, slot_data)
    234 
    235         result += 'blend mode: %s' % self.pattern_blend_mode
    236         return result
    237 
    238     @classmethod
    239     def copy_from_other(cls, other_layout):
    240         layout_copy = cls(other_layout.slot_config)
    241         layout_copy.copy_from_other(other_layout)
    242         return layout_copy
    243 
    244     @classmethod
    245     def build_from_proto(cls, payload):
    246         ship_type_id = payload.ship_type.sequential
    247         slot_config = SlotConfigurationsDataLoader.get_for_ship(ship_type_id)
    248         if not slot_config:
    249             raise SlotConfigurationUnavailable()
    250         slot_layout = cls(slot_config)
    251         slot_layout.fit_slots_from_proto(payload)
    252         slot_layout.pattern_blend_mode = payload.pattern_blend_mode
    253         return slot_layout
    254 
    255     def fit_slots_from_proto(self, payload):
    256         for slot in payload.slots:
    257             component_id = None
    258             if slot.configuration.HasField('coating'):
    259                 component_id = slot.configuration.coating.coating.sequential
    260             elif slot.configuration.HasField('pattern'):
    261                 component_id = slot.configuration.pattern.pattern.sequential
    262             if component_id is not None:
    263                 component_instance = self.create_component_instance(component_id)
    264                 if not component_instance:
    265                     log.error('unable to find component id %s, skipping it from layout %s' % (component_id, payload))
    266                     continue
    267                 if slot.configuration.HasField('coating'):
    268                     component_instance.build_params_from_proto(slot.configuration.coating.configuration)
    269                 elif slot.configuration.HasField('pattern'):
    270                     component_instance.build_params_from_proto(slot.configuration.pattern.configuration)
    271                 self._fit_slot(slot.id.sequential, component_instance, check_license=False)
    272 
    273         self.pattern_blend_mode = payload.pattern_blend_mode
    274 
    275     @classmethod
    276     def build_proto_from_layout(cls, ship_type_id, layout):
    277         proto_layout = cls.LAYOUT_PROTO()
    278         slot_list = []
    279         for slot_id, slot_data in layout.slots.iteritems():
    280             if slot_data is not None:
    281                 slot_list.append(cls.LAYOUT_PROTO.Slot(id=cls.SLOT_IDENTIFIER_PROTO(sequential=slot_id), configuration=cls.build_config_proto_from_component_instance(slot_data)))
    282 
    283         proto_layout.slots.extend(slot_list)
    284         proto_layout.ship_type.CopyFrom(cls.SHIP_TYPE_PROTO(sequential=ship_type_id))
    285         proto_layout.pattern_blend_mode = layout.pattern_blend_mode
    286         return proto_layout
    287 
    288     def create_component_instance(self, component_id):
    289         raise NotImplementedError
    290 
    291     @classmethod
    292     def build_config_proto_from_component_instance(cls, component_instance):
    293         raise NotImplementedError
    294 
    295     def get_component_license(self, component_id, component_type):
    296         raise NotImplementedError