ango commited on
Commit
2452398
1 Parent(s): f0b1638

04.10 commit

Browse files
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 CRITICAL_POWER_BASE + self._physical_critical_power_percent
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 CRITICAL_POWER_BASE + self._magical_critical_power_percent
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, Tuple
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
- def __radd__(self, other: Tuple[Attribute, Dict[int, Skill]]):
26
- attribute, skills = other
27
- for skill_id, gain in self.gain_skills.items():
28
- skill = skills[skill_id]
29
- for attr, value in gain.items():
30
- setattr(skill, attr, getattr(skill, attr) + value)
31
- for attr, value in self.gain_attributes.items():
32
- setattr(attribute, attr, getattr(attribute, attr) + value)
33
- return attribute, skills
34
-
35
- def __rsub__(self, other: Tuple[Attribute, Dict[int, Skill]]):
36
- attribute, skills = other
37
- for skill_id, gain in self.gain_skills.items():
38
- skill = skills[skill_id]
39
- for attr, value in gain.items():
40
- setattr(skill, attr, getattr(skill, attr) - value)
41
- for attr, value in self.gain_attributes.items():
42
- setattr(attribute, attr, getattr(attribute, attr) - value)
43
- return attribute, skills
 
 
 
 
 
 
 
 
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
- CRITICAL_POWER_BASE = 1.75
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
- "buff_name": name,
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
- "buff_name": name,
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 base.damage import *
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
- # 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.combat import CombatWidget
19
- # from qt.scripts.combat import combat_script
20
 
21
- from PySide6.QtWidgets import QApplication, QMainWindow, QStyleFactory, QMessageBox, QVBoxLayout, QWidget, QTabWidget, \
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.talent_widget = TalentsWidget()
53
- config_layout.addWidget(self.talent_widget, 0, 1)
 
54
 
55
- parser = top_script(self.top_widget, self.config_widget, self.equipments_widget, self.talent_widget)
 
 
 
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
- self.setLayout(layout)
 
56
 
57
  self.list = QListWidget()
58
  self.list.setSelectionMode(QAbstractItemView.SelectionMode.MultiSelection)
59
 
60
  if items:
61
- self.list.addItems(items)
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(10e8)
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, TextWithLabel
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 = TextWithLabel("基本属性")
77
  output_layout.addWidget(self.base_attr)
78
- self.magic_attr = TextWithLabel("精炼属性")
79
  output_layout.addWidget(self.magic_attr)
80
- self.embed_attr = TextWithLabel("镶嵌属性")
81
  output_layout.addWidget(self.embed_attr)
82
- output_layout.addStretch()
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, Dict, List
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
- talents: List[Dict[int, Buff]]
 
 
 
 
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
- talents=first.TALENTS,
 
 
 
 
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 base_attr_text(self):
91
- return "\n".join([f"{ATTR_TYPE_TRANSLATE[k]}:\t{v}" for k, v in self.base_attr.items()])
92
 
93
  @property
94
- def magic_attr_text(self):
95
  if strength_attr := self.strength_attr:
96
- return "\n".join([
97
- f"{ATTR_TYPE_TRANSLATE[k]}:\t{v}(+{strength_attr[k]})" for k, v in self.magic_attr.items()
98
- ])
99
  else:
100
- return "\n".join([f"{ATTR_TYPE_TRANSLATE[k]}:\t{v}" for k, v in self.magic_attr.items()])
101
 
102
  @property
103
- def embed_attr_text(self):
104
- return "\n".join([f"{ATTR_TYPE_TRANSLATE[k]}:\t{v}" for k, v in self.embed_attr.items()])
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(index):
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.set_text(equipment.base_attr_text)
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.set_text(equipment.embed_attr_text)
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(index):
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(index):
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 magic_attr_text := equipment.magic_attr_text:
262
- widget.magic_attr.text_browser.setText(magic_attr_text)
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 embed_attr_text := equipment.embed_attr_text:
276
- widget.embed_attr.text_browser.setText(embed_attr_text)
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(index):
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.currentIndexChanged.connect(equipment_update(equipment_label))
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.currentIndexChanged.connect(enchant_update(equipment_label))
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: dict
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[self.start_time[-1]] = {}
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[-1]]
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
- def top_script(top_widget: TopWidget, config_widget: QWidget,
107
- equipments_widget: EquipmentsWidget, talents_widget: TalentsWidget,
 
108
  ):
109
- # equipments_widget: EquipmentsWidget, talents_widget: TalentsWidget, recipes_widget: RecipesWidget,
110
- # consumables_widget: ConsumablesWidget, bonuses_widget: BonusesWidget,
111
- # combat_widget: CombatWidget):
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 (parser.school.kind, parser.school.major):
123
  continue
124
- if detail['school'] not in ("精简", "通用", parser.school.school):
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 = parser.school.talents[i]
136
- default_index = list(talents).index(parser.select_talents[i]) + 1
137
- talent_widget.set_items([""] + list(talents.values()))
138
- talent_widget.combo_box.setCurrentIndex(default_index)
 
 
 
 
 
 
 
 
 
 
 
 
 
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 TALENTS
 
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, Tuple
2
 
3
  from base.buff import Buff
4
  from base.recipe import damage_addition_recipe, critical_strike_recipe
5
 
6
- RECIPES: Dict[str, List[Tuple[str, dict]]] = {
7
- "雷走风切": [
8
- ("5%伤害", damage_addition_recipe([16631, 16599], 51)),
9
- ("4%伤害", damage_addition_recipe([16631, 16599], 41)),
10
- ("4%会心", critical_strike_recipe([16631, 16599], 400)),
11
- ("3%伤害", damage_addition_recipe([16631, 16599], 31)),
12
- ("3%会心", critical_strike_recipe([16631, 16599], 300)),
13
- ("2%会心", critical_strike_recipe([16631, 16599], 200)),
14
- ]
15
- ,
16
-
17
- "项王击鼎": [
18
- ("5%伤害", damage_addition_recipe([16760, 16382], 51)),
19
- ("4%伤害", damage_addition_recipe([16760, 16382], 41)),
20
- ("4%会心", critical_strike_recipe([16760, 16382], 400)),
21
- ("3%伤害", damage_addition_recipe([16760, 16382], 31)),
22
- ("3%会心", critical_strike_recipe([16760, 16382], 300)),
23
- ("2%会心", critical_strike_recipe([16760, 16382], 200)),
24
- ]
25
- ,
26
-
27
- "破釜沉舟": [
28
- ("5%伤害", damage_addition_recipe([20991], 51)),
29
- ("4%伤害", damage_addition_recipe([20991], 41)),
30
- ("4%会心", critical_strike_recipe([20991], 400)),
31
- ("3%伤害", damage_addition_recipe([20991], 31)),
32
- ("3%会心", critical_strike_recipe([20991], 300)),
33
- ("2%会心", critical_strike_recipe([20991], 200)),
34
- ]
35
- ,
36
-
37
- "上将军印": [
38
- ("4%伤害", damage_addition_recipe([16803, 16802, 16801, 16800, 17043, 19423, 19424, 32859], 41)),
39
- ("4%会心", critical_strike_recipe([16803, 16802, 16801, 16800, 17043, 19423, 19424], 400)),
40
- ("3%伤害", damage_addition_recipe([16803, 16802, 16801, 16800, 17043, 19423, 19424, 32859], 31)),
41
- ("3%会心", critical_strike_recipe([16803, 16802, 16801, 16800, 17043, 19423, 19424], 300)),
42
- ("2%伤害", damage_addition_recipe([16803, 16802, 16801, 16800, 17043, 19423, 19424, 32859], 21)),
43
- ("2%会心", critical_strike_recipe([16803, 16802, 16801, 16800, 17043, 19423, 19424], 200)),
44
- ]
45
- ,
46
-
47
- "擒龙六斩": [
48
- ("5%伤害", damage_addition_recipe([16933, 16934, 16935, 16936, 16937, 16938], 51)),
49
- ("4%伤害", damage_addition_recipe([16933, 16934, 16935, 16936, 16937, 16938], 41)),
50
- ("4%会心", critical_strike_recipe([16933, 16934, 16935, 16936, 16937, 16938], 400)),
51
- ("3%伤害", damage_addition_recipe([16933, 16934, 16935, 16936, 16937, 16938], 31)),
52
- ("3%会心", critical_strike_recipe([16933, 16934, 16935, 16936, 16937, 16938], 300)),
53
- ("2%会心", critical_strike_recipe([16933, 16934, 16935, 16936, 16937, 16938], 200)),
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
- for skill_name, recipes in RECIPES.items():
66
- for skill_name, detail in recipe.items():
67
- if not detail:
68
- continue
69
- talent[talent_id] = Buff(talent_id, detail.pop("buff_name"))
70
- for attr, value in detail.items():
71
- setattr(talent[talent_id], attr, value)
 
 
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 base.skill import PhysicalDamage, PhysicalDotDamage
2
 
3
- SKILLS = {
 
 
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
- TALENTS: List[Dict[int, dict | Buff]] = [
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 TALENTS:
75
  for talent_id, detail in talent.items():
76
  if not detail:
77
- continue
78
- talent[talent_id] = Buff(talent_id, detail.pop("buff_name"))
 
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 base.calculator import analyze_details
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