achievement_entry.py (17618B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\achievement_system\client\ui\controller\achievement_entry.py 2 from __future__ import division 3 import datetime 4 import eveicon 5 import logging 6 from achievement_system.client.api.error import AchievementAlreadyTrackedError, TrackedAchievementLimitReachedError, TrackedAchievementNotFoundError, AlreadyPinnedError, PinLimitReachedError, PinNotFoundError, DefinitionNotFoundError 7 from achievement_system.client.api.repository import get_repository 8 from achievement_system.client.ui.window.const import get_content_tag_icon, EXCLUDED_CONTRIB_METHOD_PARAM_KEYS, HISTORIC_COMPLETE_COLOR, HISTORIC_INCOMPLETE_COLOR 9 from achievement_system.client.ui.util import get_controller_for_configured_parameter 10 from achievement_system.common.const import LOGGER_NAME 11 from carbon.common.script.util.format import FmtAmt 12 from achievement_system.client.model.definition.achievement.entry import AchievementDefinitionEntry 13 from achievement_system.client.model.definition.contribution_method.progress_config import ProgressType 14 from eve.client.script.ui import eveColor, eveThemeColor 15 from localization import GetByMessageID, GetByLabel 16 from mathext import clamp 17 from player_messaging.client.ui_message import message_player 18 from signals import Signal 19 logger = logging.getLogger(LOGGER_NAME) 20 21 class AchievementEntryController(object): 22 23 def __init__(self, achievement_entry): 24 self.achievement_entry = achievement_entry 25 self._selected_milestone_index = min(self.completed_milestones, self.total_milestones - 1) 26 self.on_selected_milestone_changed = Signal('on_selected_milestone_changed') 27 28 @property 29 def state(self): 30 return get_repository().get_achievement_state(achievement_identifier=self.achievement_entry.identifier) 31 32 @property 33 def id(self): 34 return self.achievement_entry.identifier.uuid 35 36 @property 37 def icon(self): 38 if self.achievement_entry.attributes.icon_id: 39 return eveicon.get(self.achievement_entry.attributes.icon_id) 40 else: 41 return eveicon.get(self.contribution_attributes.icon_id) 42 43 @property 44 def content_tag_icon(self): 45 return get_content_tag_icon(self.achievement_entry.attributes.tags) 46 47 @property 48 def name(self): 49 return self.achievement_entry.attributes.name_message.get_localized_text() 50 51 @property 52 def description(self): 53 amount = self.target_for_milestone_index(self.current_milestone_index) 54 return GetByMessageID(self.achievement_entry.attributes.description_message.identifier, amount=amount) 55 56 @property 57 def description_for_selected_milestone(self): 58 amount = self.target_for_milestone_index(self.selected_milestone_index) 59 return GetByMessageID(self.achievement_entry.attributes.description_message.identifier, amount=amount) 60 61 @property 62 def flavor_text(self): 63 message = self.achievement_entry.attributes.flavor_text_message 64 if message is None: 65 return 66 localized_text = message.get_localized_text() 67 if len(localized_text) == 0: 68 return 69 return localized_text 70 71 @property 72 def total_milestones(self): 73 return len(self.achievement_entry.attributes.milestones) 74 75 @property 76 def completed_milestones(self): 77 if self.state is None: 78 return 0 79 return len(self.state.reached_milestones) 80 81 @property 82 def selected_milestone_index(self): 83 return self._selected_milestone_index 84 85 @property 86 def current_milestone_index(self): 87 return min(self.completed_milestones, self.total_milestones - 1) 88 89 @property 90 def progress(self): 91 max_progress = self.achievement_entry.attributes.milestones[-1].attributes.cumulated_target 92 if self.state is None: 93 return 0 94 if self.progress_type is ProgressType.CUMULATIVE: 95 progress = self.state.progress if self.state.progress else 0 96 elif self.progress_type in [ProgressType.CHECKLIST, ProgressType.SET]: 97 progress = self.state.checklist.get_progress_value() if self.state.checklist else 0 98 else: 99 progress = 0 100 return min(progress, max_progress) 101 102 @property 103 def is_checklist(self): 104 return self.progress_type == ProgressType.CHECKLIST 105 106 @property 107 def is_historic(self): 108 return self.achievement_entry.attributes.is_historic 109 110 def is_checklist_item_done(self, item_value): 111 if self.progress_type != ProgressType.CHECKLIST: 112 logger.error("trying to evaluate a checklist item %s for an non-checklist achievement, this isn't normal", item_value) 113 return False 114 if self.state.checklist is None: 115 return False 116 for value_type, values in self.state.checklist.values.items(): 117 if str(item_value) in values: 118 return True 119 120 return False 121 122 def target_for_milestone_index(self, index): 123 return self.achievement_entry.attributes.milestones[clamp(index, 0, len(self.achievement_entry.attributes.milestones) - 1)].attributes.cumulated_target 124 125 @property 126 def progress_score_text_for_current_milestone(self): 127 return self.progress_score_text_for_milestone(self.current_milestone_index) 128 129 @property 130 def progress_score_text_for_selected_milestone(self): 131 return self.progress_score_text_for_milestone(self.selected_milestone_index) 132 133 def progress_score_text_for_milestone(self, milestone_index): 134 current_value = self.progress 135 target_value = self.target_for_milestone_index(milestone_index) 136 if current_value > target_value: 137 current_value = target_value 138 if target_value <= 100000: 139 current_text = FmtAmt(current_value) 140 target_text = FmtAmt(target_value) 141 else: 142 current_text = FmtAmt(current_value, fmt='sn') 143 target_text = FmtAmt(target_value, fmt='sn') 144 return '%s/%s' % (current_text, target_text) 145 146 @property 147 def progress_score_hint_for_current_milestone(self): 148 return self.progress_score_hint_for_milestone(self.current_milestone_index) 149 150 @property 151 def progress_score_hint_for_selected_milestone(self): 152 return self.progress_score_hint_for_milestone(self.selected_milestone_index) 153 154 def progress_score_hint_for_milestone(self, milestone_index): 155 current_value = self.progress 156 target_value = self.target_for_milestone_index(milestone_index) 157 if current_value > target_value: 158 current_value = target_value 159 return '%s/%s' % (FmtAmt(current_value), FmtAmt(target_value)) 160 161 @property 162 def selected_milestone_target(self): 163 return self.target_for_milestone_index(self.selected_milestone_index) 164 165 @property 166 def percent_progress_towards_selected_milestone(self): 167 return self.progress_ratio_towards_milestone(self.selected_milestone_index) 168 169 @property 170 def percent_progress_towards_current_milestone(self): 171 return self.progress_ratio_towards_milestone(self.current_milestone_index) 172 173 def progress_ratio_towards_milestone(self, milestone_index): 174 progress = self._get_percent_progress_towards_milestone(milestone_index) 175 if 0 < progress < 0.01: 176 return 0.01 177 if 0.99 < progress < 1.0: 178 return 0.99 179 return progress 180 181 def rewards_for_milestone_index(self, index): 182 return self.achievement_entry.attributes.milestones[clamp(index, 0, len(self.achievement_entry.attributes.milestones) - 1)].attributes.rewards 183 184 def achievement_score_for_milestone_index(self, index): 185 return self.achievement_entry.attributes.milestones[index].attributes.category_points 186 187 def set_selected_milestone_index(self, index): 188 self._selected_milestone_index = clamp(index, 0, self.total_milestones - 1) 189 self.on_selected_milestone_changed() 190 191 @property 192 def selected_milestone_id(self): 193 return self.achievement_entry.attributes.milestones[self.selected_milestone_index].identifier 194 195 @property 196 def selected_milestone_rewards(self): 197 return self.rewards_for_milestone_index(self.selected_milestone_index) 198 199 @property 200 def current_milestone_rewards(self): 201 return self.rewards_for_milestone_index(self.current_milestone_index) 202 203 @property 204 def selected_milestone_score(self): 205 return self.achievement_score_for_milestone_index(self.selected_milestone_index) 206 207 @property 208 def current_milestone_score(self): 209 return self.achievement_score_for_milestone_index(self.current_milestone_index) 210 211 @property 212 def is_selected_milestone_completed(self): 213 return self.completed_milestones > 0 and self.selected_milestone_index < self.completed_milestones 214 215 @property 216 def is_current_milestone_completed(self): 217 return self.completed_milestones > 0 and self.current_milestone_index < self.completed_milestones 218 219 @property 220 def has_multiple_milestones(self): 221 return self.total_milestones > 1 222 223 @property 224 def has_flavor_text(self): 225 return self.flavor_text is not None 226 227 @property 228 def progress_type(self): 229 return self.contribution_attributes.progress_config.progress_type 230 231 @property 232 def contribution_config(self): 233 return self.achievement_entry.attributes.contribution_method 234 235 @property 236 def contribution_attributes(self): 237 contribution_identifier = self.contribution_config.identifier 238 return get_repository().definitions.get_contribution_method(contribution_identifier) 239 240 @property 241 def contribution_name(self): 242 return self.contribution_attributes.name_message.get_localized_text() 243 244 @property 245 def contribution_description(self): 246 return self.contribution_attributes.description_message.get_localized_text() 247 248 @property 249 def contribution_progress_text(self): 250 return self.contribution_attributes.progress_message.get_localized_text() 251 252 @property 253 def contribution_icon(self): 254 contribution_attributes = self.contribution_attributes 255 if contribution_attributes.icon_id: 256 return eveicon.get(contribution_attributes.icon_id) 257 else: 258 logger.error('Icon not found for contribution method %s in achievement %s', self.contribution_config.identifier, self.achievement_entry.identifier) 259 return None 260 261 @property 262 def icon_frame_textures(self): 263 ach_outer, ach_mask, ach_inner = self.achievement_entry.attributes.get_icon_frame_keys(self.is_completed) 264 cm_outer, cm_mask, cm_inner = self.contribution_attributes.get_icon_frame_keys(self.is_completed) 265 outer = ach_outer if ach_outer else cm_outer 266 mask = ach_mask if ach_mask else cm_mask 267 inner = ach_inner if ach_inner else cm_inner 268 if outer is None: 269 logger.error('achievement %s has no icon outer frame defined', self.achievement_entry.attributes.name_message.get_localized_text()) 270 if mask is None: 271 logger.error('achievement %s has no icon mask frame defined', self.achievement_entry.attributes.name_message.get_localized_text()) 272 if inner is None: 273 logger.error('achievement %s has no icon inner frame defined', self.achievement_entry.attributes.name_message.get_localized_text()) 274 return (outer, mask, inner) 275 276 @property 277 def is_completed(self): 278 return self.state is not None and not self.state.not_completed 279 280 @property 281 def progress_bar_color(self): 282 if self.is_completed: 283 if not self.is_historic: 284 return eveColor.SUCCESS_GREEN 285 return HISTORIC_COMPLETE_COLOR 286 elif not self.is_historic: 287 return eveThemeColor.THEME_ACCENT.rgba 288 else: 289 return HISTORIC_INCOMPLETE_COLOR 290 291 @property 292 def milestone_progress_bar_color(self): 293 if self.percent_progress_towards_selected_milestone == 1.0: 294 if not self.is_historic: 295 return eveColor.SUCCESS_GREEN 296 return HISTORIC_COMPLETE_COLOR 297 return self.progress_bar_color 298 299 @property 300 def is_pinned(self): 301 return get_repository().activity.is_pinned(self.achievement_entry.identifier) 302 303 @property 304 def is_tracked(self): 305 return get_repository().activity.is_tracked(self.achievement_entry.identifier) 306 307 @property 308 def is_time_limited(self): 309 return self.expiration_date is not None 310 311 @property 312 def is_expired(self): 313 if not self.is_time_limited: 314 return False 315 time_remaining = self.expiration_date - datetime.datetime.now() 316 return time_remaining < datetime.timedelta(0) 317 318 @property 319 def expiration_date(self): 320 return self.achievement_entry.attributes.expiration_date 321 322 @property 323 def time_remaining(self): 324 if self.is_expired: 325 return datetime.timedelta(0) 326 return self.expiration_date - datetime.datetime.now() 327 328 @property 329 def has_unclaimed_rewards(self): 330 unclaimed_rewards = get_repository().get_unclaimed_rewards_for_achievement(achievement_id=self.achievement_entry.identifier) 331 return unclaimed_rewards is not None 332 333 def toggle_pinned(self): 334 if self.is_pinned: 335 try: 336 get_repository().activity.unpin_achievement(self.achievement_entry.identifier) 337 except PinNotFoundError: 338 logger.warning('Pin not found for achievement: %s', self.achievement_entry.identifier) 339 340 else: 341 try: 342 get_repository().activity.pin_achievement(self.achievement_entry.identifier) 343 except AlreadyPinnedError: 344 logger.warning('Achievement already pinned: %s', self.achievement_entry.identifier) 345 except PinLimitReachedError: 346 message_player(GetByLabel('UI/Achievements/Error/PinLimitReached')) 347 348 def toggle_tracked(self): 349 if self.is_tracked: 350 self.untrack() 351 else: 352 self.track() 353 354 def track(self): 355 try: 356 get_repository().activity.track_achievement(self.achievement_entry.identifier) 357 except AchievementAlreadyTrackedError: 358 logger.warning('Achievement already tracked: %s', self.achievement_entry.identifier) 359 except TrackedAchievementLimitReachedError: 360 message_player(GetByLabel('UI/Achievements/Error/TrackedAchievementLimitReached')) 361 362 def untrack(self): 363 try: 364 get_repository().activity.untrack_achievement(self.achievement_entry.identifier) 365 except TrackedAchievementNotFoundError: 366 logger.warning('Tracked achievement not found: %s', self.achievement_entry.identifier) 367 368 def get_contribution_parameters_to_display(self): 369 exclude_list = [ x for x in EXCLUDED_CONTRIB_METHOD_PARAM_KEYS ] 370 progress_mode = self.contribution_attributes.progress_config.progress_type 371 if progress_mode == ProgressType.CHECKLIST: 372 exclude_list.append(self.contribution_attributes.progress_config.target_parameter) 373 logger.info('get_contribution_parameters_to_display: skipping %s for achievement %s as it is the checklist target parameter', self.contribution_attributes.progress_config.target_parameter, self.achievement_entry.attributes.name_message.get_localized_text()) 374 parameters_to_display = [] 375 for parameter_entry in self.contribution_attributes.parameters: 376 if parameter_entry.identifier.key not in exclude_list: 377 param_config = self.contribution_config.get_parameter_config_by_identifier(parameter_entry.identifier) 378 if param_config is not None: 379 parameters_to_display.append((parameter_entry, param_config)) 380 381 return parameters_to_display 382 383 @property 384 def parameters_to_display_as_list(self): 385 result = [] 386 include_list = [] 387 if self.progress_type == ProgressType.CHECKLIST: 388 include_list.append(self.contribution_attributes.progress_config.target_parameter) 389 if len(include_list) == 0: 390 return result 391 for parameter_entry in self.contribution_attributes.parameters: 392 if parameter_entry.identifier.key in include_list: 393 param_config = self.contribution_config.get_parameter_config_by_identifier(parameter_entry.identifier) 394 if param_config is not None: 395 result.append((parameter_entry, param_config)) 396 397 return result 398 399 def get_contribution_param_ui_controllers(self): 400 result = [] 401 for parameter_entry, param_config in self.get_contribution_parameters_to_display(): 402 component_controller = get_controller_for_configured_parameter(definition=parameter_entry.get_ui_controller(), configuration=param_config.get_ui_controller()) 403 if component_controller is None: 404 continue 405 result.append(component_controller) 406 407 return result 408 409 def _get_percent_progress_towards_milestone(self, index): 410 current_target = self.target_for_milestone_index(index) 411 return min(self.progress / current_target, 1.0) 412 413 @property 414 def category_icon(self): 415 try: 416 category_id = self.achievement_entry.attributes.parent_category 417 category_attributes = get_repository().definitions.get_category(category_id) 418 return eveicon.get(category_attributes.icon_id) 419 except DefinitionNotFoundError: 420 return eveicon.cup