Spaces:
Runtime error
Runtime error
ango
commited on
Commit
·
2452398
1
Parent(s):
f0b1638
04.10 commit
Browse files- base/attribute.py +4 -3
- base/buff.py +30 -24
- base/calculator.py +0 -50
- base/constant.py +1 -1
- base/recipe.py +7 -10
- base/skill.py +6 -2
- general/recipes.py +66 -0
- qt/app.py +25 -18
- qt/components/__init__.py +18 -9
- qt/components/dashboard.py +80 -0
- qt/components/equipments.py +7 -6
- qt/components/recipes.py +27 -0
- qt/constant.py +43 -4
- qt/scripts/__init__.py +0 -0
- qt/scripts/dashboard.py +103 -0
- qt/scripts/equipments.py +29 -24
- qt/scripts/recipes.py +39 -0
- qt/scripts/top.py +45 -15
- schools/first/__init__.py +2 -1
- schools/first/recipes.py +57 -65
- schools/first/skills.py +7 -2
- schools/first/talents.py +19 -14
- schools/first/test.py +2 -2
- utils/analyzer.py +89 -0
- {base → utils}/damage.py +0 -0
base/attribute.py
CHANGED
|
@@ -147,7 +147,7 @@ class Major:
|
|
| 147 |
_magical_overcome: float = 0
|
| 148 |
|
| 149 |
""" Major Attr Function"""
|
| 150 |
-
|
| 151 |
@property
|
| 152 |
def all_major_base(self):
|
| 153 |
return self._all_major_base
|
|
@@ -779,7 +779,7 @@ class Minor:
|
|
| 779 |
|
| 780 |
@property
|
| 781 |
def physical_critical_power_percent(self):
|
| 782 |
-
return
|
| 783 |
|
| 784 |
@physical_critical_power_percent.setter
|
| 785 |
def physical_critical_power_percent(self, physical_critical_power_percent):
|
|
@@ -814,7 +814,7 @@ class Minor:
|
|
| 814 |
|
| 815 |
@property
|
| 816 |
def magical_critical_power_percent(self):
|
| 817 |
-
return
|
| 818 |
|
| 819 |
@magical_critical_power_percent.setter
|
| 820 |
def magical_critical_power_percent(self, magical_critical_power_percent):
|
|
@@ -954,6 +954,7 @@ class Attribute(Major, Minor, Target):
|
|
| 954 |
|
| 955 |
def __init__(self):
|
| 956 |
self.all_major_base += MAJOR_BASE
|
|
|
|
| 957 |
|
| 958 |
@property
|
| 959 |
def level_reduction(self):
|
|
|
|
| 147 |
_magical_overcome: float = 0
|
| 148 |
|
| 149 |
""" Major Attr Function"""
|
| 150 |
+
|
| 151 |
@property
|
| 152 |
def all_major_base(self):
|
| 153 |
return self._all_major_base
|
|
|
|
| 779 |
|
| 780 |
@property
|
| 781 |
def physical_critical_power_percent(self):
|
| 782 |
+
return BASE_CRITICAL_POWER + self._physical_critical_power_percent
|
| 783 |
|
| 784 |
@physical_critical_power_percent.setter
|
| 785 |
def physical_critical_power_percent(self, physical_critical_power_percent):
|
|
|
|
| 814 |
|
| 815 |
@property
|
| 816 |
def magical_critical_power_percent(self):
|
| 817 |
+
return BASE_CRITICAL_POWER + self._magical_critical_power_percent
|
| 818 |
|
| 819 |
@magical_critical_power_percent.setter
|
| 820 |
def magical_critical_power_percent(self, magical_critical_power_percent):
|
|
|
|
| 954 |
|
| 955 |
def __init__(self):
|
| 956 |
self.all_major_base += MAJOR_BASE
|
| 957 |
+
self.all_critical_power_base = 0 # init critical power attr
|
| 958 |
|
| 959 |
@property
|
| 960 |
def level_reduction(self):
|
base/buff.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
from dataclasses import dataclass
|
| 2 |
-
from typing import Dict, List, Union
|
| 3 |
|
| 4 |
from base.attribute import Attribute
|
| 5 |
from base.skill import Skill
|
|
@@ -9,11 +9,10 @@ ATTR_DICT = Dict[str, Union[List[int], int]]
|
|
| 9 |
|
| 10 |
@dataclass
|
| 11 |
class Buff:
|
| 12 |
-
buff_id: int
|
| 13 |
-
buff_name: str
|
| 14 |
buff_level: int = 0
|
| 15 |
-
|
| 16 |
-
stack: int = 1
|
| 17 |
|
| 18 |
gain_skills: Dict[int, ATTR_DICT] = None
|
| 19 |
gain_attributes: ATTR_DICT = None
|
|
@@ -22,22 +21,29 @@ class Buff:
|
|
| 22 |
self.gain_skills = {}
|
| 23 |
self.gain_attributes = {}
|
| 24 |
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
from dataclasses import dataclass
|
| 2 |
+
from typing import Dict, List, Union
|
| 3 |
|
| 4 |
from base.attribute import Attribute
|
| 5 |
from base.skill import Skill
|
|
|
|
| 9 |
|
| 10 |
@dataclass
|
| 11 |
class Buff:
|
| 12 |
+
buff_id: int = 0
|
| 13 |
+
buff_name: str = ""
|
| 14 |
buff_level: int = 0
|
| 15 |
+
buff_stack: int = 0
|
|
|
|
| 16 |
|
| 17 |
gain_skills: Dict[int, ATTR_DICT] = None
|
| 18 |
gain_attributes: ATTR_DICT = None
|
|
|
|
| 21 |
self.gain_skills = {}
|
| 22 |
self.gain_attributes = {}
|
| 23 |
|
| 24 |
+
@property
|
| 25 |
+
def display_name(self):
|
| 26 |
+
return f"{self.buff_name}/{self.buff_id}-{self.buff_level}-{self.buff_stack}"
|
| 27 |
+
|
| 28 |
+
def __radd__(self, other: Union[Attribute, Dict[int, Skill]]):
|
| 29 |
+
if isinstance(other, Attribute):
|
| 30 |
+
for attr, value in self.gain_attributes.items():
|
| 31 |
+
setattr(other, attr, getattr(other, attr) + value * self.buff_stack)
|
| 32 |
+
else:
|
| 33 |
+
for skill_id, gain in self.gain_skills.items():
|
| 34 |
+
skill = other[skill_id]
|
| 35 |
+
for attr, value in gain.items():
|
| 36 |
+
setattr(skill, attr, getattr(skill, attr) + value * self.buff_stack)
|
| 37 |
+
return other
|
| 38 |
+
|
| 39 |
+
def __rsub__(self, other: Union[Attribute, Dict[int, Skill]]):
|
| 40 |
+
if isinstance(other, Attribute):
|
| 41 |
+
for attr, value in self.gain_attributes.items():
|
| 42 |
+
setattr(other, attr, getattr(other, attr) - value * self.buff_stack)
|
| 43 |
+
else:
|
| 44 |
+
for skill_id, gain in self.gain_skills.items():
|
| 45 |
+
skill = other[skill_id]
|
| 46 |
+
for attr, value in gain.items():
|
| 47 |
+
setattr(skill, attr, getattr(skill, attr) - value * self.buff_stack)
|
| 48 |
+
return other
|
| 49 |
+
|
base/calculator.py
DELETED
|
@@ -1,50 +0,0 @@
|
|
| 1 |
-
from base.attribute import Attribute
|
| 2 |
-
from qt.scripts.top import Parser, School
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
def refresh_status(existed_buffs, buffs, attribute: Attribute, school: School):
|
| 6 |
-
for buff in [buff for buff in existed_buffs if buff not in buffs]:
|
| 7 |
-
existed_buffs.remove(buff)
|
| 8 |
-
buff_id, buff_level, buff_stack = buff
|
| 9 |
-
buff = school.buffs[buff_id]
|
| 10 |
-
buff.buff_level = buff_level
|
| 11 |
-
for _ in range(buff_stack):
|
| 12 |
-
attribute, school.skills = (attribute, school.skills) - buff
|
| 13 |
-
|
| 14 |
-
for buff in [buff for buff in buffs if buff not in existed_buffs]:
|
| 15 |
-
existed_buffs.append(buff)
|
| 16 |
-
buff_id, buff_level, buff_stack = buff
|
| 17 |
-
buff = school.buffs[buff_id]
|
| 18 |
-
buff.buff_level = buff_level
|
| 19 |
-
for _ in range(buff_stack):
|
| 20 |
-
attribute, school.skills = (attribute, school.skills) + buff
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
def analyze_details(parser: Parser, attribute: Attribute):
|
| 24 |
-
existed_buffs = []
|
| 25 |
-
for start_time, record in parser.records.items():
|
| 26 |
-
for skill, status in record.items():
|
| 27 |
-
skill_id, skill_level = skill
|
| 28 |
-
skill = parser.school.skills[skill_id]
|
| 29 |
-
skill.skill_level = skill_level
|
| 30 |
-
for buffs, timeline in status.items():
|
| 31 |
-
refresh_status(existed_buffs, buffs, attribute, parser.school)
|
| 32 |
-
|
| 33 |
-
damage, critical_damage, expected_damage = skill(attribute)
|
| 34 |
-
|
| 35 |
-
status[buffs] = {
|
| 36 |
-
"damage": damage, "critical_damage": critical_damage, "expected_damage": expected_damage,
|
| 37 |
-
"timeline": [round(t / 1000, 2) for t in timeline],
|
| 38 |
-
"gradients": analyze_gradients(skill, attribute)
|
| 39 |
-
}
|
| 40 |
-
refresh_status(existed_buffs, [], attribute, parser.school)
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
def analyze_gradients(skill, attribute):
|
| 44 |
-
results = {}
|
| 45 |
-
for attr, value in attribute.grad_attrs.items():
|
| 46 |
-
origin_value = getattr(attribute, attr)
|
| 47 |
-
setattr(attribute, attr, origin_value + value)
|
| 48 |
-
_, _, results[attr] = skill(attribute)
|
| 49 |
-
setattr(attribute, attr, origin_value)
|
| 50 |
-
return results
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
base/constant.py
CHANGED
|
@@ -31,7 +31,7 @@ SHIELD_BASE_MAP = {
|
|
| 31 |
}
|
| 32 |
|
| 33 |
MAJOR_BASE = 41
|
| 34 |
-
|
| 35 |
|
| 36 |
AGILITY_TO_CRITICAL_STRIKE = 655 / BINARY_SCALE
|
| 37 |
STRENGTH_TO_ATTACK_POWER = 153 / BINARY_SCALE
|
|
|
|
| 31 |
}
|
| 32 |
|
| 33 |
MAJOR_BASE = 41
|
| 34 |
+
BASE_CRITICAL_POWER = 1.75
|
| 35 |
|
| 36 |
AGILITY_TO_CRITICAL_STRIKE = 655 / BINARY_SCALE
|
| 37 |
STRENGTH_TO_ATTACK_POWER = 153 / BINARY_SCALE
|
base/recipe.py
CHANGED
|
@@ -1,22 +1,19 @@
|
|
|
|
|
| 1 |
|
| 2 |
|
| 3 |
def damage_addition_recipe(skill_ids, value, name="伤害增加"):
|
| 4 |
-
return
|
| 5 |
-
|
| 6 |
-
"gain_skills": {
|
| 7 |
skill_id: {
|
| 8 |
"skill_damage_addition": value
|
| 9 |
} for skill_id in skill_ids
|
| 10 |
-
}
|
| 11 |
-
}
|
| 12 |
|
| 13 |
|
| 14 |
def critical_strike_recipe(skill_ids, value, name="会心增加"):
|
| 15 |
-
return
|
| 16 |
-
|
| 17 |
-
"gain_skills": {
|
| 18 |
skill_id: {
|
| 19 |
"skill_critical_strike": value
|
| 20 |
} for skill_id in skill_ids
|
| 21 |
-
}
|
| 22 |
-
}
|
|
|
|
| 1 |
+
from base.buff import Buff
|
| 2 |
|
| 3 |
|
| 4 |
def damage_addition_recipe(skill_ids, value, name="伤害增加"):
|
| 5 |
+
return Buff(
|
| 6 |
+
-1, name, gain_skills={
|
|
|
|
| 7 |
skill_id: {
|
| 8 |
"skill_damage_addition": value
|
| 9 |
} for skill_id in skill_ids
|
| 10 |
+
})
|
|
|
|
| 11 |
|
| 12 |
|
| 13 |
def critical_strike_recipe(skill_ids, value, name="会心增加"):
|
| 14 |
+
return Buff(
|
| 15 |
+
-1, name, gain_skills={
|
|
|
|
| 16 |
skill_id: {
|
| 17 |
"skill_critical_strike": value
|
| 18 |
} for skill_id in skill_ids
|
| 19 |
+
})
|
|
|
base/skill.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
from base.attribute import Attribute
|
| 2 |
from base.constant import *
|
| 3 |
-
from
|
| 4 |
|
| 5 |
from typing import List, Union
|
| 6 |
from dataclasses import dataclass
|
|
@@ -31,6 +31,10 @@ class Skill:
|
|
| 31 |
_skill_critical_strike: int = 0
|
| 32 |
_skill_critical_power: int = 0
|
| 33 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
@property
|
| 35 |
def damage_base(self):
|
| 36 |
if isinstance(self._damage_base, list):
|
|
@@ -154,7 +158,7 @@ class Skill:
|
|
| 154 |
|
| 155 |
expected_damage = critical_strike * critical_damage + (1 - critical_strike) * damage
|
| 156 |
|
| 157 |
-
return damage, critical_damage, expected_damage
|
| 158 |
|
| 159 |
|
| 160 |
class PhysicalDamage(Skill):
|
|
|
|
| 1 |
from base.attribute import Attribute
|
| 2 |
from base.constant import *
|
| 3 |
+
from utils.damage import *
|
| 4 |
|
| 5 |
from typing import List, Union
|
| 6 |
from dataclasses import dataclass
|
|
|
|
| 31 |
_skill_critical_strike: int = 0
|
| 32 |
_skill_critical_power: int = 0
|
| 33 |
|
| 34 |
+
@property
|
| 35 |
+
def display_name(self):
|
| 36 |
+
return f"{self.skill_name}/{self.skill_id}-{self.skill_level}"
|
| 37 |
+
|
| 38 |
@property
|
| 39 |
def damage_base(self):
|
| 40 |
if isinstance(self._damage_base, list):
|
|
|
|
| 158 |
|
| 159 |
expected_damage = critical_strike * critical_damage + (1 - critical_strike) * damage
|
| 160 |
|
| 161 |
+
return damage, critical_strike, critical_damage, expected_damage
|
| 162 |
|
| 163 |
|
| 164 |
class PhysicalDamage(Skill):
|
general/recipes.py
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from base.status import Status
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
class EmptyRecipe:
|
| 5 |
+
def __call__(self, status: Status):
|
| 6 |
+
pass
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class DamageRecipe:
|
| 10 |
+
def __init__(self, skills, value):
|
| 11 |
+
self.skills = skills
|
| 12 |
+
self.value = value
|
| 13 |
+
|
| 14 |
+
def __call__(self, status: Status):
|
| 15 |
+
for skill in self.skills:
|
| 16 |
+
status.skills[skill].skill_damage_addition += self.value
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
class AttackPowerRecipe:
|
| 20 |
+
def __init__(self, skills, value):
|
| 21 |
+
self.skills = skills
|
| 22 |
+
self.value = value
|
| 23 |
+
|
| 24 |
+
def __call__(self, status: Status):
|
| 25 |
+
for skill in self.skills:
|
| 26 |
+
status.skills[skill].attack_power_cof_gain += self.value
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
class CriticalRecipe:
|
| 30 |
+
def __init__(self, skills, value):
|
| 31 |
+
self.skills = skills
|
| 32 |
+
self.value = value
|
| 33 |
+
|
| 34 |
+
def __call__(self, status: Status):
|
| 35 |
+
for skill in self.skills:
|
| 36 |
+
status.skills[skill].skill_critical_strike += self.value
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
class CDReductionRecipe:
|
| 40 |
+
def __init__(self, skills, value):
|
| 41 |
+
self.skills = skills
|
| 42 |
+
self.value = value
|
| 43 |
+
|
| 44 |
+
def __call__(self, status: Status):
|
| 45 |
+
for skill in self.skills:
|
| 46 |
+
status.skills[skill].cd_base -= self.value
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
class TickIncreaseRecipe:
|
| 50 |
+
def __init__(self, skills, value):
|
| 51 |
+
self.skills = skills
|
| 52 |
+
self.value = value
|
| 53 |
+
|
| 54 |
+
def __call__(self, status: Status):
|
| 55 |
+
for skill in self.skills:
|
| 56 |
+
status.skills[skill].tick_base += self.value
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
class IntervalReductionRecipe:
|
| 60 |
+
def __init__(self, skills, value):
|
| 61 |
+
self.skills = skills
|
| 62 |
+
self.value = value
|
| 63 |
+
|
| 64 |
+
def __call__(self, status: Status):
|
| 65 |
+
for skill in self.skills:
|
| 66 |
+
status.skills[skill].interval_base -= self.value
|
qt/app.py
CHANGED
|
@@ -8,18 +8,17 @@ from qt.scripts.top import top_script
|
|
| 8 |
from qt.components.equipments import EquipmentsWidget
|
| 9 |
from qt.scripts.equipments import equipments_script
|
| 10 |
from qt.components.talents import TalentsWidget
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
# from qt.components.consumables import ConsumablesWidget
|
| 15 |
# from qt.scripts.consumables import consumables_script
|
| 16 |
# from qt.components.bonuses import BonusesWidget
|
| 17 |
# from qt.scripts.bonuses import bonuses_script
|
| 18 |
-
|
| 19 |
-
|
| 20 |
|
| 21 |
-
from PySide6.QtWidgets import QApplication, QMainWindow, QStyleFactory,
|
| 22 |
-
QGridLayout
|
| 23 |
|
| 24 |
|
| 25 |
class MainWindow(QMainWindow):
|
|
@@ -30,9 +29,6 @@ class MainWindow(QMainWindow):
|
|
| 30 |
|
| 31 |
icon = QIcon("qt/assets/icon.ico")
|
| 32 |
self.setWindowIcon(icon)
|
| 33 |
-
self.message_box = QMessageBox()
|
| 34 |
-
|
| 35 |
-
self.message_box.setWindowIcon(icon)
|
| 36 |
|
| 37 |
self.central_widget = QWidget(self)
|
| 38 |
self.setCentralWidget(self.central_widget)
|
|
@@ -46,20 +42,31 @@ class MainWindow(QMainWindow):
|
|
| 46 |
layout.addWidget(self.top_widget)
|
| 47 |
layout.addWidget(self.config_widget)
|
| 48 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
self.equipments_widget = EquipmentsWidget()
|
| 50 |
config_layout.addWidget(self.equipments_widget, 0, 0)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
|
| 52 |
-
self.
|
| 53 |
-
config_layout.addWidget(self.
|
|
|
|
| 54 |
|
| 55 |
-
parser = top_script(
|
|
|
|
|
|
|
|
|
|
| 56 |
equipments = equipments_script(self.equipments_widget)
|
|
|
|
|
|
|
|
|
|
| 57 |
|
| 58 |
-
#
|
| 59 |
-
# config_layout.addWidget(self.equipments_widget, 1, 0)
|
| 60 |
-
#
|
| 61 |
-
# config_layout.addWidget(self.equipments_widget, 1, 1)
|
| 62 |
-
#
|
| 63 |
# self.talents_widget = TalentsWidget()
|
| 64 |
# self.tab_widget.addTab(self.talents_widget, "奇穴")
|
| 65 |
#
|
|
|
|
| 8 |
from qt.components.equipments import EquipmentsWidget
|
| 9 |
from qt.scripts.equipments import equipments_script
|
| 10 |
from qt.components.talents import TalentsWidget
|
| 11 |
+
from qt.scripts.talents import talents_script
|
| 12 |
+
from qt.components.recipes import RecipesWidget
|
| 13 |
+
from qt.scripts.recipes import recipes_script
|
| 14 |
# from qt.components.consumables import ConsumablesWidget
|
| 15 |
# from qt.scripts.consumables import consumables_script
|
| 16 |
# from qt.components.bonuses import BonusesWidget
|
| 17 |
# from qt.scripts.bonuses import bonuses_script
|
| 18 |
+
from qt.components.dashboard import DashboardWidget
|
| 19 |
+
from qt.scripts.dashboard import dashboard_script
|
| 20 |
|
| 21 |
+
from PySide6.QtWidgets import QApplication, QMainWindow, QStyleFactory, QVBoxLayout, QGridLayout, QWidget, QSizePolicy
|
|
|
|
| 22 |
|
| 23 |
|
| 24 |
class MainWindow(QMainWindow):
|
|
|
|
| 29 |
|
| 30 |
icon = QIcon("qt/assets/icon.ico")
|
| 31 |
self.setWindowIcon(icon)
|
|
|
|
|
|
|
|
|
|
| 32 |
|
| 33 |
self.central_widget = QWidget(self)
|
| 34 |
self.setCentralWidget(self.central_widget)
|
|
|
|
| 42 |
layout.addWidget(self.top_widget)
|
| 43 |
layout.addWidget(self.config_widget)
|
| 44 |
|
| 45 |
+
self.dashboard_widget = DashboardWidget()
|
| 46 |
+
config_layout.addWidget(self.dashboard_widget, 0, 1)
|
| 47 |
+
self.dashboard_widget.setSizePolicy(QSizePolicy.Policy.MinimumExpanding, QSizePolicy.Policy.MinimumExpanding)
|
| 48 |
+
|
| 49 |
self.equipments_widget = EquipmentsWidget()
|
| 50 |
config_layout.addWidget(self.equipments_widget, 0, 0)
|
| 51 |
+
# self.equipments_widget.setSizePolicy(QSizePolicy.Policy.MinimumExpanding, QSizePolicy.Policy.MinimumExpanding)
|
| 52 |
+
|
| 53 |
+
self.talents_widget = TalentsWidget()
|
| 54 |
+
config_layout.addWidget(self.talents_widget, 1, 0)
|
| 55 |
+
# self.talents_widget.setSizePolicy(QSizePolicy.Policy.MinimumExpanding, QSizePolicy.Policy.MinimumExpanding)
|
| 56 |
|
| 57 |
+
self.recipes_widget = RecipesWidget()
|
| 58 |
+
config_layout.addWidget(self.recipes_widget, 2, 0)
|
| 59 |
+
# self.recipes_widget.setSizePolicy(QSizePolicy.Policy.MinimumExpanding, QSizePolicy.Policy.MinimumExpanding)
|
| 60 |
|
| 61 |
+
parser = top_script(
|
| 62 |
+
self.top_widget, self.config_widget, self.dashboard_widget,
|
| 63 |
+
self.equipments_widget, self.talents_widget, self.recipes_widget
|
| 64 |
+
)
|
| 65 |
equipments = equipments_script(self.equipments_widget)
|
| 66 |
+
talents = talents_script(self.talents_widget)
|
| 67 |
+
recipes = recipes_script(self.recipes_widget)
|
| 68 |
+
dashboard_script(parser, equipments, talents, recipes, self.dashboard_widget)
|
| 69 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 70 |
# self.talents_widget = TalentsWidget()
|
| 71 |
# self.tab_widget.addTab(self.talents_widget, "奇穴")
|
| 72 |
#
|
qt/components/__init__.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
from PySide6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel, QAbstractItemView, QTableWidgetItem, \
|
| 2 |
-
QHeaderView, QSizePolicy
|
| 3 |
from PySide6.QtWidgets import QComboBox, QRadioButton, QTextBrowser, QTextEdit, QSpinBox, QListWidget, QTableWidget
|
| 4 |
from PySide6.QtCore import Qt
|
| 5 |
|
|
@@ -31,34 +31,39 @@ class TableWithLabel(LabelWidget):
|
|
| 31 |
if headers:
|
| 32 |
self.table.setColumnCount(len(headers))
|
| 33 |
self.table.setHorizontalHeaderLabels(headers)
|
|
|
|
|
|
|
| 34 |
|
| 35 |
self.table.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers)
|
|
|
|
|
|
|
|
|
|
| 36 |
|
| 37 |
layout.addWidget(self.label)
|
| 38 |
layout.addWidget(self.table)
|
| 39 |
|
| 40 |
-
layout.addStretch()
|
| 41 |
-
|
| 42 |
def set_content(self, content):
|
| 43 |
self.table.setRowCount(len(content))
|
| 44 |
|
| 45 |
for i, row in enumerate(content):
|
| 46 |
for j, e in enumerate(row):
|
| 47 |
self.table.setItem(i, j, QTableWidgetItem(e))
|
|
|
|
| 48 |
self.table.resizeColumnsToContents()
|
| 49 |
|
| 50 |
|
| 51 |
class ListWithLabel(LabelWidget):
|
| 52 |
-
def __init__(self, label, items: list = None):
|
| 53 |
super().__init__(label)
|
| 54 |
-
layout = QVBoxLayout()
|
| 55 |
-
|
|
|
|
| 56 |
|
| 57 |
self.list = QListWidget()
|
| 58 |
self.list.setSelectionMode(QAbstractItemView.SelectionMode.MultiSelection)
|
| 59 |
|
| 60 |
if items:
|
| 61 |
-
self.
|
| 62 |
layout.addWidget(self.label)
|
| 63 |
layout.addWidget(self.list)
|
| 64 |
|
|
@@ -86,11 +91,12 @@ class ComboWithLabel(LabelWidget):
|
|
| 86 |
|
| 87 |
layout.addStretch()
|
| 88 |
|
| 89 |
-
def set_items(self, items):
|
| 90 |
self.combo_box.blockSignals(True)
|
| 91 |
self.combo_box.clear()
|
| 92 |
self.combo_box.addItems(items)
|
| 93 |
self.combo_box.blockSignals(False)
|
|
|
|
| 94 |
|
| 95 |
|
| 96 |
class RadioWithLabel(LabelWidget):
|
|
@@ -125,7 +131,7 @@ class SpinWithLabel(LabelWidget):
|
|
| 125 |
if maximum:
|
| 126 |
self.spin_box.setMaximum(maximum + 1)
|
| 127 |
else:
|
| 128 |
-
self.spin_box.setMaximum(
|
| 129 |
|
| 130 |
if value:
|
| 131 |
self.spin_box.setValue(value)
|
|
@@ -173,3 +179,6 @@ class LabelWithLabel(QWidget):
|
|
| 173 |
layout.addWidget(self.text)
|
| 174 |
|
| 175 |
layout.addStretch()
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
from PySide6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel, QAbstractItemView, QTableWidgetItem, \
|
| 2 |
+
QHeaderView, QSizePolicy, QListWidgetItem, QSpacerItem
|
| 3 |
from PySide6.QtWidgets import QComboBox, QRadioButton, QTextBrowser, QTextEdit, QSpinBox, QListWidget, QTableWidget
|
| 4 |
from PySide6.QtCore import Qt
|
| 5 |
|
|
|
|
| 31 |
if headers:
|
| 32 |
self.table.setColumnCount(len(headers))
|
| 33 |
self.table.setHorizontalHeaderLabels(headers)
|
| 34 |
+
else:
|
| 35 |
+
self.table.horizontalHeader().setVisible(False)
|
| 36 |
|
| 37 |
self.table.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers)
|
| 38 |
+
self.table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
|
| 39 |
+
self.table.verticalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
|
| 40 |
+
self.table.verticalHeader().setVisible(False)
|
| 41 |
|
| 42 |
layout.addWidget(self.label)
|
| 43 |
layout.addWidget(self.table)
|
| 44 |
|
|
|
|
|
|
|
| 45 |
def set_content(self, content):
|
| 46 |
self.table.setRowCount(len(content))
|
| 47 |
|
| 48 |
for i, row in enumerate(content):
|
| 49 |
for j, e in enumerate(row):
|
| 50 |
self.table.setItem(i, j, QTableWidgetItem(e))
|
| 51 |
+
|
| 52 |
self.table.resizeColumnsToContents()
|
| 53 |
|
| 54 |
|
| 55 |
class ListWithLabel(LabelWidget):
|
| 56 |
+
def __init__(self, label, max_select: int = 4, items: list = None):
|
| 57 |
super().__init__(label)
|
| 58 |
+
layout = QVBoxLayout(self)
|
| 59 |
+
|
| 60 |
+
self.max_select = max_select
|
| 61 |
|
| 62 |
self.list = QListWidget()
|
| 63 |
self.list.setSelectionMode(QAbstractItemView.SelectionMode.MultiSelection)
|
| 64 |
|
| 65 |
if items:
|
| 66 |
+
self.set_items(items)
|
| 67 |
layout.addWidget(self.label)
|
| 68 |
layout.addWidget(self.list)
|
| 69 |
|
|
|
|
| 91 |
|
| 92 |
layout.addStretch()
|
| 93 |
|
| 94 |
+
def set_items(self, items, default_index=0):
|
| 95 |
self.combo_box.blockSignals(True)
|
| 96 |
self.combo_box.clear()
|
| 97 |
self.combo_box.addItems(items)
|
| 98 |
self.combo_box.blockSignals(False)
|
| 99 |
+
self.combo_box.setCurrentIndex(default_index)
|
| 100 |
|
| 101 |
|
| 102 |
class RadioWithLabel(LabelWidget):
|
|
|
|
| 131 |
if maximum:
|
| 132 |
self.spin_box.setMaximum(maximum + 1)
|
| 133 |
else:
|
| 134 |
+
self.spin_box.setMaximum(10 ** 8)
|
| 135 |
|
| 136 |
if value:
|
| 137 |
self.spin_box.setValue(value)
|
|
|
|
| 179 |
layout.addWidget(self.text)
|
| 180 |
|
| 181 |
layout.addStretch()
|
| 182 |
+
|
| 183 |
+
def set_text(self, text):
|
| 184 |
+
self.text.setText(text)
|
qt/components/dashboard.py
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from PySide6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QTabWidget
|
| 2 |
+
|
| 3 |
+
from qt.components import ComboWithLabel, SpinWithLabel, TextWithLabel, LabelWithLabel, TableWithLabel
|
| 4 |
+
from base.constant import SHIELD_BASE_MAP
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
class DashboardWidget(QWidget):
|
| 8 |
+
def __init__(self):
|
| 9 |
+
super().__init__()
|
| 10 |
+
layout = QVBoxLayout()
|
| 11 |
+
self.setLayout(layout)
|
| 12 |
+
|
| 13 |
+
top = QWidget()
|
| 14 |
+
top_layout = QHBoxLayout(top)
|
| 15 |
+
layout.addWidget(top)
|
| 16 |
+
|
| 17 |
+
self.fight_select = ComboWithLabel("选择战斗")
|
| 18 |
+
top_layout.addWidget(self.fight_select)
|
| 19 |
+
self.target_level = ComboWithLabel("目标等级", items=[str(level) for level in SHIELD_BASE_MAP])
|
| 20 |
+
top_layout.addWidget(self.target_level)
|
| 21 |
+
self.duration = SpinWithLabel("战斗时长", maximum=3600, value=180)
|
| 22 |
+
top_layout.addWidget(self.duration)
|
| 23 |
+
|
| 24 |
+
self.button = QPushButton(text="开始模拟!")
|
| 25 |
+
layout.addWidget(self.button)
|
| 26 |
+
|
| 27 |
+
bottom = QWidget()
|
| 28 |
+
bottom_layout = QHBoxLayout(bottom)
|
| 29 |
+
layout.addWidget(bottom)
|
| 30 |
+
|
| 31 |
+
tab = QTabWidget()
|
| 32 |
+
bottom_layout.addWidget(tab, 2)
|
| 33 |
+
result = QWidget()
|
| 34 |
+
result_layout = QVBoxLayout(result)
|
| 35 |
+
bottom_layout.addWidget(result, 1)
|
| 36 |
+
|
| 37 |
+
attribute = QWidget()
|
| 38 |
+
attribute_layout = QHBoxLayout(attribute)
|
| 39 |
+
tab.addTab(attribute, "属性")
|
| 40 |
+
|
| 41 |
+
self.init_attribute = TableWithLabel("增益前属性", column_count=2)
|
| 42 |
+
attribute_layout.addWidget(self.init_attribute)
|
| 43 |
+
self.final_attribute = TableWithLabel("增益后属性", column_count=2)
|
| 44 |
+
attribute_layout.addWidget(self.final_attribute)
|
| 45 |
+
|
| 46 |
+
detail = QWidget()
|
| 47 |
+
detail_layout = QVBoxLayout(detail)
|
| 48 |
+
tab.addTab(detail, "伤害总结")
|
| 49 |
+
self.details = {}
|
| 50 |
+
self.skill_combo = ComboWithLabel("选择技能")
|
| 51 |
+
detail_layout.addWidget(self.skill_combo)
|
| 52 |
+
self.status_combo = ComboWithLabel("选择增益")
|
| 53 |
+
detail_layout.addWidget(self.status_combo)
|
| 54 |
+
detail_table = QWidget()
|
| 55 |
+
detail_table_layout = QHBoxLayout(detail_table)
|
| 56 |
+
self.damage_detail = TableWithLabel("伤害细节", column_count=2)
|
| 57 |
+
detail_table_layout.addWidget(self.damage_detail)
|
| 58 |
+
self.gradient_detail = TableWithLabel("属性收益", column_count=2)
|
| 59 |
+
detail_table_layout.addWidget(self.damage_detail)
|
| 60 |
+
detail_layout.addWidget(detail_table)
|
| 61 |
+
|
| 62 |
+
detail_layout.addStretch()
|
| 63 |
+
|
| 64 |
+
self.summary = TableWithLabel("伤害统计", headers=["技能/次数", "命中/%", "会心/%", "伤害/%"])
|
| 65 |
+
|
| 66 |
+
tab.addTab(self.summary, "战斗总结")
|
| 67 |
+
|
| 68 |
+
self.dps = LabelWithLabel("每秒伤害")
|
| 69 |
+
result_layout.addWidget(self.dps)
|
| 70 |
+
|
| 71 |
+
self.gradients = TableWithLabel("属性收益", column_count=2)
|
| 72 |
+
|
| 73 |
+
result_layout.addWidget(self.gradients)
|
| 74 |
+
|
| 75 |
+
result_layout.addStretch()
|
| 76 |
+
|
| 77 |
+
layout.addStretch()
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
|
qt/components/equipments.py
CHANGED
|
@@ -3,8 +3,8 @@ import os
|
|
| 3 |
|
| 4 |
from qt.constant import POSITION_MAP, STONES_POSITIONS, EQUIPMENTS_DIR, ENCHANTS_DIR, STONES_DIR, MAX_STONE_ATTR
|
| 5 |
from qt.constant import EMBED_POSITIONS, MAX_EMBED_LEVEL, MAX_STONE_LEVEL, SPECIAL_ENCHANT_POSITIONS
|
| 6 |
-
from qt.components import ComboWithLabel, RadioWithLabel,
|
| 7 |
-
from PySide6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QGridLayout, QTabWidget
|
| 8 |
from PySide6.QtCore import Qt
|
| 9 |
|
| 10 |
|
|
@@ -72,14 +72,15 @@ class EquipmentWidget(QWidget):
|
|
| 72 |
stone_attr = ComboWithLabel(f"五彩石属性-{i + 1}")
|
| 73 |
self.stone_attrs.append(stone_attr)
|
| 74 |
detail_layout.addWidget(stone_attr, 2, i + 1)
|
|
|
|
| 75 |
|
| 76 |
-
self.base_attr =
|
| 77 |
output_layout.addWidget(self.base_attr)
|
| 78 |
-
self.magic_attr =
|
| 79 |
output_layout.addWidget(self.magic_attr)
|
| 80 |
-
self.embed_attr =
|
| 81 |
output_layout.addWidget(self.embed_attr)
|
| 82 |
-
|
| 83 |
|
| 84 |
|
| 85 |
class EquipmentsWidget(QTabWidget):
|
|
|
|
| 3 |
|
| 4 |
from qt.constant import POSITION_MAP, STONES_POSITIONS, EQUIPMENTS_DIR, ENCHANTS_DIR, STONES_DIR, MAX_STONE_ATTR
|
| 5 |
from qt.constant import EMBED_POSITIONS, MAX_EMBED_LEVEL, MAX_STONE_LEVEL, SPECIAL_ENCHANT_POSITIONS
|
| 6 |
+
from qt.components import ComboWithLabel, RadioWithLabel, TableWithLabel
|
| 7 |
+
from PySide6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QGridLayout, QTabWidget, QSizePolicy, QSpacerItem
|
| 8 |
from PySide6.QtCore import Qt
|
| 9 |
|
| 10 |
|
|
|
|
| 72 |
stone_attr = ComboWithLabel(f"五彩石属性-{i + 1}")
|
| 73 |
self.stone_attrs.append(stone_attr)
|
| 74 |
detail_layout.addWidget(stone_attr, 2, i + 1)
|
| 75 |
+
self.detail_widget.hide()
|
| 76 |
|
| 77 |
+
self.base_attr = TableWithLabel("基本属性", column_count=2)
|
| 78 |
output_layout.addWidget(self.base_attr)
|
| 79 |
+
self.magic_attr = TableWithLabel("精炼属性", column_count=2)
|
| 80 |
output_layout.addWidget(self.magic_attr)
|
| 81 |
+
self.embed_attr = TableWithLabel("镶嵌属性", column_count=2)
|
| 82 |
output_layout.addWidget(self.embed_attr)
|
| 83 |
+
self.output_widget.hide()
|
| 84 |
|
| 85 |
|
| 86 |
class EquipmentsWidget(QTabWidget):
|
qt/components/recipes.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from qt.components import ListWithLabel
|
| 2 |
+
from PySide6.QtWidgets import QWidget, QGridLayout
|
| 3 |
+
|
| 4 |
+
from qt.constant import MAX_RECIPE_SKILLS
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
class RecipesWidget(QWidget):
|
| 8 |
+
def __init__(self):
|
| 9 |
+
super().__init__()
|
| 10 |
+
layout = QGridLayout()
|
| 11 |
+
self.setLayout(layout)
|
| 12 |
+
|
| 13 |
+
self.recipes = []
|
| 14 |
+
|
| 15 |
+
columns = 6
|
| 16 |
+
rows = MAX_RECIPE_SKILLS // columns
|
| 17 |
+
for i in range(rows):
|
| 18 |
+
for j in range(columns):
|
| 19 |
+
recipe = ListWithLabel("")
|
| 20 |
+
self.recipes.append(recipe)
|
| 21 |
+
layout.addWidget(recipe, i, j)
|
| 22 |
+
|
| 23 |
+
def __getitem__(self, item) -> ListWithLabel:
|
| 24 |
+
return self.recipes[item]
|
| 25 |
+
|
| 26 |
+
def values(self) -> list[ListWithLabel]:
|
| 27 |
+
return self.recipes
|
qt/constant.py
CHANGED
|
@@ -1,10 +1,13 @@
|
|
| 1 |
import os
|
|
|
|
|
|
|
| 2 |
from dataclasses import dataclass
|
| 3 |
-
from typing import Type,
|
| 4 |
|
| 5 |
from base.attribute import Attribute
|
| 6 |
from base.buff import Buff
|
| 7 |
from base.skill import Skill
|
|
|
|
| 8 |
# from general.gains import equipment
|
| 9 |
|
| 10 |
from schools import first
|
|
@@ -126,9 +129,24 @@ class School:
|
|
| 126 |
kind: str
|
| 127 |
attribute: Type[Attribute]
|
| 128 |
formation: str
|
| 129 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 130 |
skills: Dict[int, Skill]
|
| 131 |
buffs: Dict[int, Buff]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 132 |
|
| 133 |
|
| 134 |
SUPPORT_SCHOOL = {
|
|
@@ -138,9 +156,30 @@ SUPPORT_SCHOOL = {
|
|
| 138 |
kind="外功",
|
| 139 |
attribute=first.BeiAoJue,
|
| 140 |
formation="霜岚洗锋阵",
|
| 141 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 142 |
skills=first.SKILLS,
|
| 143 |
-
buffs=first.BUFFS
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 144 |
)
|
| 145 |
}
|
| 146 |
|
|
|
|
| 1 |
import os
|
| 2 |
+
|
| 3 |
+
|
| 4 |
from dataclasses import dataclass
|
| 5 |
+
from typing import Type, List, Dict
|
| 6 |
|
| 7 |
from base.attribute import Attribute
|
| 8 |
from base.buff import Buff
|
| 9 |
from base.skill import Skill
|
| 10 |
+
|
| 11 |
# from general.gains import equipment
|
| 12 |
|
| 13 |
from schools import first
|
|
|
|
| 129 |
kind: str
|
| 130 |
attribute: Type[Attribute]
|
| 131 |
formation: str
|
| 132 |
+
talent_gains: List[Dict[int, Buff]]
|
| 133 |
+
talent_decoder: Dict[int, str]
|
| 134 |
+
talent_encoder: Dict[str, int]
|
| 135 |
+
recipe_gains: Dict[str, Dict[str, Buff]]
|
| 136 |
+
recipes: Dict[str, List[str]]
|
| 137 |
skills: Dict[int, Skill]
|
| 138 |
buffs: Dict[int, Buff]
|
| 139 |
+
display_attrs: Dict[str, str]
|
| 140 |
+
|
| 141 |
+
def attr_content(self, attribute):
|
| 142 |
+
content = []
|
| 143 |
+
for attr, name in self.display_attrs.items():
|
| 144 |
+
value = getattr(attribute, attr)
|
| 145 |
+
if isinstance(value, int):
|
| 146 |
+
content.append([name, f"{value}"])
|
| 147 |
+
else:
|
| 148 |
+
content.append([name, f"{round(value * 100, 2)}%"])
|
| 149 |
+
return content
|
| 150 |
|
| 151 |
|
| 152 |
SUPPORT_SCHOOL = {
|
|
|
|
| 156 |
kind="外功",
|
| 157 |
attribute=first.BeiAoJue,
|
| 158 |
formation="霜岚洗锋阵",
|
| 159 |
+
talent_gains=first.TALENT_GAINS,
|
| 160 |
+
talent_decoder=first.TALENT_DECODER,
|
| 161 |
+
talent_encoder=first.TALENT_ENCODER,
|
| 162 |
+
recipe_gains=first.RECIPE_GAINS,
|
| 163 |
+
recipes=first.RECIPES,
|
| 164 |
skills=first.SKILLS,
|
| 165 |
+
buffs=first.BUFFS,
|
| 166 |
+
display_attrs={
|
| 167 |
+
"strength": "力道",
|
| 168 |
+
"base_physical_attack_power": "基础攻击",
|
| 169 |
+
"physical_attack_power": "攻击",
|
| 170 |
+
"base_physical_critical_strike": "会心等级",
|
| 171 |
+
"physical_critical_strike": "会心",
|
| 172 |
+
"physical_critical_power_base": "会效等级",
|
| 173 |
+
"physical_critical_power": "会效",
|
| 174 |
+
"base_physical_overcome": "基础破防",
|
| 175 |
+
"final_physical_overcome": "最终破防",
|
| 176 |
+
"physical_overcome": "破防",
|
| 177 |
+
"weapon_damage_base": "基础武器伤害",
|
| 178 |
+
"weapon_damage_rand": "浮动武器伤害",
|
| 179 |
+
"strain_base": "无双等级",
|
| 180 |
+
"strain": "无双",
|
| 181 |
+
"surplus": "破招",
|
| 182 |
+
}
|
| 183 |
)
|
| 184 |
}
|
| 185 |
|
qt/scripts/__init__.py
ADDED
|
File without changes
|
qt/scripts/dashboard.py
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from PySide6.QtWidgets import QMessageBox
|
| 2 |
+
|
| 3 |
+
from qt.components.dashboard import DashboardWidget
|
| 4 |
+
from qt.constant import ATTR_TYPE_TRANSLATE
|
| 5 |
+
from qt.scripts.top import Parser
|
| 6 |
+
from qt.scripts.equipments import Equipments
|
| 7 |
+
# from qt.scripts.consumables import Consumables
|
| 8 |
+
# from qt.scripts.bonuses import Bonuses
|
| 9 |
+
from qt.scripts.recipes import Recipes
|
| 10 |
+
from qt.scripts.talents import Talents
|
| 11 |
+
from utils.analyzer import analyze_details
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def summary_content(summary, total_damage):
|
| 15 |
+
content = []
|
| 16 |
+
for skill in sorted(summary, key=lambda x: summary[x]['damage'], reverse=True):
|
| 17 |
+
detail = summary[skill]
|
| 18 |
+
critical = round(detail['critical'], 2)
|
| 19 |
+
critical_rate = round(detail['critical'] / detail['count'] * 100, 2)
|
| 20 |
+
hit = round(detail['count'] - critical, 2)
|
| 21 |
+
hit_rate = round(100 - critical_rate, 2)
|
| 22 |
+
damage = round(detail['damage'], 2)
|
| 23 |
+
damage_rate = round(damage / total_damage * 100, 2)
|
| 24 |
+
content.append(
|
| 25 |
+
[f"{skill}/{detail['count']}",
|
| 26 |
+
f"{hit}/{hit_rate}%", f"{critical}/{critical_rate}%", f"{damage}/{damage_rate}%"]
|
| 27 |
+
)
|
| 28 |
+
return content
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
def detail_content(detail):
|
| 32 |
+
damage_content = [
|
| 33 |
+
["命中伤害", f"{detail['damage']}"],
|
| 34 |
+
["会心伤害", f"{detail['critical_damage']}"],
|
| 35 |
+
["期望会心", f"{round(detail['critical_strike'] * 100, 2)}%"],
|
| 36 |
+
["期望伤害", f"{round(detail['expected_damage'], 2)}"]
|
| 37 |
+
]
|
| 38 |
+
gradient_content = [
|
| 39 |
+
[ATTR_TYPE_TRANSLATE[k], f"{round(v / detail['expected_damage'] * 100, 2)}%"]
|
| 40 |
+
for k, v in detail['gradients'].items()
|
| 41 |
+
]
|
| 42 |
+
|
| 43 |
+
return damage_content, gradient_content
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def dashboard_script(parser: Parser,
|
| 47 |
+
equipments: Equipments, talents: Talents, recipes: Recipes,
|
| 48 |
+
# consumables: Consumables, bonuses: Bonuses,
|
| 49 |
+
dashboard_widget: DashboardWidget):
|
| 50 |
+
|
| 51 |
+
def select_fight(text):
|
| 52 |
+
index = parser.record_index[text]
|
| 53 |
+
dashboard_widget.duration.set_value(parser.duration(index))
|
| 54 |
+
|
| 55 |
+
dashboard_widget.fight_select.combo_box.currentTextChanged.connect(select_fight)
|
| 56 |
+
|
| 57 |
+
def formulate():
|
| 58 |
+
duration = dashboard_widget.duration.spin_box.value()
|
| 59 |
+
record = parser.records[parser.record_index[dashboard_widget.fight_select.combo_box.currentText()]]
|
| 60 |
+
|
| 61 |
+
school = parser.school
|
| 62 |
+
attribute = school.attribute()
|
| 63 |
+
attribute.target_level = int(dashboard_widget.target_level.combo_box.currentText())
|
| 64 |
+
for attr, value in equipments.attrs.items():
|
| 65 |
+
setattr(attribute, attr, getattr(attribute, attr) + value)
|
| 66 |
+
# for attr, value in consumables.attrs.items():
|
| 67 |
+
# setattr(attribute, attr, getattr(attribute, attr) + value)
|
| 68 |
+
|
| 69 |
+
dashboard_widget.init_attribute.set_content(school.attr_content(attribute))
|
| 70 |
+
# gains = sum([equipments.gains, talents.gains, recipes.gains, bonuses.gains], [])
|
| 71 |
+
#
|
| 72 |
+
# dashboard_widget.final_attribute.set_text(school.attr_text(attribute))
|
| 73 |
+
total_damage, total_gradient, details, summary = analyze_details(record, attribute, school)
|
| 74 |
+
dashboard_widget.dps.set_text(str(round(total_damage / duration)))
|
| 75 |
+
dashboard_widget.gradients.set_content(
|
| 76 |
+
[[ATTR_TYPE_TRANSLATE[k], f"{round(v, 2)}%"] for k, v in total_gradient.items()]
|
| 77 |
+
)
|
| 78 |
+
dashboard_widget.details = details
|
| 79 |
+
dashboard_widget.skill_combo.set_items(list(details), default_index=-1)
|
| 80 |
+
dashboard_widget.summary.set_content(summary_content(summary, total_damage))
|
| 81 |
+
|
| 82 |
+
dashboard_widget.button.clicked.connect(formulate)
|
| 83 |
+
|
| 84 |
+
def select_skill(skill):
|
| 85 |
+
if skill:
|
| 86 |
+
dashboard_widget.status_combo.set_items(list(dashboard_widget.details[skill]))
|
| 87 |
+
else:
|
| 88 |
+
dashboard_widget.status_combo.combo_box.clear()
|
| 89 |
+
|
| 90 |
+
dashboard_widget.skill_combo.combo_box.currentTextChanged.connect(select_skill)
|
| 91 |
+
|
| 92 |
+
def select_status(status):
|
| 93 |
+
if status:
|
| 94 |
+
skill = dashboard_widget.skill_combo.combo_box.currentText()
|
| 95 |
+
detail = dashboard_widget.details[skill][status]
|
| 96 |
+
damage_content, gradient_content = detail_content(detail)
|
| 97 |
+
dashboard_widget.damage_detail.set_content(damage_content)
|
| 98 |
+
dashboard_widget.gradient_detail.set_content(gradient_content)
|
| 99 |
+
else:
|
| 100 |
+
dashboard_widget.damage_detail.table.clear()
|
| 101 |
+
dashboard_widget.gradient_detail.table.clear()
|
| 102 |
+
|
| 103 |
+
dashboard_widget.status_combo.combo_box.currentTextChanged.connect(select_status)
|
qt/scripts/equipments.py
CHANGED
|
@@ -11,6 +11,9 @@ class Enchant:
|
|
| 11 |
name: str
|
| 12 |
attr: Dict[str, int]
|
| 13 |
|
|
|
|
|
|
|
|
|
|
| 14 |
def clear(self):
|
| 15 |
self.name = ""
|
| 16 |
self.attr = {}
|
|
@@ -21,6 +24,9 @@ class Stone:
|
|
| 21 |
level: int
|
| 22 |
attr: Dict[str, int]
|
| 23 |
|
|
|
|
|
|
|
|
|
|
| 24 |
def clear(self):
|
| 25 |
self.name = ""
|
| 26 |
self.attr = {}
|
|
@@ -40,6 +46,8 @@ class Equipment:
|
|
| 40 |
set_gain: Dict[int, List[int]]
|
| 41 |
|
| 42 |
def __init__(self, label):
|
|
|
|
|
|
|
| 43 |
self.label = label
|
| 44 |
self.position = POSITION_MAP[label]
|
| 45 |
|
|
@@ -87,21 +95,19 @@ class Equipment:
|
|
| 87 |
}
|
| 88 |
|
| 89 |
@property
|
| 90 |
-
def
|
| 91 |
-
return
|
| 92 |
|
| 93 |
@property
|
| 94 |
-
def
|
| 95 |
if strength_attr := self.strength_attr:
|
| 96 |
-
return "
|
| 97 |
-
f"{ATTR_TYPE_TRANSLATE[k]}:\t{v}(+{strength_attr[k]})" for k, v in self.magic_attr.items()
|
| 98 |
-
])
|
| 99 |
else:
|
| 100 |
-
return
|
| 101 |
|
| 102 |
@property
|
| 103 |
-
def
|
| 104 |
-
return
|
| 105 |
|
| 106 |
|
| 107 |
class Equipments:
|
|
@@ -177,8 +183,7 @@ def equipments_script(equipments_widget: EquipmentsWidget):
|
|
| 177 |
widget = equipments_widget[label]
|
| 178 |
equipment = equipments[label]
|
| 179 |
|
| 180 |
-
def inner(
|
| 181 |
-
equipment_name = widget.equipment.combo_box.currentText()
|
| 182 |
|
| 183 |
if not equipment_name:
|
| 184 |
equipment.clear()
|
|
@@ -197,7 +202,7 @@ def equipments_script(equipments_widget: EquipmentsWidget):
|
|
| 197 |
setattr(equipment, k, v)
|
| 198 |
|
| 199 |
if equipment.base:
|
| 200 |
-
widget.base_attr.
|
| 201 |
widget.base_attr.show()
|
| 202 |
else:
|
| 203 |
widget.base_attr.hide()
|
|
@@ -211,7 +216,7 @@ def equipments_script(equipments_widget: EquipmentsWidget):
|
|
| 211 |
if equipment.embed:
|
| 212 |
for i, (attr, value) in enumerate(equipment.embed.items()):
|
| 213 |
widget.embed_levels[i].set_label(f"镶嵌等级-{ATTR_TYPE_TRANSLATE[attr]}")
|
| 214 |
-
widget.embed_attr.
|
| 215 |
widget.embed_attr.show()
|
| 216 |
else:
|
| 217 |
widget.embed_attr.hide()
|
|
@@ -228,8 +233,7 @@ def equipments_script(equipments_widget: EquipmentsWidget):
|
|
| 228 |
widget = equipments_widget.equipments[label]
|
| 229 |
equipment = equipments[label]
|
| 230 |
|
| 231 |
-
def inner(
|
| 232 |
-
enchant_name = widget.enchant.combo_box.currentText()
|
| 233 |
if enchant_name:
|
| 234 |
enchant_detail = widget.enchant_json[enchant_name]
|
| 235 |
equipment.enchant.name = enchant_name
|
|
@@ -244,7 +248,7 @@ def equipments_script(equipments_widget: EquipmentsWidget):
|
|
| 244 |
widget = equipments_widget.equipments[label]
|
| 245 |
equipment = equipments[label]
|
| 246 |
|
| 247 |
-
def inner(
|
| 248 |
if widget.special_enchant and widget.special_enchant.radio_button.isChecked():
|
| 249 |
equipment.special_enchant_gain = equipment.special_enchant
|
| 250 |
else:
|
|
@@ -258,8 +262,8 @@ def equipments_script(equipments_widget: EquipmentsWidget):
|
|
| 258 |
|
| 259 |
def inner(index):
|
| 260 |
equipment.strength_level = index
|
| 261 |
-
if
|
| 262 |
-
widget.magic_attr.
|
| 263 |
widget.magic_attr.show()
|
| 264 |
else:
|
| 265 |
widget.magic_attr.hide()
|
|
@@ -272,8 +276,8 @@ def equipments_script(equipments_widget: EquipmentsWidget):
|
|
| 272 |
|
| 273 |
def inner(index):
|
| 274 |
equipment.embed_levels[i] = index
|
| 275 |
-
if
|
| 276 |
-
widget.embed_attr.
|
| 277 |
widget.embed_attr.show()
|
| 278 |
else:
|
| 279 |
widget.embed_attr.hide()
|
|
@@ -284,7 +288,7 @@ def equipments_script(equipments_widget: EquipmentsWidget):
|
|
| 284 |
widget = equipments_widget.equipments[label]
|
| 285 |
equipment = equipments[label]
|
| 286 |
|
| 287 |
-
def inner(
|
| 288 |
level = widget.stone_level.combo_box.currentText()
|
| 289 |
|
| 290 |
current = widget.stones_json
|
|
@@ -297,10 +301,11 @@ def equipments_script(equipments_widget: EquipmentsWidget):
|
|
| 297 |
else:
|
| 298 |
break
|
| 299 |
if level in current:
|
| 300 |
-
for k, v in current[level]:
|
| 301 |
setattr(equipment.stone, k, v)
|
| 302 |
else:
|
| 303 |
widget.stone_attrs[i].set_items([""] + [ATTR_TYPE_TRANSLATE[k] for k in current])
|
|
|
|
| 304 |
|
| 305 |
i += 1
|
| 306 |
while i < len(widget.stone_attrs):
|
|
@@ -311,11 +316,11 @@ def equipments_script(equipments_widget: EquipmentsWidget):
|
|
| 311 |
|
| 312 |
for equipment_label, equipment_widget in equipments_widget.items():
|
| 313 |
|
| 314 |
-
equipment_widget.equipment.combo_box.
|
| 315 |
if equipment_widget.special_enchant:
|
| 316 |
equipment_widget.special_enchant.radio_button.clicked.connect(special_enchant_update(equipment_label))
|
| 317 |
if equipment_widget.enchant:
|
| 318 |
-
equipment_widget.enchant.combo_box.
|
| 319 |
equipment_widget.strength_level.combo_box.currentIndexChanged.connect(strength_level_update(equipment_label))
|
| 320 |
for n, embed_widget in enumerate(equipment_widget.embed_levels):
|
| 321 |
embed_widget.combo_box.currentIndexChanged.connect(embed_level_update(n, equipment_label))
|
|
|
|
| 11 |
name: str
|
| 12 |
attr: Dict[str, int]
|
| 13 |
|
| 14 |
+
def __init__(self):
|
| 15 |
+
self.clear()
|
| 16 |
+
|
| 17 |
def clear(self):
|
| 18 |
self.name = ""
|
| 19 |
self.attr = {}
|
|
|
|
| 24 |
level: int
|
| 25 |
attr: Dict[str, int]
|
| 26 |
|
| 27 |
+
def __init__(self):
|
| 28 |
+
self.clear()
|
| 29 |
+
|
| 30 |
def clear(self):
|
| 31 |
self.name = ""
|
| 32 |
self.attr = {}
|
|
|
|
| 46 |
set_gain: Dict[int, List[int]]
|
| 47 |
|
| 48 |
def __init__(self, label):
|
| 49 |
+
self.clear()
|
| 50 |
+
|
| 51 |
self.label = label
|
| 52 |
self.position = POSITION_MAP[label]
|
| 53 |
|
|
|
|
| 95 |
}
|
| 96 |
|
| 97 |
@property
|
| 98 |
+
def base_attr_content(self):
|
| 99 |
+
return [[ATTR_TYPE_TRANSLATE[k], str(v)] for k, v in self.base_attr.items()]
|
| 100 |
|
| 101 |
@property
|
| 102 |
+
def magic_attr_content(self):
|
| 103 |
if strength_attr := self.strength_attr:
|
| 104 |
+
return [[ATTR_TYPE_TRANSLATE[k], f"{v}(+{strength_attr[k]})"] for k, v in self.magic_attr.items()]
|
|
|
|
|
|
|
| 105 |
else:
|
| 106 |
+
return [[ATTR_TYPE_TRANSLATE[k], f"{v}"] for k, v in self.magic_attr.items()]
|
| 107 |
|
| 108 |
@property
|
| 109 |
+
def embed_attr_content(self):
|
| 110 |
+
return [[ATTR_TYPE_TRANSLATE[k], str(v)] for k, v in self.embed_attr.items()]
|
| 111 |
|
| 112 |
|
| 113 |
class Equipments:
|
|
|
|
| 183 |
widget = equipments_widget[label]
|
| 184 |
equipment = equipments[label]
|
| 185 |
|
| 186 |
+
def inner(equipment_name):
|
|
|
|
| 187 |
|
| 188 |
if not equipment_name:
|
| 189 |
equipment.clear()
|
|
|
|
| 202 |
setattr(equipment, k, v)
|
| 203 |
|
| 204 |
if equipment.base:
|
| 205 |
+
widget.base_attr.set_content(equipment.base_attr_content)
|
| 206 |
widget.base_attr.show()
|
| 207 |
else:
|
| 208 |
widget.base_attr.hide()
|
|
|
|
| 216 |
if equipment.embed:
|
| 217 |
for i, (attr, value) in enumerate(equipment.embed.items()):
|
| 218 |
widget.embed_levels[i].set_label(f"镶嵌等级-{ATTR_TYPE_TRANSLATE[attr]}")
|
| 219 |
+
widget.embed_attr.set_content(equipment.embed_attr_content)
|
| 220 |
widget.embed_attr.show()
|
| 221 |
else:
|
| 222 |
widget.embed_attr.hide()
|
|
|
|
| 233 |
widget = equipments_widget.equipments[label]
|
| 234 |
equipment = equipments[label]
|
| 235 |
|
| 236 |
+
def inner(enchant_name):
|
|
|
|
| 237 |
if enchant_name:
|
| 238 |
enchant_detail = widget.enchant_json[enchant_name]
|
| 239 |
equipment.enchant.name = enchant_name
|
|
|
|
| 248 |
widget = equipments_widget.equipments[label]
|
| 249 |
equipment = equipments[label]
|
| 250 |
|
| 251 |
+
def inner(_):
|
| 252 |
if widget.special_enchant and widget.special_enchant.radio_button.isChecked():
|
| 253 |
equipment.special_enchant_gain = equipment.special_enchant
|
| 254 |
else:
|
|
|
|
| 262 |
|
| 263 |
def inner(index):
|
| 264 |
equipment.strength_level = index
|
| 265 |
+
if magic_attr_content := equipment.magic_attr_content:
|
| 266 |
+
widget.magic_attr.set_content(magic_attr_content)
|
| 267 |
widget.magic_attr.show()
|
| 268 |
else:
|
| 269 |
widget.magic_attr.hide()
|
|
|
|
| 276 |
|
| 277 |
def inner(index):
|
| 278 |
equipment.embed_levels[i] = index
|
| 279 |
+
if embed_attr_content := equipment.embed_attr_content:
|
| 280 |
+
widget.embed_attr.set_content(embed_attr_content)
|
| 281 |
widget.embed_attr.show()
|
| 282 |
else:
|
| 283 |
widget.embed_attr.hide()
|
|
|
|
| 288 |
widget = equipments_widget.equipments[label]
|
| 289 |
equipment = equipments[label]
|
| 290 |
|
| 291 |
+
def inner(_):
|
| 292 |
level = widget.stone_level.combo_box.currentText()
|
| 293 |
|
| 294 |
current = widget.stones_json
|
|
|
|
| 301 |
else:
|
| 302 |
break
|
| 303 |
if level in current:
|
| 304 |
+
for k, v in current[level].items():
|
| 305 |
setattr(equipment.stone, k, v)
|
| 306 |
else:
|
| 307 |
widget.stone_attrs[i].set_items([""] + [ATTR_TYPE_TRANSLATE[k] for k in current])
|
| 308 |
+
equipment.stone = {}
|
| 309 |
|
| 310 |
i += 1
|
| 311 |
while i < len(widget.stone_attrs):
|
|
|
|
| 316 |
|
| 317 |
for equipment_label, equipment_widget in equipments_widget.items():
|
| 318 |
|
| 319 |
+
equipment_widget.equipment.combo_box.currentTextChanged.connect(equipment_update(equipment_label))
|
| 320 |
if equipment_widget.special_enchant:
|
| 321 |
equipment_widget.special_enchant.radio_button.clicked.connect(special_enchant_update(equipment_label))
|
| 322 |
if equipment_widget.enchant:
|
| 323 |
+
equipment_widget.enchant.combo_box.currentTextChanged.connect(enchant_update(equipment_label))
|
| 324 |
equipment_widget.strength_level.combo_box.currentIndexChanged.connect(strength_level_update(equipment_label))
|
| 325 |
for n, embed_widget in enumerate(equipment_widget.embed_levels):
|
| 326 |
embed_widget.combo_box.currentIndexChanged.connect(embed_level_update(n, equipment_label))
|
qt/scripts/recipes.py
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from qt.components.recipes import RecipesWidget
|
| 2 |
+
|
| 3 |
+
from qt.constant import MAX_RECIPE_SKILLS, MAX_RECIPES
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
class Recipes:
|
| 7 |
+
def __init__(self):
|
| 8 |
+
self.recipes = [[] for _ in range(MAX_RECIPE_SKILLS)]
|
| 9 |
+
|
| 10 |
+
def __getitem__(self, item):
|
| 11 |
+
return self.recipes[item]
|
| 12 |
+
|
| 13 |
+
def __setitem__(self, key, value):
|
| 14 |
+
self.recipes[key] = value
|
| 15 |
+
|
| 16 |
+
@property
|
| 17 |
+
def gains(self):
|
| 18 |
+
return [recipe for recipes in self.recipes for recipe in recipes]
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def recipes_script(recipes_widget: RecipesWidget):
|
| 22 |
+
recipes = Recipes()
|
| 23 |
+
|
| 24 |
+
def recipe_update(i):
|
| 25 |
+
widget = recipes_widget[i]
|
| 26 |
+
|
| 27 |
+
def inner():
|
| 28 |
+
skill = widget.label.text()
|
| 29 |
+
if selected_items := widget.list.selectedItems():
|
| 30 |
+
while len(selected_items) > MAX_RECIPES:
|
| 31 |
+
selected_items.pop().setSelected(False)
|
| 32 |
+
recipes[i] = [(skill, item.text()) for item in selected_items]
|
| 33 |
+
|
| 34 |
+
return inner
|
| 35 |
+
|
| 36 |
+
for n, recipe_widget in enumerate(recipes_widget.values()):
|
| 37 |
+
recipe_widget.list.itemSelectionChanged.connect(recipe_update(n))
|
| 38 |
+
|
| 39 |
+
return recipes
|
qt/scripts/top.py
CHANGED
|
@@ -4,7 +4,9 @@ from PySide6.QtWidgets import QTabWidget, QFileDialog, QWidget
|
|
| 4 |
|
| 5 |
from base.buff import Buff
|
| 6 |
from base.skill import Skill
|
|
|
|
| 7 |
from qt.components.equipments import EquipmentsWidget
|
|
|
|
| 8 |
from qt.components.talents import TalentsWidget
|
| 9 |
from utils.lua import parse
|
| 10 |
# from qt.components.equipments import EquipmentsWidget
|
|
@@ -21,11 +23,12 @@ from qt.constant import School, SUPPORT_SCHOOL, MAX_RECIPES, MAX_STONE_LEVEL
|
|
| 21 |
|
| 22 |
|
| 23 |
class Parser:
|
| 24 |
-
records:
|
| 25 |
status: dict
|
| 26 |
|
| 27 |
start_time: list
|
| 28 |
end_time: list
|
|
|
|
| 29 |
|
| 30 |
fight_flag: bool
|
| 31 |
|
|
@@ -33,15 +36,20 @@ class Parser:
|
|
| 33 |
|
| 34 |
school: School | None
|
| 35 |
|
|
|
|
|
|
|
|
|
|
| 36 |
def reset(self):
|
| 37 |
self.fight_flag = False
|
| 38 |
|
| 39 |
-
self.records =
|
| 40 |
self.status = {}
|
| 41 |
|
| 42 |
self.start_time = []
|
| 43 |
self.end_time = []
|
| 44 |
|
|
|
|
|
|
|
| 45 |
self.school = None
|
| 46 |
|
| 47 |
def parse_info(self, detail):
|
|
@@ -55,7 +63,7 @@ class Parser:
|
|
| 55 |
def parse_time(self, detail, timestamp):
|
| 56 |
if detail[1]:
|
| 57 |
self.start_time.append(int(timestamp))
|
| 58 |
-
self.records
|
| 59 |
self.fight_flag = True
|
| 60 |
else:
|
| 61 |
self.end_time.append(int(timestamp))
|
|
@@ -75,7 +83,7 @@ class Parser:
|
|
| 75 |
if skill[0] not in self.school.skills:
|
| 76 |
return
|
| 77 |
|
| 78 |
-
current_record = self.records[self.start_time
|
| 79 |
if skill not in current_record:
|
| 80 |
current_record[skill] = {}
|
| 81 |
status = tuple(
|
|
@@ -102,26 +110,35 @@ class Parser:
|
|
| 102 |
elif row[4] == "21" and self.fight_flag:
|
| 103 |
self.parse_skill(parse(row[-1]), row[3])
|
| 104 |
|
|
|
|
|
|
|
|
|
|
| 105 |
|
| 106 |
-
|
| 107 |
-
|
|
|
|
| 108 |
):
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
parser = Parser()
|
| 113 |
|
| 114 |
def upload_logs():
|
| 115 |
file_name = QFileDialog(top_widget, "Choose File").getOpenFileName()
|
| 116 |
parser(file_name[0])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 117 |
|
| 118 |
""" Update equipment options """
|
| 119 |
for equipment_widget in equipments_widget.values():
|
| 120 |
choices = [""]
|
| 121 |
for name, detail in equipment_widget.equipment_json.items():
|
| 122 |
-
if detail['kind'] not in (
|
| 123 |
continue
|
| 124 |
-
if detail['school'] not in ("精简", "通用",
|
| 125 |
continue
|
| 126 |
choices.append(name)
|
| 127 |
|
|
@@ -132,10 +149,23 @@ def top_script(top_widget: TopWidget, config_widget: QWidget,
|
|
| 132 |
|
| 133 |
""" Update talent options """
|
| 134 |
for i, talent_widget in enumerate(talents_widget.values()):
|
| 135 |
-
talents =
|
| 136 |
-
default_index =
|
| 137 |
-
talent_widget.set_items([""] +
|
| 138 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 139 |
config_widget.show()
|
| 140 |
|
| 141 |
top_widget.upload_button.clicked.connect(upload_logs)
|
|
|
|
| 4 |
|
| 5 |
from base.buff import Buff
|
| 6 |
from base.skill import Skill
|
| 7 |
+
from qt.components.dashboard import DashboardWidget
|
| 8 |
from qt.components.equipments import EquipmentsWidget
|
| 9 |
+
from qt.components.recipes import RecipesWidget
|
| 10 |
from qt.components.talents import TalentsWidget
|
| 11 |
from utils.lua import parse
|
| 12 |
# from qt.components.equipments import EquipmentsWidget
|
|
|
|
| 23 |
|
| 24 |
|
| 25 |
class Parser:
|
| 26 |
+
records: list
|
| 27 |
status: dict
|
| 28 |
|
| 29 |
start_time: list
|
| 30 |
end_time: list
|
| 31 |
+
record_index: Dict[str, int]
|
| 32 |
|
| 33 |
fight_flag: bool
|
| 34 |
|
|
|
|
| 36 |
|
| 37 |
school: School | None
|
| 38 |
|
| 39 |
+
def duration(self, i):
|
| 40 |
+
return round((self.end_time[i] - self.start_time[i]) / 1000, 3)
|
| 41 |
+
|
| 42 |
def reset(self):
|
| 43 |
self.fight_flag = False
|
| 44 |
|
| 45 |
+
self.records = []
|
| 46 |
self.status = {}
|
| 47 |
|
| 48 |
self.start_time = []
|
| 49 |
self.end_time = []
|
| 50 |
|
| 51 |
+
self.record_index = {}
|
| 52 |
+
|
| 53 |
self.school = None
|
| 54 |
|
| 55 |
def parse_info(self, detail):
|
|
|
|
| 63 |
def parse_time(self, detail, timestamp):
|
| 64 |
if detail[1]:
|
| 65 |
self.start_time.append(int(timestamp))
|
| 66 |
+
self.records.append({})
|
| 67 |
self.fight_flag = True
|
| 68 |
else:
|
| 69 |
self.end_time.append(int(timestamp))
|
|
|
|
| 83 |
if skill[0] not in self.school.skills:
|
| 84 |
return
|
| 85 |
|
| 86 |
+
current_record = self.records[len(self.start_time) - 1]
|
| 87 |
if skill not in current_record:
|
| 88 |
current_record[skill] = {}
|
| 89 |
status = tuple(
|
|
|
|
| 110 |
elif row[4] == "21" and self.fight_flag:
|
| 111 |
self.parse_skill(parse(row[-1]), row[3])
|
| 112 |
|
| 113 |
+
self.record_index = {
|
| 114 |
+
f"{i + 1}:{round((end_time - self.start_time[i]) / 1000, 3)}": i for i, end_time in enumerate(self.end_time)
|
| 115 |
+
}
|
| 116 |
|
| 117 |
+
|
| 118 |
+
def top_script(top_widget: TopWidget, config_widget: QWidget, dashboard_widget: DashboardWidget,
|
| 119 |
+
equipments_widget: EquipmentsWidget, talents_widget: TalentsWidget, recipes_widget: RecipesWidget,
|
| 120 |
):
|
| 121 |
+
# equipments_widget: EquipmentsWidget, talents_widget: TalentsWidget, recipes_widget: RecipesWidget,
|
| 122 |
+
# consumables_widget: ConsumablesWidget, bonuses_widget: BonusesWidget,
|
| 123 |
+
# combat_widget: CombatWidget):
|
| 124 |
parser = Parser()
|
| 125 |
|
| 126 |
def upload_logs():
|
| 127 |
file_name = QFileDialog(top_widget, "Choose File").getOpenFileName()
|
| 128 |
parser(file_name[0])
|
| 129 |
+
school = parser.school
|
| 130 |
+
""" Update dashboard """
|
| 131 |
+
record_index = list(parser.record_index)
|
| 132 |
+
dashboard_widget.fight_select.set_items(record_index)
|
| 133 |
+
dashboard_widget.duration.set_value(parser.duration(parser.record_index[record_index[0]]))
|
| 134 |
|
| 135 |
""" Update equipment options """
|
| 136 |
for equipment_widget in equipments_widget.values():
|
| 137 |
choices = [""]
|
| 138 |
for name, detail in equipment_widget.equipment_json.items():
|
| 139 |
+
if detail['kind'] not in (school.kind, school.major):
|
| 140 |
continue
|
| 141 |
+
if detail['school'] not in ("精简", "通用", school.school):
|
| 142 |
continue
|
| 143 |
choices.append(name)
|
| 144 |
|
|
|
|
| 149 |
|
| 150 |
""" Update talent options """
|
| 151 |
for i, talent_widget in enumerate(talents_widget.values()):
|
| 152 |
+
talents = list(school.talent_gains[i])
|
| 153 |
+
default_index = talents.index(parser.select_talents[i]) + 1
|
| 154 |
+
talent_widget.set_items([""] + [school.talent_decoder[talent] for talent in talents],
|
| 155 |
+
default_index=default_index)
|
| 156 |
+
|
| 157 |
+
""" Update recipe options """
|
| 158 |
+
for recipe_widget in recipes_widget.values():
|
| 159 |
+
recipe_widget.list.clear()
|
| 160 |
+
recipe_widget.hide()
|
| 161 |
+
|
| 162 |
+
for i, (skill, recipes) in enumerate(school.recipes.items()):
|
| 163 |
+
recipes_widget[i].set_label(skill)
|
| 164 |
+
recipes_widget[i].set_items(recipes)
|
| 165 |
+
for n in range(MAX_RECIPES):
|
| 166 |
+
recipes_widget[i].list.item(n).setSelected(True)
|
| 167 |
+
recipes_widget[i].show()
|
| 168 |
+
|
| 169 |
config_widget.show()
|
| 170 |
|
| 171 |
top_widget.upload_button.clicked.connect(upload_logs)
|
schools/first/__init__.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
| 1 |
from schools.first.skills import SKILLS
|
| 2 |
from schools.first.buffs import BUFFS
|
| 3 |
-
from schools.first.talents import
|
|
|
|
| 4 |
from schools.first.attribute import BeiAoJue
|
|
|
|
| 1 |
from schools.first.skills import SKILLS
|
| 2 |
from schools.first.buffs import BUFFS
|
| 3 |
+
from schools.first.talents import TALENT_GAINS, TALENT_DECODER, TALENT_ENCODER
|
| 4 |
+
from schools.first.recipes import RECIPE_GAINS, RECIPES
|
| 5 |
from schools.first.attribute import BeiAoJue
|
schools/first/recipes.py
CHANGED
|
@@ -1,71 +1,63 @@
|
|
| 1 |
-
from typing import Dict, List
|
| 2 |
|
| 3 |
from base.buff import Buff
|
| 4 |
from base.recipe import damage_addition_recipe, critical_strike_recipe
|
| 5 |
|
| 6 |
-
|
| 7 |
-
"雷走风切":
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
"
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
],
|
| 55 |
-
"刀啸风吟": [
|
| 56 |
-
("5%伤害", damage_addition_recipe([16610], 51)),
|
| 57 |
-
("4%伤害", damage_addition_recipe([16610], 41)),
|
| 58 |
-
("4%会心", critical_strike_recipe([16610], 400)),
|
| 59 |
-
("3%会心", critical_strike_recipe([16610], 300)),
|
| 60 |
-
("2%会心", critical_strike_recipe([16610], 200)),
|
| 61 |
-
]
|
| 62 |
-
|
| 63 |
}
|
| 64 |
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
|
|
|
|
|
| 1 |
+
from typing import Dict, List
|
| 2 |
|
| 3 |
from base.buff import Buff
|
| 4 |
from base.recipe import damage_addition_recipe, critical_strike_recipe
|
| 5 |
|
| 6 |
+
RECIPE_GAINS: Dict[str, Dict[str, dict | Buff]] = {
|
| 7 |
+
"雷走风切": {
|
| 8 |
+
"5%伤害": damage_addition_recipe([16631, 16599], 51),
|
| 9 |
+
"4%伤害": damage_addition_recipe([16631, 16599], 41),
|
| 10 |
+
"3%伤害": damage_addition_recipe([16631, 16599], 31),
|
| 11 |
+
"4%会心": critical_strike_recipe([16631, 16599], 400),
|
| 12 |
+
"3%会心": critical_strike_recipe([16631, 16599], 300),
|
| 13 |
+
"2%会心": critical_strike_recipe([16631, 16599], 200),
|
| 14 |
+
},
|
| 15 |
+
"项王击鼎": {
|
| 16 |
+
"5%伤害": damage_addition_recipe([16760, 16382], 51),
|
| 17 |
+
"4%伤害": damage_addition_recipe([16760, 16382], 41),
|
| 18 |
+
"3%伤害": damage_addition_recipe([16760, 16382], 31),
|
| 19 |
+
"4%会心": critical_strike_recipe([16760, 16382], 400),
|
| 20 |
+
"3%会心": critical_strike_recipe([16760, 16382], 300),
|
| 21 |
+
"2%会心": critical_strike_recipe([16760, 16382], 200),
|
| 22 |
+
},
|
| 23 |
+
"破釜沉舟": {
|
| 24 |
+
"5%伤害": damage_addition_recipe([20991], 51),
|
| 25 |
+
"4%伤害": damage_addition_recipe([20991], 41),
|
| 26 |
+
"3%伤害": damage_addition_recipe([20991], 31),
|
| 27 |
+
"4%会心": critical_strike_recipe([20991], 400),
|
| 28 |
+
"3%会心": critical_strike_recipe([20991], 300),
|
| 29 |
+
"2%会心": critical_strike_recipe([20991], 200),
|
| 30 |
+
},
|
| 31 |
+
"上将军印": {
|
| 32 |
+
"4%伤害": damage_addition_recipe([16803, 16802, 16801, 16800, 17043, 19423, 19424, 32859], 41),
|
| 33 |
+
"3%伤害": damage_addition_recipe([16803, 16802, 16801, 16800, 17043, 19423, 19424, 32859], 31),
|
| 34 |
+
"2%伤害": damage_addition_recipe([16803, 16802, 16801, 16800, 17043, 19423, 19424, 32859], 21),
|
| 35 |
+
"4%会心": critical_strike_recipe([16803, 16802, 16801, 16800, 17043, 19423, 19424], 400),
|
| 36 |
+
"3%会心": critical_strike_recipe([16803, 16802, 16801, 16800, 17043, 19423, 19424], 300),
|
| 37 |
+
"2%会心": critical_strike_recipe([16803, 16802, 16801, 16800, 17043, 19423, 19424], 200),
|
| 38 |
+
},
|
| 39 |
+
"擒龙六斩": {
|
| 40 |
+
"5%伤���": damage_addition_recipe([16933, 16934, 16935, 16936, 16937, 16938], 51),
|
| 41 |
+
"4%伤害": damage_addition_recipe([16933, 16934, 16935, 16936, 16937, 16938], 41),
|
| 42 |
+
"3%伤害": damage_addition_recipe([16933, 16934, 16935, 16936, 16937, 16938], 31),
|
| 43 |
+
"4%会心": critical_strike_recipe([16933, 16934, 16935, 16936, 16937, 16938], 400),
|
| 44 |
+
"3%会心": critical_strike_recipe([16933, 16934, 16935, 16936, 16937, 16938], 300),
|
| 45 |
+
"2%会心": critical_strike_recipe([16933, 16934, 16935, 16936, 16937, 16938], 200),
|
| 46 |
+
},
|
| 47 |
+
"刀啸风吟": {
|
| 48 |
+
"5%伤害": damage_addition_recipe([16610], 51),
|
| 49 |
+
"4%伤害": damage_addition_recipe([16610], 41),
|
| 50 |
+
"4%会心": critical_strike_recipe([16610], 400),
|
| 51 |
+
"3%会心": critical_strike_recipe([16610], 300),
|
| 52 |
+
"2%会心": critical_strike_recipe([16610], 200),
|
| 53 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 54 |
}
|
| 55 |
|
| 56 |
+
RECIPES: Dict[str, List[str]] = {
|
| 57 |
+
"雷走风切": ["5%伤害", "4%伤害", "3%伤害", "4%会心", "3%会心", "2%会心"],
|
| 58 |
+
"项王击鼎": ["5%伤害", "4%伤害", "3%伤害", "4%会心", "3%会心", "2%会心"],
|
| 59 |
+
"破釜沉舟": ["5%伤害", "4%伤害", "3%伤害", "4%会心", "3%会心", "2%会心"],
|
| 60 |
+
"上将军印": ["4%伤害", "3%伤害", "2%伤害", "4%会心", "3%会心", "2%会心"],
|
| 61 |
+
"擒龙六斩": ["5%伤害", "4%伤害", "3%伤害", "4%会心", "3%会心", "2%会心"],
|
| 62 |
+
"刀啸风吟": ["5%伤害", "4%伤害", "4%会心", "3%会心", "2%会心"]
|
| 63 |
+
}
|
schools/first/skills.py
CHANGED
|
@@ -1,6 +1,8 @@
|
|
| 1 |
-
from
|
| 2 |
|
| 3 |
-
|
|
|
|
|
|
|
| 4 |
32823: {
|
| 5 |
"skill_class": PhysicalDamage,
|
| 6 |
"skill_name": "破",
|
|
@@ -288,3 +290,6 @@ for skill_id, detail in SKILLS.items():
|
|
| 288 |
SKILLS[skill_id] = detail.pop('skill_class')(skill_id, detail.pop('skill_name'))
|
| 289 |
for attr, value in detail.items():
|
| 290 |
setattr(SKILLS[skill_id], attr, value)
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Dict
|
| 2 |
|
| 3 |
+
from base.skill import PhysicalDamage, PhysicalDotDamage, Skill
|
| 4 |
+
|
| 5 |
+
SKILLS: Dict[int, Skill | dict] = {
|
| 6 |
32823: {
|
| 7 |
"skill_class": PhysicalDamage,
|
| 8 |
"skill_name": "破",
|
|
|
|
| 290 |
SKILLS[skill_id] = detail.pop('skill_class')(skill_id, detail.pop('skill_name'))
|
| 291 |
for attr, value in detail.items():
|
| 292 |
setattr(SKILLS[skill_id], attr, value)
|
| 293 |
+
|
| 294 |
+
SKILL_DECODER = {skill_id: skill.skill_name for skill_id, skill in SKILLS.items()}
|
| 295 |
+
SKILL_ENCODER = {v: k for k, v in SKILL_DECODER.items()}
|
schools/first/talents.py
CHANGED
|
@@ -2,9 +2,9 @@ from typing import Dict, List
|
|
| 2 |
|
| 3 |
from base.buff import Buff
|
| 4 |
|
| 5 |
-
|
| 6 |
-
{16691: {}},
|
| 7 |
-
{16847: {}},
|
| 8 |
{
|
| 9 |
26904: {
|
| 10 |
"buff_name": "冥鼔",
|
|
@@ -35,12 +35,12 @@ TALENTS: List[Dict[int, dict | Buff]] = [
|
|
| 35 |
}
|
| 36 |
}
|
| 37 |
},
|
| 38 |
-
{16799: {}},
|
| 39 |
-
{25633: {}},
|
| 40 |
-
{32857: {}},
|
| 41 |
-
{17047: {}},
|
| 42 |
{
|
| 43 |
-
25258: {},
|
| 44 |
16728: {
|
| 45 |
"buff_name": "星火",
|
| 46 |
"gain_attributes": {
|
|
@@ -56,7 +56,7 @@ TALENTS: List[Dict[int, dict | Buff]] = [
|
|
| 56 |
}
|
| 57 |
}
|
| 58 |
},
|
| 59 |
-
{16737: {}},
|
| 60 |
{
|
| 61 |
17056: {
|
| 62 |
"buff_name": "绝期",
|
|
@@ -67,14 +67,19 @@ TALENTS: List[Dict[int, dict | Buff]] = [
|
|
| 67 |
}
|
| 68 |
}
|
| 69 |
},
|
| 70 |
-
{16893: {}},
|
| 71 |
-
{21858: {}}
|
| 72 |
]
|
| 73 |
|
| 74 |
-
for talent in
|
| 75 |
for talent_id, detail in talent.items():
|
| 76 |
if not detail:
|
| 77 |
-
|
| 78 |
-
|
|
|
|
| 79 |
for attr, value in detail.items():
|
| 80 |
setattr(talent[talent_id], attr, value)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
|
| 3 |
from base.buff import Buff
|
| 4 |
|
| 5 |
+
TALENT_GAINS: List[Dict[int, dict | Buff]] = [
|
| 6 |
+
{16691: {"buff_name": "龙息"}},
|
| 7 |
+
{16847: {"buff_name": "归酣"}},
|
| 8 |
{
|
| 9 |
26904: {
|
| 10 |
"buff_name": "冥鼔",
|
|
|
|
| 35 |
}
|
| 36 |
}
|
| 37 |
},
|
| 38 |
+
{16799: {"buff_name": "霜天"}},
|
| 39 |
+
{25633: {"buff_name": "含风"}},
|
| 40 |
+
{32857: {"buff_name": "见尘"}},
|
| 41 |
+
{17047: {"buff_name": "分疆"}},
|
| 42 |
{
|
| 43 |
+
25258: {"buff_name": "掠关"},
|
| 44 |
16728: {
|
| 45 |
"buff_name": "星火",
|
| 46 |
"gain_attributes": {
|
|
|
|
| 56 |
}
|
| 57 |
}
|
| 58 |
},
|
| 59 |
+
{16737: {"buff_name": "楚歌"}},
|
| 60 |
{
|
| 61 |
17056: {
|
| 62 |
"buff_name": "绝期",
|
|
|
|
| 67 |
}
|
| 68 |
}
|
| 69 |
},
|
| 70 |
+
{16893: {"buff_name": "重烟"}},
|
| 71 |
+
{21858: {"buff_name": "降麒式"}}
|
| 72 |
]
|
| 73 |
|
| 74 |
+
for talent in TALENT_GAINS:
|
| 75 |
for talent_id, detail in talent.items():
|
| 76 |
if not detail:
|
| 77 |
+
talent[talent_id] = Buff()
|
| 78 |
+
else:
|
| 79 |
+
talent[talent_id] = Buff(talent_id, detail.pop("buff_name"))
|
| 80 |
for attr, value in detail.items():
|
| 81 |
setattr(talent[talent_id], attr, value)
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
TALENT_DECODER = {talent_id: talent.buff_name for talents in TALENT_GAINS for talent_id, talent in talents.items()}
|
| 85 |
+
TALENT_ENCODER = {v: k for k, v in TALENT_DECODER.items()}
|
schools/first/test.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
from
|
| 2 |
from qt.scripts.top import Parser
|
| 3 |
from schools.first.attribute import BeiAoJue
|
| 4 |
|
|
@@ -17,5 +17,5 @@ if __name__ == '__main__':
|
|
| 17 |
|
| 18 |
parser = Parser()
|
| 19 |
parser("logs.jcl")
|
| 20 |
-
analyze_details(parser, attribute)
|
| 21 |
print(parser.records)
|
|
|
|
| 1 |
+
from utils.analyzer import analyze_details
|
| 2 |
from qt.scripts.top import Parser
|
| 3 |
from schools.first.attribute import BeiAoJue
|
| 4 |
|
|
|
|
| 17 |
|
| 18 |
parser = Parser()
|
| 19 |
parser("logs.jcl")
|
| 20 |
+
analyze_details(parser, attribute, parser.school)
|
| 21 |
print(parser.records)
|
utils/analyzer.py
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from base.attribute import Attribute
|
| 2 |
+
from qt.scripts.top import School
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
def refresh_status(existed_buffs, buffs, attribute: Attribute, school: School):
|
| 6 |
+
for buff in [buff for buff in existed_buffs if buff not in buffs]:
|
| 7 |
+
existed_buffs.remove(buff)
|
| 8 |
+
buff_id, buff_level, buff_stack = buff
|
| 9 |
+
buff = school.buffs[buff_id]
|
| 10 |
+
buff.buff_level, buff.buff_stack = buff_level, buff_stack
|
| 11 |
+
attribute = attribute - buff
|
| 12 |
+
school.skills = school.skills - buff
|
| 13 |
+
|
| 14 |
+
for buff in [buff for buff in buffs if buff not in existed_buffs]:
|
| 15 |
+
existed_buffs.append(buff)
|
| 16 |
+
buff_id, buff_level, buff_stack = buff
|
| 17 |
+
buff = school.buffs[buff_id]
|
| 18 |
+
buff.buff_level, buff.buff_stack = buff_level, buff_stack
|
| 19 |
+
attribute = attribute + buff
|
| 20 |
+
school.skills = school.skills + buff
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def analyze_details(record, attribute: Attribute, school: School):
|
| 24 |
+
details = {}
|
| 25 |
+
total_damage = 0
|
| 26 |
+
total_gradients = {attr: 0 for attr in attribute.grad_attrs}
|
| 27 |
+
|
| 28 |
+
existed_buffs = []
|
| 29 |
+
for skill, status in record.items():
|
| 30 |
+
skill_id, skill_level = skill
|
| 31 |
+
skill = school.skills[skill_id]
|
| 32 |
+
skill.skill_level = skill_level
|
| 33 |
+
|
| 34 |
+
skill_detail = {}
|
| 35 |
+
details[skill.display_name] = skill_detail
|
| 36 |
+
for buffs, timeline in status.items():
|
| 37 |
+
refresh_status(existed_buffs, buffs, attribute, school)
|
| 38 |
+
|
| 39 |
+
damage, critical_strike, critical_damage, expected_damage = skill(attribute)
|
| 40 |
+
gradients = analyze_gradients(skill, attribute)
|
| 41 |
+
|
| 42 |
+
total_damage += expected_damage * len(timeline)
|
| 43 |
+
for attr, residual_damage in gradients.items():
|
| 44 |
+
total_gradients[attr] += residual_damage * len(timeline)
|
| 45 |
+
|
| 46 |
+
buffs = ";".join(school.buffs[buff_id].display_name for buff_id, _, _ in buffs)
|
| 47 |
+
if not buffs:
|
| 48 |
+
buffs = "~-~-~"
|
| 49 |
+
skill_detail[buffs] = dict(
|
| 50 |
+
damage=damage,
|
| 51 |
+
critical_strike=critical_strike,
|
| 52 |
+
critical_damage=critical_damage,
|
| 53 |
+
expected_damage=expected_damage,
|
| 54 |
+
# "timeline": [round(t / 1000, 3) for t in timeline],
|
| 55 |
+
count=len(timeline),
|
| 56 |
+
gradients=gradients
|
| 57 |
+
)
|
| 58 |
+
|
| 59 |
+
refresh_status(existed_buffs, [], attribute, school)
|
| 60 |
+
|
| 61 |
+
for attr, residual_damage in total_gradients.items():
|
| 62 |
+
total_gradients[attr] = round(residual_damage / total_damage * 100, 4)
|
| 63 |
+
|
| 64 |
+
summary = analyze_summary(details)
|
| 65 |
+
return total_damage, total_gradients, details, summary
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
def analyze_summary(details):
|
| 69 |
+
summary = {}
|
| 70 |
+
for skill, skill_detail in details.items():
|
| 71 |
+
skill = skill.split("/")[0]
|
| 72 |
+
if skill not in summary:
|
| 73 |
+
summary[skill] = {"count": 0, "hit": 0, "critical": 0, "damage": 0}
|
| 74 |
+
for buff, detail in skill_detail.items():
|
| 75 |
+
summary[skill]["count"] += detail['count']
|
| 76 |
+
summary[skill]["critical"] += detail['count'] * detail['critical_strike']
|
| 77 |
+
summary[skill]["damage"] += detail['count'] * detail['expected_damage']
|
| 78 |
+
|
| 79 |
+
return summary
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
def analyze_gradients(skill, attribute):
|
| 83 |
+
results = {}
|
| 84 |
+
for attr, value in attribute.grad_attrs.items():
|
| 85 |
+
origin_value = getattr(attribute, attr)
|
| 86 |
+
setattr(attribute, attr, origin_value + value)
|
| 87 |
+
_, _, _, results[attr] = skill(attribute)
|
| 88 |
+
setattr(attribute, attr, origin_value)
|
| 89 |
+
return results
|
{base → utils}/damage.py
RENAMED
|
File without changes
|