Spaces:
Runtime error
Runtime error
5.2 commit
Browse files- .github/workflows/nuitka-app.yml +1 -1
- base/skill.py +26 -32
- qt/app.py +1 -1
- qt/components/dashboard.py +0 -2
- qt/scripts/dashboard.py +3 -12
- qt/scripts/top.py +1 -3
- schools/__init__.py +0 -10
- utils/parser.py +145 -100
.github/workflows/nuitka-app.yml
CHANGED
@@ -26,7 +26,7 @@ jobs:
|
|
26 |
- name: Install dependencies
|
27 |
run: |
|
28 |
python -m pip install --upgrade pip
|
29 |
-
pip install - ./requirements.txt
|
30 |
- uses: Nuitka/Nuitka-Action@main
|
31 |
with:
|
32 |
nuitka-version: main
|
|
|
26 |
- name: Install dependencies
|
27 |
run: |
|
28 |
python -m pip install --upgrade pip
|
29 |
+
pip install -r ./requirements.txt
|
30 |
- uses: Nuitka/Nuitka-Action@main
|
31 |
with:
|
32 |
nuitka-version: main
|
base/skill.py
CHANGED
@@ -168,7 +168,7 @@ class Skill:
|
|
168 |
else:
|
169 |
self._skill_shield_gain = [skill_shield_gain]
|
170 |
|
171 |
-
def record(self,
|
172 |
pass
|
173 |
|
174 |
def __call__(self, attribute: Attribute):
|
@@ -184,38 +184,33 @@ class BuffConsumeSkill(Skill):
|
|
184 |
|
185 |
|
186 |
class DotSkill(Skill):
|
187 |
-
def record(self,
|
188 |
-
super().record(
|
189 |
bind_skill = self.bind_skill
|
190 |
-
if not parser.
|
191 |
-
parser.
|
192 |
-
parser.
|
193 |
-
parser.
|
194 |
-
parser.
|
195 |
|
196 |
|
197 |
class DotConsumeSkill(Skill):
|
198 |
-
def record(self,
|
199 |
-
super().record(
|
200 |
bind_skill = self.bind_skill
|
201 |
-
skill_tuple, status_tuple = parser.
|
202 |
skill_id, skill_level, skill_stack = skill_tuple
|
203 |
-
parser.
|
204 |
-
tick = min(parser.
|
205 |
-
|
206 |
-
|
207 |
-
current_record[skill_tuple][status_tuple].pop()
|
208 |
)
|
209 |
-
parser.
|
210 |
|
211 |
|
212 |
class PureSkill(Skill):
|
213 |
def __call__(self, attribute: Attribute):
|
214 |
-
damage = init_result(
|
215 |
-
self.damage_base, self.damage_rand,
|
216 |
-
0, 0,
|
217 |
-
0, 0, 0, 0
|
218 |
-
)
|
219 |
|
220 |
damage = level_reduction_result(damage, attribute.level_reduction)
|
221 |
damage = vulnerable_result(damage, attribute.vulnerable)
|
@@ -326,22 +321,21 @@ class MixingSkill(Skill):
|
|
326 |
|
327 |
|
328 |
class Damage(Skill):
|
329 |
-
def record(self,
|
330 |
-
super().record(
|
331 |
skill_tuple = (self.skill_id, skill_level, skill_stack)
|
332 |
-
status_tuple = parser.available_status(
|
333 |
-
|
334 |
-
|
335 |
-
(current_frame - parser.start_time[player_id][-1], critical)
|
336 |
)
|
337 |
return skill_tuple, status_tuple
|
338 |
|
339 |
|
340 |
class DotDamage(Damage):
|
341 |
-
def record(self,
|
342 |
-
skill_tuple, status_tuple = super().record(
|
343 |
-
parser.
|
344 |
-
parser.
|
345 |
|
346 |
|
347 |
class PetDamage(Damage):
|
|
|
168 |
else:
|
169 |
self._skill_shield_gain = [skill_shield_gain]
|
170 |
|
171 |
+
def record(self, skill_level, skill_stack, critical, parser):
|
172 |
pass
|
173 |
|
174 |
def __call__(self, attribute: Attribute):
|
|
|
184 |
|
185 |
|
186 |
class DotSkill(Skill):
|
187 |
+
def record(self, skill_level, skill_stack, critical, parser):
|
188 |
+
super().record(skill_level, skill_stack, critical, parser)
|
189 |
bind_skill = self.bind_skill
|
190 |
+
if not parser.current_ticks[bind_skill]:
|
191 |
+
parser.current_stacks[bind_skill] = 0
|
192 |
+
parser.current_ticks[bind_skill] = self.tick
|
193 |
+
parser.current_stacks[bind_skill] = min(parser.current_stacks[bind_skill] + 1, self.max_stack)
|
194 |
+
parser.current_snapshot[bind_skill] = parser.current_status.copy()
|
195 |
|
196 |
|
197 |
class DotConsumeSkill(Skill):
|
198 |
+
def record(self, skill_level, skill_stack, critical, parser):
|
199 |
+
super().record(skill_level, skill_stack, critical, parser)
|
200 |
bind_skill = self.bind_skill
|
201 |
+
skill_tuple, status_tuple = parser.current_last_dot[bind_skill]
|
202 |
skill_id, skill_level, skill_stack = skill_tuple
|
203 |
+
parser.current_ticks[skill_id] += 1
|
204 |
+
tick = min(parser.current_ticks[skill_id], self.tick)
|
205 |
+
parser.current_records[(skill_id, skill_level, skill_stack * tick)][status_tuple].append(
|
206 |
+
parser.current_records[skill_tuple][status_tuple].pop()
|
|
|
207 |
)
|
208 |
+
parser.current_ticks[skill_id] -= tick
|
209 |
|
210 |
|
211 |
class PureSkill(Skill):
|
212 |
def __call__(self, attribute: Attribute):
|
213 |
+
damage = init_result(self.damage_base, self.damage_rand, 0, 0, 0, 0, 0, 0)
|
|
|
|
|
|
|
|
|
214 |
|
215 |
damage = level_reduction_result(damage, attribute.level_reduction)
|
216 |
damage = vulnerable_result(damage, attribute.vulnerable)
|
|
|
321 |
|
322 |
|
323 |
class Damage(Skill):
|
324 |
+
def record(self, skill_level, skill_stack, critical, parser):
|
325 |
+
super().record(skill_level, skill_stack, critical, parser)
|
326 |
skill_tuple = (self.skill_id, skill_level, skill_stack)
|
327 |
+
status_tuple = parser.available_status(self.skill_id)
|
328 |
+
parser.current_records[skill_tuple][status_tuple].append(
|
329 |
+
(parser.current_frame - parser.start_frame, critical)
|
|
|
330 |
)
|
331 |
return skill_tuple, status_tuple
|
332 |
|
333 |
|
334 |
class DotDamage(Damage):
|
335 |
+
def record(self, skill_level, skill_stack, critical, parser):
|
336 |
+
skill_tuple, status_tuple = super().record(skill_level, skill_stack, critical, parser)
|
337 |
+
parser.current_last_dot[self.skill_id] = (skill_tuple, status_tuple)
|
338 |
+
parser.current_ticks[self.skill_id] -= 1
|
339 |
|
340 |
|
341 |
class PetDamage(Damage):
|
qt/app.py
CHANGED
@@ -82,7 +82,7 @@ class MainWindow(QMainWindow):
|
|
82 |
equipments = equipments_script(self.equipments_widget)
|
83 |
consumables = consumables_script(self.consumable_widget)
|
84 |
bonuses = bonuses_script(parser, self.bonus_widget)
|
85 |
-
dashboard_script(parser, self.
|
86 |
talents, recipes, equipments, consumables, bonuses)
|
87 |
|
88 |
|
|
|
82 |
equipments = equipments_script(self.equipments_widget)
|
83 |
consumables = consumables_script(self.consumable_widget)
|
84 |
bonuses = bonuses_script(parser, self.bonus_widget)
|
85 |
+
dashboard_script(parser, self.dashboard_widget,
|
86 |
talents, recipes, equipments, consumables, bonuses)
|
87 |
|
88 |
|
qt/components/dashboard.py
CHANGED
@@ -32,8 +32,6 @@ class DashboardWidget(QWidget):
|
|
32 |
top_layout = QHBoxLayout()
|
33 |
layout.addLayout(top_layout)
|
34 |
|
35 |
-
self.fight_select = ComboWithLabel("选择战斗")
|
36 |
-
top_layout.addWidget(self.fight_select)
|
37 |
self.target_level = ComboWithLabel("目标等级", items=[str(level) for level in SHIELD_BASE_MAP])
|
38 |
top_layout.addWidget(self.target_level)
|
39 |
self.duration = DoubleSpinWithLabel("战斗时长", maximum=3600, value=180)
|
|
|
32 |
top_layout = QHBoxLayout()
|
33 |
layout.addLayout(top_layout)
|
34 |
|
|
|
|
|
35 |
self.target_level = ComboWithLabel("目标等级", items=[str(level) for level in SHIELD_BASE_MAP])
|
36 |
top_layout.addWidget(self.target_level)
|
37 |
self.duration = DoubleSpinWithLabel("战斗时长", maximum=3600, value=180)
|
qt/scripts/dashboard.py
CHANGED
@@ -46,24 +46,15 @@ def detail_content(detail: Detail):
|
|
46 |
return damage_content, gradient_content
|
47 |
|
48 |
|
49 |
-
def dashboard_script(parser: Parser,
|
50 |
dashboard_widget: DashboardWidget, talents: Talents, recipes: Recipes,
|
51 |
equipments: Equipments, consumables: Consumables, bonuses: Bonuses):
|
52 |
|
53 |
-
def select_fight(text):
|
54 |
-
player_id = parser.current_player
|
55 |
-
index = parser.record_index[player_id][text]
|
56 |
-
dashboard_widget.duration.set_value(parser.duration(player_id, index))
|
57 |
-
|
58 |
-
dashboard_widget.fight_select.combo_box.currentTextChanged.connect(select_fight)
|
59 |
-
|
60 |
def formulate():
|
61 |
duration = dashboard_widget.duration.spin_box.value()
|
62 |
-
|
63 |
-
|
64 |
-
record = parser.records[player_id][parser.record_index[player_id][record_index]]
|
65 |
|
66 |
-
school = parser.school[player_id]
|
67 |
attribute = school.attribute()
|
68 |
attribute.target_level = int(dashboard_widget.target_level.combo_box.currentText())
|
69 |
for attr, value in equipments.attrs.items():
|
|
|
46 |
return damage_content, gradient_content
|
47 |
|
48 |
|
49 |
+
def dashboard_script(parser: Parser,
|
50 |
dashboard_widget: DashboardWidget, talents: Talents, recipes: Recipes,
|
51 |
equipments: Equipments, consumables: Consumables, bonuses: Bonuses):
|
52 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
53 |
def formulate():
|
54 |
duration = dashboard_widget.duration.spin_box.value()
|
55 |
+
record = parser.current_records
|
56 |
+
school = parser.school[parser.current_player]
|
|
|
57 |
|
|
|
58 |
attribute = school.attribute()
|
59 |
attribute.target_level = int(dashboard_widget.target_level.combo_box.currentText())
|
60 |
for attr, value in equipments.attrs.items():
|
qt/scripts/top.py
CHANGED
@@ -46,9 +46,7 @@ def top_script(
|
|
46 |
config_choices = list(CONFIG.get(school.school, {}))
|
47 |
config_widget.config_select.set_items(config_choices, default_index=-1)
|
48 |
""" Update dashboard """
|
49 |
-
|
50 |
-
dashboard_widget.fight_select.set_items(record_index, default_index=0)
|
51 |
-
dashboard_widget.duration.set_value(parser.duration(player_id, parser.record_index[player_id][record_index[0]]))
|
52 |
|
53 |
""" Update talent options """
|
54 |
for i, talent_widget in enumerate(talents_widget.values()):
|
|
|
46 |
config_choices = list(CONFIG.get(school.school, {}))
|
47 |
config_widget.config_select.set_items(config_choices, default_index=-1)
|
48 |
""" Update dashboard """
|
49 |
+
dashboard_widget.duration.set_value(parser.duration)
|
|
|
|
|
50 |
|
51 |
""" Update talent options """
|
52 |
for i, talent_widget in enumerate(talents_widget.values()):
|
schools/__init__.py
CHANGED
@@ -8,16 +8,6 @@ from base.skill import Skill
|
|
8 |
|
9 |
from schools import bei_ao_jue, shan_hai_xin_jue, ling_hai_jue, wu_fang, gu_feng_jue, tai_xu_jian_yi, tian_luo_gui_dao
|
10 |
|
11 |
-
SKILL_TYPE = Tuple[int, int, int]
|
12 |
-
BUFFER_TYPE = Tuple[int, int, int, bool]
|
13 |
-
# BUFFER_TYPE = Tuple[int, int]
|
14 |
-
BUFF_TYPE = Tuple[int, int, int]
|
15 |
-
TIMELINE_TYPE = List[Tuple[int, bool]]
|
16 |
-
SUB_RECORD_TYPE = Dict[Tuple[tuple, tuple], TIMELINE_TYPE]
|
17 |
-
RECORD_TYPE = Dict[SKILL_TYPE, SUB_RECORD_TYPE]
|
18 |
-
STATUS_TYPE = Dict[Tuple[int, int], int]
|
19 |
-
SNAPSHOT_TYPE = Dict[int, STATUS_TYPE]
|
20 |
-
|
21 |
|
22 |
@dataclass
|
23 |
class School:
|
|
|
8 |
|
9 |
from schools import bei_ao_jue, shan_hai_xin_jue, ling_hai_jue, wu_fang, gu_feng_jue, tai_xu_jian_yi, tian_luo_gui_dao
|
10 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
11 |
|
12 |
@dataclass
|
13 |
class School:
|
utils/parser.py
CHANGED
@@ -4,6 +4,19 @@ from base.constant import FRAME_PER_SECOND
|
|
4 |
from schools import *
|
5 |
from utils.lua import parse
|
6 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7 |
|
8 |
LABEL_MAPPING = {
|
9 |
2: "远程武器",
|
@@ -21,71 +34,89 @@ LABEL_MAPPING = {
|
|
21 |
}
|
22 |
EMBED_MAPPING: Dict[tuple, int] = {(5, 24449 - i): 8 - i for i in range(8)}
|
23 |
|
24 |
-
BUFFER_DELAY = 2
|
25 |
|
26 |
|
27 |
class Parser:
|
28 |
-
current_player:
|
29 |
-
current_frame:
|
|
|
30 |
|
31 |
-
id2name: Dict[
|
32 |
-
name2id: Dict[
|
33 |
-
|
34 |
-
|
35 |
-
status: Dict[int, STATUS_TYPE]
|
36 |
-
snapshot: Dict[int, SNAPSHOT_TYPE]
|
37 |
-
last_dot: Dict[int, Dict[int, Tuple[Tuple[int, int, int], Tuple[tuple, tuple]]]]
|
38 |
-
stacks: Dict[int, Dict[int, int]]
|
39 |
-
ticks: Dict[int, Dict[int, int]]
|
40 |
-
pets: Dict[int, int]
|
41 |
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
|
46 |
-
|
47 |
-
|
48 |
|
49 |
-
|
|
|
50 |
|
51 |
-
|
52 |
-
|
53 |
|
54 |
-
|
55 |
-
|
56 |
-
for (buff_id, buff_level), buff_stack in self.status[player_id].items():
|
57 |
-
buff = self.school[player_id].buffs[buff_id]
|
58 |
-
if buff.gain_attributes:
|
59 |
-
current_status.append((buff_id, buff_level, buff_stack))
|
60 |
-
elif buff.gain_skills and skill_id in buff.gain_skills:
|
61 |
-
current_status.append((buff_id, buff_level, buff_stack))
|
62 |
|
63 |
-
|
64 |
-
for (buff_id, buff_level), buff_stack in self.snapshot[player_id].get(skill_id, {}).items():
|
65 |
-
buff = self.school[player_id].buffs[buff_id]
|
66 |
-
if buff.gain_attributes:
|
67 |
-
snapshot_status.append((buff_id, buff_level, buff_stack))
|
68 |
-
elif buff.gain_skills and skill_id in buff.gain_skills:
|
69 |
-
snapshot_status.append((buff_id, buff_level, buff_stack))
|
70 |
|
71 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
72 |
|
73 |
def reset(self):
|
74 |
self.current_frame = 0
|
75 |
|
|
|
|
|
76 |
self.id2name = {}
|
77 |
self.name2id = {}
|
78 |
-
self.
|
79 |
-
self.
|
80 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
81 |
self.snapshot = defaultdict(dict)
|
82 |
self.last_dot = defaultdict(dict)
|
83 |
-
self.stacks = defaultdict(lambda: defaultdict(lambda: 1))
|
84 |
-
self.ticks = defaultdict(lambda: defaultdict(int))
|
85 |
-
self.pets = {}
|
86 |
|
87 |
-
self.
|
88 |
-
self.end_time = defaultdict(list)
|
89 |
|
90 |
self.select_talents = {}
|
91 |
self.select_equipments = {}
|
@@ -131,18 +162,8 @@ class Parser:
|
|
131 |
if player_id in self.school:
|
132 |
self.pets[pet_id] = player_id
|
133 |
|
134 |
-
def parse_time(self, row):
|
135 |
-
detail = row.strip("{}").split(",")
|
136 |
-
player_id = int(detail[0])
|
137 |
-
if player_id not in self.school:
|
138 |
-
return
|
139 |
-
if detail[1] == "true" and len(self.start_time[player_id]) == len(self.end_time[player_id]):
|
140 |
-
self.start_time[player_id].append(self.current_frame)
|
141 |
-
self.records[player_id].append(defaultdict(lambda: defaultdict(list)))
|
142 |
-
elif detail[1] == "false" and len(self.start_time[player_id]) - len(self.end_time[player_id]) == 1:
|
143 |
-
self.end_time[player_id].append(self.current_frame)
|
144 |
-
|
145 |
def parse_buff(self, row):
|
|
|
146 |
detail = row.strip("{}").split(",")
|
147 |
player_id = int(detail[0])
|
148 |
if player_id not in self.school:
|
@@ -150,10 +171,26 @@ class Parser:
|
|
150 |
buff_id, buff_stack, buff_level = int(detail[4]), int(detail[5]), int(detail[8])
|
151 |
if buff_id not in self.school[player_id].buffs:
|
152 |
return
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
157 |
|
158 |
def parse_skill(self, row):
|
159 |
detail = row.strip("{}").split(",")
|
@@ -169,30 +206,34 @@ class Parser:
|
|
169 |
if react or skill_id not in self.school[player_id].skills:
|
170 |
return
|
171 |
skill_stack = self.stacks[player_id][skill_id]
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
self.
|
177 |
-
|
178 |
-
def
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
|
|
|
|
|
|
|
|
196 |
|
197 |
def __call__(self, file_name):
|
198 |
self.reset()
|
@@ -202,19 +243,11 @@ class Parser:
|
|
202 |
if row[4] == "4":
|
203 |
self.parse_info(row[-1])
|
204 |
|
205 |
-
for player_id, school in self.school.items():
|
206 |
-
for talent_id in self.select_talents[player_id]:
|
207 |
-
school.talent_gains[talent_id].add_skills(school.skills)
|
208 |
-
|
209 |
for line in lines:
|
210 |
row = line.split("\t")
|
211 |
-
|
212 |
-
self.parse_record()
|
213 |
-
self.current_frame = int(row[1])
|
214 |
|
215 |
match row[4]:
|
216 |
-
case "5":
|
217 |
-
self.parse_time(row[-1])
|
218 |
case "8":
|
219 |
self.parse_pet(row[-1])
|
220 |
case "13":
|
@@ -222,14 +255,26 @@ class Parser:
|
|
222 |
case "21":
|
223 |
self.parse_skill(row[-1])
|
224 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
225 |
for player_id, school in self.school.items():
|
226 |
for talent_id in self.select_talents[player_id]:
|
227 |
school.talent_gains[talent_id].sub_skills(school.skills)
|
228 |
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
for player_id in self.end_time
|
235 |
-
}
|
|
|
4 |
from schools import *
|
5 |
from utils.lua import parse
|
6 |
|
7 |
+
FRAME_TYPE, PLAYER_ID_TYPE, PLAYER_NAME_TYPE, PET_ID_TYPE = int, int, int, int
|
8 |
+
SKILL_ID_TYPE, SKILL_LEVEL_TYPE, SKILL_STACK_TYPE, SKILL_CRITICAL_TYPE = int, int, int, bool
|
9 |
+
SKILL_BUFFER_TYPE = Tuple[SKILL_ID_TYPE, SKILL_LEVEL_TYPE, SKILL_STACK_TYPE, SKILL_CRITICAL_TYPE]
|
10 |
+
SKILL_TYPE = Tuple[SKILL_ID_TYPE, SKILL_LEVEL_TYPE, SKILL_STACK_TYPE]
|
11 |
+
BUFF_ID_TYPE, BUFF_LEVEL_TYPE, BUFF_STACK_TYPE = int, int, int
|
12 |
+
STATUS_BUFFER_TYPE = Tuple[BUFF_ID_TYPE, BUFF_LEVEL_TYPE]
|
13 |
+
STATUS_TYPE = Tuple[BUFF_ID_TYPE, BUFF_LEVEL_TYPE, BUFF_STACK_TYPE]
|
14 |
+
|
15 |
+
SNAPSHOT_TYPE = Dict[SKILL_ID_TYPE, List[STATUS_TYPE]]
|
16 |
+
|
17 |
+
TIMELINE_TYPE = List[Tuple[FRAME_TYPE, SKILL_CRITICAL_TYPE]]
|
18 |
+
SUB_RECORD_TYPE = Dict[Tuple[tuple, tuple], TIMELINE_TYPE]
|
19 |
+
RECORD_TYPE = Dict[SKILL_TYPE, SUB_RECORD_TYPE]
|
20 |
|
21 |
LABEL_MAPPING = {
|
22 |
2: "远程武器",
|
|
|
34 |
}
|
35 |
EMBED_MAPPING: Dict[tuple, int] = {(5, 24449 - i): 8 - i for i in range(8)}
|
36 |
|
37 |
+
BUFFER_DELAY = -2
|
38 |
|
39 |
|
40 |
class Parser:
|
41 |
+
current_player: PLAYER_ID_TYPE
|
42 |
+
current_frame: FRAME_TYPE
|
43 |
+
frames: List[FRAME_TYPE]
|
44 |
|
45 |
+
id2name: Dict[PLAYER_ID_TYPE, PLAYER_NAME_TYPE]
|
46 |
+
name2id: Dict[PLAYER_NAME_TYPE, PLAYER_ID_TYPE]
|
47 |
+
pets: Dict[PET_ID_TYPE, PLAYER_ID_TYPE]
|
48 |
+
records: Dict[PLAYER_ID_TYPE, RECORD_TYPE]
|
|
|
|
|
|
|
|
|
|
|
|
|
49 |
|
50 |
+
skills: Dict[FRAME_TYPE, Dict[PLAYER_ID_TYPE, List[SKILL_BUFFER_TYPE]]]
|
51 |
+
status_buffer: Dict[PLAYER_ID_TYPE, Dict[STATUS_BUFFER_TYPE, Tuple[BUFF_STACK_TYPE, FRAME_TYPE]]]
|
52 |
+
status: Dict[FRAME_TYPE, Dict[PLAYER_ID_TYPE, List[STATUS_TYPE]]]
|
53 |
|
54 |
+
stacks: Dict[PLAYER_ID_TYPE, Dict[SKILL_ID_TYPE, int]]
|
55 |
+
ticks: Dict[PLAYER_ID_TYPE, Dict[SKILL_ID_TYPE, int]]
|
56 |
|
57 |
+
snapshot: Dict[PLAYER_ID_TYPE, SNAPSHOT_TYPE]
|
58 |
+
last_dot: Dict[PLAYER_ID_TYPE, Dict[SKILL_ID_TYPE, Tuple[SKILL_TYPE, Tuple[tuple, tuple]]]]
|
59 |
|
60 |
+
start_frame: FRAME_TYPE
|
61 |
+
end_frame: FRAME_TYPE
|
62 |
|
63 |
+
select_talents: Dict[PLAYER_ID_TYPE, List[int]]
|
64 |
+
select_equipments: Dict[PLAYER_ID_TYPE, Dict[int, Dict[str, int | list]]]
|
|
|
|
|
|
|
|
|
|
|
|
|
65 |
|
66 |
+
school: Dict[PLAYER_ID_TYPE, School]
|
|
|
|
|
|
|
|
|
|
|
|
|
67 |
|
68 |
+
@property
|
69 |
+
def current_records(self):
|
70 |
+
return self.records[self.current_player]
|
71 |
+
|
72 |
+
@property
|
73 |
+
def current_skills(self):
|
74 |
+
return self.skills[self.current_frame][self.current_player]
|
75 |
+
|
76 |
+
@property
|
77 |
+
def current_status(self):
|
78 |
+
return self.status[self.current_frame][self.current_player]
|
79 |
+
|
80 |
+
@property
|
81 |
+
def current_snapshot(self):
|
82 |
+
return self.snapshot[self.current_player]
|
83 |
+
|
84 |
+
@property
|
85 |
+
def current_stacks(self):
|
86 |
+
return self.stacks[self.current_player]
|
87 |
+
|
88 |
+
@property
|
89 |
+
def current_ticks(self):
|
90 |
+
return self.ticks[self.current_player]
|
91 |
+
|
92 |
+
@property
|
93 |
+
def current_last_dot(self):
|
94 |
+
return self.last_dot[self.current_player]
|
95 |
+
|
96 |
+
@property
|
97 |
+
def duration(self):
|
98 |
+
return round((self.end_frame - self.start_frame) / FRAME_PER_SECOND, 3)
|
99 |
|
100 |
def reset(self):
|
101 |
self.current_frame = 0
|
102 |
|
103 |
+
self.frames = []
|
104 |
+
|
105 |
self.id2name = {}
|
106 |
self.name2id = {}
|
107 |
+
self.pets = {}
|
108 |
+
self.records = defaultdict(lambda: defaultdict(lambda: defaultdict(list)))
|
109 |
+
|
110 |
+
self.skills = defaultdict(lambda: defaultdict(list))
|
111 |
+
self.status_buffer = defaultdict(dict)
|
112 |
+
self.status = defaultdict(lambda: defaultdict(list))
|
113 |
+
|
114 |
+
self.stacks = defaultdict(lambda: defaultdict(lambda: 1))
|
115 |
+
self.ticks = defaultdict(lambda: defaultdict(lambda: 0))
|
116 |
self.snapshot = defaultdict(dict)
|
117 |
self.last_dot = defaultdict(dict)
|
|
|
|
|
|
|
118 |
|
119 |
+
self.start_frame = 0
|
|
|
120 |
|
121 |
self.select_talents = {}
|
122 |
self.select_equipments = {}
|
|
|
162 |
if player_id in self.school:
|
163 |
self.pets[pet_id] = player_id
|
164 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
165 |
def parse_buff(self, row):
|
166 |
+
|
167 |
detail = row.strip("{}").split(",")
|
168 |
player_id = int(detail[0])
|
169 |
if player_id not in self.school:
|
|
|
171 |
buff_id, buff_stack, buff_level = int(detail[4]), int(detail[5]), int(detail[8])
|
172 |
if buff_id not in self.school[player_id].buffs:
|
173 |
return
|
174 |
+
|
175 |
+
if (buff_id, buff_level) in self.status_buffer[player_id]:
|
176 |
+
buffer_stack, start_frame = self.status_buffer[player_id][(buff_id, buff_level)]
|
177 |
+
if buff_stack == buffer_stack:
|
178 |
+
return
|
179 |
+
for frame in range(start_frame + BUFFER_DELAY, self.current_frame + BUFFER_DELAY):
|
180 |
+
self.status[frame][player_id].append((buff_id, buff_level, buffer_stack))
|
181 |
+
self.status_buffer[player_id].pop((buff_id, buff_level))
|
182 |
+
|
183 |
+
# self.status_buffer[player_id][(buff_id, buff_level, buffer_stack)] = self.status_buffer[player_id].get(
|
184 |
+
# (buff_id, buff_level, buffer_stack), []) + [(start_frame, self.current_frame)]
|
185 |
+
if buff_stack:
|
186 |
+
self.status_buffer[player_id][(buff_id, buff_level)] = (buff_stack, self.current_frame)
|
187 |
+
|
188 |
+
def clear_status_buffer(self):
|
189 |
+
self.end_frame = self.current_frame
|
190 |
+
for player_id, status in self.status_buffer.items():
|
191 |
+
for (buff_id, buff_level), (buff_stack, start_frame) in status.items():
|
192 |
+
for frame in range(start_frame + BUFFER_DELAY, self.end_frame + BUFFER_DELAY):
|
193 |
+
self.status[frame][player_id].append((buff_id, buff_level, buff_stack))
|
194 |
|
195 |
def parse_skill(self, row):
|
196 |
detail = row.strip("{}").split(",")
|
|
|
206 |
if react or skill_id not in self.school[player_id].skills:
|
207 |
return
|
208 |
skill_stack = self.stacks[player_id][skill_id]
|
209 |
+
|
210 |
+
self.skills[self.current_frame][player_id].append((skill_id, skill_level, skill_stack, critical))
|
211 |
+
|
212 |
+
if not self.start_frame:
|
213 |
+
self.start_frame = self.current_frame
|
214 |
+
|
215 |
+
def available_status(self, skill_id):
|
216 |
+
current_status = []
|
217 |
+
for buff_id, buff_level, buff_stack in self.current_status:
|
218 |
+
buff = self.school[self.current_player].buffs[buff_id]
|
219 |
+
if buff.gain_attributes:
|
220 |
+
current_status.append((buff_id, buff_level, buff_stack))
|
221 |
+
elif buff.gain_skills and skill_id in buff.gain_skills:
|
222 |
+
current_status.append((buff_id, buff_level, buff_stack))
|
223 |
+
|
224 |
+
snapshot_status = []
|
225 |
+
for buff_id, buff_level, buff_stack in self.current_snapshot.get(skill_id, []):
|
226 |
+
buff = self.school[self.current_player].buffs[buff_id]
|
227 |
+
if buff.gain_attributes:
|
228 |
+
snapshot_status.append((buff_id, buff_level, buff_stack))
|
229 |
+
elif buff.gain_skills and skill_id in buff.gain_skills:
|
230 |
+
snapshot_status.append((buff_id, buff_level, buff_stack))
|
231 |
+
|
232 |
+
return tuple(current_status), tuple(snapshot_status)
|
233 |
+
|
234 |
+
def record(self, skill_id, skill_level, skill_stack, critical):
|
235 |
+
skill = self.school[self.current_player].skills[skill_id]
|
236 |
+
skill.record(skill_level, skill_stack, critical, self)
|
237 |
|
238 |
def __call__(self, file_name):
|
239 |
self.reset()
|
|
|
243 |
if row[4] == "4":
|
244 |
self.parse_info(row[-1])
|
245 |
|
|
|
|
|
|
|
|
|
246 |
for line in lines:
|
247 |
row = line.split("\t")
|
248 |
+
self.current_frame = int(row[1])
|
|
|
|
|
249 |
|
250 |
match row[4]:
|
|
|
|
|
251 |
case "8":
|
252 |
self.parse_pet(row[-1])
|
253 |
case "13":
|
|
|
255 |
case "21":
|
256 |
self.parse_skill(row[-1])
|
257 |
|
258 |
+
self.clear_status_buffer()
|
259 |
+
|
260 |
+
for player_id, school in self.school.items():
|
261 |
+
for talent_id in self.select_talents[player_id]:
|
262 |
+
school.talent_gains[talent_id].add_skills(school.skills)
|
263 |
+
|
264 |
+
for frame, players in self.skills.items():
|
265 |
+
self.current_frame = frame
|
266 |
+
for player_id, skills in players.items():
|
267 |
+
self.current_player = player_id
|
268 |
+
for skill_id, skill_level, skill_stack, critical in skills:
|
269 |
+
skill = self.school[player_id].skills[skill_id]
|
270 |
+
skill.record(skill_level, skill_stack, critical, self)
|
271 |
+
|
272 |
for player_id, school in self.school.items():
|
273 |
for talent_id in self.select_talents[player_id]:
|
274 |
school.talent_gains[talent_id].sub_skills(school.skills)
|
275 |
|
276 |
+
|
277 |
+
if __name__ == '__main__':
|
278 |
+
parser = Parser()
|
279 |
+
parser("../new.jcl")
|
280 |
+
print(1)
|
|
|
|