Spaces:
Runtime error
Runtime error
ango commited on
Commit ·
970efde
1
Parent(s): 5825182
04.15 commit
Browse files- qt/app.py +21 -14
- qt/components/__init__.py +6 -11
- qt/components/config.py +22 -0
- qt/components/dashboard.py +1 -1
- qt/components/equipments.py +1 -2
- qt/scripts/config.py +86 -0
- qt/scripts/dashboard.py +4 -2
- qt/scripts/top.py +9 -4
- schools/bei_ao_jue/skills.py +13 -0
- utils/analyzer.py +18 -11
- utils/parser.py +13 -12
qt/app.py
CHANGED
|
@@ -4,7 +4,9 @@ import sys
|
|
| 4 |
from PySide6.QtGui import QIcon
|
| 5 |
|
| 6 |
from qt.components.top import TopWidget
|
|
|
|
| 7 |
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.consumables import ConsumablesWidget
|
|
@@ -18,8 +20,8 @@ from qt.scripts.recipes import recipes_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
|
| 22 |
-
|
| 23 |
|
| 24 |
|
| 25 |
class MainWindow(QMainWindow):
|
|
@@ -38,37 +40,42 @@ class MainWindow(QMainWindow):
|
|
| 38 |
layout = QVBoxLayout(self.central_widget)
|
| 39 |
|
| 40 |
self.top_widget = TopWidget()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
self.bottom_widget = QWidget()
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
layout.addWidget(self.bottom_widget)
|
| 45 |
|
| 46 |
-
|
|
|
|
| 47 |
self.dashboard_widget = DashboardWidget()
|
| 48 |
-
bottom_layout.addWidget(self.
|
| 49 |
bottom_layout.addWidget(self.dashboard_widget, 1)
|
| 50 |
|
| 51 |
self.equipments_widget = EquipmentsWidget()
|
| 52 |
-
self.
|
| 53 |
self.consumable_widget = ConsumablesWidget()
|
| 54 |
-
self.
|
| 55 |
self.talents_widget = TalentsWidget()
|
| 56 |
-
self.
|
| 57 |
self.recipes_widget = RecipesWidget()
|
| 58 |
-
self.
|
| 59 |
|
| 60 |
parser = top_script(
|
| 61 |
-
self.top_widget, self.bottom_widget, self.dashboard_widget,
|
| 62 |
self.talents_widget, self.recipes_widget, self.equipments_widget, self.consumable_widget,
|
| 63 |
)
|
|
|
|
| 64 |
equipments = equipments_script(self.equipments_widget)
|
| 65 |
consumables = consumables_script(self.consumable_widget)
|
| 66 |
talents = talents_script(self.talents_widget)
|
| 67 |
recipes = recipes_script(self.recipes_widget)
|
| 68 |
dashboard_script(parser, self.dashboard_widget, talents, recipes, equipments, consumables)
|
| 69 |
|
| 70 |
-
self.bottom_widget.hide()
|
| 71 |
-
|
| 72 |
|
| 73 |
if __name__ == "__main__":
|
| 74 |
app = QApplication(sys.argv)
|
|
|
|
| 4 |
from PySide6.QtGui import QIcon
|
| 5 |
|
| 6 |
from qt.components.top import TopWidget
|
| 7 |
+
from qt.scripts.config import config_script
|
| 8 |
from qt.scripts.top import top_script
|
| 9 |
+
from qt.components.config import ConfigWidget
|
| 10 |
from qt.components.equipments import EquipmentsWidget
|
| 11 |
from qt.scripts.equipments import equipments_script
|
| 12 |
from qt.components.consumables import ConsumablesWidget
|
|
|
|
| 20 |
from qt.components.dashboard import DashboardWidget
|
| 21 |
from qt.scripts.dashboard import dashboard_script
|
| 22 |
|
| 23 |
+
from PySide6.QtWidgets import QApplication, QMainWindow, QStyleFactory
|
| 24 |
+
from PySide6.QtWidgets import QVBoxLayout, QHBoxLayout, QWidget, QTabWidget
|
| 25 |
|
| 26 |
|
| 27 |
class MainWindow(QMainWindow):
|
|
|
|
| 40 |
layout = QVBoxLayout(self.central_widget)
|
| 41 |
|
| 42 |
self.top_widget = TopWidget()
|
| 43 |
+
layout.addWidget(self.top_widget, 1)
|
| 44 |
+
|
| 45 |
+
self.config_widget = ConfigWidget()
|
| 46 |
+
layout.addWidget(self.config_widget, 1)
|
| 47 |
+
self.config_widget.hide()
|
| 48 |
+
|
| 49 |
self.bottom_widget = QWidget()
|
| 50 |
+
layout.addWidget(self.bottom_widget, 8)
|
| 51 |
+
self.bottom_widget.hide()
|
|
|
|
| 52 |
|
| 53 |
+
bottom_layout = QHBoxLayout(self.bottom_widget)
|
| 54 |
+
self.detail_widget = QTabWidget()
|
| 55 |
self.dashboard_widget = DashboardWidget()
|
| 56 |
+
bottom_layout.addWidget(self.detail_widget, 1)
|
| 57 |
bottom_layout.addWidget(self.dashboard_widget, 1)
|
| 58 |
|
| 59 |
self.equipments_widget = EquipmentsWidget()
|
| 60 |
+
self.detail_widget.addTab(self.equipments_widget, "配装")
|
| 61 |
self.consumable_widget = ConsumablesWidget()
|
| 62 |
+
self.detail_widget.addTab(self.consumable_widget, "消耗品")
|
| 63 |
self.talents_widget = TalentsWidget()
|
| 64 |
+
self.detail_widget.addTab(self.talents_widget, "奇穴")
|
| 65 |
self.recipes_widget = RecipesWidget()
|
| 66 |
+
self.detail_widget.addTab(self.recipes_widget, "秘籍")
|
| 67 |
|
| 68 |
parser = top_script(
|
| 69 |
+
self.top_widget, self.config_widget, self.bottom_widget, self.dashboard_widget,
|
| 70 |
self.talents_widget, self.recipes_widget, self.equipments_widget, self.consumable_widget,
|
| 71 |
)
|
| 72 |
+
config_script(parser, self.config_widget, self.equipments_widget)
|
| 73 |
equipments = equipments_script(self.equipments_widget)
|
| 74 |
consumables = consumables_script(self.consumable_widget)
|
| 75 |
talents = talents_script(self.talents_widget)
|
| 76 |
recipes = recipes_script(self.recipes_widget)
|
| 77 |
dashboard_script(parser, self.dashboard_widget, talents, recipes, equipments, consumables)
|
| 78 |
|
|
|
|
|
|
|
| 79 |
|
| 80 |
if __name__ == "__main__":
|
| 81 |
app = QApplication(sys.argv)
|
qt/components/__init__.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
| 1 |
-
from PySide6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel
|
| 2 |
-
|
| 3 |
-
from PySide6.QtWidgets import QComboBox, QRadioButton, QTextBrowser,
|
| 4 |
-
from PySide6.QtCore import Qt
|
| 5 |
|
| 6 |
|
| 7 |
class LabelWidget(QWidget):
|
|
@@ -143,20 +142,16 @@ class SpinWithLabel(LabelWidget):
|
|
| 143 |
|
| 144 |
|
| 145 |
class TextWithLabel(LabelWidget):
|
| 146 |
-
def __init__(self, label
|
| 147 |
super().__init__(label)
|
| 148 |
layout = QVBoxLayout(self)
|
| 149 |
|
| 150 |
-
|
| 151 |
-
self.text_browser = QTextEdit()
|
| 152 |
-
else:
|
| 153 |
-
self.text_browser = QTextBrowser()
|
| 154 |
|
| 155 |
layout.addWidget(self.label)
|
| 156 |
layout.addWidget(self.text_browser)
|
| 157 |
|
| 158 |
-
|
| 159 |
-
layout.addStretch()
|
| 160 |
|
| 161 |
def set_text(self, text):
|
| 162 |
self.text_browser.setText(text)
|
|
|
|
| 1 |
+
from PySide6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel
|
| 2 |
+
from PySide6.QtWidgets import QAbstractItemView, QTableWidgetItem, QHeaderView, QListView
|
| 3 |
+
from PySide6.QtWidgets import QComboBox, QRadioButton, QTextBrowser, QLineEdit, QSpinBox, QListWidget, QTableWidget
|
|
|
|
| 4 |
|
| 5 |
|
| 6 |
class LabelWidget(QWidget):
|
|
|
|
| 142 |
|
| 143 |
|
| 144 |
class TextWithLabel(LabelWidget):
|
| 145 |
+
def __init__(self, label):
|
| 146 |
super().__init__(label)
|
| 147 |
layout = QVBoxLayout(self)
|
| 148 |
|
| 149 |
+
self.text_browser = QLineEdit()
|
|
|
|
|
|
|
|
|
|
| 150 |
|
| 151 |
layout.addWidget(self.label)
|
| 152 |
layout.addWidget(self.text_browser)
|
| 153 |
|
| 154 |
+
layout.addStretch()
|
|
|
|
| 155 |
|
| 156 |
def set_text(self, text):
|
| 157 |
self.text_browser.setText(text)
|
qt/components/config.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from PySide6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QPushButton
|
| 2 |
+
|
| 3 |
+
from qt.components import ComboWithLabel, TextWithLabel
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
class ConfigWidget(QWidget):
|
| 7 |
+
def __init__(self):
|
| 8 |
+
super().__init__()
|
| 9 |
+
layout = QVBoxLayout(self)
|
| 10 |
+
top_layout = QHBoxLayout()
|
| 11 |
+
layout.addLayout(top_layout)
|
| 12 |
+
bottom_layout = QHBoxLayout()
|
| 13 |
+
layout.addLayout(bottom_layout)
|
| 14 |
+
|
| 15 |
+
self.config_select = ComboWithLabel("Select")
|
| 16 |
+
top_layout.addWidget(self.config_select, 1)
|
| 17 |
+
self.load_config = QPushButton("Load")
|
| 18 |
+
bottom_layout.addWidget(self.load_config, 1)
|
| 19 |
+
self.config_name = TextWithLabel("Name")
|
| 20 |
+
top_layout.addWidget(self.config_name, 1)
|
| 21 |
+
self.save_config = QPushButton("Save")
|
| 22 |
+
bottom_layout.addWidget(self.save_config, 1)
|
qt/components/dashboard.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
from PySide6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QTabWidget
|
| 2 |
|
| 3 |
-
from qt.components import ComboWithLabel, SpinWithLabel,
|
| 4 |
from base.constant import SHIELD_BASE_MAP
|
| 5 |
|
| 6 |
|
|
|
|
| 1 |
from PySide6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QTabWidget
|
| 2 |
|
| 3 |
+
from qt.components import ComboWithLabel, SpinWithLabel, LabelWithLabel, TableWithLabel
|
| 4 |
from base.constant import SHIELD_BASE_MAP
|
| 5 |
|
| 6 |
|
qt/components/equipments.py
CHANGED
|
@@ -4,8 +4,7 @@ import os
|
|
| 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
|
| 8 |
-
from PySide6.QtCore import Qt
|
| 9 |
|
| 10 |
|
| 11 |
class EquipmentWidget(QWidget):
|
|
|
|
| 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
|
|
|
|
| 8 |
|
| 9 |
|
| 10 |
class EquipmentWidget(QWidget):
|
qt/scripts/config.py
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import os
|
| 3 |
+
|
| 4 |
+
from qt.components.config import ConfigWidget
|
| 5 |
+
from qt.components.equipments import EquipmentsWidget
|
| 6 |
+
from utils.parser import Parser
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
if not os.path.exists("config"):
|
| 10 |
+
CONFIG = {}
|
| 11 |
+
else:
|
| 12 |
+
CONFIG = json.load(open("config", encoding="utf-8"))
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
def config_script(parser: Parser, config_widget: ConfigWidget, equipments_widget: EquipmentsWidget):
|
| 16 |
+
def load_config():
|
| 17 |
+
config_name = config_widget.config_select.combo_box.currentText()
|
| 18 |
+
config = CONFIG.get(parser.school.school, {}).get(config_name, {})
|
| 19 |
+
|
| 20 |
+
for label, equipment in equipments_widget.items():
|
| 21 |
+
if 'equipment' not in config[label]:
|
| 22 |
+
continue
|
| 23 |
+
else:
|
| 24 |
+
index = equipment.equipment.combo_box.findText(config[label]['equipment'])
|
| 25 |
+
equipment.equipment.combo_box.setCurrentIndex(index)
|
| 26 |
+
|
| 27 |
+
equipment.strength_level.combo_box.setCurrentIndex(config[label]['strength_level'])
|
| 28 |
+
if 'enchant' in config[label]:
|
| 29 |
+
index = equipment.enchant.combo_box.findText(config[label]['enchant'])
|
| 30 |
+
equipment.enchant.combo_box.setCurrentIndex(index)
|
| 31 |
+
if 'special_enchant' in config[label]:
|
| 32 |
+
if equipment.special_enchant.radio_button.isChecked() != config[label]['special_enchant']:
|
| 33 |
+
equipment.special_enchant.radio_button.click()
|
| 34 |
+
if 'embed_levels' in config[label]:
|
| 35 |
+
for i, embed_level in enumerate(equipment.embed_levels):
|
| 36 |
+
embed_level.combo_box.setCurrentIndex(config[label]['embed_levels'][i])
|
| 37 |
+
if 'stone_level' in config[label]:
|
| 38 |
+
equipment.stone_level.combo_box.setCurrentIndex(config[label]['stone_level'])
|
| 39 |
+
if 'stone_attrs' in config[label]:
|
| 40 |
+
for i, stone_attr in enumerate(equipment.stone_attrs):
|
| 41 |
+
index = equipment.stone_attrs[i].combo_box.findText(config[label]['stone_attrs'][i])
|
| 42 |
+
stone_attr.combo_box.setCurrentIndex(index)
|
| 43 |
+
|
| 44 |
+
config_widget.config_name.text_browser.setText(config_name)
|
| 45 |
+
|
| 46 |
+
config_widget.load_config.clicked.connect(load_config)
|
| 47 |
+
|
| 48 |
+
def save_config():
|
| 49 |
+
config_name = config_widget.config_name.text_browser.text()
|
| 50 |
+
if parser.school.school not in CONFIG:
|
| 51 |
+
CONFIG[parser.school.school] = {}
|
| 52 |
+
if config_name not in CONFIG[parser.school.school]:
|
| 53 |
+
CONFIG[parser.school.school][config_name] = {}
|
| 54 |
+
config = CONFIG[parser.school.school][config_name]
|
| 55 |
+
|
| 56 |
+
for label, equipment in equipments_widget.items():
|
| 57 |
+
config[label] = {}
|
| 58 |
+
if not (text := equipment.equipment.combo_box.currentText()):
|
| 59 |
+
continue
|
| 60 |
+
else:
|
| 61 |
+
config[label]['equipment'] = text
|
| 62 |
+
config[label]['strength_level'] = equipment.strength_level.combo_box.currentIndex()
|
| 63 |
+
if equipment.enchant:
|
| 64 |
+
config[label]['enchant'] = equipment.enchant.combo_box.currentText()
|
| 65 |
+
if equipment.special_enchant:
|
| 66 |
+
config[label]['special_enchant'] = equipment.special_enchant.radio_button.isChecked()
|
| 67 |
+
if equipment.embed_levels:
|
| 68 |
+
config[label]['embed_levels'] = [
|
| 69 |
+
embed_level.combo_box.currentIndex() for embed_level in equipment.embed_levels
|
| 70 |
+
]
|
| 71 |
+
if equipment.stone_level:
|
| 72 |
+
config[label]['stone_level'] = equipment.stone_level.combo_box.currentIndex()
|
| 73 |
+
if equipment.stone_attrs:
|
| 74 |
+
config[label]['stone_attrs'] = [
|
| 75 |
+
stone_attr.combo_box.currentText() for stone_attr in equipment.stone_attrs
|
| 76 |
+
]
|
| 77 |
+
json.dump(CONFIG, open("config", "w", encoding="utf-8"), ensure_ascii=False)
|
| 78 |
+
|
| 79 |
+
config_choices = list(CONFIG.get(parser.school.school, {}))
|
| 80 |
+
if current_select := config_widget.config_select.combo_box.currentText():
|
| 81 |
+
default_index = config_choices.index(current_select)
|
| 82 |
+
else:
|
| 83 |
+
default_index = -1
|
| 84 |
+
config_widget.config_select.set_items(config_choices, default_index=default_index)
|
| 85 |
+
|
| 86 |
+
config_widget.save_config.clicked.connect(save_config)
|
qt/scripts/dashboard.py
CHANGED
|
@@ -33,8 +33,9 @@ def detail_content(detail):
|
|
| 33 |
damage_content = [
|
| 34 |
["命中伤害", f"{detail['damage']}"],
|
| 35 |
["会心伤害", f"{detail['critical_damage']}"],
|
| 36 |
-
["
|
| 37 |
-
["
|
|
|
|
| 38 |
]
|
| 39 |
gradient_content = [
|
| 40 |
[ATTR_TYPE_TRANSLATE[k], f"{round(v / detail['expected_damage'] * 100, 2)}%"]
|
|
@@ -109,6 +110,7 @@ def dashboard_script(parser: Parser,
|
|
| 109 |
detail_widget.skill_combo.set_items(skill_choices, default_index=default_index)
|
| 110 |
|
| 111 |
def set_status(_):
|
|
|
|
| 112 |
detail_widget = dashboard_widget.detail_widget
|
| 113 |
skill = detail_widget.skill_combo.combo_box.currentText()
|
| 114 |
status = detail_widget.status_combo.combo_box.currentText()
|
|
|
|
| 33 |
damage_content = [
|
| 34 |
["命中伤害", f"{detail['damage']}"],
|
| 35 |
["会心伤害", f"{detail['critical_damage']}"],
|
| 36 |
+
["期望伤害", f"{round(detail['expected_damage'], 2)}"],
|
| 37 |
+
["会心", f"{round(detail['critical_strike'] * 100, 2)}%"],
|
| 38 |
+
["期望会心", f"{round(detail['expected_critical_strike'] * 100, 2)}%"],
|
| 39 |
]
|
| 40 |
gradient_content = [
|
| 41 |
[ATTR_TYPE_TRANSLATE[k], f"{round(v / detail['expected_damage'] * 100, 2)}%"]
|
|
|
|
| 110 |
detail_widget.skill_combo.set_items(skill_choices, default_index=default_index)
|
| 111 |
|
| 112 |
def set_status(_):
|
| 113 |
+
set_detail(None)
|
| 114 |
detail_widget = dashboard_widget.detail_widget
|
| 115 |
skill = detail_widget.skill_combo.combo_box.currentText()
|
| 116 |
status = detail_widget.status_combo.combo_box.currentText()
|
qt/scripts/top.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
| 1 |
from PySide6.QtWidgets import QFileDialog, QWidget
|
| 2 |
|
| 3 |
from general.consumables import FOODS, POTIONS, WEAPON_ENCHANTS, SNACKS, WINES, SPREADS
|
|
|
|
| 4 |
from qt.components.consumables import ConsumablesWidget
|
| 5 |
from qt.components.dashboard import DashboardWidget
|
| 6 |
from qt.components.equipments import EquipmentsWidget
|
|
@@ -11,19 +12,22 @@ from qt.components.top import TopWidget
|
|
| 11 |
# from general.consumables import FOODS, POTIONS, WEAPON_ENCHANTS, SPREADS, SNACKS, WINES
|
| 12 |
# from general.gains.formation import FORMATIONS
|
| 13 |
from qt.constant import MAX_RECIPES, MAX_STONE_LEVEL
|
|
|
|
| 14 |
from utils.parser import Parser
|
| 15 |
|
| 16 |
|
| 17 |
-
def top_script(top_widget: TopWidget, config_widget:
|
| 18 |
-
talents_widget: TalentsWidget, recipes_widget: RecipesWidget,
|
| 19 |
-
equipments_widget: EquipmentsWidget, consumables_widget: ConsumablesWidget
|
| 20 |
-
):
|
| 21 |
parser = Parser()
|
| 22 |
|
| 23 |
def upload_logs():
|
| 24 |
file_name = QFileDialog(top_widget, "Choose File").getOpenFileName()
|
| 25 |
parser(file_name[0])
|
| 26 |
school = parser.school
|
|
|
|
|
|
|
|
|
|
| 27 |
""" Update dashboard """
|
| 28 |
record_index = list(parser.record_index)
|
| 29 |
dashboard_widget.fight_select.set_items(record_index)
|
|
@@ -78,6 +82,7 @@ def top_script(top_widget: TopWidget, config_widget: QWidget, dashboard_widget:
|
|
| 78 |
consumables_widget.spread.set_items([""] + SPREADS[school.major] + SPREADS[school.kind])
|
| 79 |
|
| 80 |
config_widget.show()
|
|
|
|
| 81 |
|
| 82 |
top_widget.upload_button.clicked.connect(upload_logs)
|
| 83 |
|
|
|
|
| 1 |
from PySide6.QtWidgets import QFileDialog, QWidget
|
| 2 |
|
| 3 |
from general.consumables import FOODS, POTIONS, WEAPON_ENCHANTS, SNACKS, WINES, SPREADS
|
| 4 |
+
from qt.components.config import ConfigWidget
|
| 5 |
from qt.components.consumables import ConsumablesWidget
|
| 6 |
from qt.components.dashboard import DashboardWidget
|
| 7 |
from qt.components.equipments import EquipmentsWidget
|
|
|
|
| 12 |
# from general.consumables import FOODS, POTIONS, WEAPON_ENCHANTS, SPREADS, SNACKS, WINES
|
| 13 |
# from general.gains.formation import FORMATIONS
|
| 14 |
from qt.constant import MAX_RECIPES, MAX_STONE_LEVEL
|
| 15 |
+
from qt.scripts.config import CONFIG
|
| 16 |
from utils.parser import Parser
|
| 17 |
|
| 18 |
|
| 19 |
+
def top_script(top_widget: TopWidget, config_widget: ConfigWidget, bottom_widget: QWidget,
|
| 20 |
+
dashboard_widget: DashboardWidget, talents_widget: TalentsWidget, recipes_widget: RecipesWidget,
|
| 21 |
+
equipments_widget: EquipmentsWidget, consumables_widget: ConsumablesWidget):
|
|
|
|
| 22 |
parser = Parser()
|
| 23 |
|
| 24 |
def upload_logs():
|
| 25 |
file_name = QFileDialog(top_widget, "Choose File").getOpenFileName()
|
| 26 |
parser(file_name[0])
|
| 27 |
school = parser.school
|
| 28 |
+
""" Update config """
|
| 29 |
+
config_choices = list(CONFIG.get(school.school, {}))
|
| 30 |
+
config_widget.config_select.set_items(config_choices, default_index=-1)
|
| 31 |
""" Update dashboard """
|
| 32 |
record_index = list(parser.record_index)
|
| 33 |
dashboard_widget.fight_select.set_items(record_index)
|
|
|
|
| 82 |
consumables_widget.spread.set_items([""] + SPREADS[school.major] + SPREADS[school.kind])
|
| 83 |
|
| 84 |
config_widget.show()
|
| 85 |
+
bottom_widget.show()
|
| 86 |
|
| 87 |
top_widget.upload_button.clicked.connect(upload_logs)
|
| 88 |
|
schools/bei_ao_jue/skills.py
CHANGED
|
@@ -59,6 +59,13 @@ SKILLS: Dict[int, Skill | dict] = {
|
|
| 59 |
[280],
|
| 60 |
"interval": 48
|
| 61 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 62 |
16933: {
|
| 63 |
"skill_class": PhysicalDamage,
|
| 64 |
"skill_name": "惊燕式",
|
|
@@ -299,6 +306,12 @@ SKILLS: Dict[int, Skill | dict] = {
|
|
| 299 |
"attack_power_cof": 380,
|
| 300 |
"interval": 48
|
| 301 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 302 |
}
|
| 303 |
}
|
| 304 |
|
|
|
|
| 59 |
[280],
|
| 60 |
"interval": 48
|
| 61 |
},
|
| 62 |
+
17060: {
|
| 63 |
+
"skill_class": Skill,
|
| 64 |
+
"skill_name": "闹须弥",
|
| 65 |
+
"bind_skill": 11447,
|
| 66 |
+
"tick": 8
|
| 67 |
+
|
| 68 |
+
},
|
| 69 |
16933: {
|
| 70 |
"skill_class": PhysicalDamage,
|
| 71 |
"skill_name": "惊燕式",
|
|
|
|
| 306 |
"attack_power_cof": 380,
|
| 307 |
"interval": 48
|
| 308 |
|
| 309 |
+
},
|
| 310 |
+
26934: {
|
| 311 |
+
"skill_class": Skill,
|
| 312 |
+
"skill_name": "背水沉舟",
|
| 313 |
+
"bind_skill": 19555,
|
| 314 |
+
"tick": 8
|
| 315 |
}
|
| 316 |
}
|
| 317 |
|
utils/analyzer.py
CHANGED
|
@@ -11,7 +11,7 @@ def filter_status(status, school: School, skill_id):
|
|
| 11 |
if buff.gain_attributes or skill_id in buff.gain_skills:
|
| 12 |
buffs.append(buff)
|
| 13 |
|
| 14 |
-
return buffs
|
| 15 |
|
| 16 |
|
| 17 |
def add_buffs(current_buffs, snapshot_buffs, attribute: Attribute, skill: Skill):
|
|
@@ -42,7 +42,6 @@ def analyze_details(record, duration: int, attribute: Attribute, school: School)
|
|
| 42 |
total_gradients = {attr: 0. for attr in attribute.grad_attrs}
|
| 43 |
duration *= 1000
|
| 44 |
|
| 45 |
-
existed_buffs = []
|
| 46 |
for skill, status in record.items():
|
| 47 |
skill_id, skill_level, skill_stack = skill
|
| 48 |
skill: Skill = school.skills[skill_id]
|
|
@@ -51,7 +50,13 @@ def analyze_details(record, duration: int, attribute: Attribute, school: School)
|
|
| 51 |
skill_detail = {}
|
| 52 |
details[skill.display_name] = skill_detail
|
| 53 |
for (current_status, snapshot_status), timeline in status.items():
|
| 54 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 55 |
if not timeline:
|
| 56 |
continue
|
| 57 |
|
|
@@ -59,7 +64,7 @@ def analyze_details(record, duration: int, attribute: Attribute, school: School)
|
|
| 59 |
snapshot_buffs = filter_status(snapshot_status, school, skill_id)
|
| 60 |
add_buffs(current_buffs, snapshot_buffs, attribute, skill)
|
| 61 |
|
| 62 |
-
damage,
|
| 63 |
gradients = analyze_gradients(skill, attribute)
|
| 64 |
|
| 65 |
sub_buffs(current_buffs, snapshot_buffs, attribute, skill)
|
|
@@ -68,16 +73,18 @@ def analyze_details(record, duration: int, attribute: Attribute, school: School)
|
|
| 68 |
for attr, residual_damage in gradients.items():
|
| 69 |
total_gradients[attr] += residual_damage * len(timeline)
|
| 70 |
|
| 71 |
-
buffs =
|
| 72 |
-
|
| 73 |
-
|
|
|
|
| 74 |
if not buffs:
|
| 75 |
-
buffs = "
|
| 76 |
skill_detail[buffs] = dict(
|
| 77 |
damage=damage,
|
| 78 |
-
critical_strike=critical_strike,
|
| 79 |
critical_damage=critical_damage,
|
| 80 |
expected_damage=expected_damage,
|
|
|
|
|
|
|
| 81 |
# "timeline": [round(t / 1000, 3) for t in timeline],
|
| 82 |
count=len(timeline),
|
| 83 |
gradients=gradients
|
|
@@ -95,10 +102,10 @@ def analyze_summary(details):
|
|
| 95 |
for skill, skill_detail in details.items():
|
| 96 |
skill = skill.split("/")[0]
|
| 97 |
if skill not in summary:
|
| 98 |
-
summary[skill] = {"count": 0, "
|
| 99 |
for buff, detail in skill_detail.items():
|
| 100 |
summary[skill]["count"] += detail['count']
|
| 101 |
-
summary[skill]["critical"] += detail['count'] * detail['
|
| 102 |
summary[skill]["damage"] += detail['count'] * detail['expected_damage']
|
| 103 |
|
| 104 |
return summary
|
|
|
|
| 11 |
if buff.gain_attributes or skill_id in buff.gain_skills:
|
| 12 |
buffs.append(buff)
|
| 13 |
|
| 14 |
+
return tuple(buffs)
|
| 15 |
|
| 16 |
|
| 17 |
def add_buffs(current_buffs, snapshot_buffs, attribute: Attribute, skill: Skill):
|
|
|
|
| 42 |
total_gradients = {attr: 0. for attr in attribute.grad_attrs}
|
| 43 |
duration *= 1000
|
| 44 |
|
|
|
|
| 45 |
for skill, status in record.items():
|
| 46 |
skill_id, skill_level, skill_stack = skill
|
| 47 |
skill: Skill = school.skills[skill_id]
|
|
|
|
| 50 |
skill_detail = {}
|
| 51 |
details[skill.display_name] = skill_detail
|
| 52 |
for (current_status, snapshot_status), timeline in status.items():
|
| 53 |
+
hit_timeline, critical_timeline = [], []
|
| 54 |
+
for timestamp, critical in timeline:
|
| 55 |
+
if critical:
|
| 56 |
+
critical_timeline.append(timestamp)
|
| 57 |
+
else:
|
| 58 |
+
hit_timeline.append(timestamp)
|
| 59 |
+
timeline = [t for t in timeline if t[0] < duration]
|
| 60 |
if not timeline:
|
| 61 |
continue
|
| 62 |
|
|
|
|
| 64 |
snapshot_buffs = filter_status(snapshot_status, school, skill_id)
|
| 65 |
add_buffs(current_buffs, snapshot_buffs, attribute, skill)
|
| 66 |
|
| 67 |
+
damage, expected_critical_strike, critical_damage, expected_damage = skill(attribute)
|
| 68 |
gradients = analyze_gradients(skill, attribute)
|
| 69 |
|
| 70 |
sub_buffs(current_buffs, snapshot_buffs, attribute, skill)
|
|
|
|
| 73 |
for attr, residual_damage in gradients.items():
|
| 74 |
total_gradients[attr] += residual_damage * len(timeline)
|
| 75 |
|
| 76 |
+
buffs = ",".join(buff.display_name for buff in current_buffs)
|
| 77 |
+
if snapshot_buffs and current_buffs != snapshot_buffs:
|
| 78 |
+
buffs += f"({','.join(buff.display_name for buff in snapshot_buffs)})"
|
| 79 |
+
|
| 80 |
if not buffs:
|
| 81 |
+
buffs = "~"
|
| 82 |
skill_detail[buffs] = dict(
|
| 83 |
damage=damage,
|
|
|
|
| 84 |
critical_damage=critical_damage,
|
| 85 |
expected_damage=expected_damage,
|
| 86 |
+
critical_strike=len(critical_timeline) / (len(critical_timeline) + len(hit_timeline)),
|
| 87 |
+
expected_critical_strike=expected_critical_strike,
|
| 88 |
# "timeline": [round(t / 1000, 3) for t in timeline],
|
| 89 |
count=len(timeline),
|
| 90 |
gradients=gradients
|
|
|
|
| 102 |
for skill, skill_detail in details.items():
|
| 103 |
skill = skill.split("/")[0]
|
| 104 |
if skill not in summary:
|
| 105 |
+
summary[skill] = {"count": 0, "critical": 0, "damage": 0}
|
| 106 |
for buff, detail in skill_detail.items():
|
| 107 |
summary[skill]["count"] += detail['count']
|
| 108 |
+
summary[skill]["critical"] += detail['count'] * detail['expected_critical_strike']
|
| 109 |
summary[skill]["damage"] += detail['count'] * detail['expected_damage']
|
| 110 |
|
| 111 |
return summary
|
utils/parser.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
| 1 |
from dataclasses import dataclass
|
| 2 |
from typing import Dict, List, Type, Union, Tuple
|
|
|
|
| 3 |
|
| 4 |
from base.attribute import Attribute
|
| 5 |
from base.buff import Buff
|
|
@@ -124,8 +125,8 @@ class Parser:
|
|
| 124 |
self.records = []
|
| 125 |
self.status = {}
|
| 126 |
self.snapshot = {}
|
| 127 |
-
self.stacks =
|
| 128 |
-
self.ticks =
|
| 129 |
|
| 130 |
self.start_time = []
|
| 131 |
self.end_time = []
|
|
@@ -161,16 +162,16 @@ class Parser:
|
|
| 161 |
self.status[(buff_id, buff_level)] = buff_stack
|
| 162 |
|
| 163 |
def parse_skill(self, detail, timestamp):
|
| 164 |
-
skill_id, skill_level = detail[4], detail[5]
|
| 165 |
if skill_id not in self.school.skills:
|
| 166 |
return
|
| 167 |
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
if self.ticks
|
| 171 |
self.ticks[skill_id] -= 1
|
| 172 |
-
|
| 173 |
-
|
| 174 |
|
| 175 |
skill_tuple = (skill_id, skill_level, skill_stack)
|
| 176 |
skill = self.school.skills[skill_id]
|
|
@@ -181,10 +182,10 @@ class Parser:
|
|
| 181 |
else:
|
| 182 |
if skill_tuple not in self.current_record:
|
| 183 |
self.current_record[skill_tuple] = {}
|
| 184 |
-
|
| 185 |
-
if
|
| 186 |
-
self.current_record[skill_tuple][
|
| 187 |
-
self.current_record[skill_tuple][
|
| 188 |
|
| 189 |
def __call__(self, file_name):
|
| 190 |
self.reset()
|
|
|
|
| 1 |
from dataclasses import dataclass
|
| 2 |
from typing import Dict, List, Type, Union, Tuple
|
| 3 |
+
from collections import defaultdict
|
| 4 |
|
| 5 |
from base.attribute import Attribute
|
| 6 |
from base.buff import Buff
|
|
|
|
| 125 |
self.records = []
|
| 126 |
self.status = {}
|
| 127 |
self.snapshot = {}
|
| 128 |
+
self.stacks = defaultdict(int)
|
| 129 |
+
self.ticks = defaultdict(int)
|
| 130 |
|
| 131 |
self.start_time = []
|
| 132 |
self.end_time = []
|
|
|
|
| 162 |
self.status[(buff_id, buff_level)] = buff_stack
|
| 163 |
|
| 164 |
def parse_skill(self, detail, timestamp):
|
| 165 |
+
skill_id, skill_level, critical = detail[4], detail[5], detail[6]
|
| 166 |
if skill_id not in self.school.skills:
|
| 167 |
return
|
| 168 |
|
| 169 |
+
timestamp = int(timestamp) - self.start_time[-1]
|
| 170 |
+
skill_stack = max(1, self.stacks[skill_id])
|
| 171 |
+
if self.ticks[skill_id]:
|
| 172 |
self.ticks[skill_id] -= 1
|
| 173 |
+
if not self.ticks[skill_id]:
|
| 174 |
+
self.stacks[skill_id] = 0
|
| 175 |
|
| 176 |
skill_tuple = (skill_id, skill_level, skill_stack)
|
| 177 |
skill = self.school.skills[skill_id]
|
|
|
|
| 182 |
else:
|
| 183 |
if skill_tuple not in self.current_record:
|
| 184 |
self.current_record[skill_tuple] = {}
|
| 185 |
+
status_tuple = self.available_status(skill_id)
|
| 186 |
+
if status_tuple not in self.current_record[skill_tuple]:
|
| 187 |
+
self.current_record[skill_tuple][status_tuple] = []
|
| 188 |
+
self.current_record[skill_tuple][status_tuple].append((timestamp, critical))
|
| 189 |
|
| 190 |
def __call__(self, file_name):
|
| 191 |
self.reset()
|