diff --git a/README.md b/README.md index 21fbe11..1b326b9 100644 --- a/README.md +++ b/README.md @@ -66,3 +66,5 @@ python ./run.py | [西风驿站 猫冬](https://bbs.mihoyo.com/ys/accountCenter/postList?id=74019947) | 本项目攻略图图源 | | [Yunzai-Bot](https://github.com/Le-niao/Yunzai-Bot) | 本项使用的抽卡图片和前端资源来源 | | [Crawler-ghhw](https://github.com/DGP-Studio/Crawler-ghhw) | 本项目参考的爬虫代码 | +| [Enka.Network](https://enka.network) | 角色卡片的数据来源 | +| [miao-plugin](https://github.com/yoimiya-kokomi/miao-plugin) | 角色卡片的参考项目 | diff --git a/modules/playercards/fight_prop.py b/modules/playercards/fight_prop.py new file mode 100644 index 0000000..7e971fa --- /dev/null +++ b/modules/playercards/fight_prop.py @@ -0,0 +1,68 @@ +import enum + + +class FightProp(enum.Enum): + BASE_HP = "基础血量" + FIGHT_PROP_BASE_ATTACK = "基础攻击力" + FIGHT_PROP_BASE_DEFENSE = "基础防御力" + FIGHT_PROP_BASE_HP = "基础血量" + FIGHT_PROP_ATTACK = "攻击力" + FIGHT_PROP_ATTACK_PERCENT = "攻击力百分比" + FIGHT_PROP_HP = "生命值" + FIGHT_PROP_HP_PERCENT = "生命值百分比" + FIGHT_PROP_DEFENSE = "防御力" + FIGHT_PROP_DEFENSE_PERCENT = "防御力百分比" + FIGHT_PROP_ELEMENT_MASTERY = "元素精通" + FIGHT_PROP_CRITICAL = "暴击率" + FIGHT_PROP_CRITICAL_HURT = "暴击伤害" + FIGHT_PROP_CHARGE_EFFICIENCY = "元素充能效率" + FIGHT_PROP_FIRE_SUB_HURT = "火元素抗性" + FIGHT_PROP_ELEC_SUB_HURT = "雷元素抗性" + FIGHT_PROP_ICE_SUB_HURT = "冰元素抗性" + FIGHT_PROP_WATER_SUB_HURT = "水元素抗性" + FIGHT_PROP_WIND_SUB_HURT = "风元素抗性" + FIGHT_PROP_ROCK_SUB_HURT = "岩元素抗性" + FIGHT_PROP_GRASS_SUB_HURT = "草元素抗性" + FIGHT_PROP_FIRE_ADD_HURT = "火元素伤害加成" + FIGHT_PROP_ELEC_ADD_HURT = "雷元素伤害加成" + FIGHT_PROP_ICE_ADD_HURT = "冰元素伤害加成" + FIGHT_PROP_WATER_ADD_HURT = "水元素伤害加成" + FIGHT_PROP_WIND_ADD_HURT = "风元素伤害加成" + FIGHT_PROP_ROCK_ADD_HURT = "岩元素伤害加成" + FIGHT_PROP_GRASS_ADD_HURT = "草元素伤害加成" + FIGHT_PROP_PHYSICAL_ADD_HURT = "物理伤害加成" + FIGHT_PROP_HEAL_ADD = "治疗加成" + + +class FightPropScore(enum.Enum): + _value_: float + value: float + FIGHT_PROP_BASE_ATTACK = 1 + FIGHT_PROP_BASE_DEFENSE = 1 + FIGHT_PROP_BASE_HP = 1 + FIGHT_PROP_ATTACK = 662 / 3110 # 攻击力 + FIGHT_PROP_ATTACK_PERCENT = 4 / 3 # 攻击力百分比 + FIGHT_PROP_HP = 662 / 47800 # 生命 + FIGHT_PROP_HP_PERCENT = 4 / 3 # 生命百分比 + FIGHT_PROP_DEFENSE = 662 / 3890 # 防御力 + FIGHT_PROP_DEFENSE_PERCENT = 662 / 583 # 防御力百分比 + FIGHT_PROP_ELEMENT_MASTERY = 1 / 3 # 元素精通 + FIGHT_PROP_CRITICAL = 2 # 暴击率 + FIGHT_PROP_CRITICAL_HURT = 1 # 暴击伤害 + FIGHT_PROP_CHARGE_EFFICIENCY = 662 / 518 # 元素充能效率 + FIGHT_PROP_FIRE_SUB_HURT = 1 + FIGHT_PROP_ELEC_SUB_HURT = 1 + FIGHT_PROP_ICE_SUB_HURT = 1 + FIGHT_PROP_WATER_SUB_HURT = 1 + FIGHT_PROP_WIND_SUB_HURT = 1 + FIGHT_PROP_ROCK_SUB_HURT = 1 + FIGHT_PROP_GRASS_SUB_HURT = 1 + FIGHT_PROP_FIRE_ADD_HURT = 1 + FIGHT_PROP_ELEC_ADD_HURT = 1 + FIGHT_PROP_ICE_ADD_HURT = 1 + FIGHT_PROP_WATER_ADD_HURT = 1 + FIGHT_PROP_WIND_ADD_HURT = 1 + FIGHT_PROP_ROCK_ADD_HURT = 1 + FIGHT_PROP_GRASS_ADD_HURT = 1 + FIGHT_PROP_PHYSICAL_ADD_HURT = 1 + FIGHT_PROP_HEAL_ADD = 1 diff --git a/modules/playercards/helpers.py b/modules/playercards/helpers.py new file mode 100644 index 0000000..923a1cc --- /dev/null +++ b/modules/playercards/helpers.py @@ -0,0 +1,42 @@ +import os + +import ujson as json +from enkanetwork import EquipmentsStats + +from modules.playercards.fight_prop import FightProp, FightPropScore + +_project_path = os.path.dirname(__file__) +_fight_prop_rule_file = os.path.join(_project_path, "metadata", "FightPropRule.json") +with open(_fight_prop_rule_file, "r", encoding="utf-8") as f: + fight_prop_rule_data: dict = json.load(f) + + +class ArtifactStatsTheory: + + def __init__(self, character_name: str): + self.character_name = character_name + fight_prop_rule_list = fight_prop_rule_data.get(self.character_name, []) + self.main_prop = [FightProp(fight_prop_rule) for fight_prop_rule in fight_prop_rule_list] + if not self.main_prop: + self.main_prop = [FightProp.FIGHT_PROP_CRITICAL, FightProp.FIGHT_PROP_CRITICAL_HURT, + FightProp.FIGHT_PROP_ATTACK_PERCENT] + # 修正要评分的数值词条 + if FightProp.FIGHT_PROP_ATTACK_PERCENT in self.main_prop and FightProp.FIGHT_PROP_ATTACK not in self.main_prop: + self.main_prop.append(FightProp.FIGHT_PROP_ATTACK) + if FightProp.FIGHT_PROP_HP_PERCENT in self.main_prop and FightProp.FIGHT_PROP_HP not in self.main_prop: + self.main_prop.append(FightProp.FIGHT_PROP_HP) + if FightProp.FIGHT_PROP_DEFENSE_PERCENT in self.main_prop and \ + FightProp.FIGHT_PROP_DEFENSE not in self.main_prop: + self.main_prop.append(FightProp.FIGHT_PROP_DEFENSE) + + def theory(self, sub_stats: EquipmentsStats) -> float: + """圣遗物副词条评分 + Args: + sub_stats: 圣遗物对象 + Returns: + 返回得分 + """ + score: float = 0 + if sub_stats.prop_id in map(lambda x: x.name, self.main_prop): + score = float(FightPropScore[sub_stats.prop_id].value) * sub_stats.value + return round(score, 1) diff --git a/modules/playercards/metadata/FightPropRule.json b/modules/playercards/metadata/FightPropRule.json new file mode 100644 index 0000000..c04214e --- /dev/null +++ b/modules/playercards/metadata/FightPropRule.json @@ -0,0 +1,380 @@ +{ + "旅行者": [ + "攻击力百分比", + "暴击率", + "暴击伤害", + "元素充能效率" + ], + "安柏": [ + "攻击力百分比", + "暴击率", + "暴击伤害", + "元素精通", + "物理伤害加成" + ], + "凯亚": [ + "攻击力百分比", + "暴击率", + "暴击伤害", + "物理伤害加成", + "元素充能效率" + ], + "丽莎": [ + "攻击力百分比", + "暴击率", + "暴击伤害", + "元素精通" + ], + "芭芭拉": [ + "生命值百分比", + "攻击力百分比", + "暴击率", + "暴击伤害", + "元素充能效率", + "治疗加成" + ], + "芭芭拉-核爆": [ + "生命值百分比", + "攻击力百分比", + "暴击率", + "暴击伤害", + "元素精通", + "元素充能效率", + "治疗加成" + ], + "雷泽": [ + "攻击力百分比", + "暴击率", + "暴击伤害", + "物理伤害加成" + ], + "香菱": [ + "攻击力百分比", + "暴击率", + "暴击伤害", + "元素精通", + "元素充能效率" + ], + "北斗": [ + "攻击力百分比", + "暴击率", + "暴击伤害", + "元素精通", + "元素充能效率" + ], + "行秋": [ + "攻击力百分比", + "暴击率", + "暴击伤害", + "元素充能效率" + ], + "凝光": [ + "攻击力百分比", + "暴击率", + "暴击伤害", + "元素充能效率" + ], + "菲谢尔": [ + "攻击力百分比", + "暴击率", + "暴击伤害", + "元素精通", + "物理伤害加成" + ], + "班尼特": [ + "生命值百分比", + "攻击力百分比", + "暴击率", + "暴击伤害", + "元素充能效率", + "治疗加成" + ], + "诺艾尔": [ + "攻击力百分比", + "暴击率", + "暴击伤害", + "元素充能效率" + ], + "重云": [ + "攻击力百分比", + "暴击率", + "暴击伤害", + "元素精通", + "元素充能效率" + ], + "砂糖": [ + "攻击力百分比", + "暴击率", + "暴击伤害", + "元素精通", + "元素充能效率" + ], + "琴": [ + "攻击力百分比", + "暴击率", + "暴击伤害", + "物理伤害加成", + "元素充能效率", + "治疗加成" + ], + "迪卢克": [ + "攻击力百分比", + "暴击率", + "暴击伤害", + "元素精通" + ], + "七七": [ + "攻击力百分比", + "暴击率", + "暴击伤害", + "物理伤害加成", + "元素充能效率", + "治疗加成" + ], + "莫娜": [ + "攻击力百分比", + "暴击率", + "暴击伤害", + "元素精通", + "元素充能效率" + ], + "刻晴": [ + "攻击力百分比", + "暴击率", + "暴击伤害", + "元素精通", + "物理伤害加成" + ], + "温迪": [ + "攻击力百分比", + "暴击率", + "暴击伤害", + "元素精通", + "元素充能效率" + ], + "可莉": [ + "攻击力百分比", + "暴击率", + "暴击伤害", + "元素精通", + "元素充能效率" + ], + "迪奥娜": [ + "生命值百分比", + "攻击力百分比", + "暴击率", + "暴击伤害", + "元素充能效率", + "治疗加成" + ], + "达达利亚": [ + "攻击力百分比", + "暴击率", + "暴击伤害", + "元素精通", + "元素充能效率" + ], + "辛焱": [ + "攻击力百分比", + "暴击率", + "暴击伤害", + "物理伤害加成" + ], + "钟离": [ + "生命值百分比", + "攻击力百分比", + "暴击率", + "暴击伤害", + "物理伤害加成", + "元素充能效率" + ], + "钟离-安如磐石": [ + "生命值百分比", + "攻击力百分比", + "暴击率", + "暴击伤害", + "元素充能效率" + ], + "阿贝多": [ + "攻击力百分比", + "暴击率", + "暴击伤害" + ], + "甘雨": [ + "攻击力百分比", + "暴击率", + "暴击伤害", + "元素精通" + ], + "甘雨-永冻": [ + "攻击力百分比", + "暴击率", + "暴击伤害", + "元素充能效率" + ], + "魈": [ + "攻击力百分比", + "暴击率", + "暴击伤害", + "元素充能效率" + ], + "胡桃": [ + "生命值百分比", + "攻击力百分比", + "暴击率", + "暴击伤害", + "元素精通" + ], + "罗莎莉亚": [ + "攻击力百分比", + "暴击率", + "暴击伤害", + "物理伤害加成", + "元素充能效率" + ], + "烟绯": [ + "攻击力百分比", + "暴击率", + "暴击伤害", + "元素精通", + "元素充能效率" + ], + "优菈": [ + "攻击力百分比", + "暴击率", + "暴击伤害", + "物理伤害加成", + "元素充能效率" + ], + "枫原万叶": [ + "攻击力百分比", + "暴击率", + "暴击伤害", + "元素精通", + "元素充能效率" + ], + "神里绫华": [ + "攻击力百分比", + "暴击率", + "暴击伤害", + "元素充能效率" + ], + "早柚": [ + "攻击力百分比", + "暴击率", + "暴击伤害", + "元素精通", + "元素充能效率", + "治疗加成" + ], + "宵宫": [ + "攻击力百分比", + "暴击率", + "暴击伤害", + "元素精通" + ], + "埃洛伊": [ + "攻击力百分比", + "暴击率", + "暴击伤害" + ], + "九条裟罗": [ + "攻击力百分比", + "暴击率", + "暴击伤害", + "元素充能效率" + ], + "雷电将军": [ + "攻击力百分比", + "暴击率", + "暴击伤害", + "元素充能效率" + ], + "珊瑚宫心海": [ + "生命值百分比", + "攻击力百分比", + "元素充能效率", + "治疗加成" + ], + "托马": [ + "生命值百分比", + "攻击力百分比", + "暴击率", + "暴击伤害", + "元素充能效率" + ], + "五郎": [ + "攻击力百分比", + "暴击率", + "暴击伤害", + "元素充能效率" + ], + "荒泷一斗": [ + "攻击力百分比", + "暴击率", + "暴击伤害", + "元素充能效率" + ], + "云堇": [ + "攻击力百分比", + "暴击率", + "暴击伤害", + "元素充能效率" + ], + "申鹤": [ + "攻击力百分比", + "暴击率", + "暴击伤害", + "元素充能效率" + ], + "八重神子": [ + "攻击力百分比", + "暴击率", + "暴击伤害", + "元素精通", + "元素充能效率" + ], + "神里绫人": [ + "生命值百分比", + "攻击力百分比", + "暴击率", + "暴击伤害", + "元素充能效率" + ], + "夜兰": [ + "生命值百分比", + "暴击率", + "暴击伤害", + "元素充能效率" + ], + "久岐忍": [ + "生命值百分比", + "攻击力百分比", + "暴击率", + "暴击伤害", + "元素精通", + "元素充能效率", + "治疗加成" + ], + "鹿野院平藏": [ + "攻击力百分比", + "暴击率", + "暴击伤害", + "元素精通", + "元素充能效率" + ], + "提纳里": [ + "攻击力百分比", + "暴击率", + "暴击伤害", + "元素精通", + "元素充能效率" + ], + "柯莱": [ + "攻击力百分比", + "暴击率", + "暴击伤害", + "元素精通", + "元素充能效率" + ] +} \ No newline at end of file diff --git a/plugins/genshin/player_cards.py b/plugins/genshin/player_cards.py new file mode 100644 index 0000000..5a483ba --- /dev/null +++ b/plugins/genshin/player_cards.py @@ -0,0 +1,308 @@ +from typing import Union, List, Any, Tuple + +from enkanetwork import ( + EnkaNetworkAPI, + Equipments, + EquipmentsType, + EquipmentsStats, + Stats, + CharacterInfo, + Assets, + DigitType, EnkaServerError, Forbidden, UIDNotFounded, VaildateUIDError, HTTPException, StatsPercentage, ) +from pydantic import BaseModel +from telegram import Update +from telegram.constants import ChatAction +from telegram.ext import CommandHandler, filters, CallbackContext, MessageHandler + +from core.baseplugin import BasePlugin +from core.plugin import Plugin, handler +from core.template import TemplateService +from core.user import UserService +from core.user.error import UserNotFoundError +from modules.playercards.helpers import ArtifactStatsTheory +from utils.bot import get_all_args +from utils.decorators.error import error_callable +from utils.decorators.restricts import restricts +from utils.helpers import url_to_file +from utils.log import logger +from utils.models.base import RegionEnum + +assets = Assets(lang="chs") + + +class PlayerCards(Plugin, BasePlugin): + def __init__(self, user_service: UserService = None, template_service: TemplateService = None): + self.user_service = user_service + self.client = EnkaNetworkAPI(lang="chs") + self.template_service = template_service + + @handler(CommandHandler, command="player_card", block=False) + @handler(MessageHandler, filters=filters.Regex("^角色卡片查询(.*)"), block=False) + @restricts() + @error_callable + async def player_cards(self, update: Update, context: CallbackContext) -> None: + user = update.effective_user + message = update.effective_message + args = get_all_args(context) + await message.reply_chat_action(ChatAction.TYPING) + try: + user_info = await self.user_service.get_user_by_id(user.id) + if user_info.region == RegionEnum.HYPERION: + uid = user_info.yuanshen_uid + else: + uid = user_info.genshin_uid + except UserNotFoundError: + reply_message = await message.reply_text("未查询到账号信息,请先私聊派蒙绑定账号") + if filters.ChatType.GROUPS.filter(message): + self._add_delete_message_job( + context, reply_message.chat_id, reply_message.message_id, 30 + ) + self._add_delete_message_job( + context, message.chat_id, message.message_id, 30 + ) + return + if len(args) == 1: + character_name = args[0] + else: + reply_message = await message.reply_text("请回复角色名参数") + if filters.ChatType.GROUPS.filter(reply_message): + self._add_delete_message_job(context, message.chat_id, message.message_id) + self._add_delete_message_job(context, reply_message.chat_id, reply_message.message_id) + return + logger.info(f"用户 {user.full_name}[{user.id}] 角色卡片查询命令请求 || character_name[{character_name}] uid[{uid}]") + try: + data = await self.client.fetch_user(uid) + except EnkaServerError: + await message.reply_text("Enka.Network 服务请求错误,请稍后重试") + return + except Forbidden: + await message.reply_text("Enka.Network 服务请求被拒绝,请稍后重试") + return + except HTTPException: + await message.reply_text("Enka.Network HTTP 服务请求错误,请稍后重试") + return + except UIDNotFounded: + await message.reply_text("UID 未找到") + return + except VaildateUIDError: + await message.reply_text("UID 错误或者非法") + return + if len(data.characters) == 0: + await message.reply_text("请先将角色加入到角色展柜并允许查看角色详情") + return + for characters in data.characters: + if characters.name == character_name: + break + else: + await message.reply_text(f"角色展柜中未找到 {character_name}") + return + await message.reply_chat_action(ChatAction.UPLOAD_PHOTO) + pnd_data = await RenderTemplate(uid, characters, self.template_service).render() + await message.reply_photo(pnd_data, filename=f"player_card_{uid}_{character_name}.png") + + +class Artifact(BaseModel): + """在 enka Equipments model 基础上扩展了圣遗物评分数据""" + + equipment: Equipments + # 圣遗物评分 + score: float = 0 + # 圣遗物评级 + score_label: str = "E" + # 圣遗物评级颜色 + score_class: str = "" + # 圣遗物单行属性评分 + substat_scores: List[float] + + def __init__(self, **kwargs): + super().__init__(**kwargs) + for substat_scores in self.substat_scores: + self.score += substat_scores + self.score = round(self.score, 1) + + for r in (("D", 10), + ("C", 16.5), + ("B", 23.1), + ("A", 29.7), + ("S", 36.3), + ("SS", 42.9), + ("SSS", 49.5), + ("ACE", 56.1), + ("ACE²", 66)): + if self.score >= r[1]: + self.score_label = r[0] + self.score_class = self.get_score_class(r[0]) + + @staticmethod + def get_score_class(label: str) -> str: + mapping = { + "D": "text-neutral-400", + "C": "text-neutral-200", + "B": "text-neutral-200", + "A": "text-violet-400", + "S": "text-violet-400", + "SS": "text-yellow-400", + "SSS": "text-yellow-400", + "ACE": "text-red-500", + "ACE²": "text-red-500", + } + return mapping.get(label, "text-neutral-400") + + +class RenderTemplate: + def __init__(self, uid: Union[int, str], character: CharacterInfo, template_service: TemplateService = None): + self.uid = uid + self.template_service = template_service + # 因为需要替换线上 enka 图片地址为本地地址,先克隆数据,避免修改原数据 + self.character = character.copy(deep=True) + + async def render(self): + # 缓存所有图片到本地 + await self.cache_images() + + artifacts = self.find_artifacts() + artifact_total_score: float = sum(artifact.score for artifact in artifacts) + + artifact_total_score = round(artifact_total_score, 1) + + artifact_total_score_label: str = "E" + for r in (("D", 10), + ("C", 16.5), + ("B", 23.1), + ("A", 29.7), + ("S", 36.3), + ("SS", 42.9), + ("SSS", 49.5), + ("ACE", 56.1), + ("ACE²", 66)): + if artifact_total_score / 5 >= r[1]: + artifact_total_score_label = r[0] + + data = { + "uid": self.uid, + "character": self.character, + "stats": await self.de_stats(), + "weapon": self.find_weapon(), + # 圣遗物评分 + "artifact_total_score": artifact_total_score, + # 圣遗物评级 + "artifact_total_score_label": artifact_total_score_label, + # 圣遗物评级颜色 + "artifact_total_score_class": Artifact.get_score_class(artifact_total_score_label), + "artifacts": artifacts, + + # 需要在模板中使用的 enum 类型 + "DigitType": DigitType, + } + + # html = await self.template_service.render_async( + # "genshin/player_card", "player_card.html", data + # ) + # logger.debug(html) + + return await self.template_service.render( + "genshin/player_card", + "player_card.html", + data, + {"width": 950, "height": 1080}, + full_page=True, + ) + + async def de_stats(self) -> List[Tuple[str, Any]]: + stats = self.character.stats + items: List[Tuple[str, Any]] = [] + logger.debug(self.character.stats) + + # items.append(("基础生命值", stats.BASE_HP.to_rounded())) + items.append(("生命值", stats.FIGHT_PROP_MAX_HP.to_rounded())) + # items.append(("基础攻击力", stats.FIGHT_PROP_BASE_ATTACK.to_rounded())) + items.append(("攻击力", stats.FIGHT_PROP_CUR_ATTACK.to_rounded())) + # items.append(("基础防御力", stats.FIGHT_PROP_BASE_DEFENSE.to_rounded())) + items.append(("防御力", stats.FIGHT_PROP_CUR_DEFENSE.to_rounded())) + items.append(("暴击率", stats.FIGHT_PROP_CRITICAL.to_percentage_symbol())) + items.append( + ( + "暴击伤害", + stats.FIGHT_PROP_CRITICAL_HURT.to_percentage_symbol(), + ) + ) + items.append( + ( + "元素充能效率", + stats.FIGHT_PROP_CHARGE_EFFICIENCY.to_percentage_symbol(), + ) + ) + items.append(("元素精通", stats.FIGHT_PROP_ELEMENT_MASTERY.to_rounded())) + + # 查找元素伤害加成和治疗加成 + max_stat = StatsPercentage() # 用于记录最高元素伤害加成 避免武器特效影响 + for stat in stats: + if 40 <= stat[1].id <= 46: # 元素伤害加成 + if max_stat.value <= stat[1].value: + max_stat = stat[1] + elif stat[1].id == 29: # 物理伤害加成 + pass + elif stat[1].id != 26: # 治疗加成 + continue + value = ( + stat[1].to_rounded() + if isinstance(stat[1], Stats) + else stat[1].to_percentage_symbol() + ) + if value in ("0%", 0): + continue + name = assets.get_hash_map(stat[0]) + if name is None: + continue + items.append((name, value)) + + if max_stat.id != 0: + for item in items: + if "元素伤害加成" in item[0]: + if max_stat.to_percentage_symbol() != item[1]: + items.remove(item) + + return items + + async def cache_images(self) -> None: + """缓存所有图片到本地""" + # TODO: 并发下载所有资源 + c = self.character + # 角色 + c.image.banner.url = await url_to_file(c.image.banner.url) + + # 技能 + for item in c.skills: + item.icon.url = await url_to_file(item.icon.url) + + # 命座 + for item in c.constellations: + item.icon.url = await url_to_file(item.icon.url) + + # 装备,包括圣遗物和武器 + for item in c.equipments: + item.detail.icon.url = await url_to_file(item.detail.icon.url) + + def find_weapon(self) -> Union[Equipments, None]: + """在 equipments 数组中找到武器,equipments 数组包含圣遗物和武器""" + for item in self.character.equipments: + if item.type == EquipmentsType.WEAPON: + return item + + def find_artifacts(self) -> List[Artifact]: + """在 equipments 数组中找到圣遗物,并转换成带有分数的 model。equipments 数组包含圣遗物和武器""" + + stats = ArtifactStatsTheory(self.character.name) + + def substat_score(s: EquipmentsStats) -> float: + return stats.theory(s) + + return [ + Artifact( + equipment=e, + # 圣遗物单行属性评分 + substat_scores=[substat_score(s) for s in e.detail.substats], + ) + for e in self.character.equipments + if e.type == EquipmentsType.ARTIFACT + ] diff --git a/resources/genshin/player_card/artifacts.html b/resources/genshin/player_card/artifacts.html new file mode 100644 index 0000000..ca6bc9e --- /dev/null +++ b/resources/genshin/player_card/artifacts.html @@ -0,0 +1,62 @@ +{% for item in artifacts %} +