diff --git a/enkaToData/drawCharCard.py b/enkaToData/drawCharCard.py index c71df1a3..05ebb737 100644 --- a/enkaToData/drawCharCard.py +++ b/enkaToData/drawCharCard.py @@ -1,5 +1,5 @@ import math -import json +import json,asyncio from io import BytesIO from pathlib import Path from base64 import b64encode @@ -20,9 +20,16 @@ ETC_PATH = R_PATH / 'etc' COLOR_MAP = {'Anemo' : (3, 90, 77), 'Cryo': (5, 85, 151), 'Dendro': (4, 87, 3), 'Electro': (47, 1, 85), 'Geo': (85, 34, 1), 'Hydro': (4, 6, 114), 'Pyro': (88, 4, 4)} -SCORE_VALUE_MAP = {'暴击率': 2, '暴击伤害': 1, '元素精通': 0.25, '元素充能效率': 0.65, '百分比血量': 0.86, +SCORE_MAP = {'暴击率': 2, '暴击伤害': 1, '元素精通': 0.25, '元素充能效率': 0.65, '百分比血量': 0.86, '百分比攻击力': 1, '百分比防御力': 0.7, '血量': 0.014, '攻击力': 0.12, '防御力': 0.18} +VALUE_MAP = {'攻击力': 4.975, '血量': 4.975, '防御力': 6.2, '元素精通': 19.75, + '元素充能效率': 5.5, '暴击率': 3.3, '暴击伤害': 6.6} + +# 引入ValueMap +with open(ETC_PATH / 'ValueAttrMap.json', 'r', encoding='UTF-8') as f: + ATTR_MAP = json.load(f) + # 引入dmgMap with open(ETC_PATH / 'dmgMap.json', 'r', encoding='UTF-8') as f: dmgMap = json.load(f) @@ -31,6 +38,10 @@ with open(ETC_PATH / 'dmgMap.json', 'r', encoding='UTF-8') as f: with open(ETC_PATH / 'avatarOffsetMap.json', 'r', encoding='UTF-8') as f: avatarOffsetMap = json.load(f) +# 引入offset2 +with open(ETC_PATH / 'avatarCardOffsetMap.json', 'r', encoding='UTF-8') as f: + avatarCardOffsetMap = json.load(f) + def genshin_font_origin(size: int) -> ImageFont: return ImageFont.truetype(str(TEXT_PATH / 'yuanshen_origin.ttf'), size=size) @@ -64,9 +75,46 @@ def strLenth(r: str, size: int, limit: int = 540) -> str: async def get_artifacts_score(subName: str, subValue: int) -> int: - score = subValue * SCORE_VALUE_MAP[subName] + score = subValue * SCORE_MAP[subName] return score +async def get_artifacts_value(subName: str, subValue: int, baseAtk: int, + baseHp: int, baseDef: int, charName: str) -> int: + if charName not in ATTR_MAP: + ATTR_MAP[charName] = ['攻击力', '暴击率', '暴击伤害'] + if subName in ATTR_MAP[charName] and subName in ['血量', '防御力', '攻击力']: + if subName == '血量': + base = (subValue / baseHp) * 100 + elif subName == '防御力': + base = (subValue / baseDef) * 100 + elif subName == '攻击力': + base = (subValue / baseAtk) * 100 + value = float('{:.2f}'.format(base / VALUE_MAP[subName])) + elif subName in ['百分比血量', '百分比防御力', '百分比攻击力']: + subName = subName.replace('百分比', '') + if subName in ATTR_MAP[charName]: + value = float('{:.2f}'.format(subValue / VALUE_MAP[subName])) + else: + return 0 + else: + if subName in ATTR_MAP[charName]: + value = float('{:.2f}'.format(subValue / VALUE_MAP[subName])) + else: + value = 0 + + if charName == '胡桃' and subName == '攻击力': + value = value * 0.4 + return value + +async def get_all_artifacts_value(raw_data: dict, baseHp: int, baseAtk: int, baseDef: int, char_name: str) -> int: + artifactsValue = 0 + for aritifact in raw_data: + for i in aritifact['reliquarySubstats']: + subName = i['statName'] + subValue = i['statValue'] + value_temp = await get_artifacts_value(subName, subValue, baseAtk, baseHp, baseDef, char_name) + artifactsValue += value_temp + return artifactsValue async def get_first_main(mainName: str) -> str: if '伤害加成' in mainName: @@ -428,6 +476,23 @@ async def draw_char_card(raw_data: dict, charUrl: str = None) -> bytes: weapon_text.text((25, 335), weaponEffect, (255, 255, 255), genshin_font_origin(25)) img.paste(weapon_img, (387, 590), weapon_img) + fight_prop = raw_data['avatarFightProp'] + hp = fight_prop['hp'] + baseHp = fight_prop['baseHp'] + attack = fight_prop['atk'] + baseAtk = fight_prop['baseAtk'] + defense = fight_prop['def'] + baseDef = fight_prop['baseDef'] + em = fight_prop['elementalMastery'] + critrate = fight_prop['critRate'] + critdmg = fight_prop['critDmg'] + ce = fight_prop['energyRecharge'] + dmgBonus = fight_prop['dmgBonus'] if fight_prop['physicalDmgBonus'] <= fight_prop['dmgBonus'] else fight_prop['physicalDmgBonus'] + + hp_green = fight_prop['addHp'] + attack_green = fight_prop['addAtk'] + defense_green = fight_prop['addDef'] + # 圣遗物部分 artifactsAllScore = 0 for aritifact in raw_data['equipList']: @@ -473,14 +538,26 @@ async def draw_char_card(raw_data: dict, charUrl: str = None) -> bytes: subValueStr = str(subValue) else: subValueStr = str(subValue) + '%' - artifactsScore += await get_artifacts_score(subName, subValue) + #artifactsScore += await get_artifacts_score(subName, subValue) + value_temp = await get_artifacts_value(subName, subValue, baseAtk, baseHp, baseDef, char_name) + artifactsScore += value_temp subNameStr = subName.replace('百分比', '').replace('元素', '') - artifacts_text.text((20, 256 + index * 33), '·{}'.format(subNameStr), (255, 255, 255), + if value_temp == 0: + artifacts_color = (160, 160, 160) + elif value_temp >= 4.5: + artifacts_color = (247, 50, 50) + else: + artifacts_color = (255, 255, 100) + artifacts_text.text((20, 256 + index * 33), '·{}'.format(subNameStr), artifacts_color, genshin_font_origin(25), anchor='lm') - artifacts_text.text((268, 256 + index * 33), '{}'.format(subValueStr), (255, 255, 255), + artifacts_text.text((268, 256 + index * 33), '{}'.format(subValueStr), artifacts_color, genshin_font_origin(25), anchor='rm') + if artifactsScore >= 6: + artifactsScore_color = (247, 26, 26) + else: + artifactsScore_color = (255, 255, 255) artifactsAllScore += artifactsScore - artifacts_text.text((268, 190), f'{math.ceil(artifactsScore)}分', (255, 255, 255), genshin_font_origin(23), + artifacts_text.text((268, 190), '{:.2f}'.format(artifactsScore) + '条', artifactsScore_color, genshin_font_origin(23), anchor='rm') if artifactsPos == '生之花': @@ -510,20 +587,6 @@ async def draw_char_card(raw_data: dict, charUrl: str = None) -> bytes: # img_text.text((110, 973), q_skill_name, (255, 255, 255), genshin_font_origin(26), anchor='lm') img_text.text((103, 1016), f'{str(q_skill_level)}', (255, 255, 255), genshin_font_origin(30), anchor='mm') - fight_prop = raw_data['avatarFightProp'] - hp = fight_prop['hp'] - attack = fight_prop['atk'] - defense = fight_prop['def'] - em = fight_prop['elementalMastery'] - critrate = fight_prop['critRate'] - critdmg = fight_prop['critDmg'] - ce = fight_prop['energyRecharge'] - dmgBonus = fight_prop['dmgBonus'] if fight_prop['physicalDmgBonus'] <= fight_prop['dmgBonus'] else fight_prop['physicalDmgBonus'] - - hp_green = fight_prop['addHp'] - attack_green = fight_prop['addAtk'] - defense_green = fight_prop['addDef'] - # 属性 img_text.text((785, 174), str(round(hp)), (255, 255, 255), genshin_font_origin(28), anchor='rm') img_text.text((785, 227), str(round(attack)), (255, 255, 255), genshin_font_origin(28), anchor='rm') @@ -559,3 +622,115 @@ async def draw_char_card(raw_data: dict, charUrl: str = None) -> bytes: imgmes = 'base64://' + b64encode(result_buffer.getvalue()).decode() res = imgmes return res + +async def draw_single_card(img: Image, char: dict, index: int, color: Tuple[int, int, int], + x_limit: int, char_card_mask: Image, char_card_1: Image, img_card: Image): + + size_36 = genshin_font_origin(36) + size_46 = genshin_font_origin(46) + + img_base = Image.open(TEXT_PATH / '{}.png'.format(char['avatarElement'])) + if char['char_name'] in avatarCardOffsetMap: + offset_x, offset_y = avatarCardOffsetMap[char['char_name']][0], avatarCardOffsetMap[char['char_name']][1] + else: + offset_x, offset_y = 200, 0 + char_img = Image.open(GACHA_PATH / 'UI_Gacha_AvatarImg_{}.png'.format(char['avatarEnName'])) + + img_base.paste(char_img, (-439 + offset_x, 130 + offset_y), char_img) + img_card.paste(img_base, (-25, -260), char_card_mask) + img_card = Image.alpha_composite(img_card, char_card_1) + #img_card.paste(img_card, (0, 0), img_card) + + char_card_text = ImageDraw.Draw(img_card) + + char_card_text.text((448, 59.2), f'{str(round(char["critrate"] * 100, 2))}%', color, size_36, anchor='lm') + char_card_text.text((448, 122.2), f'{str(round(char["critdmg"] * 100, 2))}%', color, size_36, anchor='lm') + + char_card_text.text((410.9, 252.6), str(char['a_skill_level']), color, size_36, anchor='mm') + char_card_text.text((485, 252.6), str(char['e_skill_level']), color, size_36, anchor='mm') + char_card_text.text((558, 252.6), str(char['q_skill_level']), color, size_36, anchor='mm') + + if float(char['percent']) >= 100: + percent_color = (204, 57, 78) + else: + percent_color = color + + if char['value'] >= 28.5: + value_color = (204, 57, 78) + else: + value_color = color + + char_card_text.text((742, 253.1), str(char['percent']) + '%', percent_color, size_46, anchor='mm') + char_card_text.text((742, 113.1), str(char['value']) + '条', value_color, size_46, anchor='mm') + + char_card_text.text((21.2, 70.5), f'{str(char["talent_num"])}命', color, size_36, anchor='lm') + char_card_text.text((21.2, 129.8), f'{str(char["weapon_affix"])}精', color, size_36, anchor='lm') + + img.paste(img_card, ((index % x_limit) * 900, (index // x_limit) * 300), img_card) + +async def draw_cahrcard_list(uid: str,limit :int = 24) -> str: + uid_fold = PLAYER_PATH / str(uid) + char_file_list = uid_fold.glob('*') + char_list = [] + for i in char_file_list: + file_name = i.name + if '\u4e00' <= file_name[0] <= '\u9fff': + char_list.append(file_name.split('.')[0]) + if not char_list: + return '你还没有已缓存的角色!\n请先使用【强制刷新】进行刷新!' + + char_done_list = [] + for char_name in char_list: + temp = {} + with open(uid_fold / f'{char_name}.json', 'r', encoding='UTF-8') as f: + raw_data = json.load(f) + + fight_prop = raw_data['avatarFightProp'] + skillList = raw_data['avatarSkill'] + + temp['char_name'] = char_name + temp['avatarEnName'] = raw_data['avatarEnName'] + temp['avatarElement'] = raw_data['avatarElement'] + temp['percent'] = await get_char_percent(raw_data) + temp['critrate'] = fight_prop['critRate'] + temp['critdmg'] = fight_prop['critDmg'] + baseHp = fight_prop['baseHp'] + baseAtk = fight_prop['baseAtk'] + baseDef = fight_prop['baseDef'] + temp['value'] = await get_all_artifacts_value(raw_data['equipList'], baseHp, baseAtk, baseDef, char_name) + temp['value'] = float('{:.2f}'.format(temp['value'])) + temp['avatarElement'] = raw_data['avatarElement'] + temp['a_skill_level'] = skillList[0]['skillLevel'] + temp['e_skill_level'] = skillList[1]['skillLevel'] + temp['q_skill_level'] = skillList[-1]['skillLevel'] + temp['talent_num'] = len(raw_data['talentList']) + temp['weapon_affix'] = raw_data['weaponInfo']['weaponAffix'] + char_done_list.append(temp) + + # 排序 + char_done_list.sort(key=lambda x: (-float(x['percent']))) + char_done_list = char_done_list[:limit] + + char_card_1 = Image.open(TEXT_PATH / 'charcard_1.png') + char_card_mask = Image.open(TEXT_PATH / 'charcard_mask.png') + + x_limit = 2 + color = (255, 255, 255) + x_tile = (len(char_done_list) + x_limit - 1) // x_limit + y_tile = math.ceil(len(char_done_list) / x_tile) + x_tile, y_tile = x_tile if x_tile <= y_tile else y_tile, y_tile if y_tile >= x_tile else x_tile + + img = Image.new('RGBA', (900 * x_tile, 300 * y_tile), (0, 0, 0)) + img_card = Image.new('RGBA', (900, 300)) + + tasks = [] + for index, char in enumerate(char_done_list): + tasks.append(draw_single_card(img, char, index, color, x_limit, char_card_mask, char_card_1, img_card)) + await asyncio.wait(tasks) + + img = img.convert('RGB') + result_buffer = BytesIO() + img.save(result_buffer, format='JPEG', subsampling=0, quality=90) + imgmes = 'base64://' + b64encode(result_buffer.getvalue()).decode() + res = imgmes + return res diff --git a/enkaToData/etc/ValueAttrMap.json b/enkaToData/etc/ValueAttrMap.json new file mode 100644 index 00000000..fe2a571d --- /dev/null +++ b/enkaToData/etc/ValueAttrMap.json @@ -0,0 +1,264 @@ +{ + "神里绫人": [ + "血量", + "攻击力", + "暴击率", + "暴击伤害", + "元素精通" + ], + "八重神子": [ + "攻击力", + "暴击率", + "暴击伤害", + "元素精通", + "" + ], + "申鹤": [ + "攻击力", + "元素充能效率" + ], + "云堇": [ + "防御力", + "元素充能效率" + ], + "荒泷一斗": [ + "防御力", + "暴击率", + "暴击伤害" + ], + "五郎": [ + "防御力", + "元素充能效率" + ], + "班尼特": [ + "血量", + "攻击力", + "暴击率", + "暴击伤害", + "元素 充能效率" + ], + "枫原万叶": [ + "元素精通", + "暴击率", + "暴击伤害", + "元素充能效率" + ], + "雷电将军": [ + "攻击力", + "暴击率", + "暴击伤害", + "元素充能效率" + ], + "行秋": [ + "攻击力", + "暴击率", + "暴击伤害", + "元素充能效率" + ], + "钟离": [ + "血量", + "攻击力", + "暴击率", + "暴击伤害", + "元素充能效率" + ], + "神里绫华": [ + "攻击力", + "暴击率", + "暴击伤害" + ], + "香菱": [ + "攻击力", + "暴击率", + "暴击伤害", + "元素充能效率", + "元素精通" + ], + "胡桃": [ + "血量", + "暴击率", + "暴击伤害", + "元素精通" + ], + "甘雨": [ + "攻击力", + "暴击率", + "暴击伤害", + "元素精通" + ], + "温迪": [ + "元素精通", + "暴击率", + "暴击伤害", + "元素充能效率" + ], + "珊瑚宫心海": [ + "血量", + "元素充能效率" + ], + "莫娜": [ + "元素精通", + "暴击率", + "暴击伤害", + "元素充能效率" + ], + "阿贝多": [ + "防御力", + "暴击率", + "暴击伤害" + ], + "迪奥娜": [ + "血量", + "元素充能效率" + ], + "优菈": [ + "攻击力", + "暴击率", + "暴击伤害" + ], + "达达利亚": [ + "攻击力", + "暴击率", + "暴击伤害", + "元素精通" + ], + "魈": [ + "攻击力", + "暴击率", + "暴击伤害" + ], + "宵宫": [ + "攻击力", + "暴击率", + "暴击伤害", + "元素精通" + ], + "九条裟罗": [ + "攻击力", + "暴击率", + "暴击伤害", + "元素充能效率" + ], + "琴": [ + "攻击力", + "暴击率", + "暴击伤害", + "元素充能效率" + ], + "菲谢尔": [ + "攻击力", + "暴击率", + "暴击伤害" + ], + "罗莎莉亚": [ + "攻击力", + "暴击率", + "暴击伤害" + ], + "可莉": [ + "攻击力", + "暴击率", + "暴击伤害" + ], + "凝光": [ + "攻击力", + "暴击率", + "暴击伤害" + ], + "北斗": [ + "攻击力", + "暴击率", + "暴击伤害" + ], + "刻晴": [ + "攻击力", + "暴击率", + "暴击伤害" + ], + "托马": [ + "血量", + "元素充能效率" + ], + "迪卢克": [ + "攻击力", + "暴击率", + "暴击伤害", + "元素精通" + ], + "芭芭拉": [ + "血量", + "元素充能效率" + ], + "诺艾尔": [ + "防御力", + "暴击率", + "暴击伤害" + ], + "旅行者": [ + "攻击力", + "暴击率", + "暴击伤害" + ], + "重云": [ + "攻击力", + "暴击率", + "暴击伤害" + ], + "七七": [ + "攻击力", + "暴击率", + "暴击伤害", + "元素充能效率" + ], + "凯亚": [ + "攻击力", + "暴击率", + "暴击伤害" + ], + "烟绯": [ + "攻击力", + "暴击率", + "暴击伤害", + "元素精通" + ], + "早柚": [ + "元素精通", + "元素充能效率" + ], + "安柏": [ + "攻击力", + "暴击率", + "暴击伤害", + "元素精通" + ], + "丽莎": [ + "攻击力", + "暴击率", + "暴击伤害" + ], + "埃洛伊": [ + "攻击力", + "暴击率", + "暴击伤害" + ], + "辛焱": [ + "攻击力", + "暴击率", + "暴击伤害" + ], + "砂糖": [ + "元素精通", + "元素充能效率" + ], + "雷泽": [ + "攻击力", + "暴击率", + "暴击伤害" + ], + "夜兰": [ + "血量", + "暴击率", + "暴击伤害", + "元素充能效率" + ] +} \ No newline at end of file diff --git a/enkaToData/etc/avatarCardOffsetMap.json b/enkaToData/etc/avatarCardOffsetMap.json new file mode 100644 index 00000000..27b84562 --- /dev/null +++ b/enkaToData/etc/avatarCardOffsetMap.json @@ -0,0 +1 @@ +{"凯特": [200, 0], "神里绫华": [0, 0], "琴": [100, 0], "旅行者": [200, 0], "丽莎": [200, -100], "芭芭拉": [200, 0], "凯亚": [150, -150], "迪卢克": [300, 0], "雷泽": [400, 0], "安柏": [0, 200], "温迪": [100, 0], "香菱": [-100, -250], "北斗": [200, 0], "行秋": [250, -100], "魈": [200, 200], "凝光": [500, 0], "可莉": [0, 0], "钟离": [100, 0], "菲谢尔": [300, 0], "班尼特": [150, 150], "达达利亚": [0, 0], "诺艾尔": [300, -80], "七七": [200, 100], "重云": [200, 0], "甘雨": [100, 0], "阿贝多": [200, 0], "迪奥娜": [-300, 0], "莫娜": [200, 0], "刻晴": [400, 250], "砂糖": [50, 0], "辛焱": [250, 0], "罗莎莉亚": [200, 0], "胡桃": [560, 0], "枫原万叶": [150, -200], "烟绯": [-400, 0], "宵宫": [250, 0], "托马": [200, 0], "优菈": [-200, 0], "雷电将军": [100, 0], "早柚": [500, 0], "珊瑚宫心海": [200, 0], "五郎": [60, 0], "九条裟罗": [100, 0], "荒泷一斗": [100, 0], "八重神子": [300, 0], "夜兰": [80, 0], "埃洛伊": [60, 0], "申鹤": [360, 0], "云堇": [100, 0], "久岐忍": [200, 0], "神里绫人": [50, 0], "队伍测试4号": [200, 0], "场景测试": [200, 0], "裸模1号": [200, 0], "裸男": [200, 0], "联机测试": [200, 0], "成男体型测试": [200, 0], "成女体型测试": [200, 0], "少女体型测试": [200, 0], "阿葵丽雅": [200, 0], "瑶瑶": [200, 0], "少女体型测试-二号机": [200, 0], "白盒少女": [200, 0], "大剑少女": [200, 0], "后武器测试A": [200, 0], "后武器测试B": [200, 0], "后武器测试C": [200, 0], "后武器测试D": [200, 0], "长枪成女": [200, 0], "单手剑成女测试": [200, 0], "Rx白盒": [200, 0], "少男体型测试": [200, 0], "女主新普攻": [200, 0], "男主新普攻": [200, 0], "重云(测试)": [200, 0], "测试角色": [200, 0], "七七(测试)": [200, 0], "迪奥娜(测试)": [200, 0]} \ No newline at end of file diff --git a/enkaToData/texture2D/char_info_1.png b/enkaToData/texture2D/char_info_1.png index d1bc9b55..aa144161 100644 Binary files a/enkaToData/texture2D/char_info_1.png and b/enkaToData/texture2D/char_info_1.png differ diff --git a/enkaToData/texture2D/charcard_1.png b/enkaToData/texture2D/charcard_1.png new file mode 100644 index 00000000..03d598d3 Binary files /dev/null and b/enkaToData/texture2D/charcard_1.png differ diff --git a/enkaToData/texture2D/charcard_mask.png b/enkaToData/texture2D/charcard_mask.png new file mode 100644 index 00000000..c3e6e327 Binary files /dev/null and b/enkaToData/texture2D/charcard_mask.png differ