484 lines
18 KiB
Python
484 lines
18 KiB
Python
import importlib
|
||
from collections.abc import Sequence
|
||
from functools import cache
|
||
import re
|
||
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
|
||
from core.basic.equipment_registry import EquipmentRegistry
|
||
|
||
|
||
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: 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: Sequence[int] = (0,)) -> list[int]:
|
||
return list(default) if not info else [int(i) for i in info.split(',')]
|
||
|
||
|
||
def extract_effect_damage(text: str | None) -> list[float]:
|
||
if not text:
|
||
return []
|
||
|
||
matches: list[tuple[int, int, str]] = []
|
||
patterns = [
|
||
r'(?:伤害量|攻击力)\s*[::]?\s*([\d,]+)%',
|
||
r'(?<!技能)伤害\s*[::]\s*([\d,]+)%',
|
||
r'造成\s*([\d,]+)%\s*的伤害',
|
||
]
|
||
for pattern in patterns:
|
||
for match in re.finditer(pattern, text):
|
||
matches.append((match.start(), match.end(), match.group(1)))
|
||
|
||
in_damage_info = False
|
||
offset = 0
|
||
for line in text.splitlines(keepends=True):
|
||
if '伤害量信息' in line:
|
||
in_damage_info = True
|
||
offset += len(line)
|
||
continue
|
||
if in_damage_info and not line.strip():
|
||
in_damage_info = False
|
||
offset += len(line)
|
||
continue
|
||
if in_damage_info:
|
||
for match in re.finditer(r'[::]\s*([\d,]+)%', line):
|
||
matches.append((offset + match.start(), offset + match.end(), match.group(1)))
|
||
offset += len(line)
|
||
|
||
result: list[float] = []
|
||
seen: list[tuple[int, int]] = []
|
||
for start, end, value in sorted(matches, key=lambda item: item[0]):
|
||
if any(start < seen_end and end > seen_start for seen_start, seen_end in seen):
|
||
continue
|
||
seen.append((start, end))
|
||
result.append(float(value.replace(',', '')))
|
||
return result
|
||
|
||
|
||
class Equ:
|
||
id: str
|
||
name: str
|
||
rarity: str
|
||
itemType: str
|
||
itemDetailType: str
|
||
suit: list[str]
|
||
Point: list[float] | float
|
||
categorize: str
|
||
imageUrl: str
|
||
detail: str
|
||
bufferDetail: str
|
||
STR: list[float] | float
|
||
INT: list[float] | float
|
||
Vitality: list[float] | float
|
||
Spirit: list[float] | float
|
||
AtkP: list[float] | float
|
||
AtkM: list[float] | float
|
||
AtkI: list[float] | float
|
||
SkillAttack: list[float] | float
|
||
Attack: list[float] | float
|
||
Buffer: list[float] | float
|
||
max_adaptation: int
|
||
effectDamage: list[float]
|
||
|
||
def __init__(self, **kwargs):
|
||
self.__dict__.update(kwargs)
|
||
|
||
def adapt(self, adaptation: int = 0):
|
||
"""调适
|
||
|
||
Args:
|
||
adaptation (int): 调适等级
|
||
"""
|
||
temp = self.__dict__.copy()
|
||
keys = [key for key in EquData.__dict__.keys() if key[0].isupper()]
|
||
for attr in keys:
|
||
value = getattr(self, attr)
|
||
temp[attr] = value[min(adaptation, len(value) - 1)] if isinstance(value, list) else value
|
||
return Equ(**temp)
|
||
|
||
|
||
class Suit:
|
||
id: int
|
||
suitId: int
|
||
suitName: str
|
||
rarity: str
|
||
name: str
|
||
point: int
|
||
level: int
|
||
count: int
|
||
imageUrl: str
|
||
SkillAttack: float
|
||
Attack: float
|
||
|
||
def __init__(self, **kwargs):
|
||
self.__dict__.update(kwargs)
|
||
|
||
|
||
class Equipments:
|
||
def __init__(self, version='0'):
|
||
self.version = version
|
||
self.engine = get_engine(version)
|
||
try:
|
||
self.equs: list[Equ] = []
|
||
self.oaths: list[Oath] = []
|
||
self.oath_dict: dict[str, Oath] = {}
|
||
# self.stones: list[Equ] = []
|
||
self.funs = self.init_func()
|
||
self.equ_dict: dict[str, Equ] = {}
|
||
# self.stone_dict: dict[str, Equ] = {}
|
||
self.jades = []
|
||
self.enchants = []
|
||
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()
|
||
self.init_suits()
|
||
self.init_enchants()
|
||
# self.init_stones()
|
||
self.init_emblem()
|
||
self.init_jades()
|
||
self.init_oaths()
|
||
self.init_oath_suits()
|
||
finally:
|
||
self.engine.dispose()
|
||
self.engine = None
|
||
|
||
def init_equs(self):
|
||
"""从数据库中获取所有装备信息"""
|
||
with Session(self.engine) as session:
|
||
db_list = session.query(EquData).all()
|
||
keys = [key for key in EquData.__dict__.keys() if key[0].isupper()] + ['suit']
|
||
for item in db_list:
|
||
max_adaptation = 0
|
||
item.id = str(item.id)
|
||
for attr in keys:
|
||
if attr == 'suit':
|
||
value = parse_to_number_list(getattr(item, attr), [])
|
||
value = [str(int(i)) for i in value]
|
||
else:
|
||
value = parse_to_number_list(getattr(item, attr))
|
||
if attr != 'suit':
|
||
max_adaptation = max(max_adaptation, len(value) - 1)
|
||
setattr(item, attr, value)
|
||
equ_dict = {k: v for k, v in item.__dict__.items() if not k.startswith('_')}
|
||
equ_dict['max_adaptation'] = max_adaptation
|
||
equ_dict['effectDamage'] = extract_effect_damage(equ_dict.get('detail'))
|
||
equ = Equ(**equ_dict)
|
||
self.equs.append(equ)
|
||
self.equ_dict[equ.id] = equ
|
||
|
||
# def init_stones(self):
|
||
# """从数据库中获取所有装备信息"""
|
||
# with Session(self.engine) as session:
|
||
# db_list = session.query(StoneData).all()
|
||
# keys = [key for key in StoneData.__dict__.keys() if key[0].isupper()] + ['suit']
|
||
# for item in db_list:
|
||
# max_adaptation = 0
|
||
# item.id = str(item.id)
|
||
# for attr in keys:
|
||
# if attr == 'suit':
|
||
# value = parse_to_number_list(getattr(item, attr), [])
|
||
# value = [str(int(i)) for i in value]
|
||
# else:
|
||
# value = parse_to_number_list(getattr(item, attr))
|
||
# max_adaptation = max(max_adaptation, len(value) - 1)
|
||
# setattr(item, attr, value)
|
||
# stone_dict = {k: v for k, v in item.__dict__.items() if not k.startswith('_')}
|
||
# stone_dict['max_adaptation'] = max_adaptation
|
||
# stone = Equ(**stone_dict)
|
||
# self.stones.append(stone)
|
||
# self.stone_dict[stone.id] = stone
|
||
|
||
def init_suits(self):
|
||
"""从数据库中获取所有套装信息"""
|
||
with Session(self.engine) as session:
|
||
db_list = session.query(SuitData).all()
|
||
for item in db_list:
|
||
lvs = len(item.point.split(','))
|
||
suits = {k: v for k, v in item.__dict__.items() if not k.startswith('_')}
|
||
for i in range(1, lvs + 1):
|
||
suit = {k: v for k, v in suits.items()}
|
||
suit['point'] = int(suit['point'].split(',')[i - 1])
|
||
suit['level'] = i
|
||
skillAttaks = parse_to_number_list(suit['SkillAttack'], [0])
|
||
suit['SkillAttack'] = 0 if i - 1 >= len(skillAttaks) else skillAttaks[i - 1]
|
||
attacks = parse_to_number_list(suit['Attack'], [0])
|
||
suit['Attack'] = 0 if i - 1 >= len(attacks) else attacks[i - 1]
|
||
buffers = parse_to_number_list(suit['Buffer'], [0])
|
||
suit['Buffer'] = 0 if i - 1 >= len(buffers) else buffers[i - 1]
|
||
suit['effectDamage'] = extract_effect_damage(suit.get('value'))
|
||
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
|
||
|
||
def init_enchants(self):
|
||
"""从数据库中获取所有附魔信息"""
|
||
with Session(self.engine) as session:
|
||
db_list = session.query(EnchantData).all()
|
||
for item in db_list:
|
||
enchant = {k: v for k, v in item.__dict__.items() if not k.startswith('_')}
|
||
enchant['position'] = [] if enchant['itemType'] is None else enchant['itemType'].split(',')
|
||
enchant['categorize'] = [] if enchant['categorize'] is None else enchant['categorize'].split(',')
|
||
del enchant['itemType']
|
||
self.enchants.append(enchant)
|
||
pass
|
||
pass
|
||
|
||
def init_emblem(self):
|
||
"""从数据库中获取所有徽章信息"""
|
||
with Session(self.engine) as session:
|
||
db_list = session.query(EmblemData).all()
|
||
for item in db_list:
|
||
emblem = {k: v for k, v in item.__dict__.items() if not k.startswith('_')}
|
||
# emblem['position'] = [] if emblem['itemType'] is None else emblem['itemType'].split(',')
|
||
# emblem['categorize'] = [] if emblem['categorize'] is None else emblem['categorize'].split(',')
|
||
# del emblem['itemType']
|
||
self.emblems.append(emblem)
|
||
pass
|
||
pass
|
||
|
||
def init_jades(self):
|
||
"""从数据库中获取所有辟邪玉信息"""
|
||
with Session(self.engine) as session:
|
||
db_list = session.query(JadeData).all()
|
||
for item in db_list:
|
||
jade = {k: v for k, v in item.__dict__.items() if not k.startswith('_')}
|
||
self.jades.append(jade)
|
||
pass
|
||
pass
|
||
|
||
def init_oaths(self):
|
||
"""从数据库中获取所有誓约信息"""
|
||
with Session(self.engine) as session:
|
||
db_list_1 = session.query(OathData).all()
|
||
db_list_2 = session.query(PrimerData).all()
|
||
keys = [key for key in OathData.__dict__.keys() if key[0].isupper()]
|
||
for item in db_list_1 + db_list_2:
|
||
max_adaptation = 0
|
||
item.id = str(item.id)
|
||
if item.wearUrl is None:
|
||
item.wearUrl = item.imageUrl
|
||
for attr in keys:
|
||
value = parse_to_number_list(getattr(item, attr))
|
||
max_adaptation = max(max_adaptation, len(value) - 1)
|
||
setattr(item, attr, value)
|
||
for attr in ['wearUrl', 'position', 'suit']:
|
||
value = [] if getattr(item, attr) is None else getattr(item, attr).split(',')
|
||
setattr(item, attr, value)
|
||
oath_dict = {k: v for k, v in item.__dict__.items() if not k.startswith('_')}
|
||
oath_dict['max_adaptation'] = max_adaptation
|
||
oath = Oath(**oath_dict)
|
||
self.oaths.append(oath)
|
||
self.oath_dict[oath.id] = oath
|
||
|
||
def init_oath_suits(self):
|
||
"""从数据库中获取所有誓约套装信息"""
|
||
with Session(self.engine) as session:
|
||
db_list = session.query(OathSuitData).all()
|
||
db_skills_list = session.query(OathSuitSkillData).all()
|
||
skill_list = []
|
||
for item in db_skills_list:
|
||
lvs = len(item.SkillAttack.split(','))
|
||
skills = {k: v for k, v in item.__dict__.items() if not k.startswith('_')}
|
||
for i in range(1, lvs + 1):
|
||
skill = {k: v for k, v in skills.items()}
|
||
skillAttacks = parse_to_number_list(skill['SkillAttack'], [0])
|
||
skill['SkillAttack'] = 0 if i - 1 >= len(skillAttacks) else skillAttacks[i - 1]
|
||
attacks = parse_to_number_list(skill['Attack'], [0])
|
||
skill['Attack'] = 0 if i - 1 >= len(attacks) else attacks[i - 1]
|
||
buffers = parse_to_number_list(skill['Buffer'], [0])
|
||
skill['Buffer'] = 0 if i - 1 >= len(buffers) else buffers[i - 1]
|
||
skill['level'] = i
|
||
skill['effectDamage'] = extract_effect_damage(skill.get('value'))
|
||
skill_obj = OathSuitSkill(**skill)
|
||
skill_list.append(skill_obj)
|
||
|
||
for item in db_list:
|
||
lvs = len(item.point.split(','))
|
||
suits = {k: v for k, v in item.__dict__.items() if not k.startswith('_')}
|
||
for i in range(1, lvs + 1):
|
||
suit = {k: v for k, v in suits.items()}
|
||
suit['points'] = suit['point']
|
||
suit['point'] = int(suit['point'].split(',')[i - 1])
|
||
skills = [x for x in skill_list if x.suitId == suit['suitId'] and x.key == suit['key'] and x.level == i]
|
||
suit['level'] = i
|
||
skillAttaks = parse_to_number_list(suit['SkillAttack'], [0])
|
||
suit['SkillAttack'] = (0 if len(skillAttaks) == 0 else skillAttaks[0]) if i - 1 >= len(skillAttaks) else skillAttaks[i - 1]
|
||
attacks = parse_to_number_list(suit['Attack'], [0])
|
||
suit['Attack'] = (0 if len(attacks) == 0 else attacks[0]) if i - 1 >= len(attacks) else attacks[i - 1]
|
||
buffers = parse_to_number_list(suit['Buffer'], [0])
|
||
suit['Buffer'] = (0 if len(buffers) == 0 else buffers[0]) if i - 1 >= len(buffers) else buffers[i - 1]
|
||
suit['skills'] = [OathSuitSkill(**{k: v for k, v in skill.__dict__.items() if not k.startswith('_')}) for skill in skills]
|
||
oath_suit = OathSuit(**suit)
|
||
self.oath_suits.append(oath_suit)
|
||
self.oath_suit_dict.setdefault(str(suit['suitId']), []).append(oath_suit)
|
||
pass
|
||
pass
|
||
|
||
def init_func(self):
|
||
"""根据装备版本加载装备效果"""
|
||
try:
|
||
funs = importlib.import_module(f'core.equipment.version_{self.version}')
|
||
except Exception:
|
||
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
|
||
老套装需要传入suitID和count即可,point默认为0
|
||
"""
|
||
if suitId not in self.suit_dict:
|
||
return []
|
||
# 先筛选出点数小于等于point和数量小于等于count的套装
|
||
suits: list[Suit] = [suit for suit in self.suit_dict[str(suitId)] if suit.point <= point and suit.count <= count]
|
||
result: dict[int, Suit] = {}
|
||
# 取每种count套装中点数最高的
|
||
for suit in suits:
|
||
if suit.count not in result or suit.point > result[suit.count].point:
|
||
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
|
||
老套装需要传入suitID和count即可,point默认为0
|
||
"""
|
||
if suitId not in self.oath_suit_dict:
|
||
return []
|
||
# 先筛选出点数小于等于point和数量小于等于count的套装
|
||
suits: list[OathSuit] = [suit for suit in self.oath_suit_dict[str(suitId)] if suit.point <= point and suit.count <= count]
|
||
result: dict[int, OathSuit] = {}
|
||
# 取每种count套装中点数最高的
|
||
for suit in suits:
|
||
if suit.count not in result or suit.point > result[suit.count].point:
|
||
result[suit.count] = suit
|
||
return list(result.values())
|
||
|
||
|
||
class EquEffect:
|
||
name: str = ''
|
||
"""装备技能名称"""
|
||
icon: str = ''
|
||
"""技能图标"""
|
||
cd: float = 0
|
||
"""技能冷却时间"""
|
||
data: float = 0
|
||
"""伤害量"""
|
||
|
||
def __init__(self, name: str, icon: str, cd: float, data: float):
|
||
self.name = name
|
||
self.icon = icon
|
||
self.cd = cd
|
||
self.data = data
|
||
|
||
|
||
class Oath:
|
||
id: str
|
||
name: str
|
||
rarity: str
|
||
position: list[str]
|
||
suit: list[str]
|
||
Point: list[float] | float
|
||
categorize: str
|
||
imageUrl: str
|
||
detail: str
|
||
bufferDetail: str
|
||
STR: list[float] | float
|
||
INT: list[float] | float
|
||
Vitality: list[float] | float
|
||
Spirit: list[float] | float
|
||
AtkP: list[float] | float
|
||
AtkM: list[float] | float
|
||
AtkI: list[float] | float
|
||
SkillAttack: list[float] | float
|
||
Attack: list[float] | float
|
||
Buffer: list[float] | float
|
||
wearUrl: list[str]
|
||
max_adaptation: int
|
||
itemType: str
|
||
itemDetailType: str
|
||
effectDamage: list[float]
|
||
|
||
def __init__(self, **kwargs):
|
||
self.__dict__.update(kwargs)
|
||
|
||
def adapt(self, adaptation: int = 0):
|
||
"""调适
|
||
|
||
Args:
|
||
adaptation (int): 调适等级
|
||
"""
|
||
temp = self.__dict__.copy()
|
||
keys = [key for key in OathData.__dict__.keys() if key[0].isupper()]
|
||
for attr in keys:
|
||
value = getattr(self, attr)
|
||
temp[attr] = value[min(adaptation, len(value) - 1)] if isinstance(value, list) else value
|
||
return Oath(**temp)
|
||
|
||
|
||
|
||
class OathSuitSkill:
|
||
id: int
|
||
key: str
|
||
suitId: int
|
||
suitName: str
|
||
skillId: int
|
||
name: str
|
||
rarity: str
|
||
imageUrl: str
|
||
SkillAttack: float
|
||
Attack: float
|
||
Buffer: float
|
||
value: str
|
||
bufferValue: str
|
||
level: int
|
||
effectDamage: list[float]
|
||
|
||
def __init__(self, **kwargs):
|
||
for key in ['SkillAttack', 'Attack', 'Buffer']:
|
||
if kwargs.get(key) is None:
|
||
kwargs[key] = 0
|
||
self.__dict__.update(kwargs)
|
||
|
||
|
||
class OathSuit:
|
||
id: int
|
||
key: str
|
||
suitId: int
|
||
suitName: str
|
||
rarity: str
|
||
name: str
|
||
point: int
|
||
points: str
|
||
level: int
|
||
count: int
|
||
imageUrl: str
|
||
SkillAttack: float
|
||
Attack: float
|
||
Buffer: float
|
||
skills: list[OathSuitSkill]
|
||
|
||
def __init__(self, **kwargs):
|
||
self.__dict__.update(kwargs)
|
||
|
||
|
||
_equipment_registry = EquipmentRegistry(Equipments)
|
||
|
||
|
||
def get_equipment(version: str = '0') -> Equipments:
|
||
return _equipment_registry.get(version)
|