def heal_active_convert(self, act): hp = getattr(act, 'hp', 0) rcv_mult = getattr(act, 'rcv_multiplier_as_hp', 0) php = getattr(act, 'percentage_max_hp', 0) trcv_mult = getattr(act, 'team_rcv_multiplier_as_hp', 0) unbind = getattr(act, 'card_bind', 0) awoken_unbind = getattr(act, 'awoken_bind', 0) skill_text = ('Recover ' + '{:,}'.format(hp) + ' HP' if hp != 0 else ('Recover ' + fmt_mult(rcv_mult) + 'x RCV as HP' if rcv_mult != 0 else ('Recover all HP' if php == 1 else ('Recover ' + fmt_mult(php * 100) + '% of max HP' if php > 0 else ('Recover HP equal to ' + fmt_mult(trcv_mult) + 'x team\'s total RCV' if trcv_mult > 0 else ''))))) if unbind or awoken_unbind: if skill_text: skill_text += '; ' skill_text += ( 'Remove all binds and awoken skill binds' if (unbind >= 9999 and awoken_unbind) else ('Reduce binds and awoken skill binds by {:s}'.format( noun_count('turn', awoken_unbind)) if (unbind and awoken_unbind) else ('Remove all binds' if unbind >= 9999 else ('Reduce binds by {:s}'.format(noun_count('turn', unbind) ) if unbind else ('Remove all awoken skill binds' if awoken_unbind >= 9999 else ('Reduce awoken skill binds by {:s}'. format(noun_count('turn', awoken_unbind)))))))) return skill_text
def grudge_strike_convert(self, act): skill_text = 'Deal ' + self.ATTRIBUTES[ act.attribute] + ' damage to ' + self.fmt_mass_atk( act.mass_attack) + ' depending on HP level (' + fmt_mult( act.low_multiplier) + 'x at 1 HP and ' + fmt_mult( act.high_multiplier) + 'x at 100% HP)' return skill_text
def counter_attack_text(self, ls): attribute = self.ATTRIBUTES[ls.attributes[0]] multiplier = fmt_mult(ls.multiplier) if ls.chance == 1: return '{}x {} counterattack'.format(multiplier, attribute) return '{}% chance to counterattack with {}x {} damage'.format( fmt_mult(ls.chance * 100), multiplier, attribute)
def fmt_multiplier_text(self, hp_mult, atk_mult, rcv_mult, default=1): if hp_mult == atk_mult and atk_mult == rcv_mult: if hp_mult == default: return '' return self.all_stats(fmt_mult(hp_mult)) mults = [(self.hp(), hp_mult), (self.atk(), atk_mult), (self.rcv(), rcv_mult)] mults = list(filter(lambda ml: ml[1] != default, mults)) mults.sort(key=lambda ml: ml[1], reverse=True) chunks = [] x = 0 while x < len(mults): can_check_double = x + 1 < len(mults) if can_check_double and mults[x][1] == mults[x + 1][1]: chunks.append(('{} & {}'.format(mults[x][0], mults[x + 1][0]), mults[x][1])) x += 2 else: chunks.append((mults[x][0], mults[x][1])) x += 1 output = '' for c in chunks: if len(output): output += ' and ' output += '{}x {}'.format(fmt_mult(c[1]), c[0]) return output
def multi_attribute_match_convert(self, ls): attributes = multi_getattr(ls, 'match_attributes', 'attributes') if not attributes: return '' min_atk_mult = multi_getattr(ls, 'min_atk', 'atk') min_rcv_mult = multi_getattr(ls, 'min_rcv', 'rcv') min_match = multi_getattr(ls, 'min_attr', 'min_combo', 'min_match') max_mult = multi_getattr(ls, 'max_atk', 'atk') intro = self.fmt_stats_type_attr_bonus(ls, reduce_join_txt=' and ', skip_attr_all=True, atk=min_atk_mult, rcv=min_rcv_mult) if all(x == attributes[0] for x in attributes): attr_text = self.ATTRIBUTES[attributes[0]] max_mult = fmt_mult( max_mult) if len(attributes) != min_match else None max_match = len(attributes) return self.multi_of_one_attribute_match_text( intro, min_match, attr_text, max_mult, max_match) min_colors = self.attributes_format(attributes[:min_match], sep='+') alt_colors = self.attributes_format( attributes[1:], sep='+') if len(attributes) > min_match else None all_colors = self.attributes_format(attributes, sep='+') max_mult = fmt_mult(max_mult) if max_mult > min_atk_mult else None return self.multi_of_dif_attribute_match_text(intro, min_colors, alt_colors, max_mult, all_colors)
def attribute_match_text(self, ls): skill_text = self.fmt_stats_type_attr_bonus(ls, reduce_join_txt=' and ', skip_attr_all=True, atk=ls.min_atk, rcv=ls.min_rcv) skill_text += self.matching_n_or_more_attr( ls.match_attributes, ls.min_attr, is_range=ls.max_attr > ls.min_attr) if ls.atk > ls.min_atk: if ls.match_attributes == [0, 1, 2, 3, 4, 5]: skill_text += ' up to {}x at 5 colors+heal'.format( fmt_mult(ls.atk)) elif ls.max_attr < 5 and (len(ls.match_attributes) < 5 or 5 in ls.match_attributes): skill_text += ' up to {}x when matching {}'.format( fmt_mult(ls.atk), ls.max_attr) else: skill_text += ' up to {}x at '.format(fmt_mult(ls.atk)) if ls.match_attributes == [0, 1, 2, 3, 4]: skill_text += '{} colors'.format(ls.max_attr) elif ls.match_attributes == [0, 1, 2, 3, 4, 5]: skill_text += '{} colors ({}+heal)'.format( ls.max_attr, ls.max_attr - 1) elif len(ls.match_attributes) > ls.max_attr: skill_text += '{}+ of {} at once'.format( str(ls.max_attr), self.attributes_to_str(ls.match_attributes, concat='or')) else: skill_text += '{} at once'.format( self.attributes_to_str(ls.match_attributes)) return skill_text
def move_time_buff_convert(self, act): if act.static == 0: return self.fmt_duration(act.duration) + \ fmt_mult(act.percentage) + 'x orb move time' elif act.percentage == 0: return self.fmt_duration(act.duration) + \ 'increase orb move time by {:s}'.format(noun_count('second', fmt_mult(act.static))) raise ValueError()
def orb_remain_convert(self, ls): intro = self.fmt_stats_type_attr_bonus(ls, atk=ls.min_atk) if ls.base_atk in [0, 1]: return intro base_atk = fmt_mult(ls.base_atk) orb_count = str(ls.orb_count) max_atk = fmt_mult(ls.atk) if ls.bonus_atk != 0 else None return self.orb_remain_text(intro, base_atk, orb_count, max_atk)
def passive_stats_type_atk_all_hp_convert(self, ls): hp_pct = fmt_mult((1 - ls.hp) * 100) atk_mult = fmt_mult(ls.atk) type_text = '' for i in ls.types[:-1]: type_text += self.TYPES[i] + ', ' type_text += self.TYPES[int(ls.types[-1])] return self.passive_stats_type_atk_all_hp_text(hp_pct, atk_mult, type_text)
def drain_attack_convert(self, act): skill_text = 'Deal ' + \ fmt_mult(act.atk_multiplier) + 'x ATK damage to ' + self.fmt_mass_atk(act.mass_attack) if act.recover_multiplier == 1: skill_text += ' and recover the same amount as HP' else: skill_text += ' and recover ' + \ fmt_mult(act.recover_multiplier * 100) + '% of the damage as HP' return skill_text
def scaling_attribute_match_text(self, ls): min_atk = ls.min_atk min_attr = ls.min_attr if max(ls.steps, default=-1) + 1 != len(ls.steps): human_fix_logger.warning( f"Unknown behavior for rainbow skill {ls.skill_id}") if min_atk < ls.atk: if ls.min_atk == 1: min_atk = 1 + (ls.atk - ls.min_atk) / (ls.max_attr - ls.min_attr) min_attr += 1 else: return self.attribute_match_text(ls) skill_text = self.fmt_stats_type_attr_bonus(ls, reduce_join_txt=' and ', skip_attr_all=True, atk=1, rcv=1) skill_text += self.matching_n_or_more_attr(ls.match_attributes, ls.min_attr) + '; ' skill_text += self.fmt_stats_type_attr_bonus(ls, reduce_join_txt=' and ', skip_attr_all=True, atk=min_atk, shield=0) skill_text += self.matching_n_or_more_attr( ls.match_attributes, min_attr, is_range=ls.max_attr > min_attr) if ls.atk > min_atk: if ls.match_attributes == [0, 1, 2, 3, 4, 5]: skill_text += ' up to {}x at 5 colors+heal'.format( fmt_mult(ls.atk)) elif ls.max_attr < 5 and (len(ls.match_attributes) < 5 or 5 in ls.match_attributes): skill_text += ' up to {}x when matching {}'.format( fmt_mult(ls.atk), ls.max_attr) else: skill_text += ' up to {}x at '.format(fmt_mult(ls.atk)) if ls.match_attributes == [0, 1, 2, 3, 4]: skill_text += '{} colors'.format(ls.max_attr) elif ls.match_attributes == [0, 1, 2, 3, 4, 5]: skill_text += '{} colors ({}+heal)'.format( ls.max_attr, ls.max_attr - 1) elif len(ls.match_attributes) > ls.max_attr: skill_text += '{}+ of {} at once'.format( str(ls.max_attr), self.attributes_to_str(ls.match_attributes, concat='or')) else: skill_text += '{} at once'.format( self.attributes_to_str(ls.match_attributes)) return skill_text
def random_nuke_convert(self, act): if act.minimum_multiplier != act.maximum_multiplier: return 'Randomized ' + self.ATTRIBUTES[ act.attribute] + ' damage to ' + self.fmt_mass_atk( act.mass_attack) + '(' + fmt_mult( act.minimum_multiplier) + '~' + fmt_mult( act.maximum_multiplier) + 'x)' else: return 'Deal ' + fmt_mult(act.maximum_multiplier) + 'x ' + \ self.ATTRIBUTES[act.attribute] + ' damage to ' + self.fmt_mass_atk(act.mass_attack)
def orb_remain_text(self, ls): skill_text = self.fmt_stats_type_attr_bonus(ls, atk=ls.min_atk) if ls.base_atk in [0, 1]: return skill_text if skill_text: skill_text += '; ' skill_text += '{}x ATK when there are {} or fewer orbs remaining'.format( fmt_mult(ls.base_atk), ls.orb_count) if ls.bonus_atk != 0: skill_text += ' up to {}x ATK when 0 orbs left'.format( fmt_mult(ls.max_bonus_atk)) return skill_text
def multi_attribute_match_text(self, ls): if not ls.match_attributes: return '' skill_text = '' min_atk = ls.min_atk min_rcv = ls.min_rcv shield = ls.shield min_match = ls.min_match if (min_atk == 1 and ls.atk != 1) or (min_rcv == 1 and ls.rcv != 1): skill_text = self.fmt_stats_type_attr_bonus( ls, reduce_join_txt=' and ', atk=min_atk, rcv=min_rcv) skill_text += f' when matching {min_match} {self.ATTRIBUTES[ls.match_attributes[0]]} combos; ' min_match += 1 min_atk += ls.atk_step min_rcv += ls.rcv_step shield = 0 skill_text += self.fmt_stats_type_attr_bonus(ls, reduce_join_txt=' and ', skip_attr_all=True, atk=min_atk, rcv=min_rcv, shield=shield) if len(set(ls.match_attributes)) == 1: skill_text += ' when matching {}'.format(min_match) if not len(ls.match_attributes) != min_match: skill_text += '+' skill_text += ' {} combos'.format( self.ATTRIBUTES[ls.match_attributes[0]]) if len(ls.match_attributes) != min_match: skill_text += ', up to {}x at {} {} combos'.format( fmt_mult(ls.atk), len(ls.match_attributes), self.ATTRIBUTES[ls.match_attributes[0]]) else: skill_text += ' when matching {}'.format( self.attributes_to_str(ls.match_attributes[:min_match])) if len(ls.match_attributes) > min_match: if min_match == 1: skill_text += ' or {}'.format( self.attributes_to_str(ls.match_attributes[1:])) else: skill_text += ' (or {})'.format( self.attributes_to_str(ls.match_attributes[1:])) if ls.atk > min_atk: skill_text += ' up to {}x when matching {}'.format( fmt_mult(ls.atk), self.attributes_to_str(ls.match_attributes)) return skill_text
def color_cross_convert(self, ls): atk = fmt_mult(ls.crosses[0].atk) attr_list = [ self.ATTRIBUTES[ls.crosses[i].attribute] for i in range(0, len(ls.crosses)) ] return self.color_cross_text(atk, attr_list)
def bonus_time_text(self, ls): skill_text = [self.fmt_stats_type_attr_bonus(ls)] if ls.time: skill_text.append( 'Increase orb movement time by {} seconds'.format( fmt_mult(ls.time))) return self.concat_list_semicolons(skill_text)
def threshold_stats_convert(self, ls): intro = self.fmt_stats_type_attr_bonus(ls, reduce_join_txt=' and ', skip_attr_all=True) above = ls.threshold_type == ThresholdType.ABOVE threshold = fmt_mult(ls.threshold * 100) return self.threshold_stats_text(intro, above, threshold)
def type_attack_boost_convert(self, act): skill_text = self.fmt_duration(act.duration) + fmt_mult( act.multiplier) + 'x ATK for ' skill_text += self.concat_list_and([self.TYPES[t] for t in act.types]) + ' ' skill_text += pluralize('type', len(act.types)) return skill_text
def combo_match_text(self, ls): if ls.min_combos == 0: return '' skill_text = '' min_atk = ls.min_atk min_rcv = ls.min_rcv shield = ls.shield min_combos = ls.min_combos if (min_atk == 1 and ls.atk != 1) or (min_rcv == 1 and ls.rcv != 1): skill_text = self.fmt_stats_type_attr_bonus( ls, reduce_join_txt=' and ', atk=min_atk, rcv=min_rcv) skill_text += ' when {} or more combos'.format(min_combos) + '; ' min_combos += 1 min_atk += ls.atk_step min_rcv += ls.rcv_step shield = 0 skill_text += self.fmt_stats_type_attr_bonus(ls, reduce_join_txt=' and ', atk=min_atk, rcv=min_rcv, shield=shield) skill_text += ' when {} or more combos'.format(min_combos) if min_combos != ls.max_combos and ls.max_combos: skill_text += ' up to {}x at {} combos'.format( fmt_mult(ls.atk), ls.max_combos) return skill_text
def attack_attr_x_team_atk_convert(self, act): skill_text = 'Deal ' + self.ATTRIBUTES[act.attack_attribute] + \ ' damage equal to ' + fmt_mult(act.multiplier) + 'x of team\'s total ' skill_text += self.concat_list_and( [self.ATTRIBUTES[a] for a in act.team_attributes]) + ' ATK to ' skill_text += self.fmt_mass_atk(act.mass_attack) return skill_text
def random_shield_threshold_text(self, ls): threshold_text = self.passive_stats_text(ls, skip_attr_all=True) threshold_text += self.threshold_hp(ls.threshold, ls.above) if ls.chance == 1: return threshold_text else: chance = fmt_mult(ls.chance * 100) return '{}% chance to {}'.format(chance, threshold_text).lower()
def random_shield_threshold_stats(self, ls): threshold_text = self.threshold_stats_convert(ls) is_guaranteed = ls.chance == 1 if is_guaranteed: return threshold_text else: chance = fmt_mult(ls.chance * 100) return self.chance_to_text(chance, threshold_text)
def fmt_reduct_text(self, shield, reduct_att=None): if shield == 0: return '' shield_text = fmt_mult(shield * 100) if reduct_att in [None, [], [0, 1, 2, 3, 4]]: return self.reduce_all_pct(shield_text) else: color_text = self.attributes_to_str(reduct_att) return self.reduce_attr_pct(color_text, shield_text)
def dual_threshold_stats_part_convert(self, c): intro = self.fmt_stats_type_attr_bonus(c, reduce_join_txt=' and ', skip_attr_all=True) if c.threshold == 1: return self.dual_threshold_stats_part_full_hp_text(intro, c.above) threshold = fmt_mult(c.threshold * 100) return self.dual_threshold_stats_part_threshold_text( intro, c.above, threshold)
def awakening_attack_boost_convert(self, act): skill_text = self.fmt_duration(act.duration) + 'increase ATK by ' + \ fmt_mult(act.amount_per * 100) + '% for each ' awakens = [ f"{{{{ awoskills.id{a}|default('???') }}}}" for a in act.awakenings if a ] skill_text += self.concat_list_and(awakens) skill_text += ' awakening on the team' return skill_text
def awakening_shield_convert(self, act): skill_text = self.fmt_duration(act.duration) + 'reduce damage taken by ' + \ fmt_mult(act.amount_per * 100) + '% for each ' awakens = [ f"{{{{ awoskills.id{a}|default('???') }}}}" for a in act.awakenings if a ] skill_text += self.concat_list_and(awakens) skill_text += ' awakening on the team' return skill_text
def elemental_shield_convert(self, act): skill_text = self.fmt_duration(act.duration) if act.shield == 1: skill_text += 'void all ' + self.ATTRIBUTES[int( act.attribute)] + ' damage' else: skill_text += 'reduce ' + \ self.ATTRIBUTES[int(act.attribute)] + ' damage by ' + \ fmt_mult(act.shield * 100) + '%' return skill_text
def dual_threshold_stats_text(self, ls): skill_parts = [] if str(ls.atk_1) != '1' or ls.rcv_1 != 1 or ls.shield_1 != 0: skill_text = self.fmt_stats_type_attr_bonus( None, reduce_join_txt=' and ', skip_attr_all=True, atk=ls.atk_1, rcv=ls.rcv_1, types=ls.types, attributes=ls.attributes, hp=ls.hp, shield=ls.shield_1) if ls.threshold_1 == 1: skill_text += ' when HP {} full'.format( 'is' if ls.above_1 else 'is not') else: skill_text += ' when {} '.format( 'above' if ls.above_1 else 'below') skill_text += '{}% HP'.format(fmt_mult(ls.threshold_1 * 100)) skill_parts.append(skill_text) if ls.threshold_2 != 0: skill_text = self.fmt_stats_type_attr_bonus( None, reduce_join_txt=' and ', skip_attr_all=True, atk=ls.atk_2, rcv=ls.rcv_2, types=ls.types, attributes=ls.attributes, hp=ls.hp, shield=ls.shield_2) if ls.threshold_2 == 1: skill_text += ' when HP {} full'.format( 'is' if ls.above_2 else 'is not') else: skill_text += ' when {} '.format( 'above' if ls.above_2 else 'below') skill_text += '{}% HP'.format(fmt_mult(ls.threshold_2 * 100)) skill_parts.append(skill_text) return self.concat_list_semicolons(skill_parts)
def attribute_attack_boost_convert(self, act): skill_text = '' if act.rcv_boost: skill_text += self.fmt_duration(act.duration) + fmt_mult( act.multiplier) + 'x RCV' if skill_text: skill_text += '; ' skill_text += self.fmt_duration( act.duration) + self.fmt_stats_type_attr_bonus(act, atk=act.multiplier) return skill_text
def change_skyfall_convert(self, act): skill_text = self.fmt_duration(act.duration, act.max_duration) rate = fmt_mult(act.percentage * 100) if rate == '100': skill_text += 'only {} orbs will appear'.format( self.concat_list_and(self.ATTRIBUTES[i] for i in act.orbs)) else: skill_text += '{} orbs are more likely to appear by {}%'.format( self.concat_list_and(self.ATTRIBUTES[i] for i in act.orbs), rate) return skill_text