🎉 GenshinUID 3.1 beta

This commit is contained in:
KimgiaiiWuyi 2022-08-29 23:12:44 +08:00
parent e5bf2e088c
commit 7b9d06fd2f
2458 changed files with 19228 additions and 254512 deletions

2
.gitignore vendored
View File

@ -1,2 +0,0 @@
/.idea/
mihoyo_libs/mihoyo_bbs/bg/**

16
.pre-commit-config.yaml Normal file
View File

@ -0,0 +1,16 @@
ci:
autofix_commit_msg: ":rotating_light: auto fix by pre-commit-ci"
autofix_prs: true
autoupdate_branch: master
autoupdate_schedule: monthly
autoupdate_commit_msg: ":arrow_up: auto update by pre-commit-ci"
repos:
- repo: https://github.com/pycqa/isort
rev: 5.10.1
hooks:
- id: isort
- repo: https://github.com/psf/black
rev: 22.6.0
hooks:
- id: black

3
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"recommendations": ["ms-python.python", "ms-python.vscode-pylance"]
}

4
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,4 @@
{
"python.languageServer": "Pylance",
"python.analysis.typeCheckingMode": "basic"
}

5
__init__.py Normal file
View File

@ -0,0 +1,5 @@
import os.path
from nonebot import load_plugins
load_plugins(os.path.dirname(__file__))

34
all_import.py Normal file
View File

@ -0,0 +1,34 @@
import re
import base64
import asyncio
import traceback
from pathlib import Path
from typing import Any, Dict, List, Tuple, Union, Optional
import httpx
from nonebot.log import logger
from aiohttp import ClientConnectorError
from aiocqhttp.exceptions import ActionFailed
from nonebot import MessageSegment, get_bot # type: ignore
import hoshino
from hoshino import Service
from hoshino.typing import CQEvent, HoshinoBot, CommandSession # type: ignore
from hoshino.util import (
FreqLimiter,
pic2b64,
silence,
concat_pic,
filt_message,
)
from .utils.db_operation.db_operation import select_db
from .utils.message.get_image_and_at import ImageAndAt
from .utils.message.error_reply import * # noqa: F403,F401
from .utils.alias.alias_to_char_name import alias_to_char_name
from .utils.exception.handle_exception import handle_exception
from .utils.draw_image_tools.send_image_tool import convert_img
from .utils.genshin_fonts.genshin_fonts import genshin_font_origin
sv = Service('genshinuid')
hoshino_bot = get_bot()

File diff suppressed because it is too large Load Diff

View File

@ -1,154 +0,0 @@
import asyncio
import json
from pathlib import Path
import httpx
R_PATH = Path(__file__).parents[0]
MAP_PATH = R_PATH / 'map'
DATA_PATH = R_PATH / 'data'
version = '2.8.0'
avatarName2Element_fileName = f'avatarName2Element_mapping_{version}.json'
weaponHash2Name_fileName = f'weaponHash2Name_mapping_{version}.json'
weaponHash2Type_fileName = f'weaponHash2Type_mapping_{version}.json'
skillId2Name_fileName = f'skillId2Name_mapping_{version}.json'
talentId2Name_fileName = f'talentId2Name_mapping_{version}.json'
avatarId2Name_fileName = f'avatarId2Name_mapping_{version}.json'
artifact2attr_fileName = f'artifact2attr_mapping_{version}.json'
icon2Name_fileName = f'icon2Name_mapping_{version}.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 weaponHash2TypeJson() -> 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 artifact2attrJson() -> None:
with open(DATA_PATH / 'ReliquaryExcelConfigData.json', "r", encoding='UTF-8') as f:
reliquary_data = json.load(f)
with open(DATA_PATH / 'DisplayItemExcelConfigData.json', "r", encoding='UTF-8') as f:
Display_data = json.load(f)
temp = {}
for i in reliquary_data:
temp[str(i['icon'])] = raw_data[str(i['nameTextMapHash'])]
with open(MAP_PATH / icon2Name_fileName, 'w', encoding='UTF-8') as file:
json.dump(temp, file, ensure_ascii=False)
temp2 = {}
for i in Display_data:
if i['icon'].startswith('UI_RelicIcon'):
temp2[raw_data[str(i['nameTextMapHash'])]] = '_'.join(i['icon'].split('_')[:-1])
temp3 = {}
for i in temp:
for k in temp2:
if i.startswith(temp2[k]):
temp3[temp[i]] = k
with open(MAP_PATH / artifact2attr_fileName, 'w', encoding='UTF-8') as file:
json.dump(temp3, file, ensure_ascii=False)
async def main():
await avatarId2NameJson()
await avatarName2ElementJson()
await weaponHash2NameJson()
await skillId2NameJson()
await talentId2NameJson()
await weaponHash2TypeJson()
await artifact2attrJson()
asyncio.run(main())

File diff suppressed because it is too large Load Diff

View File

@ -1,421 +0,0 @@
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

View File

@ -1,762 +0,0 @@
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

View File

@ -1,297 +0,0 @@
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'
version = '2.8.0'
avatarName2Element_fileName = f'avatarName2Element_mapping_{version}.json'
weaponHash2Name_fileName = f'weaponHash2Name_mapping_{version}.json'
weaponHash2Type_fileName = f'weaponHash2Type_mapping_{version}.json'
skillId2Name_fileName = f'skillId2Name_mapping_{version}.json'
talentId2Name_fileName = f'talentId2Name_mapping_{version}.json'
avatarId2Name_fileName = f'avatarId2Name_mapping_{version}.json'
artifact2attr_fileName = f'artifact2attr_mapping_{version}.json'
icon2Name_fileName = f'icon2Name_mapping_{version}.json'
with open(MAP_PATH / avatarId2Name_fileName, "r", encoding='UTF-8') as f:
avatarId2Name = json.load(f)
with open(MAP_PATH / icon2Name_fileName, "r", encoding='UTF-8') as f:
icon2Name = json.load(f)
with open(MAP_PATH / artifact2attr_fileName, "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(
url=f'https://enka.minigg.cn/u/{str(uid)}/__data.json',
headers={'User-Agent': 'GenshinUID/2.1'},
).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())[2])]
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)
if char_data['avatarName'] in ['神里绫华', '安柏']:
char_data['avatarSkill'][0], char_data['avatarSkill'][-1] = (
char_data['avatarSkill'][-1],
char_data['avatarSkill'][0],
)
char_data['avatarSkill'][2], char_data['avatarSkill'][-1] = (
char_data['avatarSkill'][-1],
char_data['avatarSkill'][2],
)
char_data['avatarEnName'] = char_data['avatarSkill'][1]['skillIcon'].split(
'_'
)[-2]
elif char_data['avatarName'] in ['旅行者']:
char_data['avatarSkill'][0], char_data['avatarSkill'][-1] = (
char_data['avatarSkill'][-1],
char_data['avatarSkill'][0],
)
char_data['avatarSkill'][1], char_data['avatarSkill'][-1] = (
char_data['avatarSkill'][-1],
char_data['avatarSkill'][1],
)
char_data['avatarEnName'] = str(avatarId)
else:
char_data['avatarEnName'] = char_data['avatarSkill'][-1]['skillIcon'].split(
'_'
)[-2]
# 处理命座
talent_temp = []
if 'talentIdList' in char:
for index, talent in enumerate(char['talentIdList']):
talentTemp = {}
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]
artifact_set_list = []
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[
artifact_temp['aritifactName']
]
artifact_set_list.append(artifact_temp['aritifactSetsName'])
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)
equipSetList = set(artifact_set_list)
char_data['equipSets'] = {'type': '', 'set': ''}
char_data['equipList'] = artifacts_info
for equip in equipSetList:
if artifact_set_list.count(equip) >= 4:
char_data['equipSets']['type'] = '4'
char_data['equipSets']['set'] = equip
break
elif artifact_set_list.count(equip) == 1:
pass
elif artifact_set_list.count(equip) >= 2:
char_data['equipSets']['type'] += '2'
char_data['equipSets']['set'] += equip
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}刷新完成!\n本次缓存:{char_name_list_str}'

View File

@ -1 +0,0 @@
{"凯特": [200, 0], "神里绫华": [-110, 10], "琴": [100, 30], "旅行者": [200, 0], "丽莎": [260, 110], "芭芭拉": [200, 70], "凯亚": [100, 130], "迪卢克": [-350, 0], "雷泽": [60, -40], "安柏": [220, -100], "温迪": [100, 0], "香菱": [150, 170], "北斗": [0, 0], "行秋": [250, 100], "魈": [40, -135], "凝光": [-100, 20], "可莉": [0, 0], "钟离": [200, 0], "菲谢尔": [150, 0], "班尼特": [30, -70], "达达利亚": [0, -40], "诺艾尔": [150, 110], "七七": [120, -100], "重云": [120, -10], "甘雨": [100, 70], "阿贝多": [-50, -15], "迪奥娜": [350, 50], "莫娜": [50, 0], "刻晴": [20, -15], "砂糖": [130, 30], "辛焱": [-40, -40], "罗莎莉亚": [160, 30], "胡桃": [-245, -25], "枫原万叶": [45, 160], "烟绯": [345, 130], "宵宫": [70, 0], "托马": [160, 20], "优菈": [110, 30], "雷电将军": [150, 0], "早柚": [-50, 0], "珊瑚宫心海": [100, 30], "五郎": [260, 0], "九条裟罗": [180, 110], "荒泷一斗": [100, 0], "八重神子": [80, 30], "夜兰": [140, 10], "埃洛伊": [280, 0], "申鹤": [-10, 30], "云堇": [350, 100], "久岐忍": [200, 0], "神里绫人": [150, 100], "队伍测试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]}

View File

@ -1 +0,0 @@
{"凯特": [200, 0], "神里绫华": [0, 0], "琴": [100, 0], "旅行者": [0, -160], "丽莎": [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]}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 800 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 722 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 772 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 595 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 609 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 635 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 892 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 935 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 440 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 858 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 694 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 642 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 237 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 590 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 759 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 486 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 964 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 495 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 902 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 636 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 974 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 979 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 949 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 749 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 795 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 807 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 574 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 613 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 462 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 693 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 792 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 996 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 579 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 542 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 474 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Some files were not shown because too many files have changed in this diff Show More