新增:查询展柜角色
5692
enkaToData/data/AvatarExcelConfigData.json
Normal file
8314
enkaToData/data/AvatarSkillExcelConfigData.json
Normal file
6817
enkaToData/data/AvatarTalentExcelConfigData.json
Normal file
7643
enkaToData/data/WeaponExcelConfigData.json
Normal file
579516
enkaToData/data/textMap.json
Normal file
115
enkaToData/dataToMap.py
Normal file
@ -0,0 +1,115 @@
|
||||
import asyncio
|
||||
from pathlib import Path
|
||||
import httpx
|
||||
import json
|
||||
|
||||
R_PATH = Path(__file__).parents[0]
|
||||
MAP_PATH = R_PATH / 'map'
|
||||
DATA_PATH = R_PATH / 'data'
|
||||
|
||||
verison = '2.7.0'
|
||||
|
||||
avatarName2Element_fileName = f'avatarName2Element_mapping_{verison}.json'
|
||||
weaponHash2Name_fileName = f'weaponHash2Name_mapping_{verison}.json'
|
||||
weaponHash2Type_fileName = f'weaponHash2Type_mapping_{verison}.json'
|
||||
skillId2Name_fileName = f'skillId2Name_mapping_{verison}.json'
|
||||
talentId2Name_fileName = f'talentId2Name_mapping_{verison}.json'
|
||||
avatarId2Name_fileName = f'avatarId2Name_mapping_{verison}.json'
|
||||
|
||||
with open(DATA_PATH / 'textMap.json', "r", encoding='UTF-8') as f:
|
||||
raw_data = json.load(f)
|
||||
|
||||
async def avatarId2NameJson() -> None:
|
||||
with open(DATA_PATH / 'AvatarExcelConfigData.json', "r", encoding='UTF-8') as f:
|
||||
avatar_data = json.load(f)
|
||||
|
||||
temp = {}
|
||||
for i in avatar_data:
|
||||
temp[str(i['id'])] = raw_data[str(i['nameTextMapHash'])]
|
||||
|
||||
with open(MAP_PATH / avatarId2Name_fileName,'w', encoding='UTF-8') as file:
|
||||
json.dump(temp, file, ensure_ascii=False)
|
||||
|
||||
async def avatarName2ElementJson() -> None:
|
||||
with open(MAP_PATH / avatarId2Name_fileName, "r", encoding='UTF-8') as f:
|
||||
avatarId2Name = json.load(f)
|
||||
|
||||
temp = {}
|
||||
elementMap = {'风':'Anemo','岩':'Geo','草':'Dendro','火':'Pyro','水':'Hydro','冰':'Cryo','雷':'Electro'}
|
||||
for i in list(avatarId2Name.values()):
|
||||
data = json.loads(httpx.get('https://info.minigg.cn/characters?query={}'.format(i)).text)
|
||||
if 'errcode' in data:
|
||||
pass
|
||||
else:
|
||||
temp[i] = elementMap[data['element']]
|
||||
|
||||
with open(MAP_PATH / avatarName2Element_fileName,'w', encoding='UTF-8') as file:
|
||||
json.dump(temp, file, ensure_ascii=False)
|
||||
|
||||
async def weaponHash2NameJson() -> None:
|
||||
with open(DATA_PATH / 'WeaponExcelConfigData.json', "r", encoding='UTF-8') as f:
|
||||
weapon_data = json.load(f)
|
||||
|
||||
temp = {}
|
||||
for i in weapon_data:
|
||||
temp[str(i['nameTextMapHash'])] = raw_data[str(i['nameTextMapHash'])]
|
||||
|
||||
with open(MAP_PATH / weaponHash2Name_fileName,'w', encoding='UTF-8') as file:
|
||||
json.dump(temp, file, ensure_ascii=False)
|
||||
|
||||
async def weaponHash2NameJson() -> None:
|
||||
with open(DATA_PATH / 'WeaponExcelConfigData.json', "r", encoding='UTF-8') as f:
|
||||
weapon_data = json.load(f)
|
||||
|
||||
temp = {}
|
||||
for i in weapon_data:
|
||||
if i['weaponType'] == 'WEAPON_POLE':
|
||||
weaponType = "长柄武器"
|
||||
elif i['weaponType'] == 'WEAPON_BOW':
|
||||
weaponType = "弓"
|
||||
elif i['weaponType'] == 'WEAPON_SWORD_ONE_HAND':
|
||||
weaponType = "单手剑"
|
||||
elif i['weaponType'] == 'WEAPON_CLAYMORE':
|
||||
weaponType = "双手剑"
|
||||
elif i['weaponType'] == 'WEAPON_CATALYST':
|
||||
weaponType = "法器"
|
||||
else:
|
||||
weaponType = ""
|
||||
temp[str(i['nameTextMapHash'])] = weaponType
|
||||
|
||||
with open(MAP_PATH / weaponHash2Type_fileName,'w', encoding='UTF-8') as file:
|
||||
json.dump(temp, file, ensure_ascii=False)
|
||||
|
||||
async def skillId2NameJson() -> None:
|
||||
with open(DATA_PATH / 'AvatarSkillExcelConfigData.json', "r", encoding='UTF-8') as f:
|
||||
skill_data = json.load(f)
|
||||
|
||||
temp = {'Name':{},'Icon':{}}
|
||||
for i in skill_data:
|
||||
temp['Name'][str(i['id'])] = raw_data[str(i['nameTextMapHash'])]
|
||||
temp['Icon'][str(i['id'])] = i['skillIcon']
|
||||
|
||||
with open(MAP_PATH / skillId2Name_fileName,'w', encoding='UTF-8') as file:
|
||||
json.dump(temp, file, ensure_ascii=False)
|
||||
|
||||
async def talentId2NameJson() -> None:
|
||||
with open(DATA_PATH / 'AvatarTalentExcelConfigData.json', "r", encoding='UTF-8') as f:
|
||||
talent_data = json.load(f)
|
||||
|
||||
temp = {'Name':{},'Icon':{}}
|
||||
for i in talent_data:
|
||||
temp['Name'][str(i['talentId'])] = raw_data[str(i['nameTextMapHash'])]
|
||||
temp['Icon'][str(i['talentId'])] = i['icon']
|
||||
|
||||
with open(MAP_PATH / talentId2Name_fileName,'w', encoding='UTF-8') as file:
|
||||
json.dump(temp, file, ensure_ascii=False)
|
||||
|
||||
async def main():
|
||||
await avatarId2NameJson()
|
||||
await avatarName2ElementJson()
|
||||
await weaponHash2NameJson()
|
||||
await skillId2NameJson()
|
||||
await talentId2NameJson()
|
||||
await weaponHash2NameJson()
|
||||
|
||||
asyncio.run(main())
|
299
enkaToData/drawCharCard.py
Normal file
@ -0,0 +1,299 @@
|
||||
from pathlib import Path
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
from io import BytesIO
|
||||
from base64 import b64encode
|
||||
from httpx import get
|
||||
import math
|
||||
import json
|
||||
|
||||
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'
|
||||
|
||||
|
||||
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)}
|
||||
|
||||
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 draw_char_card(raw_data: dict, charUrl: str = None) -> str:
|
||||
img = Image.open(TEXT_PATH / '{}.png'.format(raw_data['avatarElement']))
|
||||
char_info_1 = Image.open(TEXT_PATH / 'char_info_1.png')
|
||||
char_imfo_mask = Image.open(TEXT_PATH / 'char_info_mask.png')
|
||||
|
||||
based_w, based_h = 320, 1024
|
||||
if charUrl:
|
||||
char_img = Image.open(BytesIO(get(charUrl).content)).convert('RGBA')
|
||||
else:
|
||||
char_img = Image.open(GACHA_PATH / 'UI_Gacha_AvatarIcon_{}.png'.format(raw_data['avatarEnName'])) #角色图像
|
||||
|
||||
# 确定图片的长宽
|
||||
w, h = char_img.size
|
||||
if (w, h) != (320, 1024):
|
||||
based_scale = '%.3f' % (based_w / based_h)
|
||||
scale_f = '%.3f' % (w / h)
|
||||
new_w = math.ceil(based_h * float(scale_f))
|
||||
new_h = math.ceil(based_w / float(scale_f))
|
||||
if scale_f > based_scale:
|
||||
bg_img2 = char_img.resize((new_w, based_h), Image.Resampling.LANCZOS)
|
||||
char_img = bg_img2.crop((new_w/2 - 160, 0, new_w/2 + 160, based_h))
|
||||
else:
|
||||
bg_img2 = char_img.resize((based_w, new_h), Image.Resampling.LANCZOS)
|
||||
char_img = bg_img2.crop((0, new_h/2 - 512, based_w, new_h/2 + 512))
|
||||
else:
|
||||
pass
|
||||
|
||||
img_temp = Image.new('RGBA', (320, 1024), (0,0,0,0))
|
||||
img_temp.paste(char_img,(0,0),char_imfo_mask)
|
||||
img.paste(img_temp, (41, 29), img_temp)
|
||||
img.paste(char_info_1, (0, 0), char_info_1)
|
||||
|
||||
#holo_img = Image.open(TEXT_PATH / 'icon_holo.png')
|
||||
#skill_holo_img = Image.open(TEXT_PATH / 'skillHolo.png')
|
||||
lock_img = Image.open(TEXT_PATH / 'icon_lock.png')
|
||||
|
||||
#color_soild = Image.new('RGBA', (950, 1850), COLOR_MAP[raw_data['avatarElement']])
|
||||
#img.paste(color_soild, (0, 0), skill_holo_img)
|
||||
|
||||
#color_holo_img = Image.new('RGBA', (100, 100), COLOR_MAP[raw_data['avatarElement']])
|
||||
|
||||
# 命座处理
|
||||
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']
|
||||
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, 570), weapon_img)
|
||||
|
||||
# 圣遗物部分
|
||||
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((180, 180), 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, (100, 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'], (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('百分比','')
|
||||
|
||||
artifacts_text.text((26, 140), mainNameNew, (255, 255, 255), genshin_font_origin(28), anchor='lm')
|
||||
artifacts_text.text((268, 140), 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)
|
||||
if subName == '血量':
|
||||
artifactsScore += subValue * 0.014
|
||||
elif subName == '攻击力':
|
||||
artifactsScore += subValue * 0.12
|
||||
elif subName == '防御力':
|
||||
artifactsScore += subValue * 0.18
|
||||
elif subName == '元素精通':
|
||||
artifactsScore += subValue * 0.25
|
||||
else:
|
||||
subValueStr = str(subValue) + '%'
|
||||
if subName == '暴击率':
|
||||
artifactsScore += subValue * 2
|
||||
elif subName == '暴击伤害':
|
||||
artifactsScore += subValue * 1
|
||||
elif subName == '元素精通':
|
||||
artifactsScore += subValue * 0.25
|
||||
elif subName == '元素充能效率':
|
||||
artifactsScore += subValue * 0.65
|
||||
elif subName == '百分比血量':
|
||||
artifactsScore += subValue * 0.86
|
||||
elif subName == '百分比攻击力':
|
||||
artifactsScore += subValue * 1
|
||||
elif subName == '百分比防御力':
|
||||
artifactsScore += subValue * 0.7
|
||||
artifacts_text.text((20, 263 + index*30), '·{}+{}'.format(subName, subValueStr), (255, 255, 255), genshin_font_origin(25), anchor='lm')
|
||||
artifactsAllScore += artifactsScore
|
||||
artifacts_text.text((268, 190), f'{math.ceil(artifactsScore)}分', (255, 255, 255), 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)
|
||||
|
||||
char_name = raw_data['avatarName']
|
||||
char_level = raw_data['avatarLevel']
|
||||
char_fetter = raw_data['avatarFetter']
|
||||
|
||||
# 评分算法
|
||||
# 圣遗物总分 + 角色等级 + (a+e+q)*4 + 武器等级 * ( 1+(武器精炼数 -1) * 0.25)
|
||||
charAllScore = artifactsAllScore + int(char_level) + \
|
||||
(a_skill_level + e_skill_level + q_skill_level) * 4 + \
|
||||
int(weaponLevel) * (1 + ((int(weaponAffix) - 1) * 0.25))
|
||||
|
||||
# 角色基本信息
|
||||
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')
|
||||
|
||||
|
||||
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']
|
||||
|
||||
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')
|
||||
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((904, 1505), f'圣遗物总分', (255, 255, 255), genshin_font_origin(45), anchor='rm')
|
||||
img_text.text((904, 1570), f'{round(artifactsAllScore, 1)}', (255, 255, 255), genshin_font_origin(60), anchor='rm')
|
||||
|
||||
img_text.text((904, 1655), f'角色评分', (255, 255, 255), genshin_font_origin(45), anchor='rm')
|
||||
img_text.text((904, 1720), f'{round(charAllScore, 1)}', (255, 255, 255), genshin_font_origin(60), anchor='rm')
|
||||
|
||||
img = img.convert('RGB')
|
||||
result_buffer = BytesIO()
|
||||
img.save(result_buffer, format='JPEG', subsampling=0, quality=90)
|
||||
#img.save(R_PATH / 'result.png', format='JPEG', subsampling=0, quality=90)
|
||||
img.save(result_buffer, format='PNG')
|
||||
imgmes = 'base64://' + b64encode(result_buffer.getvalue()).decode()
|
||||
resultmes = imgmes
|
||||
return resultmes
|
230
enkaToData/enkaToData.py
Normal file
@ -0,0 +1,230 @@
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
import httpx
|
||||
import json
|
||||
import time
|
||||
|
||||
R_PATH = Path(__file__).parents[0]
|
||||
MAP_PATH = R_PATH / 'map'
|
||||
ICON_PATH = R_PATH / 'icon'
|
||||
DATA_PATH = R_PATH / 'data'
|
||||
PLAYER_PATH = R_PATH / 'player'
|
||||
|
||||
verison = '2.7.0'
|
||||
|
||||
avatarName2Element_fileName = f'avatarName2Element_mapping_{verison}.json'
|
||||
weaponHash2Name_fileName = f'weaponHash2Name_mapping_{verison}.json'
|
||||
weaponHash2Type_fileName = f'weaponHash2Type_mapping_{verison}.json'
|
||||
skillId2Name_fileName = f'skillId2Name_mapping_{verison}.json'
|
||||
talentId2Name_fileName = f'talentId2Name_mapping_{verison}.json'
|
||||
avatarId2Name_fileName = f'avatarId2Name_mapping_{verison}.json'
|
||||
|
||||
with open(MAP_PATH / avatarId2Name_fileName, "r", encoding='UTF-8') as f:
|
||||
avatarId2Name = json.load(f)
|
||||
|
||||
with open(MAP_PATH / 'icon2Name_mapping_2.6.0.json', "r", encoding='UTF-8') as f:
|
||||
icon2Name = json.load(f)
|
||||
|
||||
with open(MAP_PATH / 'artifact2attr_mapping_2.6.0.json', "r", encoding='UTF-8') as f:
|
||||
artifact2attr = json.load(f)
|
||||
|
||||
with open(MAP_PATH / 'propId2Name_mapping.json', "r", encoding='UTF-8') as f:
|
||||
propId2Name = json.load(f)
|
||||
|
||||
with open(MAP_PATH / weaponHash2Name_fileName, "r", encoding='UTF-8') as f:
|
||||
weaponHash2Name = json.load(f)
|
||||
|
||||
with open(MAP_PATH / weaponHash2Type_fileName, "r", encoding='UTF-8') as f:
|
||||
weaponHash2Type = json.load(f)
|
||||
|
||||
with open(MAP_PATH / 'artifactId2Piece_mapping.json', "r", encoding='UTF-8') as f:
|
||||
artifactId2Piece = json.load(f)
|
||||
|
||||
with open(MAP_PATH / skillId2Name_fileName, "r", encoding='UTF-8') as f:
|
||||
skillId2Name = json.load(f)
|
||||
|
||||
with open(MAP_PATH / talentId2Name_fileName, "r", encoding='UTF-8') as f:
|
||||
talentId2Name = json.load(f)
|
||||
|
||||
with open(MAP_PATH / avatarName2Element_fileName,'r', encoding='UTF-8') as f:
|
||||
avatarName2Element = json.load(f)
|
||||
|
||||
async def enkaToData(uid: str, enka_data: Optional[dict] = None) -> dict:
|
||||
if enka_data:
|
||||
pass
|
||||
else:
|
||||
enka_data = json.loads(httpx.get(f'https://enka.shinshin.moe/u/{str(uid)}/__data.json').text)
|
||||
if enka_data == {}:
|
||||
return enka_data
|
||||
now = time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time()))
|
||||
playerInfo = enka_data['playerInfo']
|
||||
path = PLAYER_PATH / str(uid)
|
||||
path.mkdir(parents=True, exist_ok=True)
|
||||
with open(path / '{}.json'.format(str(uid)),'w', encoding='UTF-8') as file:
|
||||
json.dump(playerInfo, file, ensure_ascii=False)
|
||||
with open(path / 'rawData.json','w', encoding='UTF-8') as file:
|
||||
json.dump(enka_data, file, ensure_ascii=False)
|
||||
|
||||
if 'avatarInfoList' not in enka_data:
|
||||
return f'UID{uid}刷新失败!未打开角色展柜!'
|
||||
|
||||
char_name_list = []
|
||||
for char in enka_data['avatarInfoList']:
|
||||
# 处理基本信息
|
||||
char_data = {}
|
||||
avatarId = char['avatarId']
|
||||
char_data['playerUid'] = str(uid)
|
||||
char_data['playerName'] = enka_data['playerInfo']['nickname']
|
||||
char_data['avatarId'] = avatarId
|
||||
avatarName = avatarId2Name[str(char['avatarId'])]
|
||||
char_data['avatarName'] = avatarId2Name[str(char['avatarId'])]
|
||||
char_name_list.append(char_data['avatarName'])
|
||||
char_data['avatarFetter'] = char['fetterInfo']['expLevel']
|
||||
char_data['avatarLevel'] = char['propMap']['4001']['val']
|
||||
|
||||
try:
|
||||
char_data['avatarElement'] = avatarName2Element[char_data['avatarName']]
|
||||
except KeyError:
|
||||
check = skillId2Name['Name'][str(list(char['skillLevelMap'].keys())[0])]
|
||||
if '风' in check :
|
||||
char_data['avatarElement'] = 'Anemo'
|
||||
elif '雷' in check :
|
||||
char_data['avatarElement'] = 'Electro'
|
||||
elif '岩' in check :
|
||||
char_data['avatarElement'] = 'Geo'
|
||||
elif '草' in check :
|
||||
char_data['avatarElement'] = 'Dendro'
|
||||
elif '冰' in check :
|
||||
char_data['avatarElement'] = 'Cryo'
|
||||
elif '水' in check :
|
||||
char_data['avatarElement'] = 'Hydro'
|
||||
else:
|
||||
char_data['avatarElement'] = 'Pyro'
|
||||
|
||||
|
||||
char_data['dataTime'] = now
|
||||
|
||||
char_data['avatarSkill'] = []
|
||||
# 处理天赋
|
||||
for skill in char['skillLevelMap']:
|
||||
skill_temp = {}
|
||||
skill_temp['skillId'] = skill
|
||||
skill_temp['skillName'] = skillId2Name['Name'][skill_temp['skillId']]
|
||||
skill_temp['skillLevel'] = char['skillLevelMap'][skill]
|
||||
skill_temp['skillIcon'] = skillId2Name['Icon'][skill_temp['skillId']]
|
||||
char_data['avatarSkill'].append(skill_temp)
|
||||
|
||||
char_data['avatarEnName'] = char_data['avatarSkill'][-1]['skillIcon'].split('_')[-2]
|
||||
|
||||
# 处理命座
|
||||
talent_temp = []
|
||||
if 'talentIdList' in char:
|
||||
talentTemp = {}
|
||||
for index,talent in enumerate(char['talentIdList']):
|
||||
talentTemp['talentId'] = char['talentIdList'][index]
|
||||
talentTemp['talentName'] = talentId2Name['Name'][str(talent)]
|
||||
talentTemp['talentIcon'] = talentId2Name['Icon'][str(talent)]
|
||||
talent_temp.append(talentTemp)
|
||||
char_data['talentList'] = talent_temp
|
||||
|
||||
# 处理属性
|
||||
fight_prop = {}
|
||||
# 血量
|
||||
fight_prop['hp'] = char["fightPropMap"]["2000"]
|
||||
fight_prop['baseHp'] = char["fightPropMap"]["1"]
|
||||
fight_prop['addHp'] = char["fightPropMap"]["2000"] - char["fightPropMap"]["1"]
|
||||
# 攻击力
|
||||
fight_prop['atk'] = char["fightPropMap"]["2001"]
|
||||
fight_prop['baseAtk'] = char["fightPropMap"]["4"]
|
||||
fight_prop['addAtk'] = char["fightPropMap"]["2001"] - char["fightPropMap"]["4"]
|
||||
# 防御力
|
||||
fight_prop['def'] = char["fightPropMap"]["2002"]
|
||||
fight_prop['baseDef'] = char["fightPropMap"]["7"]
|
||||
fight_prop['addDef'] = char["fightPropMap"]["2002"] - char["fightPropMap"]["7"]
|
||||
# 元素精通
|
||||
fight_prop['elementalMastery'] = char["fightPropMap"]["28"]
|
||||
# 暴击率
|
||||
fight_prop['critRate'] = char["fightPropMap"]["20"]
|
||||
# 暴击伤害
|
||||
fight_prop['critDmg'] = char["fightPropMap"]["22"]
|
||||
# 充能效率
|
||||
fight_prop['energyRecharge'] = char["fightPropMap"]["23"]
|
||||
# 治疗&受治疗
|
||||
fight_prop['healBonus'] = char["fightPropMap"]["26"]
|
||||
fight_prop['healedBonus'] = char["fightPropMap"]["27"]
|
||||
# 物理伤害加成 & 抗性
|
||||
fight_prop['physicalDmgSub'] = char["fightPropMap"]["29"]
|
||||
fight_prop['physicalDmgBonus'] = char["fightPropMap"]["30"]
|
||||
# 伤害加成
|
||||
for i in range(40,47):
|
||||
if char["fightPropMap"][str(i)] > 0:
|
||||
fight_prop['dmgBonus'] = char["fightPropMap"][str(i)]
|
||||
break
|
||||
else:
|
||||
fight_prop['dmgBonus'] = 0
|
||||
|
||||
char_data['avatarFightProp'] = fight_prop
|
||||
|
||||
# 处理武器
|
||||
weapon_info = {}
|
||||
weapon_data = char['equipList'][-1]
|
||||
weapon_info['itemId'] = weapon_data['itemId']
|
||||
weapon_info['nameTextMapHash'] = weapon_data['flat']['nameTextMapHash']
|
||||
weapon_info['weaponIcon'] = weapon_data['flat']['icon']
|
||||
weapon_info['weaponType'] = weaponHash2Type[weapon_info['nameTextMapHash']]
|
||||
weapon_info['weaponName'] = weaponHash2Name[weapon_info['nameTextMapHash']]
|
||||
weapon_info['weaponStar'] = weapon_data['flat']['rankLevel']
|
||||
# 防止未精炼
|
||||
if 'promoteLevel' in weapon_data['weapon']:
|
||||
weapon_info['promoteLevel'] = weapon_data['weapon']['promoteLevel']
|
||||
else:
|
||||
weapon_info['promoteLevel'] = 0
|
||||
weapon_info['weaponLevel'] = weapon_data['weapon']['level']
|
||||
if 'affixMap' in weapon_data['weapon']:
|
||||
weapon_info['weaponAffix'] = list(weapon_data['weapon']['affixMap'].values())[0] + 1
|
||||
else:
|
||||
weapon_info['weaponAffix'] = 1
|
||||
weapon_info['weaponStats'] = []
|
||||
for k in weapon_data['flat']['weaponStats']:
|
||||
weapon_prop_temp = {}
|
||||
weapon_prop_temp['appendPropId'] = k['appendPropId']
|
||||
weapon_prop_temp['statName'] = propId2Name[k['appendPropId']]
|
||||
weapon_prop_temp['statValue'] = k['statValue']
|
||||
weapon_info['weaponStats'].append(weapon_prop_temp)
|
||||
# 武器特效,须请求API
|
||||
effect_raw = json.loads(httpx.get('https://info.minigg.cn/weapons?query={}'.format(weapon_info['weaponName'])).text)
|
||||
if 'effect' in effect_raw:
|
||||
effect = effect_raw['effect'].format(*effect_raw['r{}'.format(str(weapon_info['weaponAffix']))])
|
||||
else:
|
||||
effect = '无特效。'
|
||||
weapon_info['weaponEffect'] = effect
|
||||
char_data['weaponInfo'] = weapon_info
|
||||
|
||||
# 处理圣遗物
|
||||
artifacts_info = []
|
||||
artifacts_data = char['equipList'][:-1]
|
||||
for artifact in artifacts_data:
|
||||
artifact_temp = {}
|
||||
artifact_temp['itemId'] = artifact['itemId']
|
||||
artifact_temp['nameTextMapHash'] = artifact['flat']['nameTextMapHash']
|
||||
artifact_temp['icon'] = artifact['flat']['icon']
|
||||
artifact_temp['aritifactName'] = icon2Name[artifact['flat']['icon']]
|
||||
artifact_temp['aritifactSetsName'] = artifact2attr['mapping'][artifact_temp['aritifactName']]
|
||||
artifact_temp['aritifactSetPiece'] = artifactId2Piece[artifact_temp['icon'].split('_')[-1]][0]
|
||||
artifact_temp['aritifactPieceName'] = artifactId2Piece[artifact_temp['icon'].split('_')[-1]][1]
|
||||
|
||||
artifact_temp['aritifactStar'] = artifact['flat']['rankLevel']
|
||||
artifact_temp['aritifactLevel'] = artifact['reliquary']['level'] - 1
|
||||
|
||||
artifact_temp['reliquaryMainstat'] = artifact['flat']['reliquaryMainstat']
|
||||
artifact_temp['reliquaryMainstat']['statName'] = propId2Name[artifact_temp['reliquaryMainstat']['mainPropId']]
|
||||
|
||||
artifact_temp['reliquarySubstats'] = artifact['flat']['reliquarySubstats']
|
||||
for sub in artifact_temp['reliquarySubstats']:
|
||||
sub['statName'] = propId2Name[sub['appendPropId']]
|
||||
artifacts_info.append(artifact_temp)
|
||||
char_data['equipList'] = artifacts_info
|
||||
with open(path / '{}.json'.format(avatarName),'w', encoding='UTF-8') as file:
|
||||
json.dump(char_data, file, ensure_ascii=False)
|
||||
char_name_list_str = ','.join(char_name_list)
|
||||
return f'UID{uid}刷新成功!刷新角色:{char_name_list_str}'
|
1
enkaToData/etc/propMap.json
Normal file
@ -0,0 +1 @@
|
||||
{"1": "FIGHT_PROP_BASE_HP", "2": "FIGHT_PROP_HP", "3": "FIGHT_PROP_HP_PERCENT", "4": "FIGHT_PROP_BASE_ATTACK", "5": "FIGHT_PROP_ATTACK", "6": "FIGHT_PROP_ATTACK_PERCENT", "7": "FIGHT_PROP_BASE_DEFENSE", "8": "FIGHT_PROP_DEFENSE", "9": "FIGHT_PROP_DEFENSE_PERCENT", "10": "FIGHT_PROP_BASE_SPEED", "11": "FIGHT_PROP_SPEED_PERCENT", "12": "FIGHT_PROP_HP_MP_PERCENT", "13": "FIGHT_PROP_ATTACK_MP_PERCENT", "20": "FIGHT_PROP_CRITICAL", "21": "FIGHT_PROP_ANTI_CRITICAL", "22": "FIGHT_PROP_CRITICAL_HURT", "23": "FIGHT_PROP_CHARGE_EFFICIENCY", "24": "FIGHT_PROP_ADD_HURT", "25": "FIGHT_PROP_SUB_HURT", "26": "FIGHT_PROP_HEAL_ADD", "27": "FIGHT_PROP_HEALED_ADD", "28": "FIGHT_PROP_ELEMENT_MASTERY", "29": "FIGHT_PROP_PHYSICAL_SUB_HURT", "30": "FIGHT_PROP_PHYSICAL_ADD_HURT", "31": "FIGHT_PROP_DEFENCE_IGNORE_RATIO", "32": "FIGHT_PROP_DEFENCE_IGNORE_DELTA", "40": "FIGHT_PROP_FIRE_ADD_HURT", "41": "FIGHT_PROP_ELEC_ADD_HURT", "42": "FIGHT_PROP_WATER_ADD_HURT", "43": "FIGHT_PROP_GRASS_ADD_HURT", "44": "FIGHT_PROP_WIND_ADD_HURT", "45": "FIGHT_PROP_ROCK_ADD_HURT", "46": "FIGHT_PROP_ICE_ADD_HURT", "47": "FIGHT_PROP_HIT_HEAD_ADD_HURT", "50": "FIGHT_PROP_FIRE_SUB_HURT", "51": "FIGHT_PROP_ELEC_SUB_HURT", "52": "FIGHT_PROP_WATER_SUB_HURT", "53": "FIGHT_PROP_GRASS_SUB_HURT", "54": "FIGHT_PROP_WIND_SUB_HURT", "55": "FIGHT_PROP_ROCK_SUB_HURT", "56": "FIGHT_PROP_ICE_SUB_HURT", "60": "FIGHT_PROP_EFFECT_HIT", "61": "FIGHT_PROP_EFFECT_RESIST", "62": "FIGHT_PROP_FREEZE_RESIST", "63": "FIGHT_PROP_TORPOR_RESIST", "64": "FIGHT_PROP_DIZZY_RESIST", "65": "FIGHT_PROP_FREEZE_SHORTEN", "66": "FIGHT_PROP_TORPOR_SHORTEN", "67": "FIGHT_PROP_DIZZY_SHORTEN", "70": "FIGHT_PROP_MAX_FIRE_ENERGY", "71": "FIGHT_PROP_MAX_ELEC_ENERGY", "72": "FIGHT_PROP_MAX_WATER_ENERGY", "73": "FIGHT_PROP_MAX_GRASS_ENERGY", "74": "FIGHT_PROP_MAX_WIND_ENERGY", "75": "FIGHT_PROP_MAX_ICE_ENERGY", "76": "FIGHT_PROP_MAX_ROCK_ENERGY", "80": "FIGHT_PROP_SKILL_CD_MINUS_RATIO", "81": "FIGHT_PROP_SHIELD_COST_MINUS_RATIO", "1000": "FIGHT_PROP_CUR_FIRE_ENERGY", "1001": "FIGHT_PROP_CUR_ELEC_ENERGY", "1002": "FIGHT_PROP_CUR_WATER_ENERGY", "1003": "FIGHT_PROP_CUR_GRASS_ENERGY", "1004": "FIGHT_PROP_CUR_WIND_ENERGY", "1005": "FIGHT_PROP_CUR_ICE_ENERGY", "1006": "FIGHT_PROP_CUR_ROCK_ENERGY", "1010": "FIGHT_PROP_CUR_HP", "2000": "FIGHT_PROP_MAX_HP", "2001": "FIGHT_PROP_CUR_ATTACK", "2002": "FIGHT_PROP_CUR_DEFENSE", "2003": "FIGHT_PROP_CUR_SPEED", "3000": "FIGHT_PROP_NONEXTRA_ATTACK", "3001": "FIGHT_PROP_NONEXTRA_DEFENSE", "3002": "FIGHT_PROP_NONEXTRA_CRITICAL", "3003": "FIGHT_PROP_NONEXTRA_ANTI_CRITICAL", "3004": "FIGHT_PROP_NONEXTRA_CRITICAL_HURT", "3005": "FIGHT_PROP_NONEXTRA_CHARGE_EFFICIENCY", "3006": "FIGHT_PROP_NONEXTRA_ELEMENT_MASTERY", "3007": "FIGHT_PROP_NONEXTRA_PHYSICAL_SUB_HURT", "3008": "FIGHT_PROP_NONEXTRA_FIRE_ADD_HURT", "3009": "FIGHT_PROP_NONEXTRA_ELEC_ADD_HURT", "3010": "FIGHT_PROP_NONEXTRA_WATER_ADD_HURT", "3011": "FIGHT_PROP_NONEXTRA_GRASS_ADD_HURT", "3012": "FIGHT_PROP_NONEXTRA_WIND_ADD_HURT", "3013": "FIGHT_PROP_NONEXTRA_ROCK_ADD_HURT", "3014": "FIGHT_PROP_NONEXTRA_ICE_ADD_HURT", "3015": "FIGHT_PROP_NONEXTRA_FIRE_SUB_HURT", "3016": "FIGHT_PROP_NONEXTRA_ELEC_SUB_HURT", "3017": "FIGHT_PROP_NONEXTRA_WATER_SUB_HURT", "3018": "FIGHT_PROP_NONEXTRA_GRASS_SUB_HURT", "3019": "FIGHT_PROP_NONEXTRA_WIND_SUB_HURT", "3020": "FIGHT_PROP_NONEXTRA_ROCK_SUB_HURT", "3021": "FIGHT_PROP_NONEXTRA_ICE_SUB_HURT", "3022": "FIGHT_PROP_NONEXTRA_SKILL_CD_MINUS_RATIO", "3023": "FIGHT_PROP_NONEXTRA_SHIELD_COST_MINUS_RATIO", "3024": "FIGHT_PROP_NONEXTRA_PHYSICAL_ADD_HURT"}
|
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Albedo.png
Normal file
After Width: | Height: | Size: 434 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Aloy.png
Normal file
After Width: | Height: | Size: 399 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Ambor.png
Normal file
After Width: | Height: | Size: 374 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_AmborCostumeWic.png
Normal file
After Width: | Height: | Size: 390 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Ambor_TT.png
Normal file
After Width: | Height: | Size: 390 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Ayaka.png
Normal file
After Width: | Height: | Size: 519 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Ayato.png
Normal file
After Width: | Height: | Size: 537 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Barbara.png
Normal file
After Width: | Height: | Size: 423 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Beidou.png
Normal file
After Width: | Height: | Size: 488 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Bennett.png
Normal file
After Width: | Height: | Size: 419 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Chongyun.png
Normal file
After Width: | Height: | Size: 439 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Diluc.png
Normal file
After Width: | Height: | Size: 473 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Diona.png
Normal file
After Width: | Height: | Size: 374 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Eula.png
Normal file
After Width: | Height: | Size: 556 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Feiyan.png
Normal file
After Width: | Height: | Size: 410 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Fischl.png
Normal file
After Width: | Height: | Size: 451 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Ganyu.png
Normal file
After Width: | Height: | Size: 401 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Gorou.png
Normal file
After Width: | Height: | Size: 410 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Hutao.png
Normal file
After Width: | Height: | Size: 366 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Itto.png
Normal file
After Width: | Height: | Size: 551 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Kaeya.png
Normal file
After Width: | Height: | Size: 423 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Kazuha.png
Normal file
After Width: | Height: | Size: 433 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Keqing.png
Normal file
After Width: | Height: | Size: 493 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Klee.png
Normal file
After Width: | Height: | Size: 418 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Kokomi.png
Normal file
After Width: | Height: | Size: 462 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Lisa.png
Normal file
After Width: | Height: | Size: 432 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Mona.png
Normal file
After Width: | Height: | Size: 550 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_MonaCostumeWic.png
Normal file
After Width: | Height: | Size: 562 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Mona_TT.png
Normal file
After Width: | Height: | Size: 562 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Ningguang.png
Normal file
After Width: | Height: | Size: 379 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Noel.png
Normal file
After Width: | Height: | Size: 399 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Qin.png
Normal file
After Width: | Height: | Size: 448 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_QinCostumeWic.png
Normal file
After Width: | Height: | Size: 451 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Qin_TT.png
Normal file
After Width: | Height: | Size: 451 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Qiqi.png
Normal file
After Width: | Height: | Size: 498 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Razor.png
Normal file
After Width: | Height: | Size: 560 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Rosaria.png
Normal file
After Width: | Height: | Size: 515 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_RosariaCostumeWic.png
Normal file
After Width: | Height: | Size: 508 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Rosaria_TT.png
Normal file
After Width: | Height: | Size: 508 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Sara.png
Normal file
After Width: | Height: | Size: 451 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Sayu.png
Normal file
After Width: | Height: | Size: 338 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Shenhe.png
Normal file
After Width: | Height: | Size: 549 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Shinobu.png
Normal file
After Width: | Height: | Size: 444 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Shougun.png
Normal file
After Width: | Height: | Size: 575 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Sucrose.png
Normal file
After Width: | Height: | Size: 423 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Tartaglia.png
Normal file
After Width: | Height: | Size: 475 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Tohma.png
Normal file
After Width: | Height: | Size: 384 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Venti.png
Normal file
After Width: | Height: | Size: 473 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Xiangling.png
Normal file
After Width: | Height: | Size: 324 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Xiao.png
Normal file
After Width: | Height: | Size: 441 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Xingqiu.png
Normal file
After Width: | Height: | Size: 320 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Xinyan.png
Normal file
After Width: | Height: | Size: 434 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Yae.png
Normal file
After Width: | Height: | Size: 556 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Yelan.png
Normal file
After Width: | Height: | Size: 499 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Yoimiya.png
Normal file
After Width: | Height: | Size: 555 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Yunjin.png
Normal file
After Width: | Height: | Size: 529 KiB |
BIN
enkaToData/gachaImg/UI_Gacha_AvatarIcon_Zhongli.png
Normal file
After Width: | Height: | Size: 496 KiB |
BIN
enkaToData/icon/Skill_A_01.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
enkaToData/icon/Skill_A_02.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
enkaToData/icon/Skill_A_03.png
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
enkaToData/icon/Skill_A_04.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
enkaToData/icon/Skill_A_CC_Electric.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
enkaToData/icon/Skill_A_CC_Rock.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
enkaToData/icon/Skill_A_Catalyst_MD.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
enkaToData/icon/Skill_A_Dvalin_AirGun.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
enkaToData/icon/Skill_A_Thunderbolt.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
enkaToData/icon/Skill_B_Barbara_01.png
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
enkaToData/icon/Skill_C_ChargeNormal.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
enkaToData/icon/Skill_C_ElectricTransfer.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
BIN
enkaToData/icon/Skill_C_FireCracker.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
enkaToData/icon/Skill_C_Stamine_1.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
enkaToData/icon/Skill_C_Stamine_2.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
enkaToData/icon/Skill_C_Stamine_3.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
enkaToData/icon/Skill_C_Stamine_4.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
enkaToData/icon/Skill_C_Stamine_5.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
enkaToData/icon/Skill_C_ThunderCoil.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
enkaToData/icon/Skill_E_Albedo_01.png
Normal file
After Width: | Height: | Size: 6.0 KiB |
BIN
enkaToData/icon/Skill_E_Albedo_01_HD.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
enkaToData/icon/Skill_E_Aloy_01.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
enkaToData/icon/Skill_E_Aloy_01_HD.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
enkaToData/icon/Skill_E_Ambor.png
Normal file
After Width: | Height: | Size: 5.8 KiB |
BIN
enkaToData/icon/Skill_E_Ambor_HD.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
enkaToData/icon/Skill_E_Ayaka.png
Normal file
After Width: | Height: | Size: 6.7 KiB |
BIN
enkaToData/icon/Skill_E_Ayaka_HD.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
enkaToData/icon/Skill_E_Ayato_01.png
Normal file
After Width: | Height: | Size: 6.2 KiB |
BIN
enkaToData/icon/Skill_E_Ayato_01_HD.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
enkaToData/icon/Skill_E_Barbara_01.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
enkaToData/icon/Skill_E_Barbara_01_HD.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
enkaToData/icon/Skill_E_Beidou_01.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
enkaToData/icon/Skill_E_Beidou_01_HD.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
enkaToData/icon/Skill_E_Bennett_01.png
Normal file
After Width: | Height: | Size: 5.5 KiB |