import random import ujson as json from lxml import etree from PIL import ImageDraw from zhenxun.services.log import logger from zhenxun.utils.image_utils import BuildImage from ..config import draw_config from ..util import cn2py, load_font, remove_prohibited_str from .base_handle import BaseData, BaseHandle class FgoData(BaseData): pass class FgoChar(FgoData): pass class FgoCard(FgoData): pass class FgoHandle(BaseHandle[FgoData]): def __init__(self): super().__init__("fgo", "命运-冠位指定") self.data_files.append("fgo_card.json") self.max_star = 5 self.config = draw_config.fgo self.ALL_CHAR: list[FgoChar] = [] self.ALL_CARD: list[FgoCard] = [] def get_card(self, mode: int = 1) -> FgoData: if mode == 1: star = self.get_star( [8, 7, 6, 5, 4, 3], [ self.config.FGO_SERVANT_FIVE_P, self.config.FGO_SERVANT_FOUR_P, self.config.FGO_SERVANT_THREE_P, self.config.FGO_CARD_FIVE_P, self.config.FGO_CARD_FOUR_P, self.config.FGO_CARD_THREE_P, ], ) elif mode == 2: star = self.get_star( [5, 4], [self.config.FGO_CARD_FIVE_P, self.config.FGO_CARD_FOUR_P] ) else: star = self.get_star( [8, 7, 6], [ self.config.FGO_SERVANT_FIVE_P, self.config.FGO_SERVANT_FOUR_P, self.config.FGO_SERVANT_THREE_P, ], ) if star > 5: star -= 3 chars = [x for x in self.ALL_CHAR if x.star == star and not x.limited] else: chars = [x for x in self.ALL_CARD if x.star == star and not x.limited] return random.choice(chars) def get_cards(self, count: int, **kwargs) -> list[tuple[FgoData, int]]: card_list = [] # 获取所有角色 servant_count = 0 # 保底计算 card_count = 0 # 保底计算 for i in range(count): servant_count += 1 card_count += 1 if card_count == 9: # 四星卡片保底 mode = 2 elif servant_count == 10: # 三星从者保底 mode = 3 else: # 普通抽 mode = 1 card = self.get_card(mode) if isinstance(card, FgoCard) and card.star > self.max_star - 2: card_count = 0 if isinstance(card, FgoChar): servant_count = 0 card_list.append((card, i + 1)) return card_list async def generate_card_img(self, card: FgoData) -> BuildImage: sep_w = 5 sep_t = 5 sep_b = 20 w = 128 h = 140 bg = BuildImage(w + sep_w * 2, h + sep_t + sep_b) img_path = str(self.img_path / f"{cn2py(card.name)}.png") img = BuildImage(w, h, background=img_path) await bg.paste(img, (sep_w, sep_t)) # 加名字 text = card.name[:6] + "..." if len(card.name) > 7 else card.name font = load_font(fontsize=16) text_w, text_h = BuildImage.get_text_size(text, font) draw = ImageDraw.Draw(bg.markImg) draw.text( (sep_w + (w - text_w) / 2, h + sep_t + (sep_b - text_h) / 2), text, font=font, fill="gray", ) return bg def _init_data(self): self.ALL_CHAR = [ FgoChar( name=value["名称"], star=int(value["星级"]), limited=( True if not ( "圣晶石召唤" in value["入手方式"] or "圣晶石召唤(Story卡池)" in value["入手方式"] ) else False ), ) for value in self.load_data().values() ] self.ALL_CARD = [ FgoCard(name=value["名称"], star=int(value["星级"]), limited=False) for value in self.load_data("fgo_card.json").values() ] async def _update_info(self): # TODO: fgo获取链接失效 fgo_info = {} for i in range(500): url = f"http://fgo.vgtime.com/servant/ajax?card=&wd=&ids=&sort=12777&o=desc&pn={i}" result = await self.get_url(url) if not result: logger.warning(f"更新 {self.game_name_cn} page {i} 出错") continue fgo_data = json.loads(result) if int(fgo_data["nums"]) <= 0: break for x in fgo_data["data"]: name = remove_prohibited_str(x["name"]) member_dict = { "id": x["id"], "card_id": x["charid"], "头像": x["icon"], "名称": remove_prohibited_str(x["name"]), "职阶": x["classes"], "星级": int(x["star"]), "hp": x["lvmax4hp"], "atk": x["lvmax4atk"], "card_quick": x["cardquick"], "card_arts": x["cardarts"], "card_buster": x["cardbuster"], "宝具": x["tprop"], } fgo_info[name] = member_dict # 更新额外信息 for key in fgo_info.keys(): url = f'http://fgo.vgtime.com/servant/{fgo_info[key]["id"]}' result = await self.get_url(url) if not result: fgo_info[key]["入手方式"] = ["圣晶石召唤"] logger.warning(f"{self.game_name_cn} 获取额外信息错误 {key}") continue try: dom = etree.HTML(result, etree.HTMLParser()) obtain = dom.xpath( "//table[contains(string(.),'入手方式')]/tr[8]/td[3]/text()" )[0] obtain = str(obtain).strip() if "限时活动免费获取 活动结束后无法获得" in obtain: obtain = ["活动获取"] elif "非限时UP无法获得" in obtain: obtain = ["限时召唤"] else: if "&" in obtain: obtain = obtain.split("&") else: obtain = obtain.split(" ") obtain = [s.strip() for s in obtain if s.strip()] fgo_info[key]["入手方式"] = obtain except IndexError: fgo_info[key]["入手方式"] = ["圣晶石召唤"] logger.warning(f"{self.game_name_cn} 获取额外信息错误 {key}") self.dump_data(fgo_info) logger.info(f"{self.game_name_cn} 更新成功") # fgo_card.json fgo_card_info = {} for i in range(500): url = f"http://fgo.vgtime.com/equipment/ajax?wd=&ids=&sort=12958&o=desc&pn={i}" result = await self.get_url(url) if not result: logger.warning(f"更新 {self.game_name_cn}卡牌 page {i} 出错") continue fgo_data = json.loads(result) if int(fgo_data["nums"]) <= 0: break for x in fgo_data["data"]: name = remove_prohibited_str(x["name"]) member_dict = { "id": x["id"], "card_id": x["equipid"], "头像": x["icon"], "名称": name, "星级": int(x["star"]), "hp": x["lvmax_hp"], "atk": x["lvmax_atk"], "skill_e": str(x["skill_e"]).split("
")[:-1], } fgo_card_info[name] = member_dict self.dump_data(fgo_card_info, "fgo_card.json") logger.info(f"{self.game_name_cn} 卡牌更新成功") # 下载头像 for value in fgo_info.values(): await self.download_img(value["头像"], value["名称"]) for value in fgo_card_info.values(): await self.download_img(value["头像"], value["名称"])