mirror of
https://github.com/KimigaiiWuyi/GenshinUID.git
synced 2025-05-30 04:00:31 +08:00
763 lines
33 KiB
Python
763 lines
33 KiB
Python
import math
|
|
import json,asyncio
|
|
from io import BytesIO
|
|
from pathlib import Path
|
|
from typing import List, Optional, Tuple
|
|
from base64 import b64encode
|
|
|
|
from PIL import Image, ImageDraw, ImageFont, ImageChops
|
|
from httpx import get
|
|
from nonebot import logger
|
|
|
|
from .dmgCalc.dmgCalc import *
|
|
|
|
R_PATH = Path(__file__).parents[0]
|
|
TEXT_PATH = R_PATH / 'texture2D'
|
|
ICON_PATH = R_PATH / 'icon'
|
|
GACHA_PATH = R_PATH / 'gachaImg'
|
|
PLAYER_PATH = R_PATH / 'player'
|
|
RELIC_PATH = R_PATH / 'relicIcon'
|
|
MAP_PATH = R_PATH / 'map'
|
|
ETC_PATH = R_PATH / 'etc'
|
|
|
|
COLOR_MAP = {'Anemo' : (43, 170, 163), 'Cryo': (97, 168, 202), 'Dendro': (84, 169, 62),
|
|
'Electro': (150, 62, 169), 'Geo': (169, 143, 62), 'Hydro': (66, 98, 182), 'Pyro': (169, 62, 67)}
|
|
|
|
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)
|
|
|
|
# 引入offset
|
|
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)
|
|
|
|
|
|
def get_star_png(star: int) -> Image:
|
|
png = Image.open(TEXT_PATH / 's-{}.png'.format(str(star)))
|
|
return png
|
|
|
|
|
|
def strLenth(r: str, size: int, limit: int = 540) -> str:
|
|
result = ''
|
|
temp = 0
|
|
for i in r:
|
|
if temp >= limit:
|
|
result += '\n' + i
|
|
temp = 0
|
|
else:
|
|
result += i
|
|
|
|
if i.isdigit():
|
|
temp += round(size / 10 * 6)
|
|
elif i == '/':
|
|
temp += round(size / 10 * 2.2)
|
|
elif i == '.':
|
|
temp += round(size / 10 * 3)
|
|
elif i == '%':
|
|
temp += round(size / 10 * 9.4)
|
|
else:
|
|
temp += size
|
|
return result
|
|
|
|
|
|
async def get_artifacts_score(subName: str, subValue: int) -> int:
|
|
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:
|
|
equipMain = mainName[0]
|
|
elif '元素' in mainName:
|
|
equipMain = mainName[2]
|
|
elif '百分比' in mainName:
|
|
if '血量' in mainName:
|
|
equipMain = '生'
|
|
else:
|
|
equipMain = mainName[3]
|
|
else:
|
|
equipMain = mainName[0]
|
|
return equipMain
|
|
|
|
|
|
async def get_char_percent(raw_data: dict) -> str:
|
|
char_name = raw_data['avatarName']
|
|
weaponName = raw_data['weaponInfo']['weaponName']
|
|
weaponType = raw_data['weaponInfo']['weaponType']
|
|
|
|
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']
|
|
healBouns = fight_prop['healBonus']
|
|
|
|
hp_green = fight_prop['addHp']
|
|
attack_green = fight_prop['addAtk']
|
|
defense_green = fight_prop['addDef']
|
|
|
|
r = 0.9
|
|
equipMain = ''
|
|
for aritifact in raw_data['equipList']:
|
|
mainName = aritifact['reliquaryMainstat']['statName']
|
|
artifactsPos = aritifact['aritifactPieceName']
|
|
if artifactsPos == '时之沙':
|
|
equipMain += await get_first_main(mainName)
|
|
elif artifactsPos == '空之杯':
|
|
equipMain += await get_first_main(mainName)
|
|
elif artifactsPos == '理之冠':
|
|
equipMain += await get_first_main(mainName)
|
|
|
|
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['type'] in ['2','']:
|
|
seq = ''
|
|
else:
|
|
seq = '{}|{}|{}'.format(weaponName.replace('「','').replace('」',''),equipSets['set'],equipMain)
|
|
|
|
if char_name in dmgMap:
|
|
for action in dmgMap[char_name]:
|
|
if action['seq'] == seq:
|
|
cal = action
|
|
break
|
|
else:
|
|
if '钟离' in char_name:
|
|
cal = dmgMap[char_name][-1]
|
|
else:
|
|
cal = dmgMap[char_name][0]
|
|
|
|
print(seq)
|
|
print(cal)
|
|
if cal['action'] == 'E刹那之花':
|
|
effect_prop = defense
|
|
elif cal['key'] == '攻击力':
|
|
effect_prop = attack
|
|
elif cal['key'] == '防御力':
|
|
effect_prop = defense
|
|
elif cal['key'] == '血量':
|
|
effect_prop = hp
|
|
else:
|
|
effect_prop = attack
|
|
|
|
dmgBonus_value_cal = 0
|
|
dmgBonus_cal = dmgBonus
|
|
em_cal = em
|
|
|
|
if '夜兰' in char_name:
|
|
effect_prop = hp
|
|
elif '胡桃' in char_name:
|
|
effect_prop += 0.4 * hp if 0.4 * hp <= fight_prop['baseAtk'] * 4 else fight_prop['baseAtk'] * 4
|
|
elif '一斗' in char_name:
|
|
effect_prop += 0.9792 * defense
|
|
dmgBonus_value_cal += 0.35 * defense
|
|
elif '诺艾尔' in char_name:
|
|
effect_prop = attack
|
|
effect_prop += 1.3 * defense
|
|
elif '烟绯' in char_name:
|
|
dmgBonus_value_cal += 0.6 + 0.2
|
|
elif '优菈' in char_name:
|
|
r = 1.065
|
|
elif '钟离' in char_name:
|
|
r = 1.05
|
|
elif '辛焱' in char_name:
|
|
r = 1.025
|
|
|
|
if '踩班' in cal['action']:
|
|
effect_prop += 1202
|
|
effect_prop += fight_prop['baseAtk'] * 0.25
|
|
|
|
if '雾切' in weaponName:
|
|
dmgBonus_cal += 0.28
|
|
elif '弓藏' in weaponName and ('首' in cal['action'] or '击' in cal['action'] or '两段' in cal['action']):
|
|
dmgBonus_cal += 0.8
|
|
elif '飞雷' in weaponName and ('首' in cal['action'] or '击' in cal['action'] or '两段' in cal['action']):
|
|
dmgBonus_cal += 0.4
|
|
elif '阿莫斯' in weaponName:
|
|
dmgBonus_cal += 0.52
|
|
elif '破魔' in weaponName:
|
|
dmgBonus_cal += 0.18*2
|
|
elif '赤角石溃杵' in weaponName and ('首' in cal['action'] or '击' in cal['action'] or '两段' in cal['action']):
|
|
dmgBonus_value_cal += 0.4 * defense
|
|
elif '螭骨剑' in weaponName:
|
|
dmgBonus_cal += 0.4
|
|
elif '松籁响起之时' in weaponName:
|
|
effect_prop += fight_prop['baseAtk'] * 0.2
|
|
elif '试作澹月' in weaponName:
|
|
effect_prop += fight_prop['baseAtk'] * 0.72
|
|
elif '流浪乐章' in weaponName and '烟绯' in char_name:
|
|
em_cal += 480
|
|
elif '冬极' in weaponName:
|
|
effect_prop += fight_prop['baseAtk'] * 0.48
|
|
dmgBonus_cal += 0.12
|
|
|
|
if '蒸发' in cal['action'] or '融化' in cal['action']:
|
|
if '蒸发' in cal['action']:
|
|
if raw_data['avatarElement'] == 'Pyro':
|
|
k = 1.5
|
|
else:
|
|
k = 2
|
|
elif '融化' in cal['action']:
|
|
if raw_data['avatarElement'] == 'Pyro':
|
|
k = 2
|
|
else:
|
|
k = 1.5
|
|
|
|
if equipSets['type'] in ['2','']:
|
|
a = 0
|
|
else:
|
|
if '魔女' in equipSets['set']:
|
|
a = 0.15
|
|
else:
|
|
a = 0
|
|
add_dmg = k*(1+(2.78*em_cal)/(em_cal+1400)+a)
|
|
else:
|
|
add_dmg = 1
|
|
|
|
if equipSets['type'] in ['2','','22']:
|
|
pass
|
|
else:
|
|
if '追忆' in equipSets['set']:
|
|
dmgBonus_cal += 0.5
|
|
elif '绝缘' in equipSets['set']:
|
|
Bouns = ce * 0.25 if ce * 0.25 <= 0.75 else 0.75
|
|
dmgBonus_cal += Bouns
|
|
elif '乐团' in equipSets['set']:
|
|
if weaponType in ['法器', '弓']:
|
|
dmgBonus_cal += 0.35
|
|
elif '华馆' in equipSets['set']:
|
|
if raw_data['avatarElement'] == 'Geo':
|
|
effect_prop += 0.24 * defense
|
|
dmgBonus_cal += 0.24
|
|
|
|
critdmg_cal = critdmg
|
|
healBouns_cal = healBouns
|
|
|
|
if '魈' in char_name:
|
|
dmgBonus_cal += 0.906
|
|
elif '绫华' in char_name:
|
|
dmgBonus_cal += 0.18
|
|
elif '宵宫' in char_name:
|
|
dmgBonus_cal += 0.5
|
|
elif '九条' in char_name:
|
|
effect_prop += 0.9129 * fight_prop['baseAtk']
|
|
critdmg_cal += 0.6
|
|
|
|
if '治疗' in cal['action']:
|
|
if equipSets['type'] in ['2','']:
|
|
healBouns_cal += 0
|
|
else:
|
|
if '少女' in equipSets['set']:
|
|
healBouns_cal += 0.2
|
|
|
|
if cal['action'] == '扩散':
|
|
dmg = 868 * 1.15 * (1+0.6+(16*em_cal)/(em_cal+2000))
|
|
elif '霄宫' in char_name:
|
|
dmg = effect_prop * cal['power'] * (1 + critdmg_cal) * (1 + dmgBonus_cal) * 0.5 * r * add_dmg * 1.5879
|
|
elif '班尼特' in char_name and 'Q治疗' in cal['action']:
|
|
power = cal['power'].split('+')
|
|
dmg = (effect_prop * float(power[0]) / 100 + float(power[1])) * (1 + healBouns_cal)
|
|
elif '心海' in char_name and cal['action'] == '开Q普攻':
|
|
dmg = (attack * cal['power'] + hp*(0.0971 + 0.15 * healBouns_cal)) * (1 + dmgBonus_cal) * 0.5 * r * add_dmg
|
|
elif '心海' in char_name and cal['action'] == '水母回血':
|
|
dmg = (862 + 0.0748 * hp) * (1 + healBouns_cal)
|
|
elif char_name in ['芭芭拉', '早柚', '琴', '七七']:
|
|
power = cal['power'].split('+')
|
|
dmg = (effect_prop * float(power[0]) / 100 + float(power[1])) * (1 + healBouns_cal)
|
|
elif '绫人' in char_name:
|
|
dmg = (effect_prop * cal['power'] + 0.0222 * hp) * (1 + critdmg_cal) * (1 + dmgBonus_cal) * 0.5 * r * add_dmg * 1.5879
|
|
elif char_name in ['荒泷一斗', '诺艾尔']:
|
|
dmg = (effect_prop * cal['power'] + dmgBonus_value_cal) * (1 + critdmg_cal) * (1 + dmgBonus_cal) * 0.5 * r
|
|
elif '迪奥娜' in char_name:
|
|
dmg = (effect_prop * cal['power'] + 1905) * 1.9
|
|
elif '钟离' in char_name and 'E实际盾值' in cal['action']:
|
|
dmg = (2506 + hp * cal['power']) * 1.5 * 1.3
|
|
elif cal['action'] == 'Q开盾天星':
|
|
effect_prop = attack
|
|
dmg = (effect_prop * cal['power'] + 0.33 * hp) * (1 + critdmg_cal) * (1 + dmgBonus_cal) * 0.5 * r * add_dmg
|
|
elif '凝光' in char_name:
|
|
dmg = effect_prop * cal['power'] * (1 + critdmg_cal * critrate) * (1 + dmgBonus_cal) * 0.5 * r * add_dmg
|
|
elif isinstance(cal['power'], str):
|
|
if cal['power'] == '攻击力':
|
|
dmg = attack
|
|
elif cal['power'] == '防御力':
|
|
dmg = defense
|
|
else:
|
|
power = cal['power'].split('+')
|
|
dmg = effect_prop * float(power[0]) / 100 + float(power[1])
|
|
elif cal['val'] != 'any':
|
|
dmg = effect_prop * cal['power'] * (1 + critdmg_cal) * (1 + dmgBonus_cal) * 0.5 * r * add_dmg
|
|
else:
|
|
dmg = attack
|
|
print(dmg)
|
|
|
|
if cal['val'] != 'any':
|
|
percent = '{:.2f}'.format(dmg / cal['val'] * 100)
|
|
elif cal['power'] == '攻击力':
|
|
percent = '{:.2f}'.format(dmg / cal['atk'] * 100)
|
|
elif '云堇' in char_name:
|
|
percent = '{:.2f}'.format(dmg / cal['other2'] * 100)
|
|
elif cal['power'] == '防御力':
|
|
percent = '{:.2f}'.format(dmg / cal['other'] * 100)
|
|
else:
|
|
percent = 0.00
|
|
return percent
|
|
|
|
|
|
async def draw_char_card(raw_data: dict, charUrl: str = None) -> bytes:
|
|
char_name = raw_data['avatarName']
|
|
char_level = raw_data['avatarLevel']
|
|
char_fetter = raw_data['avatarFetter']
|
|
|
|
based_w, based_h = 600, 1200
|
|
if charUrl:
|
|
offset_x, offset_y = 200, 0
|
|
char_img = Image.open(BytesIO(get(charUrl).content)).convert('RGBA')
|
|
else:
|
|
if char_name in avatarOffsetMap:
|
|
offset_x, offset_y = avatarOffsetMap[char_name][0], avatarOffsetMap[char_name][1]
|
|
else:
|
|
offset_x, offset_y = 200, 0
|
|
char_img = Image.open(GACHA_PATH / 'UI_Gacha_AvatarImg_{}.png'.format(raw_data['avatarEnName'])) #角色图像
|
|
|
|
# 确定图片的长宽
|
|
w, h = char_img.size
|
|
if (w, h) != (based_w, based_h):
|
|
#offset_all = offset_x if offset_x >= offset_y else offset_y
|
|
based_new_w, based_new_h = based_w + offset_x, based_h + offset_y
|
|
based_scale = '%.3f' % (based_new_w / based_new_h)
|
|
scale_f = '%.3f' % (w / h)
|
|
new_w = math.ceil(based_new_h * float(scale_f))
|
|
new_h = math.ceil(based_new_w / float(scale_f))
|
|
if scale_f > based_scale:
|
|
bg_img2 = char_img.resize((new_w, based_new_h), Image.Resampling.LANCZOS)
|
|
x1 = new_w/2 - based_new_w /2 + offset_x
|
|
y1 = 0 + offset_y / 2
|
|
x2 = new_w/2 + based_new_w /2
|
|
y2 = based_new_h - offset_y / 2
|
|
else:
|
|
bg_img2 = char_img.resize((based_new_w , new_h), Image.Resampling.LANCZOS)
|
|
x1 = 0 + offset_x
|
|
y1 = new_h/2 - based_new_h/2 + offset_y / 2
|
|
x2 = based_new_w
|
|
y2 = new_h/2 + based_new_h/2 - offset_y / 2
|
|
char_img = bg_img2.crop((x1, y1, x2, y2))
|
|
|
|
dmg_img, dmg_len = await draw_dmgCacl_img(raw_data)
|
|
img_w, img_h = 950, 1850 + dmg_len * 40
|
|
overlay = Image.open(TEXT_PATH / 'overlay.png')
|
|
overlay_w, overlay_h = overlay.size
|
|
if overlay_h < img_h:
|
|
new_overlay_h = img_h
|
|
new_overlay_w = math.ceil(new_overlay_h * overlay_w / overlay_h)
|
|
overlay = overlay.resize((new_overlay_w, new_overlay_h), Image.Resampling.LANCZOS)
|
|
overlay = overlay.crop((0, 0, img_w, img_h))
|
|
color_img = Image.new('RGBA', overlay.size, COLOR_MAP[raw_data['avatarElement']])
|
|
img = ImageChops.overlay(color_img, overlay)
|
|
char_info_1 = Image.open(TEXT_PATH / 'char_info_1.png')
|
|
char_info_mask = Image.open(TEXT_PATH / 'char_info_mask.png')
|
|
|
|
img_temp = Image.new('RGBA', (based_w, based_h), (0,0,0,0))
|
|
img_temp.paste(char_img,(0,0),char_info_mask)
|
|
img.paste(img_temp, (0, 0), img_temp)
|
|
img.paste(char_info_1, (0, 0), char_info_1)
|
|
img.paste(dmg_img,(0,1850),dmg_img)
|
|
|
|
lock_img = Image.open(TEXT_PATH / 'icon_lock.png')
|
|
|
|
# 命座处理
|
|
for talent_num in range(0, 6):
|
|
if talent_num + 1 <= len(raw_data['talentList']):
|
|
talent = raw_data['talentList'][talent_num]
|
|
# img.paste(color_holo_img, (13,270 + talent_num * 66), holo_img)
|
|
talent_img = Image.open(ICON_PATH / '{}.png'.format(talent['talentIcon']))
|
|
talent_img_new = talent_img.resize((50, 50), Image.Resampling.LANCZOS).convert("RGBA")
|
|
img.paste(talent_img_new, (850, 375 + talent_num * 81), talent_img_new)
|
|
else:
|
|
img.paste(lock_img, (850, 375 + talent_num * 81), lock_img)
|
|
|
|
# 天赋处理
|
|
skillList = raw_data['avatarSkill']
|
|
a_skill_name = skillList[0]['skillName'].replace('普通攻击·', '')
|
|
a_skill_level = skillList[0]['skillLevel']
|
|
e_skill_name = skillList[1]['skillName']
|
|
e_skill_level = skillList[1]['skillLevel']
|
|
q_skill_name = skillList[-1]['skillName']
|
|
q_skill_level = skillList[-1]['skillLevel']
|
|
|
|
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':
|
|
e_skill_level += 3
|
|
elif skill_add[skillAdd_index] == 'Q':
|
|
q_skill_level += 3
|
|
|
|
for skill_num, skill in enumerate(skillList[0:2] + [skillList[-1]]):
|
|
skill_img = Image.open(ICON_PATH / '{}.png'.format(skill['skillIcon']))
|
|
skill_img_new = skill_img.resize((50, 50), Image.Resampling.LANCZOS).convert("RGBA")
|
|
img.paste(skill_img_new, (78, 756 + 101 * skill_num), skill_img_new)
|
|
|
|
# 武器部分
|
|
weapon_img = Image.open(TEXT_PATH / 'char_info_weapon.png')
|
|
weapon_star_img = get_star_png(raw_data['weaponInfo']['weaponStar'])
|
|
weaponName = raw_data['weaponInfo']['weaponName']
|
|
|
|
weaponAtk = raw_data['weaponInfo']['weaponStats'][0]['statValue']
|
|
weaponLevel = raw_data['weaponInfo']['weaponLevel']
|
|
weaponAffix = raw_data['weaponInfo']['weaponAffix']
|
|
weaponEffect = raw_data['weaponInfo']['weaponEffect']
|
|
weapon_type = raw_data['weaponInfo']['weaponType']
|
|
|
|
weapon_img.paste(weapon_star_img, (25, 235), weapon_star_img)
|
|
weapon_text = ImageDraw.Draw(weapon_img)
|
|
weapon_text.text((35, 80), weaponName, (255, 255, 255), genshin_font_origin(50), anchor='lm')
|
|
weapon_text.text((35, 120), weapon_type, (255, 255, 255), genshin_font_origin(20), anchor='lm')
|
|
weapon_text.text((35, 160), '基础攻击力', (255, 255, 255), genshin_font_origin(32), anchor='lm')
|
|
weapon_text.text((368, 160), str(weaponAtk), (255, 255, 255), genshin_font_origin(32), anchor='rm')
|
|
if len(raw_data['weaponInfo']['weaponStats']) == 2:
|
|
weapon_sub_info = raw_data['weaponInfo']['weaponStats'][1]['statName']
|
|
weapon_sub_value = raw_data['weaponInfo']['weaponStats'][1]['statValue']
|
|
weapon_text.text((35, 211), weapon_sub_info, (255, 255, 255), genshin_font_origin(32), anchor='lm')
|
|
weapon_text.text((368, 211), str(weapon_sub_value), (255, 255, 255), genshin_font_origin(32), anchor='rm')
|
|
else:
|
|
weapon_text.text((35, 211), '该武器无副词条', (255, 255, 255), genshin_font_origin(32), anchor='lm')
|
|
weapon_text.text((73, 303), f'Lv.{weaponLevel}', (255, 255, 255), genshin_font_origin(28), anchor='mm')
|
|
weapon_text.text((130, 305), f'精炼{str(weaponAffix)}阶', (255, 239, 173), genshin_font_origin(28), anchor='lm')
|
|
|
|
weaponEffect = strLenth(weaponEffect, 25, 455)
|
|
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']:
|
|
artifacts_img = Image.open(TEXT_PATH / 'char_info_artifacts.png')
|
|
artifacts_piece_img = Image.open(RELIC_PATH / '{}.png'.format(aritifact['icon']))
|
|
artifacts_piece_new_img = artifacts_piece_img.resize((75, 75), Image.Resampling.LANCZOS).convert("RGBA")
|
|
#artifacts_piece_new_img.putalpha(
|
|
# artifacts_piece_new_img.getchannel('A').point(lambda x: round(x * 0.5) if x > 0 else 0))
|
|
|
|
artifacts_img.paste(artifacts_piece_new_img, (195, 35), artifacts_piece_new_img)
|
|
aritifactStar_img = get_star_png(aritifact['aritifactStar'])
|
|
artifactsPos = aritifact['aritifactPieceName']
|
|
|
|
artifacts_img.paste(aritifactStar_img, (20, 165), aritifactStar_img)
|
|
artifacts_text = ImageDraw.Draw(artifacts_img)
|
|
artifacts_text.text((30, 66), aritifact['aritifactName'][:4], (255, 255, 255), genshin_font_origin(34), anchor='lm')
|
|
artifacts_text.text((30, 102), artifactsPos, (255, 255, 255), genshin_font_origin(20), anchor='lm')
|
|
|
|
mainValue = aritifact['reliquaryMainstat']['statValue']
|
|
mainName = aritifact['reliquaryMainstat']['statName']
|
|
mainLevel = aritifact['aritifactLevel']
|
|
|
|
if mainName in ['攻击力', '血量', '防御力', '元素精通']:
|
|
mainValueStr = str(mainValue)
|
|
else:
|
|
mainValueStr = str(mainValue) + '%'
|
|
|
|
mainNameNew = mainName.replace('百分比', '').replace('伤害加成', '伤加成').replace('元素', '').replace('理', '')
|
|
|
|
artifacts_text.text((30, 141), mainNameNew, (255, 255, 255), genshin_font_origin(28), anchor='lm')
|
|
artifacts_text.text((263, 141), mainValueStr, (255, 255, 255), genshin_font_origin(28), anchor='rm')
|
|
artifacts_text.text((55, 219), '+{}'.format(str(mainLevel)), (255, 255, 255), genshin_font_origin(24),
|
|
anchor='mm')
|
|
|
|
artifactsScore = 0
|
|
for index, i in enumerate(aritifact['reliquarySubstats']):
|
|
subName = i['statName']
|
|
subValue = i['statValue']
|
|
if subName in ['攻击力', '血量', '防御力', '元素精通']:
|
|
subValueStr = str(subValue)
|
|
else:
|
|
subValueStr = str(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('元素', '')
|
|
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), 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), '{:.2f}'.format(artifactsScore) + '条', artifactsScore_color, genshin_font_origin(23),
|
|
anchor='rm')
|
|
|
|
if artifactsPos == '生之花':
|
|
img.paste(artifacts_img, (18, 1075), artifacts_img)
|
|
elif artifactsPos == '死之羽':
|
|
img.paste(artifacts_img, (318, 1075), artifacts_img)
|
|
elif artifactsPos == '时之沙':
|
|
img.paste(artifacts_img, (618, 1075), artifacts_img)
|
|
elif artifactsPos == '空之杯':
|
|
img.paste(artifacts_img, (18, 1447), artifacts_img)
|
|
elif artifactsPos == '理之冠':
|
|
img.paste(artifacts_img, (318, 1447), artifacts_img)
|
|
|
|
# 角色基本信息
|
|
img_text = ImageDraw.Draw(img)
|
|
img_text.text((411, 72), char_name, (255, 255, 255), genshin_font_origin(55), anchor='lm')
|
|
img_text.text((411, 122), '等级{}'.format(char_level), (255, 255, 255), genshin_font_origin(40), anchor='lm')
|
|
img_text.text((747, 126), str(char_fetter), (255, 255, 255), genshin_font_origin(28), anchor='lm')
|
|
|
|
# aeq
|
|
# img_text.text((110, 771), a_skill_name, (255, 255, 255), genshin_font_origin(26), anchor='lm')
|
|
img_text.text((103, 812), f'{str(a_skill_level)}', (255, 255, 255), genshin_font_origin(30), anchor='mm')
|
|
|
|
# img_text.text((110, 872), e_skill_name, (255, 255, 255), genshin_font_origin(26), anchor='lm')
|
|
img_text.text((103, 915), f'{str(e_skill_level)}', (255, 255, 255), genshin_font_origin(30), anchor='mm')
|
|
|
|
# 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')
|
|
|
|
# 属性
|
|
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')
|
|
img_text.text((785, 280), str(round(defense)), (255, 255, 255), genshin_font_origin(28), anchor='rm')
|
|
img_text.text((785, 333), str(round(em)), (255, 255, 255), genshin_font_origin(28), anchor='rm')
|
|
img_text.text((785, 386), f'{str(round(critrate * 100, 2))}%', (255, 255, 255), genshin_font_origin(28),
|
|
anchor='rm')
|
|
img_text.text((785, 439), f'{str(round(critdmg * 100, 2))}%', (255, 255, 255), genshin_font_origin(28), anchor='rm')
|
|
img_text.text((785, 492), f'{str(round(ce * 100, 1))}%', (255, 255, 255), genshin_font_origin(28), anchor='rm')
|
|
img_text.text((785, 545), f'{str(round(dmgBonus * 100, 1))}%', (255, 255, 255), genshin_font_origin(28),
|
|
anchor='rm')
|
|
|
|
img_text.text((805, 174), f'(+{str(round(hp_green))})', (95, 251, 80), genshin_font_origin(28), anchor='lm')
|
|
img_text.text((805, 227), f'(+{str(round(attack_green))})', (95, 251, 80), genshin_font_origin(28), anchor='lm')
|
|
img_text.text((805, 280), f'(+{str(round(defense_green))})', (95, 251, 80), genshin_font_origin(28), anchor='lm')
|
|
|
|
uid = raw_data['playerUid']
|
|
data_time = raw_data['dataTime']
|
|
# uid
|
|
img_text.text((350, 1035), f'UID{uid}', (255, 255, 255), genshin_font_origin(24), anchor='rm')
|
|
|
|
# 数据最后更新时间
|
|
img_text.text((780, 600), f'数据最后更新于{data_time}', (255, 255, 255), genshin_font_origin(22), anchor='rm')
|
|
|
|
# 角色评分
|
|
img_text.text((768, 1557), f'{round(artifactsAllScore, 1)}', (255, 255, 255), genshin_font_origin(50), anchor='mm')
|
|
percent = await get_char_percent(raw_data)
|
|
img_text.text((768, 1690), f'{str(percent)+"%"}', (255, 255, 255), genshin_font_origin(50), anchor='mm')
|
|
|
|
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
|
|
|
|
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)
|
|
|
|
overlay = Image.open(TEXT_PATH / 'overlay.png')
|
|
color_img = Image.new('RGBA', overlay.size, COLOR_MAP[char['avatarElement']])
|
|
img_base = ImageChops.overlay(color_img, overlay)
|
|
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
|