1
This commit was merged in pull request #1.
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
from collections.abc import Mapping
|
||||
import sys
|
||||
from typing import Any, Literal
|
||||
|
||||
from core.abstract.character import CharacterProperty
|
||||
@@ -15,8 +14,11 @@ class CharacterBase(CharacterProperty):
|
||||
|
||||
BindAwake: int
|
||||
"""绑定觉醒等级"""
|
||||
skills: list['Skill'] = []
|
||||
skills_dict: dict[str, 'Skill'] = {}
|
||||
skills: list['Skill']
|
||||
skills_dict: dict[str, 'Skill']
|
||||
skills_id_dict: dict[str, 'Skill']
|
||||
skills_uuid_dict: dict[str, 'Skill']
|
||||
skill_names_cache: dict[tuple[str, bool | str], list[str]]
|
||||
equVersion: str = '0'
|
||||
"""装备版本"""
|
||||
charEquipInfo: dict[str, 'CharacterEquipInfo']
|
||||
@@ -33,9 +35,9 @@ class CharacterBase(CharacterProperty):
|
||||
"""誓约套装信息"""
|
||||
oathSkillInfo: list['OathSuitSkill']
|
||||
"""誓约技能信息"""
|
||||
equ_effect: list['EquEffect'] = []
|
||||
equ_effect: list['EquEffect']
|
||||
"""装备效果列表"""
|
||||
equ_options: dict[str, int] = {}
|
||||
equ_options: dict[str, int]
|
||||
"""装备选项列表"""
|
||||
EquEffectRatio = 1.0
|
||||
"""装备效果倍率"""
|
||||
@@ -148,6 +150,9 @@ class CharacterBase(CharacterProperty):
|
||||
self.max_point = 0
|
||||
self.skills = []
|
||||
self.skills_dict = {}
|
||||
self.skills_id_dict = {}
|
||||
self.skills_uuid_dict = {}
|
||||
self.skill_names_cache = {}
|
||||
self.load_skills()
|
||||
|
||||
# region 角色属性设置
|
||||
@@ -285,7 +290,7 @@ class CharacterBase(CharacterProperty):
|
||||
value = int(value * (1 + self.jade_effect.ElementIncrease))
|
||||
self.ElementDB[element] += value
|
||||
|
||||
def AddSkillLv(self, min: int, max: int, lv: int, type=-1, exceptSkills: list[str] = []) -> None:
|
||||
def AddSkillLv(self, min: int, max: int, lv: int, type=-1, exceptSkills: tuple[str, ...] = ()) -> None:
|
||||
"""
|
||||
增加技能等级
|
||||
type: -1 全部, 0 被动, 1 主动
|
||||
@@ -296,13 +301,13 @@ class CharacterBase(CharacterProperty):
|
||||
if (skillType == 'all' or skill.type == skillType) and skill.lv > 0 and skill.name not in exceptSkills:
|
||||
skill.lv += lv
|
||||
|
||||
def SetSkillCD(self, min=1, max=100, cd=0, exclude=[50, 85, 100]) -> None:
|
||||
def SetSkillCD(self, min=1, max=100, cd=0, exclude: tuple[int, ...] = (50, 85, 100)) -> None:
|
||||
"""设置技能CD"""
|
||||
for skill in self.skills:
|
||||
if min <= skill.learnLv <= max and (skill.type == 'active' or skill.damage) and skill.learnLv not in exclude:
|
||||
skill.cdReduce *= 1 - cd
|
||||
|
||||
def SetSkillCDRecover(self, min=1, max=100, cd=0, exclude=[50, 85, 100]) -> None:
|
||||
def SetSkillCDRecover(self, min=1, max=100, cd=0, exclude: tuple[int, ...] = (50, 85, 100)) -> None:
|
||||
"""设置技能CD恢复"""
|
||||
for skill in self.skills:
|
||||
if min <= skill.learnLv <= max and (skill.type == 'active' or skill.damage) and skill.learnLv not in exclude:
|
||||
@@ -330,17 +335,18 @@ class CharacterBase(CharacterProperty):
|
||||
|
||||
def GetSkillByID(self, id: str) -> 'Skill':
|
||||
"""通过技能ID获取技能"""
|
||||
skill = list(filter(lambda x: str(x.id) == id, self.skills))
|
||||
return skill[0] if len(skill) > 0 else None
|
||||
return self.skills_id_dict.get(str(id), None)
|
||||
|
||||
def GetSkillByUUID(self, uuid: str) -> 'Skill':
|
||||
"""通过技能UUID获取技能"""
|
||||
skill = list(filter(lambda x: str(x.uuid) == uuid, self.skills))
|
||||
return skill[0] if len(skill) > 0 else None
|
||||
return self.skills_uuid_dict.get(str(uuid), None)
|
||||
|
||||
def GetSkillNames(self, type: Literal['active', 'passive', 'all'] = 'all', damage: Literal[True, False, 'all'] = 'all') -> list[str]:
|
||||
"""获取技能名字"""
|
||||
return [skill.name for skill in self.skills if (type == 'all' or skill.type == type) and (damage == 'all' or skill.damage == damage)]
|
||||
key = (type, damage)
|
||||
if key not in self.skill_names_cache:
|
||||
self.skill_names_cache[key] = [skill.name for skill in self.skills if (type == 'all' or skill.type == type) and (damage == 'all' or skill.damage == damage)]
|
||||
return self.skill_names_cache[key]
|
||||
|
||||
def GetWeaponType(self) -> tuple[str, str]:
|
||||
"""获取武器类型"""
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import sys
|
||||
from functools import cache
|
||||
from uuid import uuid1
|
||||
|
||||
from core.basic.equipment import OathSuit, Suit, get_equipment
|
||||
@@ -7,6 +8,16 @@ from ..roleinfo import CharacterEmblemInfo, CharacterEquipInfo, CharacterOathInf
|
||||
from .base import CharacterBase
|
||||
|
||||
|
||||
@cache
|
||||
def get_skill_classes(module_name: str) -> tuple[type, ...]:
|
||||
module = sys.modules[module_name]
|
||||
return tuple(
|
||||
memberClass
|
||||
for name in dir(module)
|
||||
if isinstance((memberClass := getattr(module, name)), type) and memberClass.__name__.startswith('Skill')
|
||||
)
|
||||
|
||||
|
||||
class CalcEquipMixin(CharacterBase):
|
||||
"""装备计算、角色初始化相关方法"""
|
||||
|
||||
@@ -22,11 +33,12 @@ class CalcEquipMixin(CharacterBase):
|
||||
info['emblems'] = {}
|
||||
if info.get('oaths', None) is None:
|
||||
info['oaths'] = {}
|
||||
equs = get_equipment(self.equVersion)
|
||||
for key in info['equips']:
|
||||
self.charEquipInfo[key] = CharacterEquipInfo(info['equips'][key], self.equVersion, key)
|
||||
self.charEquipInfo[key] = CharacterEquipInfo(info['equips'][key], self.equVersion, key, equs)
|
||||
for key in info['oaths']:
|
||||
# 导入誓约打造信息、誓约信息
|
||||
self.charOathInfo.append(CharacterOathInfo(info['oaths'][key], self.equVersion, key))
|
||||
self.charOathInfo.append(CharacterOathInfo(info['oaths'][key], self.equVersion, key, equs))
|
||||
try:
|
||||
# 异常处理 彩色等级自动取最小值
|
||||
minEmblemLv = min([info['emblems'][key]['lv'] for key in ['红色', '蓝色', '绿色', '黄色']], default=1)
|
||||
@@ -36,7 +48,7 @@ class CalcEquipMixin(CharacterBase):
|
||||
info['emblems'][key]['part'] = key
|
||||
self.charEmblemInfo.append(CharacterEmblemInfo(info['emblems'][key]))
|
||||
pass
|
||||
except Exception as e:
|
||||
except Exception:
|
||||
pass
|
||||
self.charOathSkillId = info.get('oathSkill', 1)
|
||||
|
||||
@@ -59,13 +71,12 @@ class CalcEquipMixin(CharacterBase):
|
||||
|
||||
def load_skills(self) -> None:
|
||||
"""加载技能"""
|
||||
module = sys.modules[self.moduleName]
|
||||
for name in dir(module):
|
||||
memberClass = getattr(module, name)
|
||||
if isinstance(memberClass, type) and memberClass.__name__.startswith('Skill'):
|
||||
skill = memberClass(char=self)
|
||||
self.skills_dict[skill.name] = skill
|
||||
self.skills.append(skill)
|
||||
for memberClass in get_skill_classes(self.moduleName):
|
||||
skill = memberClass(char=self)
|
||||
self.skills_dict[skill.name] = skill
|
||||
self.skills_id_dict[str(skill.id)] = skill
|
||||
self.skills_uuid_dict[str(skill.uuid)] = skill
|
||||
self.skills.append(skill)
|
||||
|
||||
# endregion
|
||||
|
||||
@@ -77,27 +88,27 @@ class CalcEquipMixin(CharacterBase):
|
||||
oathSuitInfo = {}
|
||||
correction = 0
|
||||
equs = get_equipment(self.equVersion)
|
||||
for item in filter(lambda x: x.equInfo is not None, self.charEquipInfo.values()):
|
||||
for item in self.charEquipInfo.values():
|
||||
partEqu = item.equInfo
|
||||
if partEqu is None:
|
||||
continue
|
||||
# partFusion = item.fusionInfo
|
||||
if partEqu is not None:
|
||||
for suit in partEqu.suit:
|
||||
if suitInfo.get(suit, None) is None:
|
||||
suitInfo[suit] = {'point': 0, 'count': 0}
|
||||
if partEqu.Point == 0:
|
||||
suitInfo[suit]['count'] += 1
|
||||
else:
|
||||
suitInfo[suit]['point'] += partEqu.Point
|
||||
for suit in partEqu.suit:
|
||||
suit_item = suitInfo.setdefault(suit, {'point': 0, 'count': 0})
|
||||
if partEqu.Point == 0:
|
||||
suit_item['count'] += 1
|
||||
else:
|
||||
suit_item['point'] += partEqu.Point
|
||||
for item in self.charOathInfo:
|
||||
partOath = item.oathInfo
|
||||
if partOath is not None:
|
||||
for suit in partOath.suit:
|
||||
if oathSuitInfo.get(suit, None) is None:
|
||||
oathSuitInfo[suit] = {'point': 0, 'count': 0}
|
||||
if partOath.Point == 0:
|
||||
oathSuitInfo[suit]['count'] += 1
|
||||
else:
|
||||
oathSuitInfo[suit]['point'] += partOath.Point
|
||||
if partOath is None:
|
||||
continue
|
||||
for suit in partOath.suit:
|
||||
oath_suit_item = oathSuitInfo.setdefault(suit, {'point': 0, 'count': 0})
|
||||
if partOath.Point == 0:
|
||||
oath_suit_item['count'] += 1
|
||||
else:
|
||||
oath_suit_item['point'] += partOath.Point
|
||||
max_point_suit = max((suit for suit in suitInfo if suitInfo[suit]['point'] > 0), key=lambda suit: suitInfo[suit]['point'], default=None)
|
||||
max_point_oath_suit = max((suit for suit in oathSuitInfo if oathSuitInfo[suit]['point'] > 0), key=lambda suit: oathSuitInfo[suit]['point'], default=None)
|
||||
zero_point_suits = [suit for suit in suitInfo if suitInfo[suit]['point'] == 0]
|
||||
@@ -140,17 +151,19 @@ class CalcEquipMixin(CharacterBase):
|
||||
oathSuitList += equs.get_oath_suit_info(suit, oathSuitInfo[suit]['point'], 0)
|
||||
if len(oathSuitList) == 0:
|
||||
continue
|
||||
oath_suit_by_suit_id = {item.suitId: item for item in oathSuitList}
|
||||
for suit in suitList:
|
||||
i = suit.id
|
||||
func = equs.funs.execture(f'suit_{i}')
|
||||
suit = next((x for x in equs.suits if str(x.id) == str(i)), None)
|
||||
suit = equs.suit_id_dict.get(str(i), None)
|
||||
if suit is None:
|
||||
continue
|
||||
# 誓约和套装进行匹配
|
||||
# 如果存在誓约和套装是同一个套装
|
||||
# 则直接不执行原套装效果,执行誓约套装增强的效果
|
||||
oathEffect = next((x for x in oathSuitList if x.suitId == suit.suitId), None)
|
||||
if suit is not None:
|
||||
filtered_dict = {k: v for k, v in suit.__dict__.items() if k[0].isupper()}
|
||||
self.SetStatus(**filtered_dict)
|
||||
oathEffect = oath_suit_by_suit_id.get(suit.suitId, None)
|
||||
filtered_dict = {k: v for k, v in suit.__dict__.items() if k[0].isupper()}
|
||||
self.SetStatus(**filtered_dict)
|
||||
if func is not None and oathEffect is None:
|
||||
# print(f'套装 {suit.name} 生效')
|
||||
func(self)
|
||||
@@ -160,15 +173,15 @@ class CalcEquipMixin(CharacterBase):
|
||||
self.max_point = 0 if res['suit'] is None else res.get('suit', {}).get('point', 0)
|
||||
try:
|
||||
self.oath_max_point = oathSuitInfo.get(max_point_oath_suit, {}).get('point', 0)
|
||||
except:
|
||||
except Exception:
|
||||
self.oath_max_point = 0
|
||||
self.oathSuitInfo = []
|
||||
self.oathSkillInfo = []
|
||||
# 当套装点数达到太初时,誓约技能生效
|
||||
if self.max_point >= 2550:
|
||||
oathSuit = next((x for x in oathSuitList if x.suitId == max_point_oath_suit), None)
|
||||
oathSuit = oath_suit_by_suit_id.get(max_point_oath_suit, None)
|
||||
if oathSuit is not None:
|
||||
skills = list(filter(lambda x: x.suitId == oathSuit.suitId, oathSuit.skills))
|
||||
skills = [x for x in oathSuit.skills if x.suitId == oathSuit.suitId]
|
||||
skill = next((x for x in oathSuit.skills if str(x.skillId) == str(self.charOathSkillId)), None)
|
||||
# 誓约套装效果生效
|
||||
filtered_dict = {k: v for k, v in oathSuit.__dict__.items() if k[0].isupper()}
|
||||
@@ -214,11 +227,11 @@ class CalcEquipMixin(CharacterBase):
|
||||
"""计算装备基础效果、附魔"""
|
||||
effects = get_equipment(self.equVersion).funs
|
||||
# for detail in [(item.equInfo, item.fusionInfo, item.enchant, item.emblem_0, item.emblem_1) for item in filter(lambda x: x.equInfo is not None, self.charEquipInfo.values())]:
|
||||
for detail in [(item.equInfo, item.enchant) for item in filter(lambda x: x.equInfo is not None, self.charEquipInfo.values())]:
|
||||
equ = detail[0]
|
||||
for item in self.charEquipInfo.values():
|
||||
equ = item.equInfo
|
||||
if equ is None or equ.itemType == '副武器':
|
||||
continue
|
||||
enchat = detail[1]
|
||||
enchat = item.enchant
|
||||
fun = effects.execture(f'equ_{equ.id}')
|
||||
if fun is None:
|
||||
continue
|
||||
@@ -316,20 +329,20 @@ class CalcEquipMixin(CharacterBase):
|
||||
|
||||
def calc_basic(self):
|
||||
"""计算基础属性:防具精通、增幅、强化等"""
|
||||
char_equip_info = self.charEquipInfo
|
||||
for part in ['上衣', '头肩', '下装', '腰带', '鞋']:
|
||||
cur = self.charEquipInfo[part]
|
||||
cur = char_equip_info[part]
|
||||
if cur.equInfo is None:
|
||||
continue
|
||||
equ = cur.equInfo
|
||||
reinforce = self.charEquipInfo[part].reinforce
|
||||
reinforce = cur.reinforce
|
||||
if equ is None:
|
||||
continue
|
||||
for key in self.防具精通属性:
|
||||
value = 精通计算(115, equ.rarity, reinforce, equ.itemDetailType, self.防具类型, self.buffer, key)
|
||||
key = get_key_by_value(key)
|
||||
setattr(self, key, getattr(self, key) + value)
|
||||
for part in self.charEquipInfo.keys():
|
||||
cur = self.charEquipInfo[part]
|
||||
for part, cur in char_equip_info.items():
|
||||
if cur.equInfo is None:
|
||||
continue
|
||||
if cur.reinforceType == 1:
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
import importlib
|
||||
from database.models import EmblemData, OathData, OathSuitData, OathSuitSkillData, PrimerData, Session, EquData, StoneData, SuitData, EnchantData, JadeData
|
||||
from collections.abc import Sequence
|
||||
from functools import cache
|
||||
from sqlalchemy.orm import Session
|
||||
from database.models import EmblemData, OathData, OathSuitData, OathSuitSkillData, PrimerData, EquData, SuitData, EnchantData, JadeData
|
||||
from database.connect import get_db_engine as get_engine
|
||||
|
||||
|
||||
def parse_to_number_list(info: str, default: list[float] = [0]) -> list[float]:
|
||||
return default if not info else [float(i) for i in info.split(',')]
|
||||
def parse_to_number_list(info: str, default: Sequence[float] = (0,)) -> list[float]:
|
||||
return list(default) if not info else [float(i) for i in info.split(',')]
|
||||
|
||||
|
||||
def parse_to_string_list(info: str, default: list[str] = ['']) -> list[str]:
|
||||
return default if not info else [i for i in info.split(',')]
|
||||
def parse_to_string_list(info: str, default: Sequence[str] = ('',)) -> list[str]:
|
||||
return list(default) if not info else [i for i in info.split(',')]
|
||||
|
||||
|
||||
def parse_to_int_list(info: str, default: list[int] = [0]) -> list[int]:
|
||||
return default if not info else [int(i) for i in info.split(',')]
|
||||
def parse_to_int_list(info: str, default: Sequence[int] = (0,)) -> list[int]:
|
||||
return list(default) if not info else [int(i) for i in info.split(',')]
|
||||
|
||||
|
||||
class Equ:
|
||||
@@ -89,6 +92,7 @@ class Equipments:
|
||||
self.emblems = []
|
||||
self.suits: list[Suit] = []
|
||||
self.suit_dict: dict[str, Suit] = {}
|
||||
self.suit_id_dict: dict[str, Suit] = {}
|
||||
self.oath_suits: list[OathSuit] = []
|
||||
self.oath_suit_dict: dict[str, OathSuit] = {}
|
||||
self.init_equs()
|
||||
@@ -99,7 +103,6 @@ class Equipments:
|
||||
self.init_jades()
|
||||
self.init_oaths()
|
||||
self.init_oath_suits()
|
||||
self.engine.dispose()
|
||||
|
||||
def init_equs(self):
|
||||
"""从数据库中获取所有装备信息"""
|
||||
@@ -166,6 +169,7 @@ class Equipments:
|
||||
suit_obj = Suit(**suit)
|
||||
self.suits.append(suit_obj)
|
||||
self.suit_dict.setdefault(str(suit['suitId']), []).append(suit_obj)
|
||||
self.suit_id_dict[str(suit_obj.id)] = suit_obj
|
||||
pass
|
||||
pass
|
||||
|
||||
@@ -279,6 +283,7 @@ class Equipments:
|
||||
funs = importlib.import_module('core.equipment.version_0')
|
||||
return funs
|
||||
|
||||
@cache
|
||||
def get_suit_info(self, suitId: str | int, point: int = 0, count: int = 0) -> list[Suit]:
|
||||
"""根据套装点数返回对应适用的套装属性\n
|
||||
新套装只需要传入suitID和point即可,count默认为0\n
|
||||
@@ -295,6 +300,7 @@ class Equipments:
|
||||
result[suit.count] = suit
|
||||
return list(result.values())
|
||||
|
||||
@cache
|
||||
def get_oath_suit_info(self, suitId: str | int, point: int = 0, count: int = 0) -> list['OathSuit']:
|
||||
"""根据套装点数返回对应适用的誓约套装属性\n
|
||||
新套装只需要传入suitID和point即可,count默认为0\n
|
||||
|
||||
@@ -39,7 +39,7 @@ class CharacterInfo(enum.Enum):
|
||||
AtkI = '独立攻击', '/ui/UI_ProfileIcon_008.png', lambda x: round(x, 0)
|
||||
"""独立攻击力"""
|
||||
|
||||
ElementDB = '攻击属性', '/ui/UI_ProfileIcon_009.png', lambda x: f'火({int(x['火'])})/冰({int(x['冰'])})/光({int(x['光'])})/暗({int(x['暗'])})'
|
||||
ElementDB = '攻击属性', '/ui/UI_ProfileIcon_009.png', lambda x: f"火({int(x['火'])})/冰({int(x['冰'])})/光({int(x['光'])})/暗({int(x['暗'])})"
|
||||
"""攻击属性"""
|
||||
|
||||
SkillAttack = '技能伤害', '/ui/UI_ProfileIcon_047.png', lambda x: f'{format(x * 100 - 100, ".1f")}%'
|
||||
|
||||
@@ -32,12 +32,15 @@ class CharacterEquipInfo:
|
||||
precision: float
|
||||
"""秘宝精度"""
|
||||
|
||||
def __init__(self, info={}, equVerison=0, part='') -> None:
|
||||
def __init__(self, info=None, equVerison=0, part='', equipments=None) -> None:
|
||||
if info is None:
|
||||
info = {}
|
||||
# info['fusion'] = info.get('fusion', None)
|
||||
info['precision'] = info.get('precision', 0)
|
||||
# info['weaponFusion'] = info.get('weaponFusion', None)
|
||||
self.__dict__.update(info)
|
||||
equ = get_equipment(equVerison).equ_dict.get(self.id, None)
|
||||
equipments = equipments or get_equipment(equVerison)
|
||||
equ = equipments.equ_dict.get(self.id, None)
|
||||
# fusion = get_equipment(equVerison).stone_dict.get(self.fusion, None)
|
||||
if equ is not None and (equ.itemType == part or equ.itemDetailType == part or (equ.itemType == '武器' and part == '副武器')):
|
||||
self.equInfo = equ.adapt(self.adaptation)
|
||||
@@ -60,10 +63,13 @@ class CharacterOathInfo:
|
||||
oathInfo: Union['Oath', None]
|
||||
"""誓约信息"""
|
||||
|
||||
def __init__(self, info={}, equVerison=0, position='') -> None:
|
||||
def __init__(self, info=None, equVerison=0, position='', equipments=None) -> None:
|
||||
if info is None:
|
||||
info = {}
|
||||
self.__dict__.update(info)
|
||||
self.id = str(self.id)
|
||||
oath = get_equipment(equVerison).oath_dict.get(self.id, None)
|
||||
equipments = equipments or get_equipment(equVerison)
|
||||
oath = equipments.oath_dict.get(self.id, None)
|
||||
if oath is not None and position in oath.position:
|
||||
self.oathInfo = oath.adapt(self.adaptation)
|
||||
self.adaptation = min(self.adaptation, self.oathInfo.max_adaptation)
|
||||
@@ -79,7 +85,9 @@ class CharacterEmblemInfo:
|
||||
lv: int
|
||||
"""星徽等级"""
|
||||
|
||||
def __init__(self, info={}) -> None:
|
||||
def __init__(self, info=None) -> None:
|
||||
if info is None:
|
||||
info = {}
|
||||
self.__dict__.update(info)
|
||||
|
||||
|
||||
@@ -89,7 +97,9 @@ class CharacterWeaponInfo(CharacterEquipInfo):
|
||||
upgradeDetail: dict
|
||||
"""贴膜详情"""
|
||||
|
||||
def __init__(self, info={}) -> None:
|
||||
def __init__(self, info=None) -> None:
|
||||
if info is None:
|
||||
info = {}
|
||||
self.__dict__.update(info)
|
||||
|
||||
|
||||
|
||||
+82
-43
@@ -1,50 +1,77 @@
|
||||
from copy import deepcopy
|
||||
from copy import copy
|
||||
from collections.abc import Callable
|
||||
from functools import cache
|
||||
import json
|
||||
import re
|
||||
from typing import Literal, TYPE_CHECKING
|
||||
from typing import Any, Literal, TYPE_CHECKING
|
||||
from .formula import 武器冷却惩罚
|
||||
from database.connect import get_redis
|
||||
from database.connect import BACKEND_DIR, get_redis_client
|
||||
from api.core.Redis import get_redis_info
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.basic.character import Character
|
||||
|
||||
characterLv = 115 + 5
|
||||
OPENAPI_DIR = BACKEND_DIR / 'openapi'
|
||||
|
||||
|
||||
def _identity(value: Any) -> Any:
|
||||
return value
|
||||
|
||||
|
||||
@cache
|
||||
def get_data(prefix: str, key: int | str, func = lambda x: x):
|
||||
redis = next(get_redis())
|
||||
def _get_skill_data_suffixes(skill_class: type) -> tuple[str, ...]:
|
||||
return tuple(key.replace('data', '') for key in dir(skill_class) if key.startswith('data') and not key.startswith('plus'))
|
||||
|
||||
|
||||
@cache
|
||||
def _get_skill_damage_suffixes(skill_class: type) -> tuple[str, ...]:
|
||||
return tuple(key.replace('data', '') for key in dir(skill_class) if key.startswith('data') and not key.startswith('dataplus'))
|
||||
|
||||
|
||||
@cache
|
||||
def _get_skill_dataplus_suffixes(skill_class: type) -> tuple[str, ...]:
|
||||
return tuple(key.replace('dataplus', '') for key in dir(skill_class) if key.startswith('dataplus'))
|
||||
|
||||
|
||||
@cache
|
||||
def get_data(prefix: str, key: int | str, func: Callable[[Any], Any] = _identity):
|
||||
prefix_parts = prefix.split('/')
|
||||
job = prefix_parts[0]
|
||||
jobGrow = prefix_parts[1]
|
||||
skill = prefix_parts[-1]
|
||||
|
||||
def get_custom_cn():
|
||||
with open('./openapi/custom_cn.json', encoding='utf-8') as f:
|
||||
with (OPENAPI_DIR / 'custom_cn.json').open(encoding='utf-8') as f:
|
||||
custom_cn = json.load(f)
|
||||
return custom_cn
|
||||
|
||||
def get_skill_info():
|
||||
# This function should retrieve the skill info based on job and jobGrow
|
||||
# For now, we return a placeholder dictionary
|
||||
with open(f'./openapi/data/{prefix}.json', encoding='utf-8') as f:
|
||||
with (OPENAPI_DIR / 'data' / f'{prefix}.json').open(encoding='utf-8') as f:
|
||||
skill_data = json.load(f)
|
||||
custom_cn = get_redis_info(redis, 'dcalc:custom_cn', get_custom_cn, without_gzip=True)
|
||||
job = prefix.split('/')[0]
|
||||
jobGrow = prefix.split('/')[1]
|
||||
skill = prefix.split('/')[-1]
|
||||
try:
|
||||
if isinstance(key, int):
|
||||
data = [0] + list(map(lambda x: x["optionValue"].get("value" + str(key+1), 0), skill_data["levelInfo"]["rows"]))
|
||||
data = [0] + [row["optionValue"].get("value" + str(key+1), 0) for row in skill_data["levelInfo"]["rows"]]
|
||||
data = [0 if x is None or x == "-" else x for x in data]
|
||||
elif key == "vps":
|
||||
data = [ {"name":x["name"],"desc":x["desc"]} for x in skill_data.get("evolution",[])]
|
||||
elif key == "custom":
|
||||
data = next((item.get('custom', 1.0) for item in custom_cn
|
||||
data = next((item.get('custom', 1.0) for item in custom_cn or []
|
||||
if item['job'] == job and item['jobGrow'] == jobGrow and item['skillId'] == skill), 1.0)
|
||||
except Exception as e:
|
||||
# print(f"get skill {prefix}/{key} data error",e)
|
||||
except Exception:
|
||||
# print(f"get skill {prefix}/{key} data error")
|
||||
data = None
|
||||
if isinstance(key, int) and not data:
|
||||
return [0]
|
||||
if key == 'vps' and data is None:
|
||||
return []
|
||||
if key == 'custom' and data is None:
|
||||
return 1.0
|
||||
return data
|
||||
|
||||
data = get_redis_info(redis, f'dcalc:skill:{prefix}:{key}', get_skill_info, without_gzip=True)
|
||||
with get_redis_client() as redis:
|
||||
data = get_redis_info(redis, f'dcalc:skill:{prefix}:{key}', get_skill_info, without_gzip=True)
|
||||
return func(data)
|
||||
|
||||
|
||||
@@ -174,65 +201,76 @@ class Skill:
|
||||
self._lv = value
|
||||
|
||||
def effect(self, old: int, new: int):
|
||||
weapon_type = self.char.GetWeaponType()[0]
|
||||
weapon_options = self.char.武器选项
|
||||
for assoc in self.associate:
|
||||
type = assoc.get('type', '*skillDamage')
|
||||
skills = assoc.get('skills', '*')
|
||||
data = assoc.get('data', [0] * self.maxLv)
|
||||
ratio = assoc.get('ratio', 100)
|
||||
weapon = assoc.get('weapon', self.char.武器选项)
|
||||
weapon = assoc.get('weapon', weapon_options)
|
||||
exceptSkills = assoc.get('exceptSkills', [])
|
||||
if type[0] not in ['*', '+', '=', '$']:
|
||||
if self.precondition() and (self.char.GetWeaponType()[0] in weapon or len(weapon) == len(self.char.武器选项)):
|
||||
if self.precondition() and (weapon_type in weapon or len(weapon) == len(weapon_options)):
|
||||
getattr(self, type)(old, new, data, skills, exceptSkills, ratio, weapon)
|
||||
continue
|
||||
if self.precondition():
|
||||
self.apply_association(type, old, new, data, skills, exceptSkills, ratio, weapon)
|
||||
self.apply_association(type, old, new, data, skills, exceptSkills, ratio, weapon, weapon_type, weapon_options)
|
||||
|
||||
def precondition(self):
|
||||
"""effect的前置条件"""
|
||||
return True
|
||||
|
||||
def apply_association(self, type, old, new, data, skills, exceptSkills, ratio=100, weapon=[]):
|
||||
def apply_association(self, type, old, new, data, skills, exceptSkills, ratio=100, weapon=(), weapon_type=None, weapon_options=()):
|
||||
try:
|
||||
if (self.char.GetWeaponType()[0] not in weapon) and not (self.char.GetWeaponType()[0] is None and len(weapon) == len(self.char.武器选项)):
|
||||
if (weapon_type not in weapon) and not (weapon_type is None and len(weapon) == len(weapon_options)):
|
||||
return
|
||||
if type.startswith('$*'):
|
||||
if 'cdReduce' in type:
|
||||
value = (1 - data[new] / ratio) / (1 - data[old] / ratio)
|
||||
value = (1 - self.get_data_value(data, new) / ratio) / (1 - self.get_data_value(data, old) / ratio)
|
||||
else:
|
||||
value = (1 + data[new] / ratio) / (1 + data[old] / ratio)
|
||||
eval(f'self.char.SetStatus({type[2:]}={value})')
|
||||
value = (1 + self.get_data_value(data, new) / ratio) / (1 + self.get_data_value(data, old) / ratio)
|
||||
self.char.SetStatus(**{type[2:]: value})
|
||||
elif type.startswith('$+'):
|
||||
value = data[new] / ratio - data[old] / ratio
|
||||
eval(f'self.char.SetStatus({type[2:]}={value})')
|
||||
value = self.get_data_value(data, new) / ratio - self.get_data_value(data, old) / ratio
|
||||
self.char.SetStatus(**{type[2:]: value})
|
||||
else:
|
||||
if skills == '*':
|
||||
skills = self.char.GetSkillNames('all', True)
|
||||
exceptSkills = set(exceptSkills)
|
||||
for name in skills:
|
||||
if name in exceptSkills:
|
||||
continue
|
||||
skill = self.char.GetSkillByName(name)
|
||||
if skill is not None:
|
||||
self.update_skill_attribute(skill, type, old, new, data, ratio, weapon)
|
||||
self.update_skill_attribute(skill, type, old, new, data, ratio, weapon, weapon_type, weapon_options)
|
||||
except Exception as e:
|
||||
# print(f"Error applying association {type} for skill {self.name}: {e}")
|
||||
raise e
|
||||
|
||||
def update_skill_attribute(self, skill, type, old, new, data, ratio=100, weapon=[]):
|
||||
if (self.char.GetWeaponType()[0] not in weapon) and not (self.char.GetWeaponType()[0] is None and len(weapon) == len(self.char.武器选项)):
|
||||
def update_skill_attribute(self, skill, type, old, new, data, ratio=100, weapon=(), weapon_type=None, weapon_options=()):
|
||||
if (weapon_type not in weapon) and not (weapon_type is None and len(weapon) == len(weapon_options)):
|
||||
return
|
||||
old_value = self.get_data_value(data, old)
|
||||
new_value = self.get_data_value(data, new)
|
||||
if type.startswith('*'):
|
||||
if 'cdReduce' in type or type == 'cd' or 'cdRatio' in type:
|
||||
setattr(skill, type[1:], getattr(skill, type[1:]) * (1 - data[new] / ratio) / (1 - data[old] / ratio))
|
||||
setattr(skill, type[1:], getattr(skill, type[1:]) * (1 - new_value / ratio) / (1 - old_value / ratio))
|
||||
else:
|
||||
setattr(skill, type[1:], getattr(skill, type[1:]) * (1 + data[new] / ratio) / (1 + data[old] / ratio))
|
||||
setattr(skill, type[1:], getattr(skill, type[1:]) * (1 + new_value / ratio) / (1 + old_value / ratio))
|
||||
elif type.startswith('+'):
|
||||
if type[1:] == 'lv' and self.char.setInfo.get('skills', {}).get(str(skill.id),{}).get('lv',0) == 0:
|
||||
return
|
||||
else:
|
||||
setattr(skill, type[1:], getattr(skill, type[1:]) + data[new] / ratio - data[old] / ratio)
|
||||
setattr(skill, type[1:], getattr(skill, type[1:]) + new_value / ratio - old_value / ratio)
|
||||
elif type.startswith('='):
|
||||
setattr(skill, type[1:], data[new])
|
||||
setattr(skill, type[1:], new_value)
|
||||
|
||||
@staticmethod
|
||||
def get_data_value(data, index: int):
|
||||
if 0 <= index < len(data):
|
||||
return data[index]
|
||||
return 0
|
||||
|
||||
def skillInfo(self, mode: str | None = None):
|
||||
pass
|
||||
@@ -252,8 +290,7 @@ class ActiveSkill(Skill):
|
||||
|
||||
def __init__(self, char):
|
||||
super().__init__(char)
|
||||
keys = [key.replace('data', '') for key in dir(self) if key.startswith('data') and not key.startswith('plus')]
|
||||
for i in keys:
|
||||
for i in _get_skill_data_suffixes(self.__class__):
|
||||
power = getattr(self, f'power{i}', None)
|
||||
if power is None:
|
||||
setattr(self, f'power{i}', 1)
|
||||
@@ -262,7 +299,12 @@ class ActiveSkill(Skill):
|
||||
setattr(self, f'hit{i}', 0)
|
||||
|
||||
def skillInfo(self, mode: str | None = None):
|
||||
basic = deepcopy(self)
|
||||
basic = copy(self)
|
||||
for key, value in self.__dict__.items():
|
||||
if isinstance(value, list):
|
||||
setattr(basic, key, value.copy())
|
||||
elif isinstance(value, dict):
|
||||
setattr(basic, key, value.copy())
|
||||
basic.setUP()
|
||||
if mode is not None:
|
||||
basic.currentMode = mode
|
||||
@@ -298,8 +340,7 @@ class ActiveSkill(Skill):
|
||||
|
||||
def getSkillData(self, lv: int = 0):
|
||||
res = 0
|
||||
keys = [key.replace('data', '') for key in dir(self) if key.startswith('data') and not key.startswith('dataplus')]
|
||||
for i in keys:
|
||||
for i in _get_skill_damage_suffixes(self.__class__):
|
||||
data = getattr(self, f'data{i}', [])
|
||||
plus = getattr(self, f'plus{i}', 0)
|
||||
hit = getattr(self, f'hit{i}', 0)
|
||||
@@ -309,8 +350,7 @@ class ActiveSkill(Skill):
|
||||
hit = getattr(self, f'hit{i}', 0)
|
||||
power = getattr(self, f'power{i}', 1)
|
||||
res += hit * power * (data[lv] + plus)
|
||||
keys = [key.replace('dataplus', '') for key in dir(self) if key.startswith('dataplus')]
|
||||
for i in keys:
|
||||
for i in _get_skill_dataplus_suffixes(self.__class__):
|
||||
data = getattr(self, f'dataplus{i}', 0)
|
||||
hit = getattr(self, f'hitplus{i}', 1)
|
||||
power = getattr(self, f'powerplus{i}', 1)
|
||||
@@ -397,8 +437,7 @@ class BuffSkill(Skill):
|
||||
|
||||
def __init__(self, char):
|
||||
super().__init__(char)
|
||||
keys = [key.replace('data', '') for key in dir(self) if key.startswith('data') and not key.startswith('plus')]
|
||||
for i in keys:
|
||||
for i in _get_skill_data_suffixes(self.__class__):
|
||||
power = getattr(self, f'power{i}', None)
|
||||
if power is None:
|
||||
setattr(self, f'power{i}', 1)
|
||||
|
||||
Reference in New Issue
Block a user