import pandas as pd from base.attribute import Attribute from base.skill import Skill, DotDamage, NpcDamage, PetDamage from utils.parser import School, Parser, CALCULATE_COLUMNS DAMAGE_COLUMNS = ["damage", "critical_damage", "critical_strike", "expected_damage"] def filter_status(status, school: School): buffs = [] for buff_id, buff_level, buff_stack in status: buff = school.buffs[buff_id] if buff.activate: buffs.append(buff) return buffs def add_buffs(current_buffs, snapshot_buffs, target_buffs, attribute: Attribute, skill: Skill): if not snapshot_buffs: for buff in current_buffs: buff.add_all(attribute, skill) elif isinstance(skill, DotDamage): for buff in snapshot_buffs: buff.add_dot(attribute, skill, True) for buff in current_buffs: buff.add_dot(attribute, skill, False) elif isinstance(skill, NpcDamage): for buff in snapshot_buffs: buff.add_all(attribute, skill) elif isinstance(skill, PetDamage): for buff in snapshot_buffs: buff.add_all(attribute, skill) for buff in target_buffs: buff.add_all(attribute, skill) def sub_buffs(current_buffs, snapshot_buffs, target_buffs, attribute: Attribute, skill: Skill): if not snapshot_buffs: for buff in current_buffs: buff.sub_all(attribute, skill) elif isinstance(skill, DotDamage): for buff in snapshot_buffs: buff.sub_dot(attribute, skill, True) for buff in current_buffs: buff.sub_dot(attribute, skill, False) elif isinstance(skill, NpcDamage): for buff in snapshot_buffs: buff.sub_all(attribute, skill) elif isinstance(skill, PetDamage): for buff in snapshot_buffs: buff.sub_all(attribute, skill) for buff in target_buffs: buff.sub_all(attribute, skill) def analyze_records(parser: Parser, duration: int, attribute: Attribute): records: pd.DataFrame = parser.current_records school = parser.current_school condition = (records.player_id == parser.current_player) & (records.time < duration) if parser.current_target: condition = condition & (records.target_id == parser.current_target) records = records[condition].copy() damage_columns = [(0 for _ in DAMAGE_COLUMNS)] * len(records) grad_attrs = list(attribute.grad_attrs) gradient_columns = [(0 for _ in grad_attrs)] * len(records) for row, indices in records.groupby(CALCULATE_COLUMNS).indices.items(): skill_id, skill_level, skill_stack, current_status, target_status, snapshot_index = row skill: Skill = school.skills[skill_id] skill.skill_level, skill.skill_stack = skill_level, skill_stack current_buffs = filter_status(current_status, school) target_buffs = filter_status(target_status, school) if snapshot_index < 0: snapshot_buffs = tuple() else: snapshot_buffs = filter_status(records.loc[snapshot_index].current_status, school) add_buffs(current_buffs, snapshot_buffs, target_buffs, attribute, skill) damage_tuple = skill(attribute) gradient_tuple = analyze_gradients(skill, attribute) for index in indices: damage_columns[index] = damage_tuple gradient_columns[index] = gradient_tuple sub_buffs(current_buffs, snapshot_buffs, target_buffs, attribute, skill) records[DAMAGE_COLUMNS] = damage_columns records[grad_attrs] = gradient_columns return records def analyze_gradients(skill, attribute): results = [] for attr, value in attribute.grad_attrs.items(): origin_value = getattr(attribute, attr) setattr(attribute, attr, origin_value + value) _, _, _, expected_damage = skill(attribute) results.append(expected_damage) setattr(attribute, attr, origin_value) return results