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
|