Files
dcalc/backend/core/basic/character/calc_equip.py
T

485 lines
22 KiB
Python

import sys
from functools import cache
from uuid import uuid1
from core.character.refs.base import bind_skill_refs
from core.basic.equipment import OathSuit, Suit, get_equipment
from ..formula import 增幅计算, 强化技攻, 武器强化计算, 精通计算, 耳环计算, 左右计算, 锻造计算
from ..roleinfo import CharacterEmblemInfo, CharacterEquipInfo, CharacterOathInfo, get_key_by_value
from .base import CharacterBase
from .config_view import CalcConfigView
from .effect_apply import apply_uppercase_attrs
from ..skill_effects import skill_effect_batch
from ..skill import Skill
@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 is not Skill
and issubclass(memberClass, Skill)
and memberClass.__name__.startswith('Skill')
)
class CalcEquipMixin(CharacterBase):
"""装备计算、角色初始化相关方法"""
# region 打造信息导入
def SetDetail(self, info: dict[str, dict]) -> None:
"""打造信息导入"""
self.charEquipInfo = {}
self.charOathInfo = []
self.charEmblemInfo = []
self.bindAwake = info.get('bindAwake', 50)
if info.get('emblems', None) is None:
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, equs)
for key in info['oaths']:
# 导入誓约打造信息、誓约信息
self.charOathInfo.append(CharacterOathInfo(info['oaths'][key], self.equVersion, key, equs))
try:
# 异常处理 彩色等级自动取最小值
minEmblemLv = min([info['emblems'][key]['lv'] for key in ['红色', '蓝色', '绿色', '黄色']], default=1)
info['emblems']['彩色']['lv'] = minEmblemLv
for key in info['emblems']:
# 导入徽章打造信息、徽章信息
info['emblems'][key]['part'] = key
self.charEmblemInfo.append(CharacterEmblemInfo(info['emblems'][key]))
pass
except Exception:
pass
self.charOathSkillId = info.get('oathSkill', 1)
def calc_init(self, setInfo: dict[str, dict]):
self.equ_options = setInfo.get('options', {})
self.setInfo = setInfo
self.SetDetail(setInfo)
self.SetSkills(setInfo.get('skills', {}))
@skill_effect_batch
def SetSkills(self, skills: dict[str, dict]):
"""批量设置基础等级及 VP/UP;等级不含关联加级,共享等级由 level_source 决定。"""
for key in skills:
skill = self.GetSkillByID(key)
if skill is not None:
if skill.hasUP:
skill.up = skills[key].get('up', 0)
if skill.hasVP:
skill.vp = skills[key].get('vp', 0)
self.skill_effects.set_input_level(skill, skills[key].get('lv', 0))
def load_skills(self) -> None:
"""加载技能"""
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)
# 职业变体通过数据 prefix 复用同一身份范围,不依赖显示名称或模块别名。
prefix = getattr(sys.modules[self.moduleName], 'prefix', '')
bind_skill_refs(self, '.'.join(prefix.split('/')[:2]))
# endregion
# region 套装 / 装备 / 时装 / 基础属性 / 辟邪玉 / 杂项
def calc_suits(self):
"""套装效果统计"""
suitInfo = {}
oathSuitInfo = {}
correction = 0
equs = get_equipment(self.equVersion)
for item in self.charEquipInfo.values():
partEqu = item.equInfo
if partEqu is None:
continue
# partFusion = item.fusionInfo
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 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]
# 套装没有到太初的时候,优先把誓约套装的点数加到装备套装上,以达到更高的套装等级
if suitInfo is not None and oathSuitInfo is not None and max_point_suit is not None and 0 < suitInfo[max_point_suit]['point'] < 2550 and max_point_oath_suit is not None and oathSuitInfo.get(max_point_oath_suit, None) is not None:
correction = min(2550 - suitInfo[max_point_suit]['point'], oathSuitInfo[max_point_oath_suit]['point'])
suitInfo[max_point_suit]['point'] += correction
oathSuitInfo[max_point_oath_suit]['point'] -= correction
pass
suits = zero_point_suits + ([max_point_suit] if max_point_suit is not None else [])
suitList: list[Suit] = []
oathSuitList: list[OathSuit] = []
res = {'suit': None, 'oath': None, 'correction': correction}
# print(max_point_suit, suitList)
for suit in suits:
if suitInfo[suit]['point'] == 0:
suitList += equs.get_suit_info(suit, 0, suitInfo[suit]['count'])
else:
suitList += equs.get_suit_info(suit, suitInfo[suit]['point'], 0)
if len(suitList) == 0:
continue
if suit == max_point_suit:
self.suitInfo = suitList
temp = suitList[-1]
res['suit'] = {
'id': temp.id,
'name': temp.name,
'rarity': temp.rarity,
'point': suitInfo[suit]['point'],
'count': suitInfo[suit]['count'],
'level': temp.level,
'suitId': suit,
'imageUrl': temp.imageUrl,
# 'value': temp.value,
}
for suit in oathSuitInfo:
if oathSuitInfo[suit]['point'] == 0:
oathSuitList += equs.get_oath_suit_info(suit, 0, oathSuitInfo[suit]['count'])
else:
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 = equs.suit_id_dict.get(str(i), None)
if suit is None:
continue
# 誓约和套装进行匹配
# 如果存在誓约和套装是同一个套装
# 则直接不执行原套装效果,执行誓约套装增强的效果
oathEffect = oath_suit_by_suit_id.get(suit.suitId, None)
apply_uppercase_attrs(self, suit)
if func is not None and oathEffect is None:
# print(f'套装 {suit.name} 生效')
func(self, suit.effectDamage)
if max_point_suit != max_point_oath_suit:
self.charOathSkillId = '1'
oathSkillEffect = []
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 Exception:
self.oath_max_point = 0
self.oathSuitInfo = []
self.oathSkillInfo = []
# 当套装点数达到太初时,誓约技能生效
if self.max_point >= 2550:
oathSuit = oath_suit_by_suit_id.get(max_point_oath_suit, None)
if oathSuit is not None:
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)
# 誓约套装效果生效
apply_uppercase_attrs(self, oathSuit)
if skill is not None:
base_suit = next((x for x in suitList if x.suitId == oathSuit.suitId), None)
suit_effect_damage = [] if base_suit is None else base_suit.effectDamage
oath_effect_damage = skill.effectDamage
oathSkillEffect.append((skill, oath_effect_damage, suit_effect_damage))
# 誓约技能效果生效
apply_uppercase_attrs(self, skill)
self.oathSkillInfo.append(skill)
self.oathSuitInfo.append(oathSuit)
if oathSuit.suitId == max_point_oath_suit:
res['oath'] = {
'id': oathSuit.id,
'name': skill.name,
'rarity': oathSuit.rarity,
'point': oathSuitInfo[oathSuit.suitId]['point'],
'count': oathSuitInfo[oathSuit.suitId]['count'],
'level': oathSuit.level,
'imageUrl': oathSuit.imageUrl,
'suitId': oathSuit.suitId,
'skills': [{'id': skill.id, 'skillId': i.skillId, 'name': i.name, 'imageUrl': i.imageUrl, 'enable': i.skillId == '1' or max_point_oath_suit == max_point_suit} for i in skills],
# 'value': oathSuit.value,
}
for skill, oath_effect_damage, suit_effect_damage in oathSkillEffect:
func = equs.funs.execture(f'oath_skill_{skill.id}')
if func is not None:
# print(f'誓约技能 {skill.id} 生效')
func(self, oath_effect_damage, suit_effect_damage)
# 太初积分以上,每超过70点数增加1%技能攻击力
if self.max_point >= 2550:
skillAttack = (self.max_point - 2550) // 70 * 0.02
buffer = (self.max_point - 2550) // 70 * 400
self.SetStatus(SkillAttack=skillAttack, Buffer=buffer)
# 誓约积分
if self.oath_max_point >= 1200:
self.SetSkillCD(1, 100, 0.185)
SkillAttack, Buffer = equs.funs.execture('oathPointEffect')(self.oath_max_point)
self.SetStatus(SkillAttack=SkillAttack, Buffer=Buffer)
return res
def calc_equs(self):
"""计算装备基础效果、附魔"""
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 item in self.charEquipInfo.values():
equ = item.equInfo
if equ is None or equ.itemType == '副武器':
continue
enchat = item.enchant
fun = effects.execture(f'equ_{equ.id}')
if fun is None:
continue
apply_uppercase_attrs(self, equ)
fun(self, equ.effectDamage)
fun = effects.execture(f'enchant_{enchat}')
if fun is not None:
fun(self)
# emblem_0 = detail[3]
# emblem_1 = detail[4]
# if str(emblem_0).isdigit():
# fun = effects.execture(f'emblem_{emblem_0}')
# if fun is not None:
# fun(self)
# else:
# skill = self.GetSkillByName(emblem_0)
# if skill is not None:
# skill.lv += 1
# self.SetStatus(四维=8)
# fun = effects.execture(f'emblem_{emblem_1}')
# if fun is not None:
# fun(self)
# if fusion is None:
# continue
# fun = effects.execture(f'stone_{fusion.id}')
# filtered_dict = {k: v for k, v in fusion.__dict__.items() if k[0].isupper()}
# self.SetStatus(**filtered_dict)
# if fun is not None:
# fun(self)
def calc_emblems(self):
"""计算徽章效果"""
effects = get_equipment(self.equVersion).funs
emblemCombine = effects.execture('emblem_combine')
for emblem in self.charEmblemInfo:
for item in emblem.items:
fun = effects.execture(f'emblem_{item}')
if fun is not None:
fun(self)
else:
skill = self.GetSkillByName(item)
if skill is not None:
skill.lv += 1
self.SetStatus(四维=8)
emblemCombine(self, emblem.part, emblem.lv, emblem.items)
pass
pass
def calc_oath_upgrade(self):
"""计算装备基础效果"""
oath = next(
(item for item in self.charOathInfo
if item.oathInfo is not None and item.oathInfo.itemDetailType == '誓约'),
None,
)
if oath is not None:
for index, upgrade in enumerate(oath.upgrade):
if index <= 3:
if upgrade == 1:
self.SetStatus(SkillAttack=0.04, Buffer=800)
elif upgrade == 2:
self.SetSkillCD(cd=0.08)
self.SetStatus(EquEffectRatio=0.04, Buffer=800)
elif upgrade == 3:
# 誓约积分 +25
oath.oathInfo.Point += 25
elif upgrade == 1:
self.SetStatus(
SpeedA=0.04, SpeedM=0.04, SpeedR=0.04,
SkillAttack=0.04, Buffer=800,
)
elif upgrade in (2, 3):
self.SetStatus(SkillAttack=0.04, Buffer=800)
pass
pass
def calc_oaths(self):
"""计算装备基础效果"""
effects = get_equipment(self.equVersion).funs
for item in self.charOathInfo:
oath = item.oathInfo
if oath is None:
continue
fun = effects.execture(f'oath_{oath.id}')
if fun is None:
continue
apply_uppercase_attrs(self, oath)
fun(self)
def calc_avatar(self, avatar: dict):
"""计算时装效果"""
equ = get_equipment(self.equVersion)
dress = {key: value for key, value in avatar.items() if key in ['头部', '帽子', '脸部', '胸部', '上衣', '腰带', '下装', '鞋']}
equ.funs.calc_dress_effect(dress, self)
avatarElse = {key: value for key, value in avatar.items() if key not in ['头部', '帽子', '脸部', '胸部', '上衣', '腰带', '下装', '鞋']}
for key in avatarElse:
value = avatarElse[key].get('enchant', None)
emblem_0 = avatarElse[key].get('emblem_0', None)
emblem_1 = avatarElse[key].get('emblem_1', None)
if value is not None:
fun = equ.funs.execture(f'enchant_{value}')
if fun is not None:
fun(self)
for i in [emblem_0, emblem_1]:
if i is None:
continue
fun = equ.funs.execture(f'emblem_{i}')
if fun is not None:
fun(self)
def calc_weapon(self, cur: 'CharacterEquipInfo'):
"""武器强化、锻造计算"""
value_1 = 锻造计算(115, '史诗', cur.refine, cur.equInfo.itemDetailType, 1.12 if '传世武器' in cur.equInfo.categorize else 0)
value_2 = 武器强化计算(115, '史诗', cur.reinforce, cur.equInfo.itemDetailType, '独立', 1.12 if '传世武器' in cur.equInfo.categorize else 0)
value = max(value_1, value_2)
self.SetStatus(AtkI=value)
if '传世武器' in cur.equInfo.categorize:
value = 武器强化计算(115, '史诗', cur.reinforce, cur.equInfo.itemDetailType, '物理', 1.12)
self.SetStatus(AtkP=value, AtkM=value)
else:
value = 武器强化计算(115, '史诗', cur.reinforce, cur.equInfo.itemDetailType, '物理')
self.SetStatus(AtkP=value)
value = 武器强化计算(115, '史诗', cur.reinforce, cur.equInfo.itemDetailType, '魔法')
self.SetStatus(AtkM=value)
def calc_basic(self):
"""计算基础属性:防具精通、增幅、强化等"""
char_equip_info = self.charEquipInfo
for part in ['上衣', '头肩', '下装', '腰带', '鞋']:
cur = char_equip_info[part]
if cur.equInfo is None:
continue
equ = cur.equInfo
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, cur in char_equip_info.items():
if cur.equInfo is None:
continue
if cur.reinforceType == 1:
value = 增幅计算(115, '史诗', cur.reinforce)
self.SetStatus(STR=value, INT=value, Spirit=value, Vitality=value)
value = 强化技攻(cur.reinforce, cur.reinforceType, part)
self.SetStatus(SkillAttack=value)
if '武器' in part:
self.calc_weapon(cur)
if part == '耳环':
value = 耳环计算(115, '史诗', cur.reinforce)
self.SetStatus(AtkM=value, AtkP=value, AtkI=value)
if part in ['辅助装备', '魔法石']:
value = 左右计算(115, '史诗', cur.reinforce)
self.SetStatus(STR=value, INT=value, Spirit=value, Vitality=value)
def calc_jade(self, jades):
"""计算辟邪玉效果"""
funs = get_equipment(self.equVersion).funs
for key in jades:
jade = jades[key]
fun = funs.execture(f'jade_{jade.get("id", 0)}')
if fun is not None:
fun(self, jade.get('value', 0))
def calc_sundry(self, sundry: dict):
"""计算杂项效果"""
funs = get_equipment(self.equVersion).funs
primer = next((x for x in self.charOathInfo if x.id == '2000500'), None)
if primer:
fog = sundry.get('fog', 0)
stats, Point, SkillAttack, Attack, Buffer = funs.execture('fog_oath_info')(fog)
primer.oathInfo.STR = primer.oathInfo.INT = primer.oathInfo.Vitality = primer.oathInfo.Spirit = stats
primer.oathInfo.Point = Point
primer.oathInfo.SkillAttack = SkillAttack
primer.oathInfo.Attack = Attack
primer.oathInfo.Buffer = Buffer
# funs.execture('sundry_0')(
# self,
# sundry.get('medal_rarity', 0),
# sundry.get('medal_reinforce', 0),
# [sundry.get('medal_gem_0', 0), sundry.get('medal_gem_1', 0), sundry.get('medal_gem_2', 0), sundry.get('medal_gem_3', 0)],
# )
funs.execture('sundry_6')(self, sundry.get('adventure', 1))
# funs.execture('sundry_5')(self, sundry.get('fog', 1))
funs.execture('sundry_7')(self, sundry.get('marriage_house', 0), sundry.get('marriage_ring', 0))
funs.execture('sundry_9')(self, sundry.get('contract', 0))
funs.execture('sundry_10')(
self,
sundry.get('collection_type', 0),
sundry.get('collection_num_0', 0),
sundry.get('collection_num_1', 0),
)
funs.execture('sundry_13')(self, sundry.get('costume_card', 0))
# endregion
# region 主计算入口
def calc(self, setInfo: dict[str, dict]):
config = CalcConfigView(setInfo).as_legacy_dict()
# 装备等外部加成作为技能关联的基础值;批次结束后按最终技能状态重建关联。
# 装备之间仍有先后依赖(如先计算套装积分),不能据此任意交换下面的调用。
with self.skill_effects.batch():
self.calc_init(config)
self.SetBaseStatus()
self.calc_jade(config.get('jades', []))
self.calc_avatar(config.get('avatar', {}))
self.calc_basic()
self.calc_sundry(config.get('sundry', {}))
self.calc_emblems()
self.calc_oath_upgrade()
suit = self.calc_suits()
self.calc_equs()
self.calc_oaths()
skillInfos = []
if self.buffer:
skillInfos = self.calc_buffer_skills()
info = self.get_buffer_info()
else:
if len(self.输出类型选项) > 1:
temp = self.输出类型选项[0]
if '固伤' in temp:
if self.INT > self.STR:
self.输出类型 = '魔法固伤'
else:
self.输出类型 = '物理固伤'
else:
if self.INT > self.STR and self.AtkM > self.AtkP:
self.输出类型 = '魔法百分比'
else:
self.输出类型 = '物理百分比'
skillInfos = self.calc_carry_skills(config.get('DSB', False), config.get('BUFF', False))
info = self.get_carry_info()
return {'uuid': uuid1().hex, 'skills': skillInfos, 'info': info, 'suits': suit, 'buffer': self.buffer}
# endregion