mirror of
https://github.com/PaiGramTeam/MibooGram.git
synced 2024-11-16 04:45:27 +00:00
✨ Support zzz avatar list
This commit is contained in:
parent
037f5ef3d2
commit
a9caa544d0
@ -8,8 +8,8 @@ from typing import TYPE_CHECKING, Union
|
||||
from pydantic import ValidationError
|
||||
from simnet import ZZZClient, Region
|
||||
from simnet.errors import BadRequest as SimnetBadRequest, InvalidCookies, NetworkError, CookieException, NeedChallenge
|
||||
from simnet.models.genshin.calculator import CalculatorCharacterDetails
|
||||
from simnet.models.genshin.chronicle.characters import Character
|
||||
from simnet.models.zzz.calculator import ZZZCalculatorCharacter
|
||||
from simnet.models.zzz.character import ZZZPartialCharacter
|
||||
from simnet.utils.player import recognize_game_biz
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
from sqlalchemy.orm.exc import StaleDataError
|
||||
@ -87,13 +87,13 @@ class CharacterDetails(Plugin):
|
||||
self,
|
||||
uid: int,
|
||||
character_id: int,
|
||||
) -> Optional["CalculatorCharacterDetails"]:
|
||||
) -> Optional["ZZZCalculatorCharacter"]:
|
||||
name = self.get_qname(uid, character_id)
|
||||
data = await self.redis.get(name)
|
||||
if data is None:
|
||||
return None
|
||||
json_data = str(data, encoding="utf-8")
|
||||
return CalculatorCharacterDetails.parse_raw(json_data)
|
||||
return ZZZCalculatorCharacter.parse_raw(json_data)
|
||||
|
||||
async def set_character_details(self, player_id: int, character_id: int, data: str):
|
||||
randint = random.randint(1, 30) # nosec
|
||||
@ -135,7 +135,7 @@ class CharacterDetails(Plugin):
|
||||
self,
|
||||
uid: int,
|
||||
character_id: int,
|
||||
) -> Optional["CalculatorCharacterDetails"]:
|
||||
) -> Optional["ZZZCalculatorCharacter"]:
|
||||
async with AsyncSession(self.database.engine) as session:
|
||||
statement = (
|
||||
select(CharacterDetailsSQLModel)
|
||||
@ -146,7 +146,7 @@ class CharacterDetails(Plugin):
|
||||
data = results.first()
|
||||
if data is not None:
|
||||
try:
|
||||
return CalculatorCharacterDetails.parse_raw(data.data)
|
||||
return ZZZCalculatorCharacter.parse_raw(data.data)
|
||||
except ValidationError as exc:
|
||||
logger.error("解析数据出现异常 ValidationError", exc_info=exc)
|
||||
await session.delete(data)
|
||||
@ -158,11 +158,11 @@ class CharacterDetails(Plugin):
|
||||
return None
|
||||
|
||||
async def get_character_details(
|
||||
self, client: "ZZZClient", character: "Union[int,Character]"
|
||||
) -> Optional["CalculatorCharacterDetails"]:
|
||||
self, client: "ZZZClient", character: "Union[int,ZZZPartialCharacter]"
|
||||
) -> Optional["ZZZCalculatorCharacter"]:
|
||||
"""缓存 character_details 并定时对其进行数据存储 当遇到 Too Many Requests 可以获取以前的数据"""
|
||||
uid = client.player_id
|
||||
if isinstance(character, Character):
|
||||
if isinstance(character, ZZZPartialCharacter):
|
||||
character_id = character.id
|
||||
else:
|
||||
character_id = character
|
||||
@ -171,7 +171,7 @@ class CharacterDetails(Plugin):
|
||||
if detail is not None:
|
||||
return detail
|
||||
try:
|
||||
detail = await client.get_character_details(character_id)
|
||||
detail = (await client.get_zzz_character_info([character_id])).characters[0]
|
||||
except SimnetBadRequest as exc:
|
||||
if "Too Many Requests" in exc.message:
|
||||
return await self.get_character_details_for_mysql(uid, character_id)
|
||||
@ -179,7 +179,7 @@ class CharacterDetails(Plugin):
|
||||
asyncio.create_task(self.set_character_details(uid, character_id, detail.json(by_alias=True)))
|
||||
return detail
|
||||
try:
|
||||
return await client.get_character_details(character_id)
|
||||
return (await client.get_zzz_character_info([character_id])).characters[0]
|
||||
except SimnetBadRequest as exc:
|
||||
if "Too Many Requests" in exc.message:
|
||||
logger.warning("Too Many Requests")
|
||||
|
249
plugins/zzz/avatar_list.py
Normal file
249
plugins/zzz/avatar_list.py
Normal file
@ -0,0 +1,249 @@
|
||||
import asyncio
|
||||
import math
|
||||
from typing import List, Optional, TYPE_CHECKING, Dict, Union, Tuple, Any
|
||||
|
||||
from arkowrapper import ArkoWrapper
|
||||
from pydantic import BaseModel
|
||||
from simnet.models.zzz.calculator import ZZZCalculatorCharacter
|
||||
from simnet.models.zzz.character import ZZZPartialCharacter
|
||||
from telegram.constants import ChatAction
|
||||
from telegram.ext import filters
|
||||
|
||||
from core.dependence.assets import AssetsService, AssetsCouldNotFound
|
||||
from core.plugin import Plugin, handler
|
||||
from core.services.cookies import CookiesService
|
||||
from core.services.template.models import FileType
|
||||
from core.services.template.services import TemplateService
|
||||
from core.services.wiki.services import WikiService
|
||||
from gram_core.plugin.methods.inline_use_data import IInlineUseData
|
||||
from gram_core.services.template.models import RenderGroupResult
|
||||
from plugins.tools.genshin import GenshinHelper, CharacterDetails
|
||||
from utils.const import RESOURCE_DIR
|
||||
from utils.log import logger
|
||||
from utils.uid import mask_number
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from simnet import ZZZClient
|
||||
from telegram.ext import ContextTypes
|
||||
from telegram import Update
|
||||
from gram_core.services.template.models import RenderResult
|
||||
|
||||
MAX_AVATAR_COUNT = 40
|
||||
|
||||
|
||||
class EquipmentData(BaseModel):
|
||||
id: int
|
||||
name: str
|
||||
level: int
|
||||
eidolon: int
|
||||
rarity: int
|
||||
icon: str
|
||||
|
||||
|
||||
class SkillData(BaseModel):
|
||||
id: int
|
||||
level: int
|
||||
max_level: int
|
||||
|
||||
|
||||
class AvatarData(BaseModel):
|
||||
id: int
|
||||
name: str
|
||||
level: int
|
||||
eidolon: int
|
||||
rarity: int
|
||||
icon: str = ""
|
||||
skills: List[SkillData]
|
||||
equipment: Optional[EquipmentData] = None
|
||||
|
||||
|
||||
class AvatarListPlugin(Plugin):
|
||||
"""练度统计"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
cookies_service: CookiesService = None,
|
||||
assets_service: AssetsService = None,
|
||||
template_service: TemplateService = None,
|
||||
wiki_service: WikiService = None,
|
||||
helper: GenshinHelper = None,
|
||||
character_details: CharacterDetails = None,
|
||||
) -> None:
|
||||
self.cookies_service = cookies_service
|
||||
self.assets_service = assets_service
|
||||
self.template_service = template_service
|
||||
self.wiki_service = wiki_service
|
||||
self.helper = helper
|
||||
self.character_details = character_details
|
||||
|
||||
async def get_avatar_data(self, character_id: int, client: "ZZZClient") -> Optional["ZZZCalculatorCharacter"]:
|
||||
return await self.character_details.get_character_details(client, character_id)
|
||||
|
||||
@staticmethod
|
||||
async def get_avatars_data(client: "ZZZClient") -> List["ZZZPartialCharacter"]:
|
||||
task_info_results = (await client.get_zzz_characters()).characters
|
||||
|
||||
return sorted(
|
||||
list(filter(lambda x: x, task_info_results)),
|
||||
key=lambda x: (
|
||||
x.level,
|
||||
x.rarity,
|
||||
x.rank,
|
||||
),
|
||||
reverse=True,
|
||||
)
|
||||
|
||||
async def get_avatars_details(
|
||||
self, characters: List["ZZZPartialCharacter"], client: "ZZZClient"
|
||||
) -> Dict[int, "ZZZCalculatorCharacter"]:
|
||||
async def _task(cid):
|
||||
return await self.get_avatar_data(cid, client)
|
||||
|
||||
task_detail_results = await asyncio.gather(*[_task(character.id) for character in characters])
|
||||
|
||||
return {character.id: detail for character, detail in zip(characters, task_detail_results)}
|
||||
|
||||
@staticmethod
|
||||
def get_skill_data(character: Optional["ZZZCalculatorCharacter"]) -> List[SkillData]:
|
||||
if not character:
|
||||
return [SkillData(id=i, level=1, max_level=10) for i in range(1, 5)]
|
||||
return [SkillData(id=skill.skill_type, level=skill.level, max_level=10) for skill in character.skills]
|
||||
|
||||
@staticmethod
|
||||
def fix_rarity(rarity: str) -> int:
|
||||
return {"S": 5, "A": 4, "B": 3}.get(rarity, 4)
|
||||
|
||||
async def get_final_data(self, characters: List["ZZZPartialCharacter"], client: "ZZZClient") -> List[AvatarData]:
|
||||
details = await self.get_avatars_details(characters, client)
|
||||
data = []
|
||||
for character in characters:
|
||||
try:
|
||||
detail = details.get(character.id)
|
||||
equip = (
|
||||
EquipmentData(
|
||||
id=detail.weapon.id,
|
||||
name=detail.weapon.name,
|
||||
level=detail.weapon.level,
|
||||
eidolon=detail.weapon.star,
|
||||
rarity=self.fix_rarity(detail.weapon.rarity),
|
||||
icon=self.assets_service.weapon.icon(detail.weapon.id, detail.weapon.name).as_uri(),
|
||||
)
|
||||
if detail.weapon
|
||||
else None
|
||||
)
|
||||
avatar = AvatarData(
|
||||
id=character.id,
|
||||
name=character.name,
|
||||
level=character.level,
|
||||
eidolon=character.rank,
|
||||
rarity=self.fix_rarity(character.rarity),
|
||||
icon=self.assets_service.avatar.icon(character.id, character.name).as_uri(),
|
||||
skills=self.get_skill_data(detail),
|
||||
equipment=equip,
|
||||
)
|
||||
data.append(avatar)
|
||||
except AssetsCouldNotFound as e:
|
||||
logger.warning("未找到角色 %s[%s] 的资源: %s", character.name, character.id, e)
|
||||
return data
|
||||
|
||||
async def avatar_list_render(
|
||||
self,
|
||||
base_render_data: Dict,
|
||||
avatar_datas: List[AvatarData],
|
||||
only_one_page: bool,
|
||||
) -> Union[Tuple[Any], List["RenderResult"], None]:
|
||||
def render_task(start_id: int, c: List[AvatarData]):
|
||||
_render_data = {
|
||||
"avatar_datas": c, # 角色数据
|
||||
"start_id": start_id, # 开始序号
|
||||
}
|
||||
_render_data.update(base_render_data)
|
||||
return self.template_service.render(
|
||||
"zzz/avatar_list/main.html",
|
||||
_render_data,
|
||||
viewport={"width": 1040, "height": 500},
|
||||
full_page=True,
|
||||
query_selector=".container",
|
||||
file_type=FileType.PHOTO,
|
||||
ttl=30 * 24 * 60 * 60,
|
||||
)
|
||||
|
||||
if only_one_page:
|
||||
return [await render_task(0, avatar_datas)]
|
||||
image_count = len(avatar_datas)
|
||||
while image_count > MAX_AVATAR_COUNT:
|
||||
image_count /= 2
|
||||
image_count = math.ceil(image_count)
|
||||
avatar_datas_group = [avatar_datas[i : i + image_count] for i in range(0, len(avatar_datas), image_count)]
|
||||
tasks = [render_task(i * image_count, c) for i, c in enumerate(avatar_datas_group)]
|
||||
return await asyncio.gather(*tasks)
|
||||
|
||||
async def add_theme_data(self, data: Dict, player_id: int):
|
||||
res = RESOURCE_DIR / "img"
|
||||
data["avatar"] = (res / "avatar.png").as_uri()
|
||||
data["background"] = (res / "home.png").as_uri()
|
||||
return data
|
||||
|
||||
async def render(self, client: "ZZZClient", all_avatars: bool = False) -> List["RenderResult"]:
|
||||
characters: List["ZZZPartialCharacter"] = await self.get_avatars_data(client)
|
||||
|
||||
has_more = (not all_avatars) and len(characters) > MAX_AVATAR_COUNT
|
||||
if has_more:
|
||||
characters = characters[:MAX_AVATAR_COUNT]
|
||||
avatar_datas = await self.get_final_data(characters, client)
|
||||
|
||||
base_render_data = {
|
||||
"uid": mask_number(client.player_id), # 玩家uid
|
||||
"has_more": has_more, # 是否显示了全部角色
|
||||
}
|
||||
await self.add_theme_data(base_render_data, client.player_id)
|
||||
return await self.avatar_list_render(base_render_data, avatar_datas, has_more)
|
||||
|
||||
@handler.command("avatars", cookie=True, block=False)
|
||||
@handler.message(filters.Regex(r"^(全部)?练度统计$"), cookie=True, block=False)
|
||||
async def avatar_list(self, update: "Update", _: "ContextTypes.DEFAULT_TYPE"):
|
||||
user_id = await self.get_real_user_id(update)
|
||||
message = update.effective_message
|
||||
uid, offset = self.get_real_uid_or_offset(update)
|
||||
all_avatars = "全部" in message.text or "all" in message.text # 是否发送全部角色
|
||||
self.log_user(update, logger.info, "[bold]练度统计[/bold]: all=%s", all_avatars, extra={"markup": True})
|
||||
await message.reply_chat_action(ChatAction.TYPING)
|
||||
|
||||
async with self.helper.genshin(user_id, player_id=uid, offset=offset) as client:
|
||||
notice = await message.reply_text("彦卿需要收集整理数据,还请耐心等待哦~")
|
||||
self.add_delete_message_job(notice, delay=60)
|
||||
images = await self.render(client, all_avatars)
|
||||
|
||||
for group in ArkoWrapper(images).group(10): # 每 10 张图片分一个组
|
||||
await RenderGroupResult(results=group).reply_media_group(message, write_timeout=60)
|
||||
|
||||
self.log_user(
|
||||
update,
|
||||
logger.info,
|
||||
"[bold]练度统计[/bold]发送图片成功",
|
||||
extra={"markup": True},
|
||||
)
|
||||
|
||||
async def avatar_list_use_by_inline(self, update: "Update", context: "ContextTypes.DEFAULT_TYPE") -> None:
|
||||
callback_query = update.callback_query
|
||||
user = update.effective_user
|
||||
user_id = user.id
|
||||
uid = IInlineUseData.get_uid_from_context(context)
|
||||
self.log_user(update, logger.info, "查询练度统计")
|
||||
|
||||
async with self.helper.genshin(user_id, player_id=uid) as client:
|
||||
client: "ZZZClient"
|
||||
images = await self.render(client)
|
||||
render = images[0]
|
||||
await render.edit_inline_media(callback_query)
|
||||
|
||||
async def get_inline_use_data(self) -> List[Optional[IInlineUseData]]:
|
||||
return [
|
||||
IInlineUseData(
|
||||
text="练度统计",
|
||||
hash="avatar_list",
|
||||
callback=self.avatar_list_use_by_inline,
|
||||
cookie=True,
|
||||
player=True,
|
||||
)
|
||||
]
|
131
resources/zzz/avatar_list/main.html
Normal file
131
resources/zzz/avatar_list/main.html
Normal file
@ -0,0 +1,131 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Avatar List</title>
|
||||
<link type="text/css" href="./style.css" rel="stylesheet"/>
|
||||
<link type="text/css" href="../../styles/public.css" rel="stylesheet"/>
|
||||
<script src="../../js/tailwindcss-3.1.8.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container flex flex-col justify-center p-6">
|
||||
<div class="header rounded-xl p-2 mb-6" style='background-image: url("{{ background }}") '>
|
||||
<div class="frame rounded-lg border-solid border-2 bg-contain p-4 flex items-center">
|
||||
<img class="w-28 h-28 rounded-full mr-4" src="{{ avatar }}" alt="Avatar">
|
||||
<div>
|
||||
<div class="uid text-xl italic px-2">UID: {{ uid }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content rounded-xl overflow-hidden flex flex-col justify-center">
|
||||
<table class="table text-center border-collapse">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-right">#</th>
|
||||
<th colspan="2">角色</th>
|
||||
<th>等级</th>
|
||||
<th>意象</th>
|
||||
<th>普攻</th>
|
||||
<th>特殊</th>
|
||||
<th>闪避</th>
|
||||
<th>连携</th>
|
||||
<th>核心</th>
|
||||
<th>支援</th>
|
||||
<th colspan="4">音擎</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for avatar_data in avatar_datas %}
|
||||
{% set equipment = avatar_data.equipment %}
|
||||
{% if avatar_data.rarity == 5 %}
|
||||
{% set row_bg = 'gold' %}
|
||||
{% else %}
|
||||
{% set row_bg = 'nongold' %}
|
||||
{% endif %}
|
||||
{% if equipment != none %}
|
||||
{% set equip_star = equipment.rarity %}
|
||||
{% else %}
|
||||
{% set equip_star = '' %}
|
||||
{% endif %}
|
||||
<tr class="h-8 border border-b">
|
||||
<td class="text-right {{row_bg}}">{{ start_id + loop.index }}</td>
|
||||
<td class="role {{row_bg}}">
|
||||
<img class="h-8 pl-2 img-drop-shadow" src="{{ avatar_data.icon }}"/>
|
||||
</td>
|
||||
<td class="role text-left {{row_bg}}">{{ avatar_data.name }}</td>
|
||||
<td>{{ avatar_data.level }}</td>
|
||||
<td
|
||||
{% set constellation = avatar_data.eidolon %}
|
||||
{% if constellation != 0 %}
|
||||
class="color {{ ['green', 'cyan', 'blue', 'purple', 'pink', 'red'][constellation - 1] }}"
|
||||
{% endif %}
|
||||
>
|
||||
<span class="number role inline-block">{{ constellation }}</span>
|
||||
<div class="role bg"></div>
|
||||
</td>
|
||||
{% for skill in avatar_data.skills %}
|
||||
{% set talent_style = 'talent' %}
|
||||
{% set skill_level = skill.level %}
|
||||
|
||||
{% if skill_level < 4 %}
|
||||
{% set talent_style = talent_style + ' talent-level-first' %}
|
||||
{% endif %}
|
||||
|
||||
{% if skill.max_level == skill.level %}
|
||||
{% if loop.index == 1 %}
|
||||
{% set talent_max_style = " talent-level-max" %}
|
||||
{% else %}
|
||||
{% set talent_max_style = " talent-level-max-img" %}
|
||||
{% endif %}
|
||||
{% set talent_style = talent_style + talent_max_style %}
|
||||
{% endif %}
|
||||
|
||||
{% if skill.max_level != skill.level %}
|
||||
{% if skill_level < 4 %}
|
||||
{% set talent_style = talent_style + ' talent-level-1' %}
|
||||
{% elif skill_level < 6 %}
|
||||
{% set talent_style = talent_style + ' talent-level-2' %}
|
||||
{% elif skill_level < 9 %}
|
||||
{% set talent_style = talent_style + ' talent-level-3' %}
|
||||
{% else %}
|
||||
{% set talent_style = talent_style + ' talent-level-4' %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<td class="{{ talent_style }}">{{ skill.level }}</td>
|
||||
{% endfor %}
|
||||
{% if equipment != none %}
|
||||
<td class="weapon-{{ equip_star }}-star text-left pl-3">
|
||||
{% if equipment.level < 10 %}
|
||||
Lv.{{ equipment.level }}
|
||||
{% else %}
|
||||
Lv.{{ equipment.level }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="weapon-{{ equip_star }}-star color border-none {{ ['green', 'cyan', 'blue', 'purple', 'red'][equipment.eidolon - 1] }}">
|
||||
<span class="weapon number">{{ equipment.eidolon }}</span>
|
||||
<div class="weapon bg p-1">
|
||||
<div class="frame-border border-solid border"></div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="weapon weapon-{{ equip_star }}-star">
|
||||
<img class="h-8 pl-1" src="{{ equipment.icon }}" alt="weapon">
|
||||
</td>
|
||||
<td class="weapon-{{ equip_star }}-star text-left">{{ equipment.name }}</td>
|
||||
{% else %}
|
||||
<td colspan="4"></td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="notice">
|
||||
{% if has_more %}
|
||||
<div>
|
||||
*想查看完整数据请在指令中加上<code>all</code>或者<code>全部</code>: <code>/avatars all</code>、<code>全部练度统计</code>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
167
resources/zzz/avatar_list/style.css
Normal file
167
resources/zzz/avatar_list/style.css
Normal file
@ -0,0 +1,167 @@
|
||||
:root {
|
||||
--white: rgb(246 248 249);
|
||||
--bg-color: rgb(233 229 220);
|
||||
--h-color: rgb(203 189 162);
|
||||
--red: rgb(255 86 33/ 80%);
|
||||
--pink: rgb(215 57 203/80%);
|
||||
--purple: rgb(159 68 211/80%);
|
||||
--blue: rgb(98 168 233/ 80%);
|
||||
--cyan: rgb(4 150 255/80%);
|
||||
--green: rgb(67 185 124/ 80%);
|
||||
--grey: rgb(189 191 190);
|
||||
}
|
||||
|
||||
/* 上半 */
|
||||
.container {
|
||||
width: 1000px !important;
|
||||
max-width: 1000px !important;
|
||||
}
|
||||
|
||||
.header {
|
||||
/*background: #e0dad3 url(../gacha_log/img/) no-repeat right;*/
|
||||
box-shadow: 0 0 8px #72a2ae79;
|
||||
background-size: cover;
|
||||
background-position: top;
|
||||
}
|
||||
|
||||
.frame {
|
||||
border-color: #cdbea8;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.text-shadow {
|
||||
text-shadow: 0 0.08em 0.1em #00000023;
|
||||
}
|
||||
|
||||
/* 下半 */
|
||||
.content {
|
||||
box-shadow: 0 0 8px #72a2ae79;
|
||||
font-size: 21px;
|
||||
}
|
||||
|
||||
.table thead {
|
||||
background-color: #e0dad3;
|
||||
}
|
||||
.table tr {
|
||||
border-color: #00000021;
|
||||
}
|
||||
.table tr:nth-child(even) {
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
|
||||
.role img {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
.weapon img {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
/* 计数+菱形图案 */
|
||||
.weapon img,
|
||||
.color {
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
}
|
||||
.role.bg,
|
||||
.weapon.bg {
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
}
|
||||
.role.bg {
|
||||
top: 5px;
|
||||
left: 17px;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
transform: rotateZ(45deg);
|
||||
}
|
||||
.weapon.bg {
|
||||
top: 0;
|
||||
left: -8px;
|
||||
transform: skewX(-11deg);
|
||||
}
|
||||
.frame-border {
|
||||
border-color: #f7e2ad2b;
|
||||
width: 62px;
|
||||
height: 27px;
|
||||
}
|
||||
|
||||
.color .number {
|
||||
color: white;
|
||||
}
|
||||
.green .bg {
|
||||
background-color: #3c7d7f;
|
||||
}
|
||||
.cyan .bg {
|
||||
background-color: #4898c7;
|
||||
}
|
||||
.blue .bg {
|
||||
background-color: #4576b4;
|
||||
}
|
||||
.purple .bg {
|
||||
background-color: #955ec9;
|
||||
}
|
||||
.pink .bg {
|
||||
background-color: #c24e9a;
|
||||
}
|
||||
.red .bg {
|
||||
background-color: #c24e4e;
|
||||
}
|
||||
|
||||
/* 武器、角色背景 */
|
||||
.weapon-1-star {
|
||||
background-color: #cfcfcf;
|
||||
}
|
||||
.weapon-2-star {
|
||||
background-color: #c0edee;
|
||||
}
|
||||
.weapon-3-star {
|
||||
background-color: #bedcff;
|
||||
}
|
||||
.weapon-4-star, .nongold {
|
||||
background-color: #d3c8fa;
|
||||
}
|
||||
.weapon-5-star, .gold {
|
||||
background-color: #f9e7bb;
|
||||
}
|
||||
|
||||
.notice {
|
||||
background-image: linear-gradient(135deg, #9452a5 10%, #fff5c3 100%);
|
||||
}
|
||||
|
||||
.talent {
|
||||
background-size: contain, 1.6em;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
text-shadow: 1px 1px 2px rgb(0 0 0 /20%);
|
||||
z-index: -1 !important;
|
||||
border-right-width: 0 !important;
|
||||
border-left-width: 0 !important;
|
||||
}
|
||||
|
||||
.talent-level-first {
|
||||
background-color: rgb(189, 191, 190) !important;
|
||||
}
|
||||
|
||||
.talent-level-1 {
|
||||
background-color: rgb(189, 191, 190);
|
||||
}
|
||||
|
||||
.talent-level-2 {
|
||||
background-color: var(--green);
|
||||
}
|
||||
|
||||
.talent-level-3 {
|
||||
background-color: var(--blue);
|
||||
}
|
||||
|
||||
.talent-level-4 {
|
||||
background-color: rgb(190, 160, 250);
|
||||
}
|
||||
|
||||
.talent-level-max {
|
||||
background-image: linear-gradient(90deg, rgba(251, 129, 124, 0.8) 0%, rgba(255, 93, 85, 0.65) 50%, rgba(251, 129, 124, 0.8) 100%) !important;
|
||||
}
|
||||
|
||||
.talent-level-max-img {
|
||||
background-image: linear-gradient(90deg, rgba(251, 129, 124, 0.8) 0%, rgba(255, 93, 85, 0.65) 50%, rgba(251, 129, 124, 0.8) 100%) !important;
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 29 KiB |
Binary file not shown.
Before Width: | Height: | Size: 47 KiB |
Loading…
Reference in New Issue
Block a user