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

skill_training.py (9574B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\characterskills\skill_training.py
      2 from contextlib import contextmanager
      3 from characterskills.util import GetSkillPointsPerMinute, GetSPForLevelRaw
      4 import gametime
      5 from carbon.common.lib.const import MIN
      6 
      7 class SkillAttributeMissingError(Exception):
      8     pass
      9 
     10 
     11 class CharacterSkillInterface(object):
     12 
     13     def get_primary_attribute_id(self, skill_type_id):
     14         raise NotImplementedError()
     15 
     16     def get_secondary_attribute_id(self, skill_type_id):
     17         raise NotImplementedError()
     18 
     19     def get_skill_rank(self, skill_type_id):
     20         raise NotImplementedError()
     21 
     22     def get_skill_level(self, skill_type_id):
     23         raise NotImplementedError()
     24 
     25     def get_character_attribute_at_time(self, attribute_id, time_offset):
     26         raise NotImplementedError()
     27 
     28     def get_skill_points(self, skill_type_id):
     29         raise NotImplementedError()
     30 
     31     def get_skill_accelerator_boosters(self):
     32         raise NotImplementedError()
     33 
     34 
     35 class SkillLevelAlreadyTrainedError(Exception):
     36     pass
     37 
     38 
     39 class SkillPointsTrainedPerMinuteError(Exception):
     40     pass
     41 
     42 
     43 class SkillTrainingTimeCalculator(object):
     44 
     45     def __init__(self, character_skill_interface):
     46         self._character_skill_interface = character_skill_interface
     47         self._current_time_function = None
     48         self.reset_current_time_function()
     49 
     50     def _get_primary_attribute_value_at_time(self, skill_type_id, time_offset):
     51         attribute_id = self._character_skill_interface.get_primary_attribute_id(skill_type_id)
     52         if not attribute_id:
     53             raise SkillAttributeMissingError("Skill %s is missing it's primary attribute")
     54         return self._character_skill_interface.get_character_attribute_at_time(attribute_id, time_offset)
     55 
     56     def _get_secondary_attribute_value_at_time(self, skill_type_id, time_offset):
     57         attribute_id = self._character_skill_interface.get_secondary_attribute_id(skill_type_id)
     58         if not attribute_id:
     59             raise SkillAttributeMissingError("Skill %s is missing it's secondary attribute")
     60         return self._character_skill_interface.get_character_attribute_at_time(attribute_id, time_offset)
     61 
     62     def _get_starting_skill_points(self, skill_level, skill_rank, skill_type_id, to_level):
     63         if skill_level == to_level - 1:
     64             from_skillpoints = self._character_skill_interface.get_skill_points(skill_type_id)
     65         else:
     66             from_skillpoints = GetSPForLevelRaw(skill_rank, to_level - 1)
     67         return from_skillpoints
     68 
     69     def _get_time_offset(self, training_start_time):
     70         time_offset = 0
     71         if training_start_time:
     72             time_offset = training_start_time - self._get_current_time()
     73         return time_offset
     74 
     75     def _get_current_time(self):
     76         return self._current_time_function()
     77 
     78     def _get_skill_points_per_minute_at(self, skill_type_id, time_offset):
     79         primary_attribute_value = self._get_primary_attribute_value_at_time(skill_type_id, time_offset)
     80         secondary_attribute_value = self._get_secondary_attribute_value_at_time(skill_type_id, time_offset)
     81         sp_per_minute = GetSkillPointsPerMinute(primary_attribute_value, secondary_attribute_value)
     82         if sp_per_minute <= 0:
     83             raise SkillPointsTrainedPerMinuteError('Skill points trained per minute is %s which suggests bad character attribute values. Skill type=%s, primary attribute=s, secondary attribute=%s.', sp_per_minute, skill_type_id, primary_attribute_value, secondary_attribute_value)
     84         return sp_per_minute
     85 
     86     def get_remaining_skill_points(self, skill_type_id, to_level):
     87         skill_rank = self._character_skill_interface.get_skill_rank(skill_type_id)
     88         skill_level = self._character_skill_interface.get_skill_level(skill_type_id)
     89         from_skillpoints = self._get_starting_skill_points(skill_level, skill_rank, skill_type_id, to_level)
     90         to_skillpoints = GetSPForLevelRaw(skill_rank, to_level)
     91         remaining_skillpoints = to_skillpoints - from_skillpoints
     92         return remaining_skillpoints
     93 
     94     def _get_remaining_skill_points_given_existing_skill_points(self, skill_type_id, to_level, existing_skill_points):
     95         skill_rank = self._character_skill_interface.get_skill_rank(skill_type_id)
     96         to_skillpoints = GetSPForLevelRaw(skill_rank, to_level)
     97         remaining_skillpoints = to_skillpoints - existing_skill_points
     98         if remaining_skillpoints <= 0:
     99             raise SkillLevelAlreadyTrainedError('Skill type %s already trained to level %s. Has %s skill points while needing only %s to achieve the level', skill_type_id, to_level, existing_skill_points, to_skillpoints)
    100         return remaining_skillpoints
    101 
    102     def _get_booster_expiry_time_offsets(self, training_start_time):
    103         boosters = self._character_skill_interface.get_skill_accelerator_boosters()
    104         if not boosters:
    105             return []
    106         expiry_times = boosters.get_expiry_times_after_time(training_start_time)
    107         return [ expiry_time - training_start_time for expiry_time in expiry_times ]
    108 
    109     def _get_training_time_given_skill_points_to_train(self, skill_type_id, skillpoints_to_train, training_start_time):
    110         time_offset = self._get_time_offset(training_start_time)
    111         skillpoints_per_minute = self._get_skill_points_per_minute_at(skill_type_id, time_offset)
    112         accumulated_skillpoints = 0
    113         accumulated_time = 0
    114         for expiry_time_offset in self._get_booster_expiry_time_offsets(training_start_time):
    115             time_left_until_expiry = float(expiry_time_offset - accumulated_time)
    116             skillpoints_until_expiry = time_left_until_expiry / MIN * skillpoints_per_minute
    117             if accumulated_skillpoints + skillpoints_until_expiry >= skillpoints_to_train:
    118                 break
    119             accumulated_skillpoints += skillpoints_until_expiry
    120             accumulated_time += time_left_until_expiry
    121             skillpoints_per_minute = self._get_skill_points_per_minute_at(skill_type_id, time_offset + expiry_time_offset)
    122 
    123         remaining_skillpoints = skillpoints_to_train - accumulated_skillpoints
    124         total_time = accumulated_time + long(float(remaining_skillpoints) / float(skillpoints_per_minute) * MIN)
    125         return total_time
    126 
    127     def get_skill_points_and_time_to_train(self, skill_type_id, to_level, training_start_time):
    128         skillpoints_to_train = self.get_remaining_skill_points(skill_type_id, to_level)
    129         total_time = self._get_training_time_given_skill_points_to_train(skill_type_id, skillpoints_to_train, training_start_time)
    130         return (skillpoints_to_train, total_time)
    131 
    132     def get_training_time(self, skill_type_id, to_level, training_start_time = None):
    133         skillpoints_to_train, training_time = self.get_skill_points_and_time_to_train(skill_type_id, to_level, training_start_time)
    134         return training_time
    135 
    136     def get_skill_points_and_time_to_train_given_existing_skill_points(self, skill_type_id, to_level, training_start_time, existing_skill_points):
    137         skillpoints_to_train = self._get_remaining_skill_points_given_existing_skill_points(skill_type_id, to_level, existing_skill_points)
    138         total_time = self._get_training_time_given_skill_points_to_train(skill_type_id, skillpoints_to_train, training_start_time)
    139         return (skillpoints_to_train, total_time)
    140 
    141     def get_skill_points_trained_at_sample_time(self, skill_type_id, training_start_time, sample_time):
    142         starting_skillpoints = self._character_skill_interface.get_skill_points(skill_type_id) or 0
    143         if training_start_time > sample_time:
    144             return starting_skillpoints
    145         time_offset = self._get_time_offset(training_start_time)
    146         skillpoints_per_minute = self._get_skill_points_per_minute_at(skill_type_id, time_offset)
    147         sample_offset = sample_time - training_start_time
    148         time_left_until_sample = sample_offset
    149         accumulated_skillpoints = 0
    150         accumulated_time = 0
    151         for expiry_time_offset in self._get_booster_expiry_time_offsets(training_start_time):
    152             if expiry_time_offset >= sample_offset:
    153                 time_left_until_sample = sample_offset - accumulated_time
    154                 break
    155             time_left_until_expiry = expiry_time_offset - accumulated_time
    156             skillpoints_until_expiry = time_left_until_expiry / float(MIN) * skillpoints_per_minute
    157             accumulated_skillpoints += skillpoints_until_expiry
    158             accumulated_time += time_left_until_expiry
    159             skillpoints_per_minute = self._get_skill_points_per_minute_at(skill_type_id, time_offset + expiry_time_offset)
    160 
    161         accumulated_skillpoints += time_left_until_sample / float(MIN) * skillpoints_per_minute
    162         return int(accumulated_skillpoints + starting_skillpoints)
    163 
    164     def get_skill_accelerator_boosters(self):
    165         return self._character_skill_interface.get_skill_accelerator_boosters()
    166 
    167     def set_current_time_function(self, function):
    168         self._current_time_function = function
    169 
    170     def reset_current_time_function(self):
    171         self.set_current_time_function(gametime.GetWallclockTime)
    172 
    173     @contextmanager
    174     def specific_current_time_context(self, time):
    175 
    176         def get_current_time():
    177             return time
    178 
    179         sab = self.get_skill_accelerator_boosters()
    180         sab.set_current_time_function(get_current_time)
    181         self.set_current_time_function(get_current_time)
    182         try:
    183             yield self
    184         finally:
    185             sab.reset_current_time_function()
    186             self.reset_current_time_function()