diff --git a/model/avatar/_avatar.py b/model/avatar/_avatar.py index dac47f7..b620269 100644 --- a/model/avatar/_avatar.py +++ b/model/avatar/_avatar.py @@ -1,5 +1,5 @@ from model.avatar._talente import AvatarTalents -from model.enums import Association, AvatarQuality, Element, WeaponType +from model.enums import Association, AvatarQuality, Element, PropType, WeaponType from model.other import ItemCount from utils.model import BaseModel @@ -13,6 +13,7 @@ __all__ = ( "Story", "Seuyu", "AvatarAttribute", + "AddProp", ) @@ -98,11 +99,22 @@ class AvatarInfo(BaseModel): """故事""" +class AddProp(BaseModel): + """属性加成""" + + type: PropType + """属性类型""" + value: float + """属性值""" + + class AvatarPromote(BaseModel): promote_level: int = 0 """突破等级""" max_level: int """解锁的等级上限""" + add_props: list[AddProp] = [] + """属性加成""" coin: int = 0 """摩拉""" @@ -134,28 +146,10 @@ class AvatarAttribute(BaseModel): """防御力""" Critical: float """暴击率""" - CriticalDamage: float + CriticalHurt: float """暴击伤害""" ChargeEfficiency: float """元素充能效率""" - ElementalMastery: float = 0 - """元素精通""" - PhysicalAddHurt: float = 0 - """物理伤害加成""" - PyroAddHurt: float = 0 - """火元素伤害加成""" - HydroAddHurt: float = 0 - """水元素伤害加成""" - AnemoAddHurt: float = 0 - """风元素伤害加成""" - ElectroAddHurt: float = 0 - """雷元素伤害加成""" - DendroAddHurt: float = 0 - """草元素伤害加成""" - CryoAddHurt: float = 0 - """冰元素伤害加成""" - GeoAddHurt: float = 0 - """岩元素伤害加成""" class Avatar(BaseModel): diff --git a/model/avatar/_talente.py b/model/avatar/_talente.py index 21f85bf..d9b5276 100644 --- a/model/avatar/_talente.py +++ b/model/avatar/_talente.py @@ -21,22 +21,6 @@ __all__ = ( ) -class TalentAttribute(BaseModel): - level: int = 1 - """等级""" - param_descriptions: list[str] = [] - """参数描述""" - param_list: list[float] = [] - """参数列表""" - - break_level: int = 0 - """所需突破等级""" - coin: int = 0 - """摩拉""" - cost_items: list[ItemCount] = [] - """消耗物品""" - - class Talent(BaseModel): name: str """天赋名称""" @@ -48,14 +32,29 @@ class Talent(BaseModel): """解锁等级""" +class TalentAttribute(BaseModel): + level: int = 1 + """等级""" + params: list[float] = [] + """参数数值""" + + break_level: int = 0 + """所需突破等级""" + coin: int = 0 + """摩拉""" + cost_items: list[ItemCount] = [] + """消耗物品""" + + class CombatTalent(Talent): """战斗天赋""" cooldown: float = 0 """冷却时间""" - + param_descriptions: list[str] = [] + """参数描述""" attributes: list[TalentAttribute] - """数值参数列表""" + """数值参数""" class NormalAttack(CombatTalent): diff --git a/scripts/avatar.py b/scripts/avatar.py index c4a21dc..5bf6706 100644 --- a/scripts/avatar.py +++ b/scripts/avatar.py @@ -3,8 +3,10 @@ from typing import TypeVar import ujson as json from aiofiles import open as async_open +from humps import pascalize from model.avatar import ( + AddProp, AlternateSprint, Avatar, AvatarAttribute, @@ -28,7 +30,7 @@ from model.avatar import ( TalentAttribute, UtilityPassive, ) -from model.enums import Association, AvatarQuality, Element, WeaponType +from model.enums import Association, AvatarQuality, Element, PropType, WeaponType from model.other import ItemCount from utils.const import PROJECT_ROOT from utils.funcs import remove_rich_tag @@ -117,9 +119,21 @@ elements_map = { ): Element.Geo, (471154292, 821712868, 1128382182, 3053155130, 4168416172): Element.Null, } +prop_type_map = { + "Hp": PropType.HP, + "RockAddHurt": PropType.Geo, + "ElecAddHurt": PropType.Electro, + "FireAddHurt": PropType.Pyro, + "WaterAddHurt": PropType.Hydro, + "IceAddHurt": PropType.Cryo, + "WindAddHurt": PropType.Anemo, + "GrassAddHurt": PropType.Dendro, +} -def get_skill_attributes(proud_skill_group_id: int) -> list[TalentAttribute]: +def get_skill_attributes( + proud_skill_group_id: int, +) -> tuple[list[TalentAttribute], list[str]]: proud_skill_datas = sorted( filter( lambda x: x["proudSkillGroupId"] == proud_skill_group_id, @@ -128,6 +142,7 @@ def get_skill_attributes(proud_skill_group_id: int) -> list[TalentAttribute]: key=lambda x: x["level"], ) result: list[TalentAttribute] = [] + param_descriptions: list[str] = [] for proud_skill_data in proud_skill_datas: param_descriptions = list( filter( @@ -147,8 +162,7 @@ def get_skill_attributes(proud_skill_group_id: int) -> list[TalentAttribute]: result.append( TalentAttribute( level=proud_skill_data["level"], - param_descriptions=param_descriptions, - param_list=proud_skill_data["paramList"][:param_num], + params=proud_skill_data["paramList"][:param_num], break_level=proud_skill_data.get("breakLevel", 0), coin=proud_skill_data.get("coinCost", 0), cost_items=list( @@ -159,7 +173,7 @@ def get_skill_attributes(proud_skill_group_id: int) -> list[TalentAttribute]: ), ) ) - return result + return result, param_descriptions def parse_skill(skill_id: int, skill_cls: type[CombatTalent]) -> CombatTalent: @@ -170,13 +184,22 @@ def parse_skill(skill_id: int, skill_cls: type[CombatTalent]) -> CombatTalent: cooldown = ( skill_data.get("cdTime", 0) if "cooldown" in skill_cls.__fields__ else None ) - attributes = get_skill_attributes(skill_data["proudSkillGroupId"]) + attributes, param_descriptions = get_skill_attributes( + skill_data["proudSkillGroupId"] + ) return skill_cls( **{ i[0]: i[1] for i in zip( - ["name", "description", "icon", "cooldown", "attributes"], - [_name, _description, icon, cooldown, attributes], + [ + "name", + "description", + "icon", + "cooldown", + "attributes", + "param_descriptions", + ], + [_name, _description, icon, cooldown, attributes, param_descriptions], ) if i is not None } @@ -209,9 +232,9 @@ def parse_passive_talent( description=_description or "", icon=skill_data["icon"], promote_level=_promote_level, + param_descriptions=param_descriptions, attribute=TalentAttribute( - param_descriptions=param_descriptions, - param_list=_param_list, + params=_param_list, break_level=skill_data.get("breakLevel", 0), ), ) @@ -331,7 +354,7 @@ async def parse_avatar_data(lang: Lang): Attack=data["attackBase"], Defense=data["defenseBase"], Critical=data["critical"], - CriticalDamage=data["criticalHurt"], + CriticalHurt=data["criticalHurt"], ChargeEfficiency=data["chargeEfficiency"], ) @@ -342,47 +365,32 @@ async def parse_avatar_data(lang: Lang): skill_ids = list(filter(lambda x: x != 0, skill_depot_data["skills"])) # 普通攻击 normal_attack = parse_skill(skill_ids[0], NormalAttack) - # 元素战技 - elemental_skill = ( - parse_skill(skill_ids[1], ElementalSkill) - if id not in [10000005, 10000007] - else None - ) # 冲刺技能 alternate_sprint = ( parse_skill(skill_ids[2], AlternateSprint) if len(skill_ids) == 3 else None ) - # 元素爆发 - burst_skill = ( - parse_skill(skill_depot_data["energySkill"], ElementalBurst) - if id not in [10000005, 10000007] - else None - ) - # 第一次突破被动天赋 - first_passive = ( - parse_passive_talent( + if id not in [10000005, 10000007]: + # 元素战技 + elemental_skill = parse_skill(skill_ids[1], ElementalSkill) + # 元素爆发 + burst_skill = parse_skill(skill_depot_data["energySkill"], ElementalBurst) + # 第一次突破被动天赋 + first_passive = parse_passive_talent( skill_depot_data["inherentProudSkillOpens"][0], FirstAscensionPassive ) - if id not in [10000005, 10000007] - else None - ) - # 第四次突破被动天赋 - fourth_passive = ( - parse_passive_talent( + # 第四次突破被动天赋 + fourth_passive = parse_passive_talent( skill_depot_data["inherentProudSkillOpens"][1], FourthAscensionPassive ) - if id not in [10000005, 10000007] - else None - ) - # 实用固有天赋 - utility_passive = ( - parse_passive_talent( + # 实用固有天赋 + utility_passive = parse_passive_talent( skill_depot_data["inherentProudSkillOpens"][2], UtilityPassive ) - if id not in [10000005, 10000007] - else None - ) - # 杂项固有天赋 + else: + elemental_skill = ( + burst_skill + ) = first_passive = fourth_passive = utility_passive = None + # 杂项固有天赋 if skill_depot_data["inherentProudSkillOpens"][3]: miscellaneous_passive = parse_passive_talent( skill_depot_data["inherentProudSkillOpens"][3], MiscellaneousPassive @@ -415,10 +423,33 @@ async def parse_avatar_data(lang: Lang): items.append( ItemCount(item_id=item_data["id"], count=item_data["count"]) ) + add_props = [] + for add_prop_data in promote_data["addProps"]: + _string = pascalize( + add_prop_data["propType"] + .removeprefix("FIGHT_PROP_") + .removeprefix("BASE_") + .lower() + ).replace("Hp", "HP") + prop_type_string = { + **prop_type_map, + **PropType.__members__, + **{v: v for k, v in PropType.__members__.items()}, + }[_string] + prop_type = PropType(prop_type_string) + add_props.append( + AddProp( + type=prop_type, + value=add_prop_data.get( + "value", attributes.dict().get(prop_type, 0) + ), + ) + ) promotes.append( AvatarPromote( promote_level=promote_data.get("promoteLevel", 0), max_level=promote_data["unlockMaxLevel"], + add_props=add_props, coin=promote_data.get("scoinCost", 0), cost_items=items, ) @@ -457,10 +488,11 @@ async def parse_avatar_data(lang: Lang): constellations=constellations, ) avatar_list.append(avatar) + async with async_open(out_path / "avatar.json", encoding="utf-8", mode="w") as file: await file.write( json.dumps( - [i.dict() for i in avatar_list], + [i.dict(exclude_none=True) for i in avatar_list], ensure_ascii=False, encode_html_chars=False, indent=4, diff --git a/scripts/item.py b/scripts/item.py index b90c877..a4b3133 100644 --- a/scripts/item.py +++ b/scripts/item.py @@ -77,7 +77,7 @@ async def parse_item_data( async with async_open(out_path / "item.json", encoding="utf-8", mode="w") as file: await file.write( json.dumps( - [i.dict() for i in item_list], + [i.dict(exclude_none=True) for i in item_list], ensure_ascii=False, encode_html_chars=False, indent=4, diff --git a/utils/funcs.py b/utils/funcs.py index b5a40bf..4c62ed7 100644 --- a/utils/funcs.py +++ b/utils/funcs.py @@ -5,10 +5,10 @@ except ImportError: __all__ = ("remove_rich_tag",) +reich_pattern = r"(<(?P[a-z]+?)=(?P.+?)>.+?)" + def remove_rich_tag(string: str | None) -> str: """去除富文本标签""" if string is not None: - return re.sub( - r"(<(?P[a-z]+?)=(?P.+?)>.+?)", "", string - ) + return re.sub(reich_pattern, "", string)