Support zzz agent detail

This commit is contained in:
xtaodada 2024-07-06 17:31:50 +08:00
parent 0e51d50a05
commit 037f5ef3d2
Signed by: xtaodada
GPG Key ID: 4CBB3F4FA8C85659
72 changed files with 9209 additions and 2 deletions

View File

@ -2212,7 +2212,7 @@ name = "simnet"
version = "0.1.22"
requires_python = "<4.0,>=3.8"
git = "https://github.com/PaiGramTeam/SIMNet"
revision = "9839a06bb87403443ac2cbea0ffde4156b05d902"
revision = "ed7c061d82af4fe8c5a07f0d10c568b5e467662b"
summary = "Modern API wrapper for Genshin Impact & Honkai: Star Rail built on asyncio and pydantic."
groups = ["default"]
dependencies = [

View File

@ -26,6 +26,7 @@ class SetCommandPlugin(Plugin):
BotCommand("signal_log_import", "导入调频记录"),
BotCommand("signal_log_export", "导出调频记录"),
BotCommand("signal_log_delete", "删除调频记录"),
BotCommand("agent_detail", "角色详细信息"),
# Cookie 查询类
BotCommand("sign", "米游社绝区零每日签到"),
BotCommand("dailynote_tasks", "自动便笺提醒"),

350
plugins/zzz/agent_detail.py Normal file
View File

@ -0,0 +1,350 @@
import math
from typing import TYPE_CHECKING, List, Tuple, Optional, Union
from simnet.models.zzz.calculator import ZZZCalculatorCharacterDetails, ZZZCalculatorAvatarProperty
from telegram import (
InlineKeyboardButton,
InlineKeyboardMarkup,
)
from telegram.constants import ChatAction
from telegram.ext import filters
from core.config import config
from core.dependence.redisdb import RedisDB
from core.plugin import Plugin, handler
from core.services.template.services import TemplateService
from metadata.shortname import roleToName, idToRole
from plugins.tools.genshin import GenshinHelper
from utils.log import logger
from utils.uid import mask_number
if TYPE_CHECKING:
from simnet import ZZZClient
from telegram import Update
from telegram.ext import ContextTypes
from core.services.template.models import RenderResult
__all__ = ("AgentDetailPlugin",)
class ZZZCalculatorAvatarPropertyModify(ZZZCalculatorAvatarProperty):
@property
def icon(self) -> str:
return {
"生命值": "hp",
"攻击力": "atk",
"防御力": "def",
"冲击力": "impact",
"暴击率": "crit-rate",
"暴击伤害": "crit-dmg",
"异常掌控": "anomaly-proficiency",
"异常精通": "anomaly-mastery",
"穿透率": "pen-ratio",
"能量自动回复": "energy-regen",
}.get(self.property_name, "")
color_map = {
"1011": "#c8e16c",
"1021": "#a0351c",
"1031": "#e6adaa",
"1041": "#febb2e",
"1061": "#c8d7bd",
"1081": "#af3e3a",
"1101": "#de643d",
"1111": "#ddc374",
"1121": "#a68d73",
"1131": "#28bdcc",
"1141": "#d0d3e0",
"1151": "#e8cda2",
"1181": "#b75339",
"1191": "#c9becc",
"1211": "#c4c1b1",
"1241": "#4e7ebd",
"1281": "#e9d892",
}
class NeedClient(Exception):
"""无缓存,需要 StarRailClient"""
class AgentDetailPlugin(Plugin.Conversation):
"""角色详细信息查询"""
def __init__(
self,
helper: GenshinHelper,
template_service: TemplateService,
redis: RedisDB,
):
self.template_service = template_service
self.helper = helper
self.qname = "plugins:agent_detail"
self.redis = redis.client
self.expire = 15 * 60 # 15分钟
self.kitsune: Optional[str] = None
async def set_characters_for_redis(
self,
uid: int,
nickname: str,
data: "ZZZCalculatorCharacterDetails",
) -> None:
data_k = f"{self.qname}:{uid}:data"
json_data = data.json(by_alias=True)
await self.redis.set(data_k, json_data, ex=self.expire)
async def del_characters_for_redis(
self,
uid: int,
) -> None:
data_k = f"{self.qname}:{uid}:data"
await self.redis.delete(data_k)
async def get_characters_for_redis(
self,
uid: int,
) -> Tuple[Optional[str], Optional["ZZZCalculatorCharacterDetails"]]:
data_k = f"{self.qname}:{uid}:data"
data_v = await self.redis.get(data_k)
if data_v is None:
return None, None
json_data = str(data_v, encoding="utf-8")
return "", ZZZCalculatorCharacterDetails.parse_raw(json_data)
async def get_characters(
self, uid: int, client: "ZZZClient" = None
) -> Tuple[Optional[str], Optional["ZZZCalculatorCharacterDetails"]]:
nickname, data = await self.get_characters_for_redis(uid)
if nickname is None or data is None:
if not client:
raise NeedClient
data1 = await client.get_zzz_characters()
cids = [i.id for i in data1.characters]
data = await client.get_zzz_character_info(cids)
await self.set_characters_for_redis(client.player_id, "", data)
return nickname, data
def parse_render_data(self, data: "ZZZCalculatorCharacterDetails", nickname: str, ch_id: int, uid: int):
char = None
for i in data.characters:
if i.id == ch_id:
char = i
break
props = [ZZZCalculatorAvatarPropertyModify(**i.dict()) for i in char.properties]
return {
"uid": mask_number(uid),
"char": char,
"props": props,
"color": color_map.get(str(char.id), "#010101"),
}
@staticmethod
def gen_button(
data: "ZZZCalculatorCharacterDetails",
user_id: Union[str, int],
uid: int,
page: int = 1,
) -> List[List[InlineKeyboardButton]]:
"""生成按钮"""
buttons = []
if data.characters:
buttons = [
InlineKeyboardButton(
idToRole(value.id),
callback_data=f"get_role_detail|{user_id}|{uid}|{idToRole(value.id)}",
)
for value in data.characters
if value.id
]
all_buttons = [buttons[i : i + 4] for i in range(0, len(buttons), 4)]
send_buttons = all_buttons[(page - 1) * 3 : page * 3]
last_page = page - 1 if page > 1 else 0
all_page = math.ceil(len(all_buttons) / 3)
next_page = page + 1 if page < all_page and all_page > 1 else 0
last_button = []
if last_page:
last_button.append(
InlineKeyboardButton(
"<< 上一页",
callback_data=f"get_role_detail|{user_id}|{uid}|{last_page}",
)
)
if last_page or next_page:
last_button.append(
InlineKeyboardButton(
f"{page}/{all_page}",
callback_data=f"get_role_detail|{user_id}|{uid}|empty_data",
)
)
if next_page:
last_button.append(
InlineKeyboardButton(
"下一页 >>",
callback_data=f"get_role_detail|{user_id}|{uid}|{next_page}",
)
)
if last_button:
send_buttons.append(last_button)
return send_buttons
async def get_render_result(
self, data: "ZZZCalculatorCharacterDetails", nickname: str, ch_id: int, uid: int
) -> "RenderResult":
final = self.parse_render_data(data, nickname, ch_id, uid)
return await self.template_service.render(
"zzz/agent_detail/agent_detail.html",
final,
{"width": 1024, "height": 1200},
query_selector=".shareContainer_2ZR-xeyd",
full_page=True,
)
@staticmethod
def get_caption(data: "ZZZCalculatorCharacterDetails", character_id: int) -> str:
tags = []
for character in data.characters:
if character.id == character_id:
tags.append(character.name)
tags.append(f"等级{character.level}")
tags.append(f"命座{character.rank}")
if weapon := character.weapon:
tags.append(weapon.name)
tags.append(f"武器等级{weapon.level}")
tags.append(f"{weapon.star}")
break
return "#" + " #".join(tags)
@handler.command(command="agent_detail", block=False)
@handler.message(filters=filters.Regex("^角色详细信息查询(.*)"), block=False)
async def command_start(self, update: "Update", context: "ContextTypes.DEFAULT_TYPE") -> None:
user_id = await self.get_real_user_id(update)
message = update.effective_message
uid, offset = self.get_real_uid_or_offset(update)
args = self.get_args(context)
ch_name = None
for i in args:
if i.startswith("@"):
continue
ch_name = roleToName(i)
if ch_name:
break
self.log_user(
update,
logger.info,
"角色详细信息查询命令请求 || character_name[%s]",
ch_name,
)
await message.reply_chat_action(ChatAction.TYPING)
async with self.helper.genshin(user_id, player_id=uid, offset=offset) as client:
nickname, data = await self.get_characters(client.player_id, client)
uid = client.player_id
if ch_name is None:
buttons = self.gen_button(data, user_id, uid)
if isinstance(self.kitsune, str):
photo = self.kitsune
else:
photo = open("resources/img/aaa.jpg", "rb")
await message.reply_chat_action(ChatAction.UPLOAD_PHOTO)
reply_message = await message.reply_photo(
photo=photo,
caption=f"请选择你要查询的角色 - UID {uid}",
reply_markup=InlineKeyboardMarkup(buttons),
)
if reply_message.photo:
self.kitsune = reply_message.photo[-1].file_id
return
for characters in data.characters:
if idToRole(characters.id) == ch_name:
break
else:
await message.reply_text(f"未在游戏中找到 {ch_name} ,请检查角色是否存在,或者等待角色数据更新后重试")
return
await message.reply_chat_action(ChatAction.UPLOAD_PHOTO)
render_result = await self.get_render_result(data, nickname, characters.id, client.player_id)
await render_result.reply_photo(
message,
filename=f"{client.player_id}.png",
reply_markup=self.get_custom_button(user_id, uid, characters.id),
caption=self.get_caption(data, characters.id),
)
@handler.callback_query(pattern=r"^get_role_detail\|", block=False)
async def get_role_detail(self, update: "Update", _: "ContextTypes.DEFAULT_TYPE") -> None:
callback_query = update.callback_query
user = callback_query.from_user
message = callback_query.message
async def get_role_detail_callback(
callback_query_data: str,
) -> Tuple[str, int, int]:
_data = callback_query_data.split("|")
_user_id = int(_data[1])
_uid = int(_data[2])
_result = _data[3]
logger.debug(
"callback_query_data函数返回 result[%s] user_id[%s] uid[%s]",
_result,
_user_id,
_uid,
)
return _result, _user_id, _uid
result, user_id, uid = await get_role_detail_callback(callback_query.data)
if user.id != user_id:
await callback_query.answer(text="这不是你的按钮!\n" + config.notice.user_mismatch, show_alert=True)
return
if result == "empty_data":
await callback_query.answer(text="此按钮不可用", show_alert=True)
return
page = 0
if result.isdigit():
page = int(result)
logger.info(
"用户 %s[%s] 角色详细信息查询命令请求 || page[%s] uid[%s]",
user.full_name,
user.id,
page,
uid,
)
else:
logger.info(
"用户 %s[%s] 角色详细信息查询命令请求 || character_name[%s] uid[%s]",
user.full_name,
user.id,
result,
uid,
)
try:
nickname, data = await self.get_characters(uid)
except NeedClient:
async with self.helper.genshin(user.id, player_id=uid) as client:
nickname, data = await self.get_characters(client.player_id, client)
if page:
buttons = self.gen_button(data, user.id, uid, page)
await message.edit_reply_markup(reply_markup=InlineKeyboardMarkup(buttons))
await callback_query.answer(f"已切换到第 {page}", show_alert=False)
return
for characters in data.characters:
if idToRole(characters.id) == result:
break
else:
await message.delete()
await callback_query.answer(
f"未在游戏中找到 {result} ,请检查角色是否存在,或者等待角色数据更新后重试",
show_alert=True,
)
return
await callback_query.answer(text="正在渲染图片中 请稍等 请不要重复点击按钮", show_alert=False)
await message.reply_chat_action(ChatAction.UPLOAD_PHOTO)
render_result = await self.get_render_result(data, nickname, characters.id, uid)
render_result.filename = f"role_detail_{uid}_{result}.png"
render_result.caption = self.get_caption(data, characters.id)
await render_result.edit_media(message, reply_markup=self.get_custom_button(user.id, uid, characters.id))
@staticmethod
def get_custom_button(user_id: int, uid: int, char_id: int) -> Optional[InlineKeyboardMarkup]:
return None

View File

@ -88,7 +88,7 @@ rich==13.7.1
sentry-sdk==2.7.1
setuptools==70.2.0
shellingham==1.5.4
simnet @ git+https://github.com/PaiGramTeam/SIMNet@9839a06bb87403443ac2cbea0ffde4156b05d902
simnet @ git+https://github.com/PaiGramTeam/SIMNet@ed7c061d82af4fe8c5a07f0d10c568b5e467662b
six==1.16.0
smmap==5.0.1
sniffio==1.3.1

Binary file not shown.

BIN
resources/img/aaa.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

BIN
resources/img/rarity/A.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

BIN
resources/img/rarity/B.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
resources/img/rarity/S.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -0,0 +1,297 @@
<!DOCTYPE html>
<html lang="en" gt-area="sea" gt-platform="pc" gt-device="undefined" style="--gpx: 1px;" mi18n-lang="zh-cn">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>绝区零战绩</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="preconnect" href="https://webstatic.hoyoverse.com/">
<link rel="stylesheet" href="./css/font.css">
<link rel="stylesheet" href="./css/swiper.min.css">
<meta name="viewport"
content="initial-scale=1,maximum-scale=1,user-scalable=no,width=device-width,viewport-fit=cover">
<meta name="description" content="">
<meta name="x-op-env" content="v1.0.14">
<link href="./css/vendors_f08f35cc.css" rel="stylesheet">
<link href="./css/pc_6db636c8.css" rel="stylesheet">
<link rel="stylesheet" type="text/css"
href="./css/pc_zzz_bangbooo_all~pc_zzz_challenge_all~pc_zzz_roles_all~pc_zzz_roles_detail_7c5ef2b9.css">
<link rel="stylesheet" type="text/css" href="./css/pc_zzz_roles_detail_8479dbdd.css">
<link rel="stylesheet" type="text/css" href="./css/style.css">
</head>
<body>
<div data-v-5e800caa="" class="pc-root-layout" id="1081">
<div data-v-5e800caa="" class="homeBg_3YryAw-I pc-layout-bg"></div>
<div data-v-5e800caa="" class="pc-root-layout__container">
<div data-v-5e800caa="" class="shareContainer_2ZR-xeyd">
<div class="share-decoIcon tw-absolute tw-right-10 tw-top-10 tw-z-[1] tw-h-[50px] tw-w-[120px]"
style="display: none;">
</div>
<div class="shareBg_1ijiCPI_" style="display: none;"></div>
<div class="share-header" style="display: none;"></div>
<div class="sharePage_1Rb3Oz8D share-page"> <!---->
<div data-v-5e800caa="" class="pc-root-layout__content">
<div data-v-7c666a6a="" data-v-5e800caa="" class="role-detail detailPage_1CFihQoV" uid=""
switches="[object Object],[object Object],[object Object]">
<div class="pc-detail-page-bg detailPageBg_327tx5T6"
style="background-image: url('https://act.hoyolab.com/app/mihoyo-zzz-game-record/images/page-bg-pc.9bd180ff.png'); background-color: rgb(1, 1, 1);"></div>
<div class="header_1FGsRcEF">
<div class="title_rlJZtEfB">代理人详情 - UID {{ uid }}</div>
</div>
<div data-v-7c666a6a="" class="role-detail-container"
style="background: linear-gradient({{ color }} 18%, rgb(1, 1, 1) 42%);">
<div data-v-7c666a6a="" class="infos-container">
<div data-v-7c666a6a="" class="infos-main-content">
<div data-v-7c666a6a="" class="role-infos">
<div data-v-7c666a6a="" class="role-name-bg"
style="background-color: {{ color }};">
</div>
<img data-v-7c666a6a="" src="https://act-webstatic.hoyoverse.com/game_record/zzz/role_vertical_painting/role_vertical_painting_{{ char.id }}.png"
alt="" class="role-avatar">
<div data-v-7c666a6a="" class="role-base-info">
<img data-v-7c666a6a="" src="../../img/rarity/{{ char.rarity }}.png"
alt="" class="rarity-icon">
<div data-v-7c666a6a="" class="base-info">
<div data-v-7c666a6a="">
<div data-v-7c666a6a=""><p data-v-7c666a6a="">{{ char.name }}</p>
<span data-v-7c666a6a="">Lv40</span></div>
<div data-v-7c666a6a="">
<img data-v-7c666a6a="" src="../../img/attribute/{{ char.element_type }}.png"
alt="">
<img data-v-7c666a6a="" src="../../img/profession/{{ char.avatar_profession }}.png"
alt="">
</div>
</div>
<div data-v-7342b717="" data-v-7c666a6a="" class="rank-list"
style="width: 224px; height: 24px;">
<svg data-v-7342b717="" viewBox="0 0 80 80"
style="width: 24px; height: 24px; color: rgb(62, 62, 62);">
<path data-v-7342b717="" fill="currentColor"
d="M30,27.28l-1.72,7.87H34L29.12,57.31h15l7.56-34.62H40.19Q37,26.68,30,27.28Z"></path>
<path data-v-7342b717="" fill="currentColor"
d="M40,0A40,40,0,1,0,80,40,40,40,0,0,0,40,0Zm0,71.91A31.91,31.91,0,1,1,71.91,40,32,32,0,0,1,40,71.91Z"></path>
</svg>
<svg data-v-7342b717="" viewBox="0 0 80 80"
style="width: 24px; height: 24px; color: rgb(62, 62, 62);">
<path data-v-7342b717="" fill="currentColor"
d="M51.92,22.69H36.82q-9.18,0-10.61,6.49L25,34.72H40l.5-2.07c.36-1.45,1.08-2.17,2.16-2.17a1.33,1.33,0,0,1,1.31.65,4.82,4.82,0,0,1,.09.65,4,4,0,0,1-2,3.76L21.68,50.08l-1.59,7.23H53.57l1.69-7.79H42.57L54.71,41a9.56,9.56,0,0,0,3.8-6.19l1.19-5.63a6.88,6.88,0,0,0,.2-1.94Q59.56,22.7,51.92,22.69Z"></path>
<path data-v-7342b717="" fill="currentColor"
d="M40,0A40,40,0,1,0,80,40,40,40,0,0,0,40,0Zm0,71.91A31.91,31.91,0,1,1,71.91,40,32,32,0,0,1,40,71.91Z"></path>
</svg>
<svg data-v-7342b717="" viewBox="0 0 80 80"
style="width: 24px; height: 24px; color: rgb(62, 62, 62);">
<path data-v-7342b717="" fill="currentColor"
d="M51.29,22.69h-15q-9.18,0-10.54,6.45l-.87,4.11H39.77l.28-1c.39-1.33,1.09-1.94,2.11-1.82.66.09,1.05.3,1.17.65a1.74,1.74,0,0,1,.23.73,4.27,4.27,0,0,1,0,.83c-.35,1.52-1.43,2.29-3.23,2.29H37.34l-1.69,7.79h3.24c1.26,0,1.93.47,2,1.41a3.71,3.71,0,0,1-.1.91l-.48,2.06C40,48.77,39.18,49.58,38,49.52c-.77,0-1.19-.52-1.27-1.46a4,4,0,0,1,0-.9L37.3,45h-15L21,51a6.51,6.51,0,0,0-.16,2q.33,4.37,7.89,4.37h15q9.18,0,10.72-7.28l.7-3.29a13.56,13.56,0,0,0,.37-3.43,5.45,5.45,0,0,0-.58-1.94q-.82-1.74-4-2.47,6.3-1.73,7.41-6.83L59,29.13a6.28,6.28,0,0,0,.18-1.86Q58.84,22.7,51.29,22.69Z"></path>
<path data-v-7342b717="" fill="currentColor"
d="M40,0A40,40,0,1,0,80,40,40,40,0,0,0,40,0Zm0,71.91A31.91,31.91,0,1,1,71.91,40,32,32,0,0,1,40,71.91Z"></path>
</svg>
<svg data-v-7342b717="" viewBox="0 0 80 80"
style="width: 24px; height: 24px; color: rgb(62, 62, 62);">
<path data-v-7342b717="" fill="currentColor"
d="M57.26,22.69H34.81L20.14,43.37l-1.56,7.1h17.6l-1.51,6.84h15l1.51-6.84h5l1.75-7.87h-5ZM37.86,42.6H33.29L40,33Z"></path>
<path data-v-7342b717="" fill="currentColor"
d="M40,0A40,40,0,1,0,80,40,40,40,0,0,0,40,0Zm0,71.91A31.91,31.91,0,1,1,71.91,40,32,32,0,0,1,40,71.91Z"></path>
</svg>
<svg data-v-7342b717="" viewBox="0 0 80 80"
style="width: 24px; height: 24px; color: rgb(62, 62, 62);">
<path data-v-7342b717="" fill="currentColor"
d="M23,42.94H38.11c.59-1.09,1.3-1.64,2.12-1.64a1.53,1.53,0,0,1,1.27.52,13.89,13.89,0,0,1,0,1.9L40.78,47c-.38,1.73-1.16,2.57-2.34,2.51-.92,0-1.34-.55-1.28-1.56.08-.43.15-.84.2-1.23s.11-.76.18-1.11H22.47L21.26,50.9a6.49,6.49,0,0,0-.17,1.91q.35,4.5,8,4.5H44.21Q53.48,57.31,55,50l2-9.3a9.25,9.25,0,0,0,.26-2.64q-.41-5.28-8.32-5.28a25.37,25.37,0,0,0-9.13,1.87l.91-4.16H59.25L61,22.69H27.41Z"></path>
<path data-v-7342b717="" fill="currentColor"
d="M40,0A40,40,0,1,0,80,40,40,40,0,0,0,40,0Zm0,71.91A31.91,31.91,0,1,1,71.91,40,32,32,0,0,1,40,71.91Z"></path>
</svg>
<svg data-v-7342b717="" viewBox="0 0 80 80"
style="width: 24px; height: 24px; color: rgb(62, 62, 62);">
<path data-v-7342b717="" fill="currentColor"
d="M51.29,22.69h-15q-9.13,0-10.54,6.45L21,51a7.15,7.15,0,0,0-.17,1.9q.35,4.45,7.9,4.46h15q9.21,0,10.76-7.27L56,43a9.1,9.1,0,0,0,.26-2.68q-.41-5.12-8.23-5.11a23.11,23.11,0,0,0-9.1,1.86l.86-3.82a6.46,6.46,0,0,1,.82-2.15,2,2,0,0,1,1.46-.65,1.29,1.29,0,0,1,1.25.64,1.08,1.08,0,0,1,.26.64,2.81,2.81,0,0,1-.12.81l-.09.59H58.11l.91-4a6.28,6.28,0,0,0,.18-1.86Q58.84,22.69,51.29,22.69Zm-11.9,20.6a1.4,1.4,0,0,1,1.27.56.91.91,0,0,1,.15.52,4,4,0,0,1-.13,1.17l-.39,1.77A2.75,2.75,0,0,1,39.47,49a2.12,2.12,0,0,1-1.47.56A1.24,1.24,0,0,1,36.8,49a3,3,0,0,1-.16-.7,4,4,0,0,1,.11-.95l.36-1.69C37.43,44.07,38.19,43.29,39.39,43.29Z"></path>
<path data-v-7342b717="" fill="currentColor"
d="M40,0A40,40,0,1,0,80,40,40,40,0,0,0,40,0Zm0,71.91A31.91,31.91,0,1,1,71.91,40,32,32,0,0,1,40,71.91Z"></path>
</svg>
</div>
<img data-v-7c666a6a=""
src="{{ char.group_icon_path }}"
alt="" class="group-icon"></div>
</div>
</div>
<ul data-v-7c666a6a="" class="property-info">
{% for prop in props %}
<li data-v-7c666a6a="">
<div data-v-7c666a6a="">
<img data-v-7c666a6a="" src="./images/prop-{{ prop.icon }}-icon.png"
alt=""> <span data-v-7c666a6a="">{{ prop.property_name }}</span>
</div>
<div data-v-7c666a6a="">
{% if prop.add != '' %}
<span data-v-7c666a6a="" class="base-add-prop">
<span data-v-7c666a6a="">{{ prop.base }}</span>
<span data-v-7c666a6a="">+{{ prop.add }}</span>
</span>
{% endif %}
<span data-v-7c666a6a="" class="final-prop">{{ prop.final }}</span>
</div>
</li>
{% endfor %}
</ul>
</div>
<div data-v-7c666a6a="" class="equipment-info"><h2 data-v-7c666a6a=""><span
data-v-7c666a6a="">装备</span> <img data-v-7c666a6a=""
src="./images/title-icon.0b3eeb69.png"
alt=""></h2>
<div data-v-7c666a6a="" class="suit-effects"><h3 data-v-7c666a6a="">
套装激活效果</h3>
{% set equip_suits = char.equip_suits %}
{% if equip_suits %}
<ul data-v-7c666a6a="">
{% for suit in equip_suits %}
<li data-v-7c666a6a="">
<div data-v-7c666a6a="">
<p data-v-7c666a6a="">{{ suit.name }} [{{ suit.own }}/4]</p>
<p data-v-7c666a6a="">
{% if suit.own > 3 %}
{{ suit.desc2_html | safe }}
{% else %}
{% if suit.own > 1 %}
{{ suit.desc1_html | safe }}
{% endif %}
{% endif %}
</p> <img data-v-7c666a6a=""
src="./images/weapon-suit-icon.f75f0d28.png"
alt="">
</div>
</li>
{% endfor %}
</ul>
{% else %}
<ul data-v-7c666a6a="" class="empty-content">
<li data-v-7c666a6a=""><p data-v-7c666a6a="">暂无套装激活效果</p></li>
</ul>
{% endif %}
</div>
<div data-v-7c666a6a="" class="main-content">
{% set weapon = char.weapon %}
{% if weapon %}
<div data-v-7c666a6a="" class="weapon-info">
<img data-v-7c666a6a="" src="./images/weapon-bg-{{ weapon.rarity }}.png" alt="" class="bg">
<img
data-v-7c666a6a=""
src="{{ weapon.icon }}" alt=""
class="icon">
{% if weapon.star > 0 %}
<img data-v-7c666a6a="" src="./images/role-star-{{ weapon.star }}.png"
class="rank">
{% endif %}
<span data-v-7c666a6a="" class="level">Lv{{ weapon.level }}</span>
</div>
{% endif %}
<!-- 驱动盘-->
{% set equips = char.equip_map %}
{% for i in range(1, 7) %}
<div data-v-7c666a6a="" class="equip-info equip-info-{{ loop.index }}">
{% if loop.index < 4 %}
{% set side = 'left' %}
{% else %}
{% set side = 'right' %}
{% endif %}
{% set equip = equips[loop.index | string] %}
{% if equip %}
<div data-v-7c666a6a="">
<img data-v-7c666a6a="" src="{{ equip.icon }}" alt="" class="icon">
<span data-v-7c666a6a="" class="level">Lv{{ equip.level }}</span>
</div>
<img data-v-7c666a6a=""
src="./images/equip-bg-{{ side }}-{{ equip.rarity}}.png" alt=""
class="bg">
{% else %}
<div data-v-7c666a6a="">
<img data-v-7c666a6a="" src="./images/equip-disable-bg.png" alt="" class="icon">
</div>
{% endif %}
</div>
{% endfor %}
</div>
</div>
{% set skills = char.skills %}
<div data-v-7c666a6a="" class="skill-info"><h2 data-v-7c666a6a=""><span
data-v-7c666a6a="">技能</span> <img data-v-7c666a6a=""
src="./images/title-icon.0b3eeb69.png"
alt=""></h2>
<ul data-v-7c666a6a="">
<li data-v-7c666a6a="" class="skill-item">
<div data-v-7c666a6a="">
<div data-v-7c666a6a=""><img data-v-7c666a6a=""
src="./images/skill-icon-0.0d9692b6.png"
alt="">
<p data-v-7c666a6a=""><span data-v-7c666a6a="">{{ skills[0].level_str }}</span> <span
data-v-7c666a6a="">LEVEL</span></p>
<h2 data-v-7c666a6a="">普通攻击</h2>
<h3 data-v-7c666a6a="">普通攻击</h3></div>
</div>
</li>
<li data-v-7c666a6a="" class="skill-item">
<div data-v-7c666a6a="">
<div data-v-7c666a6a=""><img data-v-7c666a6a=""
src="./images/skill-icon-2.d863591b.png"
alt="">
<p data-v-7c666a6a=""><span data-v-7c666a6a="">{{ skills[2].level_str }}</span> <span
data-v-7c666a6a="">LEVEL</span></p>
<h2 data-v-7c666a6a="">闪避</h2>
<h3 data-v-7c666a6a="">闪避</h3></div>
</div>
</li>
<li data-v-7c666a6a="" class="skill-item">
<div data-v-7c666a6a="">
<div data-v-7c666a6a=""><img data-v-7c666a6a=""
src="./images/skill-icon-6.3fc55b66.png"
alt="">
<p data-v-7c666a6a=""><span data-v-7c666a6a="">{{ skills[5].level_str }}</span> <span
data-v-7c666a6a="">LEVEL</span></p>
<h2 data-v-7c666a6a="">支援技</h2>
<h3 data-v-7c666a6a="">支援技</h3></div>
</div>
</li>
<li data-v-7c666a6a="" class="skill-item">
<div data-v-7c666a6a="">
<div data-v-7c666a6a=""><img data-v-7c666a6a=""
src="./images/skill-icon-1.e2f84ffb.png"
alt="">
<p data-v-7c666a6a=""><span data-v-7c666a6a="">{{ skills[1].level_str }}</span> <span
data-v-7c666a6a="">LEVEL</span></p>
<h2 data-v-7c666a6a="">特殊技</h2>
<h3 data-v-7c666a6a="">特殊技</h3></div>
</div>
</li>
<li data-v-7c666a6a="" class="skill-item">
<div data-v-7c666a6a="">
<div data-v-7c666a6a=""><img data-v-7c666a6a=""
src="./images/skill-icon-3.afdb8abe.png"
alt="">
<p data-v-7c666a6a=""><span data-v-7c666a6a="">{{ skills[3].level_str }}</span> <span
data-v-7c666a6a="">LEVEL</span></p>
<h2 data-v-7c666a6a="">连携技</h2>
<h3 data-v-7c666a6a="">连携技</h3></div>
</div>
</li>
<li data-v-7c666a6a="" class="skill-item">
<div data-v-7c666a6a="">
<div data-v-7c666a6a=""><img data-v-7c666a6a=""
src="./images/skill-icon-5.3d486da1.png"
alt="">
<p data-v-7c666a6a=""><span data-v-7c666a6a="">{{ skills[4].level_str }}</span> <span
data-v-7c666a6a="">LEVEL</span></p>
<h2 data-v-7c666a6a="">核心技</h2>
<h3 data-v-7c666a6a="">核心技</h3></div>
</div>
</li>
</ul>
</div>
</div>
</div> <!----> <!----> <!----> <!---->
<div data-modal-id="__gt-popup__" data-modal-count="0" data-modal-visible-count="0"
class="gt-modal gt-popup-root" style="z-index: 100;"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,4 @@
@font-face {
font-family: 'inpin hongmengti';
src: url("../../../fonts/ZZZ_Default.otf")
}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 551 B