from pathlib import Path from typing import List, Optional, Tuple import json from PIL import Image, ImageDraw, ImageFont, ImageChops DMG_PATH = Path(__file__).parents[0] DMG_TEXT_PATH = DMG_PATH / 'texture2d' version = '2.8.0' avatarName2SkillAdd_fileName = f'avatarName2SkillAdd_mapping_{version}.json' with open(DMG_PATH / avatarName2SkillAdd_fileName, "r", encoding='UTF-8') as f: avatarName2SkillAdd = json.load(f) with open(DMG_PATH / 'char_action.json', "r", encoding='UTF-8') as f: char_action = json.load(f) with open(DMG_PATH / 'artifacts_effect.json', "r", encoding='UTF-8') as f: artifacts_effect_map = json.load(f) with open(DMG_PATH / 'weapons_effect.json', "r", encoding='UTF-8') as f: weapons_effect_map = json.load(f) with open(DMG_PATH / 'char_talent_effect.json', "r", encoding='UTF-8') as f: char_talent_effect_map = json.load(f) with open(DMG_PATH / 'char_skill_effect.json', "r", encoding='UTF-8') as f: char_skill_effect_map = json.load(f) dmgBar_1 = Image.open(DMG_TEXT_PATH / 'dmgBar_1.png') dmgBar_2 = Image.open(DMG_TEXT_PATH / 'dmgBar_2.png') def genshin_font_origin(size: int) -> ImageFont: return ImageFont.truetype(str(DMG_TEXT_PATH / 'yuanshen_origin.ttf'), size=size) async def draw_dmgCacl_img(raw_data: dict) -> Image: char_name = raw_data['avatarName'] char_level = int(raw_data['avatarLevel']) weaponName = raw_data['weaponInfo']['weaponName'] weaponType = raw_data['weaponInfo']['weaponType'] weaponAffix = raw_data['weaponInfo']['weaponAffix'] skillList = raw_data['avatarSkill'] a_skill_name = skillList[0]['skillName'].replace('普通攻击·', '') prop = {} prop['A_skill_level'] = skillList[0]['skillLevel'] e_skill_name = skillList[1]['skillName'] prop['E_skill_level'] = skillList[1]['skillLevel'] q_skill_name = skillList[-1]['skillName'] prop['Q_skill_level'] = skillList[-1]['skillLevel'] enemy_level = char_level skill_add = avatarName2SkillAdd[char_name] for skillAdd_index in range(0, 2): if len(raw_data['talentList']) >= 3 + skillAdd_index * 2: if skill_add[skillAdd_index] == 'E': prop['E_skill_level'] += 3 elif skill_add[skillAdd_index] == 'Q': prop['Q_skill_level'] += 3 fight_prop = raw_data['avatarFightProp'] prop['basehp'] = fight_prop['baseHp'] prop['baseattack'] = fight_prop['baseAtk'] prop['basedefense'] = fight_prop['baseDef'] prop['hp'] = fight_prop['hp'] prop['attack'] = fight_prop['atk'] prop['defense'] = fight_prop['def'] prop['em'] = fight_prop['elementalMastery'] prop['critrate'] = fight_prop['critRate'] prop['critdmg'] = fight_prop['critDmg'] prop['ce'] = fight_prop['energyRecharge'] prop['physicalDmgBonus'] = physicalDmgBonus = fight_prop['physicalDmgBonus'] prop['dmgBonus'] = dmgBonus = fight_prop['dmgBonus'] prop['healBouns'] = fight_prop['healBonus'] prop['shieldBouns'] = 0 if char_name not in char_action: faild_img = Image.new('RGBA', (950, 1)) return faild_img, 0 power_list = char_action[char_name] for prop_attr in [ 'dmgBonus', 'critrate', 'critdmg', 'addDmg', 'd', 'r', 'ignoreDef', ]: if prop_attr in ['addDmg', 'd', 'r', 'ignoreDef']: prop['{}'.format(prop_attr)] = 0 for prop_limit in ['A', 'B', 'C', 'E', 'Q']: prop['{}_{}'.format(prop_limit, prop_attr)] = 0 for prop_limit in ['A', 'B', 'C', 'E', 'Q']: if weaponType == '法器' or char_name in ['荒泷一斗', '刻晴', '诺艾尔']: prop['{}_dmgBonus'.format(prop_limit)] = dmgBonus elif weaponType == '弓': if prop_limit in ['A', 'C']: prop['{}_dmgBonus'.format(prop_limit)] = physicalDmgBonus elif prop_limit in ['B', 'E', 'Q']: prop['{}_dmgBonus'.format(prop_limit)] = dmgBonus else: if prop_limit in ['A', 'B', 'C']: prop['{}_dmgBonus'.format(prop_limit)] = physicalDmgBonus elif prop_limit in ['E', 'Q']: prop['{}_dmgBonus'.format(prop_limit)] = dmgBonus prop['hp_green'] = fight_prop['addHp'] prop['attack_green'] = fight_prop['addAtk'] prop['defense_green'] = fight_prop['addDef'] prop['r'] = 0.1 prop['a'] = 0 prop['g'] = 0 prop['k'] = 1 # 计算圣遗物套装 if 'equipSets' in raw_data: equipSets = raw_data['equipSets'] else: artifact_set_list = [] for i in raw_data['equipList']: artifact_set_list.append(i['aritifactSetsName']) equipSetList = set(artifact_set_list) equipSets = {'type': '', 'set': ''} for equip in equipSetList: if artifact_set_list.count(equip) >= 4: equipSets['type'] = '4' equipSets['set'] = equip break elif artifact_set_list.count(equip) == 1: pass elif artifact_set_list.count(equip) >= 2: equipSets['type'] += '2' equipSets['set'] += '|' + equip if equipSets['set'].startswith('|'): equipSets['set'] = equipSets['set'][1:] all_effect = [] # 计算圣遗物buff if equipSets['type'] == '4': all_effect.append(artifacts_effect_map[equipSets['set']]['4']) elif equipSets['type'] == '2': all_effect.append(artifacts_effect_map[equipSets['set']]['2']) elif equipSets['type'] == '22': if equipSets['set'][-2] == '之': first_artifact = equipSets['set'][-3:] else: first_artifact = equipSets['set'][-2:] second_artifact = equipSets['set'][:2] temp = [] temp_number = 0 for artifacts_single_effect in artifacts_effect_map: if first_artifact in artifacts_single_effect: temp_number += 1 temp.append(artifacts_effect_map[artifacts_single_effect]['2']) if temp_number >= 2: break all_effect.extend(temp) # 计算武器buff weapon_effet = weapons_effect_map[weaponName][str(weaponAffix)] all_effect.append(weapon_effet) # 计算技能buff for talent in char_talent_effect_map[char_name]: if int(talent) <= len(raw_data['talentList']): all_effect.append(char_talent_effect_map[char_name][talent]) else: break # 计算角色buff for skill in char_skill_effect_map[char_name]: if int(skill) <= char_level: all_effect.append(char_skill_effect_map[char_name][skill]) else: break power_effect = '' if 'effect' in power_list: for skill_effect_single in power_list['effect']: skill_effect_name = skill_effect_single['name'] skill_effect_value = skill_effect_single['value'] skill_effect = skill_effect_single['effect'] skill_effect_level = prop['{}_skill_level'.format(skill_effect_name)] skill_effect_value_detail = skill_effect_value[skill_effect_level] if skill_effect[-1] == '}': skill_effect_value_detail = skill_effect_value_detail.replace('%', '') add_limit = skill_effect.split(':') if len(add_limit) == 1: for i in power_list: if i == 'effect': pass else: power_list[i]['power_name'] = ( '开{}后 '.format(skill_effect_name) + power_list[i]['power_name'] ) else: for i in power_list: if i == 'effect': pass else: add_type = i[0] if '重击' in i or '蓄力' in i: add_type = 'B' elif '冲击伤害' in i: add_type = 'C' if add_type in add_limit[0]: power_list[i]['power_name'] = ( '开{}后 '.format(skill_effect_name) + power_list[i]['power_name'] ) power_effect = skill_effect.format(skill_effect_value_detail) all_effect.append(power_effect) del power_list['effect'] sp = [] # 计算全部的buff,添加入属性 if all_effect: all_effect = ';'.join(all_effect) all_effect_list = all_effect.split(';') for effect in all_effect_list: if effect == '': continue effect_limit = '' if ':' in effect: pass else: effect = ':' + effect effect_limit = effect.split(':')[0] effect_attr = effect.split(':')[1].split('+')[0] effect_value = effect.split(':')[1].split('+')[-1] base_check = True if '%' in effect_value: effect_value_base_on_attr = effect_value.split('%')[-1] effect_value_base_on_value = '%'.join(effect_value.split('%')[:-1]) if '%' in effect_value_base_on_value: effect_value_base_on_max_value = effect_value_base_on_value.split( '%' )[0] effect_value_base_on_value = effect_value_base_on_value.split('%')[ -1 ] effect_now_value = ( float(effect_value_base_on_value) * prop[effect_value_base_on_attr] ) effect_value = ( float(effect_value_base_on_max_value) if effect_now_value >= float(effect_value_base_on_max_value) else effect_now_value ) else: effect_value = ( float(effect_value_base_on_value) * prop[effect_value_base_on_attr] ) base_check = False if effect_attr != 'em': effect_value = float(effect_value) / 100 if effect_attr in ['hp', 'attack', 'defense'] and base_check: effect_value += effect_value * prop['base{}'.format(effect_attr)] else: effect_value = float(effect_value) if effect_limit: if '\u4e00' <= effect_limit[0] <= '\u9fff': sp.append( { 'effect_name': effect_limit, 'effect_attr': effect_attr, 'effect_value': effect_value, } ) else: for limit in effect_limit: prop['{}_{}'.format(limit, effect_attr)] += effect_value else: prop['{}'.format(effect_attr)] += effect_value w = 950 h = 40 * (len(power_list) + 1) result_img = Image.new('RGBA', (w, h), (0, 0, 0, 0)) for i in range(0, len(power_list) + 1): if i % 2 == 0: result_img.paste(dmgBar_1, (0, i * 40)) else: result_img.paste(dmgBar_2, (0, i * 40)) result_draw = ImageDraw.Draw(result_img) text_color = (255, 255, 255) title_color = (255, 255, 100) text_size = genshin_font_origin(28) result_draw.text((45, 22), '角色动作', title_color, text_size, anchor='lm') result_draw.text((460, 22), '暴击伤害', title_color, text_size, anchor='lm') result_draw.text((695, 22), '期望伤害', title_color, text_size, anchor='lm') for index, power_name in enumerate(power_list): attack_type = power_name[0] if '重击' in power_name or '瞄准射击' in power_name: attack_type = 'B' elif '冲击伤害' in power_name: attack_type = 'C' elif '段' in power_name and '伤害' in power_name: attack_type = 'A' sp_dmgBonus = 0 sp_addDmg = 0 if sp: for sp_single in sp: if sp_single['effect_name'] == power_name[1:]: if sp_single['effect_attr'] == 'dmgBouns': sp_dmgBonus += sp_single['effect_value'] elif sp_single['effect_attr'] == 'addDmg': sp_addDmg += sp_single['effect_value'] if '攻击' in power_list[power_name]['type']: effect_prop = prop['attack'] elif '生命值' in power_list[power_name]['type']: effect_prop = prop['hp'] elif '防御' in power_list[power_name]['type']: effect_prop = prop['defense'] power = power_list[power_name]['value'][ prop['{}_skill_level'.format(power_name[0])] ] power_plus = power_list[power_name]['plus'] power_percent, power_value = await power_to_value(power, power_plus) dmgBonus_cal = prop['{}_dmgBonus'.format(attack_type)] + sp_dmgBonus critdmg_cal = prop['critdmg'] + prop['{}_critdmg'.format(attack_type)] critrate_cal = prop['critrate'] + prop['{}_critrate'.format(attack_type)] d_cal = (char_level + 100) / ( (char_level + 100) + (1 - prop['{}_d'.format(attack_type)]) * (1 - prop['{}_ignoreDef'.format(attack_type)]) * (enemy_level + 100) ) r = 1 - prop['r'] e_dmg = prop['k'] * (1 + (2.78 * prop['em']) / (prop['em'] + 1400) + prop['a']) add_dmg = prop['{}_addDmg'.format(attack_type)] + sp_addDmg if '治疗' in power_name: crit_dmg = avg_dmg = (effect_prop * power_percent + power_value) * ( 1 + prop['healBouns'] ) elif '护盾' in power_name: crit_dmg = avg_dmg = (effect_prop * power_percent + power_value) * ( 1 + prop['shieldBouns'] ) elif '提升' in power_name or '提高' in power_name: continue else: crit_dmg = (effect_prop * power_percent + power_value) * ( 1 + critdmg_cal ) * (1 + dmgBonus_cal) * d_cal * r + add_dmg avg_dmg = ( (crit_dmg - add_dmg) * critrate_cal + (1 - critrate_cal) * (effect_prop * power_percent + power_value) * (1 + dmgBonus_cal) * d_cal * r + add_dmg ) result_draw.text( (45, 22 + (index + 1) * 40), power_list[power_name]['power_name'], text_color, text_size, anchor='lm', ) result_draw.text( (460, 22 + (index + 1) * 40), str(round(crit_dmg)), text_color, text_size, anchor='lm', ) result_draw.text( (695, 22 + (index + 1) * 40), str(round(avg_dmg)), text_color, text_size, anchor='lm', ) return result_img, len(power_list) + 2 async def power_to_value(power: str, power_plus: int) -> List: """ 将power转换为value """ if '+' in power: power_percent = (float(power.split('+')[0].replace('%', '')) / 100) * power_plus power_value = power.split('+')[1] if '%' in power_value: power_percent += float(power_value.replace('%', '')) / 100 * power_plus power_value = 0 else: power_value = float(power_value) else: power_percent = float(power.replace('%', '')) / 100 * power_plus power_value = 0 return power_percent, power_value