ango commited on
Commit
581e199
·
1 Parent(s): a05a2de

5.13 commit

Browse files
base/buff.py CHANGED
@@ -7,33 +7,16 @@ from base.skill import Skill
7
  ATTR_DICT = Dict[str, Union[List[int], int]]
8
 
9
 
10
- @dataclass
11
- class Buff:
12
- buff_id: int
 
13
  _buff_name: Union[List[str], str] = ""
14
  buff_level: int = 0
15
  buff_stack: int = 1
16
 
17
- frame_shift: int = 0
18
- second_shift: int = 0
19
- activate: bool = True
20
-
21
  max_stack: int = 1
22
-
23
- gain_skills: Dict[int, ATTR_DICT] = None
24
- gain_attributes: ATTR_DICT = None
25
-
26
- SNAPSHOT_ATTRS = ["attack_power", "critical_strike", "critical_power", "strain", "damage_addition", "pve_addition"]
27
-
28
- def __post_init__(self):
29
- if self.gain_skills is None:
30
- self.gain_skills = {}
31
- if self.gain_attributes is None:
32
- self.gain_attributes = {}
33
-
34
- @property
35
- def shifted(self):
36
- return self.second_shift or self.frame_shift
37
 
38
  @property
39
  def buff_name(self):
@@ -52,6 +35,28 @@ class Buff:
52
  else:
53
  self._buff_name = [buff_name]
54
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  @property
56
  def display_name(self):
57
  return f"{self.buff_name}#{self.buff_id}-{self.buff_level}-{self.buff_stack}"
 
7
  ATTR_DICT = Dict[str, Union[List[int], int]]
8
 
9
 
10
+ class BaseBuff:
11
+ SNAPSHOT_ATTRS = ["attack_power", "critical_strike", "critical_power", "strain", "damage_addition", "pve_addition"]
12
+ PET_ATTRS = ["attack_power", "critical_power", "overcome", "strain", "damage_addition", "pve_addition"]
13
+
14
  _buff_name: Union[List[str], str] = ""
15
  buff_level: int = 0
16
  buff_stack: int = 1
17
 
 
 
 
 
18
  max_stack: int = 1
19
+ interval: int = 0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
  @property
22
  def buff_name(self):
 
35
  else:
36
  self._buff_name = [buff_name]
37
 
38
+
39
+ @dataclass
40
+ class Buff(BaseBuff):
41
+ buff_id: int
42
+
43
+ frame_shift: int = 0
44
+ second_shift: int = 0
45
+ activate: bool = True
46
+
47
+ gain_skills: Dict[int, ATTR_DICT] = None
48
+ gain_attributes: ATTR_DICT = None
49
+
50
+ def __post_init__(self):
51
+ if self.gain_skills is None:
52
+ self.gain_skills = {}
53
+ if self.gain_attributes is None:
54
+ self.gain_attributes = {}
55
+
56
+ @property
57
+ def shifted(self):
58
+ return self.second_shift or self.frame_shift
59
+
60
  @property
61
  def display_name(self):
62
  return f"{self.buff_name}#{self.buff_id}-{self.buff_level}-{self.buff_stack}"
base/skill.py CHANGED
@@ -6,23 +6,11 @@ from typing import List, Union
6
  from dataclasses import dataclass
7
 
8
 
9
- @dataclass
10
- class Skill:
11
- skill_id: int
12
  _skill_name: Union[List[str], str] = ""
13
  skill_level: int = 0
14
  skill_stack: int = 1
15
 
16
- activate: bool = True
17
-
18
- bind_skill: int = None
19
- bind_buff: int = None
20
- max_stack: int = 1
21
- tick: int = 1
22
- interval: int = 0
23
- duration: int = 0
24
- last_dot: bool = True
25
-
26
  _damage_base: Union[List[int], int] = 0
27
  _damage_rand: Union[List[int], int] = 0
28
 
@@ -44,10 +32,6 @@ class Skill:
44
  skill_critical_strike: int = 0
45
  skill_critical_power: int = 0
46
 
47
- @property
48
- def display_name(self):
49
- return f"{self.skill_name}#{self.skill_id}-{self.skill_level}-{self.skill_stack}"
50
-
51
  @property
52
  def skill_name(self):
53
  if not isinstance(self._skill_name, list):
@@ -154,7 +138,7 @@ class Skill:
154
  self._weapon_damage_cof = weapon_damage_cof
155
  else:
156
  self._weapon_damage_cof = [weapon_damage_cof]
157
-
158
  @property
159
  def skill_shield_gain(self):
160
  if not isinstance(self._skill_shield_gain, list):
@@ -188,41 +172,93 @@ class Skill:
188
  self._skill_pve_addition = skill_pve_addition
189
  else:
190
  self._skill_pve_addition = [skill_pve_addition]
191
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
192
  def record(self, critical, parser):
193
  pass
194
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
195
  def __call__(self, attribute: Attribute):
196
  return 0
197
 
198
 
199
- class HiddenBuffSkill(Skill):
200
- def record(self, critical, parser):
201
- super().record(critical, parser)
202
- if self.bind_buff not in parser.current_school.buffs:
203
- return
204
- max_stack = parser.current_school.buffs[self.bind_buff].max_stack
205
- buff = (self.bind_buff, 1)
206
- parser.current_hidden_buffs.pop(buff, None)
207
- parser.current_target_buffs[buff] = min(parser.current_target_buffs.get(buff, 0) + 1, max_stack)
208
- end_frame = parser.current_frame + self.duration
209
- parser.current_hidden_buffs[buff] = end_frame
210
-
211
-
212
  class DotSkill(Skill):
213
  def record(self, critical, parser):
214
  super().record(critical, parser)
215
  bind_skill = parser.current_school.skills[self.bind_skill]
216
- if not parser.current_ticks[self.bind_skill]:
217
- parser.current_stacks[self.bind_skill] = 0
218
- parser.current_ticks[self.bind_skill] = bind_skill.tick
219
- parser.current_stacks[self.bind_skill] = min(parser.current_stacks[self.bind_skill] + 1, bind_skill.max_stack)
220
- parser.current_dot_snapshot[self.bind_skill] = parser.current_player_buffs.copy()
 
 
221
 
222
 
223
  class DotConsumeSkill(Skill):
224
  def consume_next(self, parser):
225
- tick = min(parser.current_ticks[self.bind_skill], self.tick)
226
  parser.current_next_dot[self.bind_skill] = tick
227
 
228
  def consume_last(self, parser):
@@ -230,12 +266,12 @@ class DotConsumeSkill(Skill):
230
  return
231
  skill_tuple, status_tuple = last_dot
232
  skill_id, skill_level, skill_stack = skill_tuple
233
- parser.current_ticks[skill_id] += 1
234
- tick = min(parser.current_ticks[skill_id], self.tick)
235
  parser.current_records[(skill_id, skill_level, skill_stack * tick)][status_tuple].append(
236
  parser.current_records[skill_tuple][status_tuple].pop()
237
  )
238
- parser.current_ticks[skill_id] -= tick
239
 
240
  def record(self, critical, parser):
241
  super().record(critical, parser)
@@ -248,7 +284,7 @@ class DotConsumeSkill(Skill):
248
  class Damage(Skill):
249
  def record(self, critical, parser):
250
  super().record(critical, parser)
251
- skill_stack = parser.current_stacks[self.skill_id]
252
  skill_tuple = (self.skill_id, self.skill_level, skill_stack)
253
  status_tuple = parser.status(self.skill_id)
254
  parser.current_records[skill_tuple][status_tuple].append(
@@ -257,6 +293,21 @@ class Damage(Skill):
257
  return skill_tuple, status_tuple
258
 
259
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
260
  class NpcDamage(Damage):
261
  pass
262
 
@@ -296,21 +347,6 @@ class PetDamage(Damage):
296
  return damage, critical_damage, expected_damage, critical_strike
297
 
298
 
299
- class DotDamage(Damage):
300
- def record(self, critical, parser):
301
- skill_tuple, status_tuple = super().record(critical, parser)
302
-
303
- if tick := parser.current_next_dot.pop(self.skill_id, None):
304
- _, _, skill_stack = skill_tuple
305
- parser.current_records[(self.skill_id, self.skill_level, skill_stack * tick)][status_tuple].append(
306
- parser.current_records[skill_tuple][status_tuple].pop()
307
- )
308
- parser.current_ticks[self.skill_id] -= tick
309
- else:
310
- parser.current_last_dot[self.skill_id] = (skill_tuple, status_tuple)
311
- parser.current_ticks[self.skill_id] -= 1
312
-
313
-
314
  class PureSkill(Skill):
315
  def __call__(self, attribute: Attribute):
316
  damage = init_result(self.damage_base, self.damage_rand, 0, 0, 0, 0, 0, 0)
 
6
  from dataclasses import dataclass
7
 
8
 
9
+ class BaseSkill:
 
 
10
  _skill_name: Union[List[str], str] = ""
11
  skill_level: int = 0
12
  skill_stack: int = 1
13
 
 
 
 
 
 
 
 
 
 
 
14
  _damage_base: Union[List[int], int] = 0
15
  _damage_rand: Union[List[int], int] = 0
16
 
 
32
  skill_critical_strike: int = 0
33
  skill_critical_power: int = 0
34
 
 
 
 
 
35
  @property
36
  def skill_name(self):
37
  if not isinstance(self._skill_name, list):
 
138
  self._weapon_damage_cof = weapon_damage_cof
139
  else:
140
  self._weapon_damage_cof = [weapon_damage_cof]
141
+
142
  @property
143
  def skill_shield_gain(self):
144
  if not isinstance(self._skill_shield_gain, list):
 
172
  self._skill_pve_addition = skill_pve_addition
173
  else:
174
  self._skill_pve_addition = [skill_pve_addition]
175
+
176
+
177
+ @dataclass
178
+ class Skill(BaseSkill):
179
+ skill_id: int
180
+
181
+ activate: bool = True
182
+
183
+ interval: int = 0
184
+ bind_skill: int = 0
185
+ tick: int = 1
186
+ max_stack: int = 1
187
+ last_dot: bool = True
188
+
189
+ pre_effects: list = None
190
+ pre_buffs: dict = None
191
+ pre_target_buffs: dict = None
192
+ post_effects: list = None
193
+ post_buffs: dict = None
194
+ post_target_buffs: dict = None
195
+
196
+ def __post_init__(self):
197
+ if not self.pre_effects:
198
+ self.pre_effects = []
199
+ if not self.pre_buffs:
200
+ self.pre_buffs = {}
201
+ if not self.pre_target_buffs:
202
+ self.pre_target_buffs = {}
203
+ if not self.post_effects:
204
+ self.post_effects = []
205
+ if not self.post_buffs:
206
+ self.post_buffs = {}
207
+ if not self.post_target_buffs:
208
+ self.post_target_buffs = {}
209
+
210
+ @property
211
+ def display_name(self):
212
+ return f"{self.skill_name}#{self.skill_id}-{self.skill_level}-{self.skill_stack}"
213
+
214
+ def pre_record(self, parser):
215
+ for (buff_id, buff_level), buff_stack in self.pre_buffs.items():
216
+ buff_level = buff_level if buff_level else self.skill_level
217
+ parser.refresh_buff(buff_id, buff_level, buff_stack)
218
+ for (buff_id, buff_level), buff_stack in self.pre_target_buffs.items():
219
+ buff_level = buff_level if buff_level else self.skill_level
220
+ parser.refresh_target_buff(buff_id, buff_level, buff_stack)
221
+ for effect in self.pre_effects:
222
+ effect(parser)
223
+
224
  def record(self, critical, parser):
225
  pass
226
 
227
+ def post_record(self, parser):
228
+ for (buff_id, buff_level), buff_stack in self.post_buffs.items():
229
+ buff_level = buff_level if buff_level else self.skill_level
230
+ parser.refresh_buff(buff_id, buff_level, buff_stack)
231
+ for (buff_id, buff_level), buff_stack in self.post_target_buffs.items():
232
+ buff_level = buff_level if buff_level else self.skill_level
233
+ parser.refresh_target_buff(buff_id, buff_level, buff_stack)
234
+ for effect in self.post_effects:
235
+ effect(parser)
236
+
237
+ def parse(self, critical, parser):
238
+ self.pre_record(parser)
239
+ self.record(critical, parser)
240
+ self.post_record(parser)
241
+
242
  def __call__(self, attribute: Attribute):
243
  return 0
244
 
245
 
 
 
 
 
 
 
 
 
 
 
 
 
 
246
  class DotSkill(Skill):
247
  def record(self, critical, parser):
248
  super().record(critical, parser)
249
  bind_skill = parser.current_school.skills[self.bind_skill]
250
+ if not parser.current_dot_ticks[self.bind_skill]:
251
+ parser.current_dot_stacks[self.bind_skill] = 0
252
+ parser.current_dot_ticks[self.bind_skill] = bind_skill.tick
253
+ parser.current_dot_stacks[self.bind_skill] = min(
254
+ parser.current_dot_stacks.get(self.bind_skill, 0) + 1, bind_skill.max_stack
255
+ )
256
+ parser.current_dot_snapshot[self.bind_skill] = parser.current_buff_stacks.copy()
257
 
258
 
259
  class DotConsumeSkill(Skill):
260
  def consume_next(self, parser):
261
+ tick = min(parser.current_dot_ticks[self.bind_skill], self.tick)
262
  parser.current_next_dot[self.bind_skill] = tick
263
 
264
  def consume_last(self, parser):
 
266
  return
267
  skill_tuple, status_tuple = last_dot
268
  skill_id, skill_level, skill_stack = skill_tuple
269
+ parser.current_dot_ticks[skill_id] += 1
270
+ tick = min(parser.current_dot_ticks[skill_id], self.tick)
271
  parser.current_records[(skill_id, skill_level, skill_stack * tick)][status_tuple].append(
272
  parser.current_records[skill_tuple][status_tuple].pop()
273
  )
274
+ parser.current_dot_ticks[skill_id] -= tick
275
 
276
  def record(self, critical, parser):
277
  super().record(critical, parser)
 
284
  class Damage(Skill):
285
  def record(self, critical, parser):
286
  super().record(critical, parser)
287
+ skill_stack = parser.current_dot_stacks[self.skill_id]
288
  skill_tuple = (self.skill_id, self.skill_level, skill_stack)
289
  status_tuple = parser.status(self.skill_id)
290
  parser.current_records[skill_tuple][status_tuple].append(
 
293
  return skill_tuple, status_tuple
294
 
295
 
296
+ class DotDamage(Damage):
297
+ def record(self, critical, parser):
298
+ skill_tuple, status_tuple = super().record(critical, parser)
299
+
300
+ if tick := parser.current_next_dot.pop(self.skill_id, None):
301
+ _, _, skill_stack = skill_tuple
302
+ parser.current_records[(self.skill_id, self.skill_level, skill_stack * tick)][status_tuple].append(
303
+ parser.current_records[skill_tuple][status_tuple].pop()
304
+ )
305
+ parser.current_dot_ticks[self.skill_id] -= tick
306
+ else:
307
+ parser.current_last_dot[self.skill_id] = (skill_tuple, status_tuple)
308
+ parser.current_dot_ticks[self.skill_id] -= 1
309
+
310
+
311
  class NpcDamage(Damage):
312
  pass
313
 
 
347
  return damage, critical_damage, expected_damage, critical_strike
348
 
349
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
350
  class PureSkill(Skill):
351
  def __call__(self, attribute: Attribute):
352
  damage = init_result(self.damage_base, self.damage_rand, 0, 0, 0, 0, 0, 0)
general/buffs/team.py CHANGED
@@ -22,6 +22,7 @@ GENERAL_BUFFS = {
22
  },
23
  3465: {
24
  "buff_name": "破甲",
 
25
  "gain_attributes": {
26
  "physical_shield_gain": -102
27
  }
 
22
  },
23
  3465: {
24
  "buff_name": "破甲",
25
+ "interval": 128,
26
  "gain_attributes": {
27
  "physical_shield_gain": -102
28
  }
general/skills.py CHANGED
@@ -3,13 +3,15 @@ from typing import Dict
3
  from base.skill import PhysicalDamage, MagicalDamage, Skill, PureDamage
4
 
5
  GENERAL_SKILLS: Dict[int, Skill | dict] = {
6
- 29535: {
7
- "skill_class": MagicalDamage,
8
- "skill_name": "逐云寒蕊",
9
- "damage_base": 40,
10
- "damage_rand": 17,
11
- "attack_power_cof": [90, 200 * 1.2],
12
- "skill_shield_gain": -1024
 
 
13
  },
14
  29536: {
15
  "skill_class": PhysicalDamage,
 
3
  from base.skill import PhysicalDamage, MagicalDamage, Skill, PureDamage
4
 
5
  GENERAL_SKILLS: Dict[int, Skill | dict] = {
6
+ **{
7
+ skill_id: {
8
+ "skill_class": MagicalDamage,
9
+ "skill_name": "逐云寒蕊",
10
+ "damage_base": 40,
11
+ "damage_rand": 17,
12
+ "attack_power_cof": [90, 200 * 1.2],
13
+ "skill_shield_gain": -1024
14
+ } for skill_id in (29532, 29533, 29534, 29535)
15
  },
16
  29536: {
17
  "skill_class": PhysicalDamage,
general/skills/team.py CHANGED
@@ -1,13 +1,12 @@
1
  from typing import Dict
2
 
3
- from base.skill import PhysicalDamage, MagicalDamage, Skill, PureDamage, HiddenBuffSkill
4
 
5
  GENERAL_SKILLS: Dict[int, Skill | dict] = {
6
  13778: {
7
- "skill_class": HiddenBuffSkill,
8
  "skill_name": "乘龙箭",
9
- "bind_buff": 3465,
10
- "duration": 128
11
  },
12
  29535: {
13
  "skill_class": MagicalDamage,
 
1
  from typing import Dict
2
 
3
+ from base.skill import PhysicalDamage, MagicalDamage, Skill
4
 
5
  GENERAL_SKILLS: Dict[int, Skill | dict] = {
6
  13778: {
7
+ "skill_class": Skill,
8
  "skill_name": "乘龙箭",
9
+ "post_target_buffs": {(3465, 1): 1},
 
10
  },
11
  29535: {
12
  "skill_class": MagicalDamage,
schools/ao_xue_zhan_yi/__init__.py CHANGED
@@ -7,4 +7,4 @@ from schools.ao_xue_zhan_yi.attribute import AoXueZhanYi
7
 
8
 
9
  def prepare(self, player_id):
10
- pass
 
7
 
8
 
9
  def prepare(self, player_id):
10
+ self.buff_stacks[player_id][(-1, 1)] = 5
schools/ao_xue_zhan_yi/buffs.py CHANGED
@@ -10,6 +10,10 @@ BUFFS = {
10
  "physical_critical_power_gain": 41
11
  }
12
  },
 
 
 
 
13
  6121: {
14
  "buff_name": "驰骋",
15
  "gain_attributes": {
@@ -19,10 +23,10 @@ BUFFS = {
19
  6363: {
20
  "buff_name": "激雷",
21
  "gain_attributes": {
22
- "physical_attack_power_gain": [205, 0] * 2,
23
- "physical_overcome_gain": [205, 0] * 2,
24
- "physical_critical_strike_gain": [0] + [3000] * 3,
25
- "all_shield_ignore": [0] * 3 + [717]
26
  }
27
  },
28
  14981: {
@@ -44,24 +48,30 @@ BUFFS = {
44
  "all_damage_addition": 72
45
  }
46
  },
47
- # 12608: {
48
- # "buff_name": "风虎",
49
- # "frame_shift": 1,
50
- # "gain_skills": {
51
- # skill_id: {
52
- # "skill_damage_addition": [51, 102, 154, 205, 256]
53
- # } for skill_id in [18207] + [18603] + [18773, 15002] + [702, 24898, 6526]
54
- # }
55
- # },
56
- 26008: {
 
57
  "buff_name": "战心",
58
- "frame_shift": -2,
59
  "gain_skills": {
60
  3442: {
61
  "attack_power_cof_gain": 1.2
62
  }
63
  }
64
- }
 
 
 
 
 
65
  }
66
 
67
  for buff_id, detail in BUFFS.items():
 
10
  "physical_critical_power_gain": 41
11
  }
12
  },
13
+ -1: {
14
+ "buff_name": "战意",
15
+ "max_stack": 5
16
+ },
17
  6121: {
18
  "buff_name": "驰骋",
19
  "gain_attributes": {
 
23
  6363: {
24
  "buff_name": "激雷",
25
  "gain_attributes": {
26
+ "physical_attack_power_gain": [205, 0, 205, 0],
27
+ "physical_overcome_gain": [205, 0, 205, 0],
28
+ "physical_critical_strike_gain": [0, 3000, 3000, 3000],
29
+ "all_shield_ignore": [0, 0, 0, 717]
30
  }
31
  },
32
  14981: {
 
48
  "all_damage_addition": 72
49
  }
50
  },
51
+ -12608: {
52
+ "buff_name": "风虎",
53
+ "activate": False,
54
+ "interval": 16,
55
+ "gain_skills": {
56
+ skill_id: {
57
+ "skill_damage_addition": [51, 102, 154, 205, 256]
58
+ } for skill_id in [18207] + [18603] + [18773, 15002] + [702, 24898, 6526]
59
+ }
60
+ },
61
+ -26008: {
62
  "buff_name": "战心",
63
+ "interval": 4,
64
  "gain_skills": {
65
  3442: {
66
  "attack_power_cof_gain": 1.2
67
  }
68
  }
69
+ },
70
+ -28169: {
71
+ "buff_name": "龙印",
72
+ "interval": 480,
73
+ "max_stack": 3
74
+ },
75
  }
76
 
77
  for buff_id, detail in BUFFS.items():
schools/ao_xue_zhan_yi/skills.py CHANGED
@@ -3,6 +3,16 @@ from typing import Dict
3
  from base.skill import Skill, DotSkill, PhysicalDamage, PhysicalDotDamage
4
  from general.skills import GENERAL_SKILLS
5
 
 
 
 
 
 
 
 
 
 
 
6
  SKILLS: Dict[int, Skill | dict] = {
7
  32820: {
8
  "skill_class": PhysicalDamage,
@@ -37,7 +47,8 @@ SKILLS: Dict[int, Skill | dict] = {
37
  "damage_base": 39,
38
  "damage_rand": 3,
39
  "attack_power_cof": 16,
40
- "weapon_damage_cof": 1024
 
41
  },
42
  431: {
43
  "skill_class": PhysicalDamage,
@@ -55,13 +66,14 @@ SKILLS: Dict[int, Skill | dict] = {
55
  "weapon_damage_cof": 1024
56
  },
57
  702: {
58
- "skill_class": PhysicalDamage,
59
  "skill_name": "灭",
60
  "skill_level": 19,
61
  "damage_base": 160 * 1.3,
62
  "damage_rand": 10,
63
  "attack_power_cof": 170 * 1.12,
64
- "weapon_damage_cof": 1024
 
65
  },
66
  18207: {
67
  "skill_class": PhysicalDamage,
@@ -69,8 +81,9 @@ SKILLS: Dict[int, Skill | dict] = {
69
  "skill_level": 28,
70
  "damage_base": 160,
71
  "damage_rand": 15,
72
- "attack_power_cof": 158 * 1.05*1.1*1.1*1.12*1.1,
73
- "weapon_damage_cof": 1024
 
74
  },
75
  18603: {
76
  "skill_class": PhysicalDamage,
@@ -79,7 +92,8 @@ SKILLS: Dict[int, Skill | dict] = {
79
  "damage_base": 289 * 0.65,
80
  "damage_rand": 15,
81
  "attack_power_cof": 184 * 1.1 * 1.1 * 1.12 * 1.1 * 1.1 * 1.2,
82
- "weapon_damage_cof": 1024
 
83
  },
84
  18773: {
85
  "skill_class": PhysicalDamage,
@@ -88,7 +102,8 @@ SKILLS: Dict[int, Skill | dict] = {
88
  "damage_base": 445 * 0.5,
89
  "damage_rand": 15,
90
  "attack_power_cof": 197 * 1.1 * 1.1 * 1.15 * 1.1 * 1.1 * 1.12 * 0.9 * 1.1 * 1.05,
91
- "weapon_damage_cof": 1024
 
92
  },
93
  37618: {
94
  "skill_class": PhysicalDamage,
@@ -114,6 +129,10 @@ SKILLS: Dict[int, Skill | dict] = {
114
  "attack_power_cof": 170 * 1.12 * 1,
115
  "weapon_damage_cof": 1024 / 4
116
  },
 
 
 
 
117
  15002: {
118
  "skill_class": PhysicalDamage,
119
  "skill_name": "龙牙",
@@ -124,6 +143,11 @@ SKILLS: Dict[int, Skill | dict] = {
124
  "attack_power_cof": 197 * 1.1 * 1.1 * 1.15 * 0.4 * 1.1 * 1.1 * 1.12 * 0.9 * 1.1 * 1.05,
125
  "weapon_damage_cof": 1024 * 0.1
126
  },
 
 
 
 
 
127
  25772: {
128
  "skill_class": PhysicalDamage,
129
  "skill_name": "龙牙·神兵",
@@ -136,7 +160,7 @@ SKILLS: Dict[int, Skill | dict] = {
136
  "skill_name": "画角闻龙",
137
  "damage_base": 523 * 0.95,
138
  "damage_rand": 523 * 0.1,
139
- "attack_power_cof": 205
140
  },
141
  }
142
 
 
3
  from base.skill import Skill, DotSkill, PhysicalDamage, PhysicalDotDamage
4
  from general.skills import GENERAL_SKILLS
5
 
6
+
7
+ class 战意判定(Skill):
8
+ final_buff = -12608
9
+ bind_buff = -1
10
+
11
+ def record(self, critical, parser):
12
+ if buff_level := parser.current_buff_stacks.get((self.bind_buff, 1)):
13
+ parser.refresh_buff(self.final_buff, buff_level)
14
+
15
+
16
  SKILLS: Dict[int, Skill | dict] = {
17
  32820: {
18
  "skill_class": PhysicalDamage,
 
47
  "damage_base": 39,
48
  "damage_rand": 3,
49
  "attack_power_cof": 16,
50
+ "weapon_damage_cof": 1024,
51
+ "post_buffs": {(-1, 1): 3}
52
  },
53
  431: {
54
  "skill_class": PhysicalDamage,
 
66
  "weapon_damage_cof": 1024
67
  },
68
  702: {
69
+ "skill_class": type("Mixing", (PhysicalDamage, DotSkill), {}),
70
  "skill_name": "灭",
71
  "skill_level": 19,
72
  "damage_base": 160 * 1.3,
73
  "damage_rand": 10,
74
  "attack_power_cof": 170 * 1.12,
75
+ "weapon_damage_cof": 1024,
76
+ "bind_skill": 3442,
77
  },
78
  18207: {
79
  "skill_class": PhysicalDamage,
 
81
  "skill_level": 28,
82
  "damage_base": 160,
83
  "damage_rand": 15,
84
+ "attack_power_cof": 158 * 1.05 * 1.1 * 1.1 * 1.12 * 1.1,
85
+ "weapon_damage_cof": 1024,
86
+ "post_buffs": {(-1, 1): 1}
87
  },
88
  18603: {
89
  "skill_class": PhysicalDamage,
 
92
  "damage_base": 289 * 0.65,
93
  "damage_rand": 15,
94
  "attack_power_cof": 184 * 1.1 * 1.1 * 1.12 * 1.1 * 1.1 * 1.2,
95
+ "weapon_damage_cof": 1024,
96
+ "post_buffs": {(-1, 1): 2}
97
  },
98
  18773: {
99
  "skill_class": PhysicalDamage,
 
102
  "damage_base": 445 * 0.5,
103
  "damage_rand": 15,
104
  "attack_power_cof": 197 * 1.1 * 1.1 * 1.15 * 1.1 * 1.1 * 1.12 * 0.9 * 1.1 * 1.05,
105
+ "weapon_damage_cof": 1024,
106
+ "post_buffs": {(-1, 1): -3}
107
  },
108
  37618: {
109
  "skill_class": PhysicalDamage,
 
129
  "attack_power_cof": 170 * 1.12 * 1,
130
  "weapon_damage_cof": 1024 / 4
131
  },
132
+ 18740: {
133
+ "skill_class": 战意判定,
134
+ "skill_name": "战意判定"
135
+ },
136
  15002: {
137
  "skill_class": PhysicalDamage,
138
  "skill_name": "龙牙",
 
143
  "attack_power_cof": 197 * 1.1 * 1.1 * 1.15 * 0.4 * 1.1 * 1.1 * 1.12 * 0.9 * 1.1 * 1.05,
144
  "weapon_damage_cof": 1024 * 0.1
145
  },
146
+ 1850: {
147
+ "skill_class": Skill,
148
+ "skill_name": "特效触发",
149
+ "post_buffs": {(-1, 1): 5}
150
+ },
151
  25772: {
152
  "skill_class": PhysicalDamage,
153
  "skill_name": "龙牙·神兵",
 
160
  "skill_name": "画角闻龙",
161
  "damage_base": 523 * 0.95,
162
  "damage_rand": 523 * 0.1,
163
+ "attack_power_cof": 205,
164
  },
165
  }
166
 
schools/ao_xue_zhan_yi/talents.py CHANGED
@@ -1,6 +1,6 @@
1
  from typing import Dict
2
 
3
- from base.attribute import Attribute
4
  from base.gain import Gain
5
  from base.skill import Skill
6
 
@@ -36,21 +36,21 @@ class 神勇(Gain):
36
 
37
 
38
  class 风虎(Gain):
 
 
 
 
 
 
 
 
39
  def add_skills(self, skills: Dict[int, Skill]):
40
- skills[18207].skill_damage_addition += 205
41
- skills[18603].skill_damage_addition += 102
42
- for skill_id in [18773, 15002]:
43
- skills[skill_id].skill_damage_addition += 256
44
- for skill_id in [702, 24898, 6526]:
45
- skills[skill_id].skill_damage_addition += 102
46
 
47
  def sub_skills(self, skills: Dict[int, Skill]):
48
- skills[18207].skill_damage_addition -= 205
49
- skills[18603].skill_damage_addition -= 102
50
- for skill_id in [18773, 15002]:
51
- skills[skill_id].skill_damage_addition -= 256
52
- for skill_id in [702, 24898, 6526]:
53
- skills[skill_id].skill_damage_addition -= 102
54
 
55
 
56
  class 骁勇(Gain):
@@ -61,6 +61,20 @@ class 骁勇(Gain):
61
  skills[3442].attack_power_cof_gain /= 1.12
62
 
63
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  TALENT_GAINS: Dict[int, Gain] = {
65
  18487: Gain("百折"),
66
  5656: 封侯("封侯"),
@@ -72,11 +86,11 @@ TALENT_GAINS: Dict[int, Gain] = {
72
  14824: Gain("驰骋"),
73
  6511: Gain("牧云"),
74
  5666: 风虎("风虎"),
75
- 6781: Gain("战心"),
76
  6524: Gain("破楼兰"),
77
  5678: Gain("夜征"),
78
  15001: Gain("龙血"),
79
- 6517: Gain("虎贲")
80
  }
81
 
82
  TALENTS = [
 
1
  from typing import Dict
2
 
3
+ from base.buff import Buff
4
  from base.gain import Gain
5
  from base.skill import Skill
6
 
 
36
 
37
 
38
  class 风虎(Gain):
39
+ def add_buffs(self, buffs: Dict[int, Buff]):
40
+ buffs[-12608].activate = True
41
+
42
+ def sub_buffs(self, buffs: Dict[int, Buff]):
43
+ buffs[-12608].activate = False
44
+
45
+
46
+ class 战心(Gain):
47
  def add_skills(self, skills: Dict[int, Skill]):
48
+ skills[702].pre_buffs[(-26008, 1)] = 1
49
+ skills[702].post_buffs[(-1, 1)] = 3
 
 
 
 
50
 
51
  def sub_skills(self, skills: Dict[int, Skill]):
52
+ skills[702].pre_buffs.pop((-26008, 1))
53
+ skills[702].post_buffs.pop((-1, 1))
 
 
 
 
54
 
55
 
56
  class 骁勇(Gain):
 
61
  skills[3442].attack_power_cof_gain /= 1.12
62
 
63
 
64
+ class 虎贲(Gain):
65
+ @staticmethod
66
+ def effect(parser):
67
+ if parser.current_buff_stacks.get((-28169, 1)) == 3:
68
+ parser.refresh_buff(-1, 1, 3)
69
+ parser.refresh_buff(-28169, 1)
70
+
71
+ def add_skills(self, skills: Dict[int, Skill]):
72
+ skills[18773].post_effects.append(self.effect)
73
+
74
+ def sub_skills(self, skills: Dict[int, Skill]):
75
+ skills[18773].post_effects.remove(self.effect)
76
+
77
+
78
  TALENT_GAINS: Dict[int, Gain] = {
79
  18487: Gain("百折"),
80
  5656: 封侯("封侯"),
 
86
  14824: Gain("驰骋"),
87
  6511: Gain("牧云"),
88
  5666: 风虎("风虎"),
89
+ 6781: 战心("战心"),
90
  6524: Gain("破楼兰"),
91
  5678: Gain("夜征"),
92
  15001: Gain("龙血"),
93
+ 6517: 虎贲("虎贲")
94
  }
95
 
96
  TALENTS = [
schools/bei_ao_jue/buffs.py CHANGED
@@ -1,7 +1,6 @@
1
  from base.buff import Buff
2
  from general.buffs import GENERAL_BUFFS
3
 
4
-
5
  BUFFS = {
6
  11378: {
7
  "buff_name": "朔气",
@@ -11,15 +10,17 @@ BUFFS = {
11
  "physical_critical_power_gain": 41
12
  }
13
  },
14
- 18384: {
15
  "buff_name": "含风",
 
16
  "gain_attributes": {
17
  "physical_critical_strike_gain": 1000,
18
  "physical_critical_power_gain": 102
19
  }
20
  },
21
- 23066: {
22
  "buff_name": "含风",
 
23
  "gain_skills": {
24
  skill_id: {
25
  "skill_damage_addition": 102,
@@ -42,4 +43,4 @@ for buff_id, detail in BUFFS.items():
42
 
43
  for buff_id, buff in GENERAL_BUFFS.items():
44
  if buff_id not in BUFFS:
45
- BUFFS[buff_id] = buff
 
1
  from base.buff import Buff
2
  from general.buffs import GENERAL_BUFFS
3
 
 
4
  BUFFS = {
5
  11378: {
6
  "buff_name": "朔气",
 
10
  "physical_critical_power_gain": 41
11
  }
12
  },
13
+ -18384: {
14
  "buff_name": "含风",
15
+ "interval": 384,
16
  "gain_attributes": {
17
  "physical_critical_strike_gain": 1000,
18
  "physical_critical_power_gain": 102
19
  }
20
  },
21
+ -23066: {
22
  "buff_name": "含风",
23
+ "interval": 384,
24
  "gain_skills": {
25
  skill_id: {
26
  "skill_damage_addition": 102,
 
43
 
44
  for buff_id, buff in GENERAL_BUFFS.items():
45
  if buff_id not in BUFFS:
46
+ BUFFS[buff_id] = buff
schools/bei_ao_jue/talents.py CHANGED
@@ -35,6 +35,16 @@ class 阳关(Gain):
35
  skills[32859].skill_damage_addition -= 154
36
 
37
 
 
 
 
 
 
 
 
 
 
 
38
  class 星火(Gain):
39
  def add_attribute(self, attribute: Attribute):
40
  attribute.strength_gain += 102
@@ -65,7 +75,7 @@ TALENT_GAINS: Dict[int, Gain] = {
65
  26904: 冥鼓("冥鼔"),
66
  17042: 阳关("阳关"),
67
  16799: Gain("霜天"),
68
- 25633: Gain("含风"),
69
  32857: Gain("见尘"),
70
  17047: Gain("分疆"),
71
  25258: Gain("掠关"),
 
35
  skills[32859].skill_damage_addition -= 154
36
 
37
 
38
+ class 含风(Gain):
39
+ def add_skills(self, skills: Dict[int, Skill]):
40
+ skills[16610].pre_buffs[(-18384, 1)] = 1
41
+ skills[16610].pre_buffs[(-23066, 2)] = 1
42
+
43
+ def sub_skills(self, skills: Dict[int, Skill]):
44
+ skills[16610].pre_buffs.pop((-18384, 1))
45
+ skills[16610].pre_buffs.pop((-23066, 2))
46
+
47
+
48
  class 星火(Gain):
49
  def add_attribute(self, attribute: Attribute):
50
  attribute.strength_gain += 102
 
75
  26904: 冥鼓("冥鼔"),
76
  17042: 阳关("阳关"),
77
  16799: Gain("霜天"),
78
+ 25633: 含风("含风"),
79
  32857: Gain("见尘"),
80
  17047: Gain("分疆"),
81
  25258: Gain("掠关"),
schools/bing_xin_jue/__init__.py CHANGED
@@ -7,5 +7,5 @@ from schools.bing_xin_jue.attribute import BingXinJue
7
 
8
 
9
  def prepare(self, player_id):
10
- self.player_buffs[player_id][(409, 21)] = 10
11
- self.player_buffs[player_id][(17969, 1)] = 1
 
7
 
8
 
9
  def prepare(self, player_id):
10
+ self.buff_stacks[player_id][(409, 21)] = 10
11
+ self.buff_stacks[player_id][(17969, 1)] = 1
schools/bing_xin_jue/buffs.py CHANGED
@@ -70,4 +70,4 @@ for buff_id, detail in BUFFS.items():
70
 
71
  for buff_id, buff in GENERAL_BUFFS.items():
72
  if buff_id not in BUFFS:
73
- BUFFS[buff_id] = buff
 
70
 
71
  for buff_id, buff in GENERAL_BUFFS.items():
72
  if buff_id not in BUFFS:
73
+ BUFFS[buff_id] = buff
schools/du_jing/skills.py CHANGED
@@ -11,7 +11,7 @@ class 灵蛇引(Skill):
11
  def record(self, critical, parser):
12
  super().record(critical, parser)
13
  pet_buffs = {(bind_buff, 1): 1 for bind_buff in self.bind_buffs}
14
- parser.current_next_pet_snapshot.append(pet_buffs)
15
 
16
 
17
  SKILLS: Dict[int, Skill | dict] = {
 
11
  def record(self, critical, parser):
12
  super().record(critical, parser)
13
  pet_buffs = {(bind_buff, 1): 1 for bind_buff in self.bind_buffs}
14
+ parser.current_next_pet_buff_stacks.append(pet_buffs)
15
 
16
 
17
  SKILLS: Dict[int, Skill | dict] = {
schools/fen_shan_jing/buffs.py CHANGED
@@ -12,14 +12,14 @@ BUFFS: Dict[int, Buff | dict] = {
12
  "physical_critical_power_gain": 41
13
  }
14
  },
15
- 9052: {
16
- "buff_name": "绝刀",
17
- "gain_skills": {
18
- 13075: {
19
- "skill_damage_addition": [205, 410, 614, 819] * 2
20
- }
21
- }
22
- },
23
  8244: {
24
  "buff_name": "血怒",
25
  "gain_attributes": {
 
12
  "physical_critical_power_gain": 41
13
  }
14
  },
15
+ # 9052: {
16
+ # "buff_name": "绝刀",
17
+ # "gain_skills": {
18
+ # 13075: {
19
+ # "skill_damage_addition": [205, 410, 614, 819] * 2
20
+ # }
21
+ # }
22
+ # },
23
  8244: {
24
  "buff_name": "血怒",
25
  "gain_attributes": {
schools/gu_feng_jue/buffs.py CHANGED
@@ -1,7 +1,6 @@
1
  from base.buff import Buff
2
  from general.buffs import GENERAL_BUFFS
3
 
4
-
5
  BUFFS = {
6
  11378: {
7
  "buff_name": "朔气",
@@ -16,7 +15,7 @@ BUFFS = {
16
  "gain_attributes": {
17
  "physical_overcome_gain": 102,
18
  "physical_critical_strike_gain": 1000,
19
- "physical_critical_power_gain": 102
20
  }
21
  },
22
  24557: {
@@ -25,7 +24,7 @@ BUFFS = {
25
  skill_id: {
26
  "skill_damage_addition": 154,
27
  }
28
- for skill_id in [32602, 32603, 32604, 32234] + [32235, 32236, 32237, 32238, 32239, 32891, 32892]
29
  }
30
  },
31
  24180: {
@@ -42,7 +41,7 @@ BUFFS = {
42
  "all_shield_ignore": 410
43
  }
44
  },
45
- -32513: {
46
  "buff_name": "涤瑕",
47
  "activate": False,
48
  "gain_skills": {
@@ -60,4 +59,4 @@ for buff_id, detail in BUFFS.items():
60
 
61
  for buff_id, buff in GENERAL_BUFFS.items():
62
  if buff_id not in BUFFS:
63
- BUFFS[buff_id] = buff
 
1
  from base.buff import Buff
2
  from general.buffs import GENERAL_BUFFS
3
 
 
4
  BUFFS = {
5
  11378: {
6
  "buff_name": "朔气",
 
15
  "gain_attributes": {
16
  "physical_overcome_gain": 102,
17
  "physical_critical_strike_gain": 1000,
18
+ "physical_critical_power_gain": 100
19
  }
20
  },
21
  24557: {
 
24
  skill_id: {
25
  "skill_damage_addition": 154,
26
  }
27
+ for skill_id in [32602, 32603, 32604] + [32234] + [32235, 32236, 32237, 32238, 32239, 32891, 32892]
28
  }
29
  },
30
  24180: {
 
41
  "all_shield_ignore": 410
42
  }
43
  },
44
+ -24056: {
45
  "buff_name": "涤瑕",
46
  "activate": False,
47
  "gain_skills": {
 
59
 
60
  for buff_id, buff in GENERAL_BUFFS.items():
61
  if buff_id not in BUFFS:
62
+ BUFFS[buff_id] = buff
schools/gu_feng_jue/skills.py CHANGED
@@ -6,15 +6,18 @@ from general.skills import GENERAL_SKILLS
6
 
7
 
8
  class 横刀断浪流血(Skill):
 
 
 
9
  def record(self, critical, parser):
10
- bind_skill = parser.current_school.skills(self.bind_skill)
11
  bind_buff = self.bind_buff
12
- parser.current_ticks[self.bind_skill] = bind_skill.tick
13
- parser.current_stacks[self.bind_skill] = self.max_stack
14
- for level in range(self.max_stack):
15
- parser.current_target_buffs.pop((bind_buff, level + 1), None)
16
- parser.current_target_buffs[(bind_buff, self.max_stack)] = 1
17
- parser.current_dot_snapshot[self.bind_skill] = parser.current_player_buffs.copy()
18
 
19
 
20
  SKILLS: Dict[int, Skill | dict] = {
@@ -182,8 +185,7 @@ SKILLS: Dict[int, Skill | dict] = {
182
  "skill_class": 横刀断浪流血,
183
  "skill_name": "流血",
184
  "bind_skill": 24443,
185
- "bind_buff": -32513,
186
- "max_stack": i + 1,
187
  } for i, skill_id in enumerate([32874, 32873, 32872, 32871, 32870, 32869])
188
  },
189
  32234: {
 
6
 
7
 
8
  class 横刀断浪流血(Skill):
9
+ bind_buff: int = -24222
10
+ stack: int
11
+
12
  def record(self, critical, parser):
13
+ bind_skill = parser.current_school.skills[self.bind_skill]
14
  bind_buff = self.bind_buff
15
+ for level in range(self.stack):
16
+ parser.clear_target_buff(bind_buff, level + 1)
17
+ parser.refresh_target_buff(bind_buff, self.stack, 1)
18
+ parser.current_dot_ticks[self.bind_skill] = bind_skill.tick
19
+ parser.current_dot_stacks[self.bind_skill] = self.stack
20
+ parser.current_dot_snapshot[self.bind_skill] = parser.current_buff_stacks.copy()
21
 
22
 
23
  SKILLS: Dict[int, Skill | dict] = {
 
185
  "skill_class": 横刀断浪流血,
186
  "skill_name": "流血",
187
  "bind_skill": 24443,
188
+ "stack": i + 1,
 
189
  } for i, skill_id in enumerate([32874, 32873, 32872, 32871, 32870, 32869])
190
  },
191
  32234: {
schools/gu_feng_jue/talents.py CHANGED
@@ -50,10 +50,10 @@ class 涣衍(Gain):
50
 
51
  class 涤瑕(Gain):
52
  def add_buffs(self, buffs: Dict[int, Buff]):
53
- buffs[-32513].activate = True
54
 
55
  def sub_buffs(self, buffs: Dict[int, Buff]):
56
- buffs[-32513].activate = False
57
 
58
 
59
  TALENT_GAINS: Dict[int, Gain] = {
 
50
 
51
  class 涤瑕(Gain):
52
  def add_buffs(self, buffs: Dict[int, Buff]):
53
+ buffs[-24222].activate = True
54
 
55
  def sub_buffs(self, buffs: Dict[int, Buff]):
56
+ buffs[-24222].activate = False
57
 
58
 
59
  TALENT_GAINS: Dict[int, Gain] = {
schools/hua_jian_you/skills.py CHANGED
@@ -21,8 +21,8 @@ class DotConsumeSkill(Skill):
21
  else:
22
  new_status_tuple = status_tuple
23
  skill_id, skill_level, skill_stack = skill_tuple
24
- parser.current_ticks[skill_id] += 1
25
- tick = parser.current_ticks.pop(skill_id)
26
  parser.current_records[(skill_id, skill_level, skill_stack * tick)][new_status_tuple].append(
27
  parser.current_records[skill_tuple][status_tuple].pop()
28
  )
 
21
  else:
22
  new_status_tuple = status_tuple
23
  skill_id, skill_level, skill_stack = skill_tuple
24
+ parser.current_dot_ticks[skill_id] += 1
25
+ tick = parser.current_dot_ticks.pop(skill_id)
26
  parser.current_records[(skill_id, skill_level, skill_stack * tick)][new_status_tuple].append(
27
  parser.current_records[skill_tuple][status_tuple].pop()
28
  )
schools/shan_hai_xin_jue/recipes.py CHANGED
@@ -7,9 +7,9 @@ from base.recipe import damage_addition_recipe, critical_strike_recipe, pve_addi
7
  RECIPE_GAINS: Dict[str, Dict[str, Gain]] = {
8
  "劲风簇": {
9
  "4%会心": critical_strike_recipe([35866], 400),
10
- "3%伤害": damage_addition_recipe([35866], 31),
11
  "3%会心": critical_strike_recipe([35866], 300),
12
- "2%伤害": damage_addition_recipe([35866], 21)
13
  },
14
  "饮羽簇": {
15
  "15%伤害": pve_addition_recipe([35987], 154),
 
7
  RECIPE_GAINS: Dict[str, Dict[str, Gain]] = {
8
  "劲风簇": {
9
  "4%会心": critical_strike_recipe([35866], 400),
10
+ "3%伤害": damage_addition_recipe([35866, 36453], 31),
11
  "3%会心": critical_strike_recipe([35866], 300),
12
+ "2%伤害": damage_addition_recipe([35866, 36453], 21)
13
  },
14
  "饮羽簇": {
15
  "15%伤害": pve_addition_recipe([35987], 154),
schools/tai_xu_jian_yi/__init__.py CHANGED
@@ -7,4 +7,4 @@ from schools.tai_xu_jian_yi.attribute import TaiXuJianYi
7
 
8
 
9
  def prepare(self, player_id):
10
- self.player_buffs[player_id][(9949, 1)] = 3
 
7
 
8
 
9
  def prepare(self, player_id):
10
+ self.buff_stacks[player_id][(9949, 1)] = 3
schools/yi_jin_jing/__init__.py CHANGED
@@ -7,4 +7,4 @@ from schools.yi_jin_jing.attribute import YiJinJing
7
 
8
 
9
  def prepare(self, player_id):
10
- self.player_buffs[player_id][(10023, 1)] = 1
 
7
 
8
 
9
  def prepare(self, player_id):
10
+ self.buff_stacks[player_id][(10023, 1)] = 1
schools/yi_jin_jing/buffs.py CHANGED
@@ -12,14 +12,17 @@ BUFFS = {
12
  },
13
  890: {
14
  "buff_name": "普渡",
 
15
  "max_stack": 2
16
  },
17
  12479: {
18
  "buff_name": "普渡",
 
19
  "max_stack": 3
20
  },
21
  19635: {
22
  "buff_name": "普渡",
 
23
  "gain_attributes": {
24
  "magical_vulnerable": [41, 82, 123]
25
  }
@@ -96,4 +99,4 @@ for buff_id, detail in BUFFS.items():
96
 
97
  for buff_id, buff in GENERAL_BUFFS.items():
98
  if buff_id not in BUFFS:
99
- BUFFS[buff_id] = buff
 
12
  },
13
  890: {
14
  "buff_name": "普渡",
15
+ "interval": 352,
16
  "max_stack": 2
17
  },
18
  12479: {
19
  "buff_name": "普渡",
20
+ "interval": 352,
21
  "max_stack": 3
22
  },
23
  19635: {
24
  "buff_name": "普渡",
25
+ "interval": 4,
26
  "gain_attributes": {
27
  "magical_vulnerable": [41, 82, 123]
28
  }
 
99
 
100
  for buff_id, buff in GENERAL_BUFFS.items():
101
  if buff_id not in BUFFS:
102
+ BUFFS[buff_id] = buff
schools/yi_jin_jing/skills.py CHANGED
@@ -1,25 +1,26 @@
1
  from typing import Dict
2
 
3
- from base.skill import Skill, HiddenBuffSkill, DotSkill, PhysicalDamage, MagicalDamage, MagicalDotDamage
4
  from general.skills import GENERAL_SKILLS
5
 
6
 
7
  class 明法判定(Skill):
8
  final_buff = 19635
 
9
 
10
  def record(self, critical, parser):
11
- buff_level = parser.current_target_buffs.get((self.bind_buff, 1))
12
- if buff_level:
13
- parser.current_target_buffs[(self.final_buff, buff_level)] = 1
14
 
15
 
16
  class 明法移除(Skill):
17
  final_buff = 19635
 
18
 
19
  def record(self, critical, parser):
20
- buff_level = parser.current_target_buffs.get((self.bind_buff, 1), 0)
21
  for level in range(buff_level):
22
- parser.current_target_buffs.pop((self.final_buff, level + 1), None)
23
 
24
 
25
  SKILLS: Dict[int, Skill | dict] = {
@@ -41,7 +42,6 @@ SKILLS: Dict[int, Skill | dict] = {
41
  26989: {
42
  "skill_class": 明法判定,
43
  "skill_name": "明法判定",
44
- "bind_buff": 890,
45
  },
46
  26991: {
47
  "skill_class": 明法移除,
@@ -54,7 +54,7 @@ SKILLS: Dict[int, Skill | dict] = {
54
  "weapon_damage_cof": [1024, 2048, 1024, 1024, 2048],
55
  },
56
  17641: {
57
- "skill_class": type("Mixing", (MagicalDamage, HiddenBuffSkill), {}),
58
  "skill_name": "普渡四方",
59
  "damage_base": [23, 27, 31, 38, 43, 50, 54, 58] + [e * 0.5 for e in
60
  [123, 133, 143, 153, 163, 173, 183, 193, 203, 213, 223, 233,
@@ -64,8 +64,7 @@ SKILLS: Dict[int, Skill | dict] = {
64
  "attack_power_cof": [16 * 1.1 * 1.15 * 1.1 * 1.05 * 1.2] * 9 +
65
  [(16 + (i - 9) * 6) * 1.1 * 1.15 * 1.1 * 1.05 * 1.2 for i in range(10, 28)] +
66
  [128 * 1.1 * 1.15 * 1.1 * 1.05 * 1.2],
67
- "bind_buff": 890,
68
- "duration": 352
69
  },
70
  236: {
71
  "skill_class": MagicalDamage,
@@ -92,35 +91,29 @@ SKILLS: Dict[int, Skill | dict] = {
92
  "bind_skill": 743
93
  },
94
  3848: {
95
- "skill_class": type("Mixing", (MagicalDamage, HiddenBuffSkill), {}),
96
  "skill_name": "韦陀献杵",
97
  "damage_base": [77, 83, 90, 94, 100, 105, 108, 111, 114, 117, 120, 123, 126, 129, 132, 135, 138, 141, 144, 147,
98
  150, 153, 156, 159, 162, 165, 168, 171, 174],
99
  "damage_rand": [5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
100
  10],
101
- "attack_power_cof": 144 * 1.2 * 1.15 * 1.15 * 1.35 * 0.9 * 1.15 * 1.1 * 1.05 * 1.1,
102
- "bind_buff": 0,
103
- "duration": 352
104
  },
105
  3849: {
106
- "skill_class": type("Mixing", (MagicalDamage, HiddenBuffSkill), {}),
107
  "skill_name": "韦陀献杵",
108
  "damage_base": [73, 87, 100, 114, 127, 141, 154, 168, 181, 195, 208, 222, 235, 249, 262, 276, 289, 303, 316,
109
  330, 343, 357, 370, 384, 397, 411, 424, 438, 451],
110
  "damage_gain": 0.4 / 3,
111
  "attack_power_cof": 48 * 1.2 * 1.15 * 1.15 * 1.35 * 0.9 * 1.15 * 1.1 * 1.05 * 1.1,
112
- "bind_buff": 0,
113
- "duration": 352
114
  },
115
  3850: {
116
- "skill_class": type("Mixing", (MagicalDamage, HiddenBuffSkill), {}),
117
  "skill_name": "韦陀献杵",
118
  "damage_base": [73, 87, 100, 114, 127, 141, 154, 168, 181, 195, 208, 222, 235, 249, 262, 276, 289, 303, 316,
119
  330, 343, 357, 370, 384, 397, 411, 424, 438, 451],
120
  "damage_gain": 0.4 * 2 / 3,
121
  "attack_power_cof": 96 * 1.2 * 1.15 * 1.15 * 1.35 * 0.9 * 1.15 * 1.1 * 1.05 * 1.1,
122
- "bind_buff": 0,
123
- "duration": 352
124
  },
125
  28619: {
126
  "skill_class": MagicalDamage,
 
1
  from typing import Dict
2
 
3
+ from base.skill import Skill, DotSkill, PhysicalDamage, MagicalDamage, MagicalDotDamage
4
  from general.skills import GENERAL_SKILLS
5
 
6
 
7
  class 明法判定(Skill):
8
  final_buff = 19635
9
+ bind_buff = 890
10
 
11
  def record(self, critical, parser):
12
+ if buff_level := parser.current_target_buff_stacks.get((self.bind_buff, 1)):
13
+ parser.current_target_buff_stacks[(self.final_buff, buff_level)] = 1
 
14
 
15
 
16
  class 明法移除(Skill):
17
  final_buff = 19635
18
+ bind_buff = 890
19
 
20
  def record(self, critical, parser):
21
+ buff_level = parser.current_target_buff_stacks.get((self.bind_buff, 1), 0)
22
  for level in range(buff_level):
23
+ parser.current_target_buff_stacks.pop((self.final_buff, level + 1), None)
24
 
25
 
26
  SKILLS: Dict[int, Skill | dict] = {
 
42
  26989: {
43
  "skill_class": 明法判定,
44
  "skill_name": "明法判定",
 
45
  },
46
  26991: {
47
  "skill_class": 明法移除,
 
54
  "weapon_damage_cof": [1024, 2048, 1024, 1024, 2048],
55
  },
56
  17641: {
57
+ "skill_class": MagicalDamage,
58
  "skill_name": "普渡四方",
59
  "damage_base": [23, 27, 31, 38, 43, 50, 54, 58] + [e * 0.5 for e in
60
  [123, 133, 143, 153, 163, 173, 183, 193, 203, 213, 223, 233,
 
64
  "attack_power_cof": [16 * 1.1 * 1.15 * 1.1 * 1.05 * 1.2] * 9 +
65
  [(16 + (i - 9) * 6) * 1.1 * 1.15 * 1.1 * 1.05 * 1.2 for i in range(10, 28)] +
66
  [128 * 1.1 * 1.15 * 1.1 * 1.05 * 1.2],
67
+ "post_target_buffs": {(890, 1): 1}
 
68
  },
69
  236: {
70
  "skill_class": MagicalDamage,
 
91
  "bind_skill": 743
92
  },
93
  3848: {
94
+ "skill_class": MagicalDamage,
95
  "skill_name": "韦陀献杵",
96
  "damage_base": [77, 83, 90, 94, 100, 105, 108, 111, 114, 117, 120, 123, 126, 129, 132, 135, 138, 141, 144, 147,
97
  150, 153, 156, 159, 162, 165, 168, 171, 174],
98
  "damage_rand": [5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
99
  10],
100
+ "attack_power_cof": 144 * 1.2 * 1.15 * 1.15 * 1.35 * 0.9 * 1.15 * 1.1 * 1.05 * 1.1
 
 
101
  },
102
  3849: {
103
+ "skill_class": MagicalDamage,
104
  "skill_name": "韦陀献杵",
105
  "damage_base": [73, 87, 100, 114, 127, 141, 154, 168, 181, 195, 208, 222, 235, 249, 262, 276, 289, 303, 316,
106
  330, 343, 357, 370, 384, 397, 411, 424, 438, 451],
107
  "damage_gain": 0.4 / 3,
108
  "attack_power_cof": 48 * 1.2 * 1.15 * 1.15 * 1.35 * 0.9 * 1.15 * 1.1 * 1.05 * 1.1,
 
 
109
  },
110
  3850: {
111
+ "skill_class": MagicalDamage,
112
  "skill_name": "韦陀献杵",
113
  "damage_base": [73, 87, 100, 114, 127, 141, 154, 168, 181, 195, 208, 222, 235, 249, 262, 276, 289, 303, 316,
114
  330, 343, 357, 370, 384, 397, 411, 424, 438, 451],
115
  "damage_gain": 0.4 * 2 / 3,
116
  "attack_power_cof": 96 * 1.2 * 1.15 * 1.15 * 1.35 * 0.9 * 1.15 * 1.1 * 1.05 * 1.1,
 
 
117
  },
118
  28619: {
119
  "skill_class": MagicalDamage,
schools/yi_jin_jing/talents.py CHANGED
@@ -15,15 +15,17 @@ class 涅果(Gain):
15
  class 明法(Gain):
16
  def add_skills(self, skills: Dict[int, Skill]):
17
  for skill_id in (26989, 26991, 17641):
18
- skills[skill_id].bind_buff = 12479
 
19
  for skill_id in (3848, 3849, 3850):
20
- skills[skill_id].bind_buff = 12479
21
 
22
  def sub_skills(self, skills: Dict[int, Skill]):
23
  for skill_id in (26989, 26991, 17641):
24
- skills[skill_id].bind_buff = 890
 
25
  for skill_id in (3848, 3849, 3850):
26
- skills[skill_id].bind_buff = 0
27
 
28
 
29
  class 华香(Gain):
 
15
  class 明法(Gain):
16
  def add_skills(self, skills: Dict[int, Skill]):
17
  for skill_id in (26989, 26991, 17641):
18
+ skills[skill_id].post_target_buffs.pop((890, 1))
19
+ skills[skill_id].post_target_buffs = {(12479, 1): 1}
20
  for skill_id in (3848, 3849, 3850):
21
+ skills[skill_id].post_target_buffs[(12479, 1)] = 1
22
 
23
  def sub_skills(self, skills: Dict[int, Skill]):
24
  for skill_id in (26989, 26991, 17641):
25
+ skills[skill_id].post_target_buffs.pop((12479, 1))
26
+ skills[skill_id].post_target_buffs[(890, 1)] = 1
27
  for skill_id in (3848, 3849, 3850):
28
+ skills[skill_id].post_target_buffs.pop((12479, 1))
29
 
30
 
31
  class 华香(Gain):
schools/yin_long_jue/buffs.py CHANGED
@@ -79,4 +79,4 @@ for buff_id, detail in BUFFS.items():
79
 
80
  for buff_id, buff in GENERAL_BUFFS.items():
81
  if buff_id not in BUFFS:
82
- BUFFS[buff_id] = buff
 
79
 
80
  for buff_id, buff in GENERAL_BUFFS.items():
81
  if buff_id not in BUFFS:
82
+ BUFFS[buff_id] = buff
schools/zi_xia_gong/__init__.py CHANGED
@@ -7,4 +7,4 @@ from schools.zi_xia_gong.attribute import ZiXiaGong
7
 
8
 
9
  def prepare(self, player_id):
10
- self.player_buffs[player_id][(17918, 1)] = 1
 
7
 
8
 
9
  def prepare(self, player_id):
10
+ self.buff_stacks[player_id][(17918, 1)] = 1
utils/damage.py CHANGED
@@ -8,6 +8,7 @@ def defense_result(shield_base, shield_gain, shield_ignore, shield_constant):
8
  shield = shield_base
9
  shield += int(shield * shield_gain / BINARY_SCALE)
10
  shield -= int(shield * shield_ignore / BINARY_SCALE)
 
11
  return int(shield * BINARY_SCALE / (shield + shield_constant))
12
 
13
 
 
8
  shield = shield_base
9
  shield += int(shield * shield_gain / BINARY_SCALE)
10
  shield -= int(shield * shield_ignore / BINARY_SCALE)
11
+ shield = max(shield, 0)
12
  return int(shield * BINARY_SCALE / (shield + shield_constant))
13
 
14
 
utils/parser.py CHANGED
@@ -35,7 +35,7 @@ LABEL_MAPPING = {
35
  EMBED_MAPPING: Dict[tuple, int] = {(5, 24449 - i): 8 - i for i in range(8)}
36
 
37
 
38
- class Parser:
39
  current_player: PLAYER_ID_TYPE
40
  current_caster: CASTER_ID_TYPE
41
  current_target: TARGET_ID_TYPE
@@ -46,24 +46,24 @@ class Parser:
46
 
47
  id2name: Dict[PLAYER_ID_TYPE | TARGET_ID_TYPE, PLAYER_NAME_TYPE]
48
  name2id: Dict[PLAYER_NAME_TYPE, PLAYER_ID_TYPE | TARGET_ID_TYPE]
49
- employers: Dict[PET_ID_TYPE, PLAYER_ID_TYPE]
50
 
51
  records: Dict[PLAYER_ID_TYPE, Dict[TARGET_ID_TYPE, RECORD_TYPE]]
52
 
53
  frame_shift_buffs: Dict[FRAME_TYPE, Dict[PLAYER_ID_TYPE, Dict[BUFF_TYPE, BUFF_STACK_TYPE]]]
54
  second_shift_buffs: Dict[SECOND_TYPE, Dict[PLAYER_ID_TYPE, Dict[BUFF_TYPE, BUFF_STACK_TYPE]]]
55
- hidden_buffs: Dict[TARGET_ID_TYPE, Dict[PLAYER_ID_TYPE, Dict[BUFF_TYPE, FRAME_TYPE]]]
56
 
57
- player_buffs: Dict[PLAYER_ID_TYPE, Dict[BUFF_TYPE, BUFF_STACK_TYPE]]
58
- target_buffs: Dict[TARGET_ID_TYPE, Dict[PLAYER_ID_TYPE, Dict[BUFF_TYPE, BUFF_STACK_TYPE]]]
 
 
59
 
60
- stacks: Dict[TARGET_ID_TYPE, Dict[PLAYER_ID_TYPE, Dict[SKILL_ID_TYPE, int]]]
61
- ticks: Dict[TARGET_ID_TYPE, Dict[PLAYER_ID_TYPE, Dict[SKILL_ID_TYPE, int]]]
62
-
63
- pet_snapshot: Dict[PET_ID_TYPE, Dict[BUFF_TYPE, BUFF_STACK_TYPE]]
64
- next_pet_snapshot: Dict[PLAYER_ID_TYPE, List[Dict[BUFF_TYPE, BUFF_STACK_TYPE]]]
65
  dot_snapshot: Dict[TARGET_ID_TYPE, Dict[PLAYER_ID_TYPE, Dict[SKILL_ID_TYPE, Dict[BUFF_TYPE, BUFF_STACK_TYPE]]]]
66
 
 
 
67
  last_dot: Dict[TARGET_ID_TYPE, Dict[PLAYER_ID_TYPE, Dict[SKILL_ID_TYPE, Tuple[SKILL_TYPE, Tuple[tuple, tuple]]]]]
68
  next_dot: Dict[TARGET_ID_TYPE, Dict[PLAYER_ID_TYPE, Dict[SKILL_ID_TYPE, int]]]
69
 
@@ -89,39 +89,43 @@ class Parser:
89
  return self.records[self.current_player][self.current_target]
90
 
91
  @property
92
- def current_hidden_buffs(self):
93
- return self.hidden_buffs[self.current_target][self.current_player]
94
 
95
  @property
96
- def current_player_buffs(self):
97
- return self.player_buffs[self.current_player]
98
 
99
  @property
100
- def current_target_buffs(self):
101
- return self.target_buffs[self.current_target][self.current_player]
 
 
 
 
102
 
103
  @property
104
  def current_snapshot(self):
105
- if self.current_caster in self.pet_snapshot:
106
- return self.pet_snapshot[self.current_caster]
107
  else:
108
  return self.dot_snapshot[self.current_target][self.current_player].get(self.current_skill, {})
109
 
110
  @property
111
- def current_next_pet_snapshot(self):
112
- return self.next_pet_snapshot[self.current_player]
113
 
114
  @property
115
  def current_dot_snapshot(self):
116
  return self.dot_snapshot[self.current_target][self.current_player]
117
 
118
  @property
119
- def current_stacks(self):
120
- return self.stacks[self.current_target][self.current_player]
121
 
122
  @property
123
- def current_ticks(self):
124
- return self.ticks[self.current_target][self.current_player]
125
 
126
  @property
127
  def current_last_dot(self):
@@ -131,32 +135,28 @@ class Parser:
131
  def current_next_dot(self):
132
  return self.next_dot[self.current_target][self.current_player]
133
 
134
- @property
135
- def duration(self):
136
- return round((self.end_frame - self.start_frame) / FRAME_PER_SECOND, 3)
137
-
138
  def reset(self):
139
  self.current_frame = 0
140
  self.current_second = 0
141
 
142
  self.id2name = {}
143
  self.name2id = {}
144
- self.employers = {}
145
 
146
  self.records = defaultdict(lambda: defaultdict(lambda: defaultdict(lambda: defaultdict(list))))
147
 
148
- self.hidden_buffs = defaultdict(lambda: defaultdict(dict))
149
  self.frame_shift_buffs = defaultdict(lambda: defaultdict(dict))
150
  self.second_shift_buffs = defaultdict(lambda: defaultdict(dict))
151
 
152
- self.player_buffs = defaultdict(dict)
153
- self.target_buffs = defaultdict(lambda: defaultdict(dict))
 
 
154
 
155
- self.stacks = defaultdict(lambda: defaultdict(lambda: defaultdict(lambda: 1)))
156
- self.ticks = defaultdict(lambda: defaultdict(lambda: defaultdict(lambda: 0)))
157
 
158
- self.pet_snapshot = dict()
159
- self.next_pet_snapshot = defaultdict(list)
160
  self.dot_snapshot = defaultdict(lambda: defaultdict(dict))
161
  self.last_dot = defaultdict(lambda: defaultdict(dict))
162
  self.next_dot = defaultdict(lambda: defaultdict(dict))
@@ -169,6 +169,46 @@ class Parser:
169
  self.players = {}
170
  self.targets = defaultdict(list)
171
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
172
  @staticmethod
173
  def parse_equipments(detail):
174
  select_equipments = {}
@@ -207,26 +247,25 @@ class Parser:
207
 
208
  def parse_npc(self, row):
209
  detail = row.strip("{}").split(",")
210
- npc_id, player_id = int(detail[0]), int(detail[3])
211
  if npc_id in self.id2name:
212
  return
213
 
214
  npc_name = detail[1]
215
  self.id2name[npc_id] = npc_name
216
  self.name2id[npc_name] = npc_id
217
- if player_id in self.players:
218
- self.employers[npc_id] = player_id
219
 
220
  def parse_pet(self, row):
221
  detail = row.strip().strip("{}")
222
  pet_id = int(detail[0])
223
- if pet_id in self.employers:
224
- player_id = self.employers[pet_id]
225
- if self.next_pet_snapshot[player_id]:
226
- pet_buffs = self.next_pet_snapshot[player_id].pop()
227
  else:
228
- pet_buffs = {}
229
- self.pet_snapshot[pet_id] = {**self.player_buffs[player_id].copy(), **pet_buffs}
230
 
231
  def parse_shift_buff(self, row):
232
  detail = row.strip("{}").split(",")
@@ -250,9 +289,9 @@ class Parser:
250
  for player_id, shift_buffs in self.frame_shift_buffs.pop(frame).items():
251
  for buff, buff_stack in shift_buffs.items():
252
  if buff_stack:
253
- self.player_buffs[player_id][buff] = buff_stack
254
  else:
255
- self.player_buffs[player_id].pop(buff, None)
256
 
257
  def parse_second_shift_status(self):
258
  for second in list(self.second_shift_buffs):
@@ -261,26 +300,44 @@ class Parser:
261
  for player_id, shift_buffs in self.second_shift_buffs.pop(second).items():
262
  for buff, buff_stack in shift_buffs.items():
263
  if buff_stack:
264
- self.player_buffs[player_id][buff] = buff_stack
265
  else:
266
- self.player_buffs[player_id].pop(buff, None)
267
-
268
- def parse_hidden_buffs(self):
269
- for target_id in self.hidden_buffs:
270
- for player_id, hidden_buffs in self.hidden_buffs[target_id].items():
271
- for buff, end_frame in hidden_buffs.items():
 
 
 
 
 
 
 
 
 
272
  if end_frame < self.current_frame:
273
- self.target_buffs[target_id][player_id].pop(buff, None)
 
 
 
274
 
275
  def parse_buff(self, row):
276
  detail = row.strip("{}").split(",")
277
  caster_id = int(detail[0])
278
- if caster_id in self.employers:
279
- player_id = self.employers[caster_id]
280
- buffs = self.pet_snapshot.get(caster_id, {})
 
 
 
 
 
 
281
  else:
282
  player_id = caster_id
283
- buffs = self.player_buffs[player_id]
284
 
285
  if player_id not in self.players:
286
  return
@@ -294,15 +351,15 @@ class Parser:
294
  return
295
 
296
  if buff_stack:
297
- buffs[(buff_id, buff_level)] = buff_stack
298
  else:
299
- buffs.pop((buff_id, buff_level), None)
300
 
301
  def parse_skill(self, row):
302
  detail = row.strip("{}").split(",")
303
  caster_id, target_id = int(detail[0]), int(detail[1])
304
- if caster_id in self.employers:
305
- player_id = self.employers[caster_id]
306
  else:
307
  player_id = caster_id
308
 
@@ -324,11 +381,11 @@ class Parser:
324
  self.current_skill = skill_id
325
  skill = self.players[player_id].skills[skill_id]
326
  skill.skill_level = skill_level
327
- skill.record(critical, self)
328
 
329
  def status(self, skill_id):
330
  current_status = []
331
- for (buff_id, buff_level), buff_stack in self.current_player_buffs.items():
332
  buff = self.current_school.buffs[buff_id]
333
  if buff.gain_attributes:
334
  current_status.append((buff_id, buff_level, buff_stack))
@@ -345,7 +402,7 @@ class Parser:
345
  snapshot_status.append((buff_id, buff_level, buff_stack))
346
 
347
  target_status = []
348
- for (buff_id, buff_level), buff_stack in self.current_target_buffs.items():
349
  buff = self.current_school.buffs[buff_id]
350
  if buff.gain_attributes:
351
  target_status.append((buff_id, buff_level, buff_stack))
@@ -382,7 +439,7 @@ class Parser:
382
  if (current_frame := int(row[1])) != self.current_frame:
383
  self.current_frame = current_frame
384
  self.parse_frame_shift_status()
385
- self.parse_hidden_buffs()
386
  # if (current_second := int(row[3])) != self.current_second:
387
  # self.current_second = current_second
388
  # self.parse_frame_shift_status()
 
35
  EMBED_MAPPING: Dict[tuple, int] = {(5, 24449 - i): 8 - i for i in range(8)}
36
 
37
 
38
+ class BaseParser:
39
  current_player: PLAYER_ID_TYPE
40
  current_caster: CASTER_ID_TYPE
41
  current_target: TARGET_ID_TYPE
 
46
 
47
  id2name: Dict[PLAYER_ID_TYPE | TARGET_ID_TYPE, PLAYER_NAME_TYPE]
48
  name2id: Dict[PLAYER_NAME_TYPE, PLAYER_ID_TYPE | TARGET_ID_TYPE]
49
+ pet2employer: Dict[PET_ID_TYPE, PLAYER_ID_TYPE]
50
 
51
  records: Dict[PLAYER_ID_TYPE, Dict[TARGET_ID_TYPE, RECORD_TYPE]]
52
 
53
  frame_shift_buffs: Dict[FRAME_TYPE, Dict[PLAYER_ID_TYPE, Dict[BUFF_TYPE, BUFF_STACK_TYPE]]]
54
  second_shift_buffs: Dict[SECOND_TYPE, Dict[PLAYER_ID_TYPE, Dict[BUFF_TYPE, BUFF_STACK_TYPE]]]
 
55
 
56
+ buff_stacks: Dict[CASTER_ID_TYPE, Dict[BUFF_TYPE, BUFF_STACK_TYPE]]
57
+ buff_intervals: Dict[CASTER_ID_TYPE, Dict[BUFF_TYPE, FRAME_TYPE]]
58
+ target_buff_stacks: Dict[TARGET_ID_TYPE, Dict[PLAYER_ID_TYPE, Dict[BUFF_TYPE, BUFF_STACK_TYPE]]]
59
+ target_buff_intervals: Dict[TARGET_ID_TYPE, Dict[PLAYER_ID_TYPE, Dict[BUFF_TYPE, FRAME_TYPE]]]
60
 
61
+ dot_stacks: Dict[TARGET_ID_TYPE, Dict[PLAYER_ID_TYPE, Dict[SKILL_ID_TYPE, int]]]
62
+ dot_ticks: Dict[TARGET_ID_TYPE, Dict[PLAYER_ID_TYPE, Dict[SKILL_ID_TYPE, int]]]
 
 
 
63
  dot_snapshot: Dict[TARGET_ID_TYPE, Dict[PLAYER_ID_TYPE, Dict[SKILL_ID_TYPE, Dict[BUFF_TYPE, BUFF_STACK_TYPE]]]]
64
 
65
+ next_pet_buff_stacks: Dict[PLAYER_ID_TYPE, List[Dict[BUFF_TYPE, BUFF_STACK_TYPE]]]
66
+
67
  last_dot: Dict[TARGET_ID_TYPE, Dict[PLAYER_ID_TYPE, Dict[SKILL_ID_TYPE, Tuple[SKILL_TYPE, Tuple[tuple, tuple]]]]]
68
  next_dot: Dict[TARGET_ID_TYPE, Dict[PLAYER_ID_TYPE, Dict[SKILL_ID_TYPE, int]]]
69
 
 
89
  return self.records[self.current_player][self.current_target]
90
 
91
  @property
92
+ def current_buff_stacks(self):
93
+ return self.buff_stacks[self.current_player]
94
 
95
  @property
96
+ def current_buff_intervals(self):
97
+ return self.buff_intervals[self.current_player]
98
 
99
  @property
100
+ def current_target_buff_stacks(self):
101
+ return self.target_buff_stacks[self.current_target][self.current_player]
102
+
103
+ @property
104
+ def current_target_buff_intervals(self):
105
+ return self.target_buff_intervals[self.current_target][self.current_player]
106
 
107
  @property
108
  def current_snapshot(self):
109
+ if self.current_caster in self.pet2employer:
110
+ return self.buff_stacks[self.current_caster]
111
  else:
112
  return self.dot_snapshot[self.current_target][self.current_player].get(self.current_skill, {})
113
 
114
  @property
115
+ def current_next_pet_buff_stacks(self):
116
+ return self.next_pet_buff_stacks[self.current_player]
117
 
118
  @property
119
  def current_dot_snapshot(self):
120
  return self.dot_snapshot[self.current_target][self.current_player]
121
 
122
  @property
123
+ def current_dot_stacks(self):
124
+ return self.dot_stacks[self.current_target][self.current_player]
125
 
126
  @property
127
+ def current_dot_ticks(self):
128
+ return self.dot_ticks[self.current_target][self.current_player]
129
 
130
  @property
131
  def current_last_dot(self):
 
135
  def current_next_dot(self):
136
  return self.next_dot[self.current_target][self.current_player]
137
 
 
 
 
 
138
  def reset(self):
139
  self.current_frame = 0
140
  self.current_second = 0
141
 
142
  self.id2name = {}
143
  self.name2id = {}
144
+ self.pet2employer = {}
145
 
146
  self.records = defaultdict(lambda: defaultdict(lambda: defaultdict(lambda: defaultdict(list))))
147
 
 
148
  self.frame_shift_buffs = defaultdict(lambda: defaultdict(dict))
149
  self.second_shift_buffs = defaultdict(lambda: defaultdict(dict))
150
 
151
+ self.buff_stacks = defaultdict(dict)
152
+ self.buff_intervals = defaultdict(dict)
153
+ self.target_buff_stacks = defaultdict(lambda: defaultdict(dict))
154
+ self.target_buff_intervals = defaultdict(lambda: defaultdict(dict))
155
 
156
+ self.dot_stacks = defaultdict(lambda: defaultdict(lambda: defaultdict(lambda: 1)))
157
+ self.dot_ticks = defaultdict(lambda: defaultdict(lambda: defaultdict(lambda: 0)))
158
 
159
+ self.next_pet_buff_stacks = defaultdict(list)
 
160
  self.dot_snapshot = defaultdict(lambda: defaultdict(dict))
161
  self.last_dot = defaultdict(lambda: defaultdict(dict))
162
  self.next_dot = defaultdict(lambda: defaultdict(dict))
 
169
  self.players = {}
170
  self.targets = defaultdict(list)
171
 
172
+ def refresh_buff(self, buff_id, buff_level, buff_stack=1):
173
+ buff = self.current_school.buffs[buff_id]
174
+ buff_tuple = (buff_id, buff_level)
175
+ stack = max(min(self.current_buff_stacks.get(buff_tuple, 0) + buff_stack, buff.max_stack), 0)
176
+ if stack:
177
+ self.current_buff_stacks[buff_tuple] = stack
178
+ if buff.interval > 0:
179
+ self.current_buff_intervals[buff_tuple] = self.current_frame + buff.interval + 1
180
+ else:
181
+ self.current_buff_stacks.pop(buff_tuple, None)
182
+ self.current_buff_intervals.pop(buff_tuple, None)
183
+
184
+ def refresh_target_buff(self, buff_id, buff_level, buff_stack=1):
185
+ buff = self.current_school.buffs[buff_id]
186
+ buff_tuple = (buff_id, buff_level)
187
+ stack = max(min(self.current_target_buff_stacks.get(buff_tuple, 0) + buff_stack, buff.max_stack), 0)
188
+ if stack:
189
+ self.current_target_buff_stacks[buff_tuple] = stack
190
+ if buff.interval > 0:
191
+ self.current_target_buff_intervals[buff_tuple] = self.current_frame + buff.interval + 1
192
+ else:
193
+ self.current_target_buff_stacks.pop(buff_tuple, None)
194
+ self.current_target_buff_intervals.pop(buff_tuple, None)
195
+
196
+ def clear_buff(self, buff_id, buff_level):
197
+ buff_tuple = (buff_id, buff_level)
198
+ self.current_buff_stacks.pop(buff_tuple, None)
199
+ self.current_buff_intervals.pop(buff_tuple, None)
200
+
201
+ def clear_target_buff(self, buff_id, buff_level):
202
+ buff_tuple = (buff_id, buff_level)
203
+ self.current_target_buff_stacks.pop(buff_tuple, None)
204
+ self.current_target_buff_intervals.pop(buff_tuple, None)
205
+
206
+
207
+ class Parser(BaseParser):
208
+ @property
209
+ def duration(self):
210
+ return round((self.end_frame - self.start_frame) / FRAME_PER_SECOND, 3)
211
+
212
  @staticmethod
213
  def parse_equipments(detail):
214
  select_equipments = {}
 
247
 
248
  def parse_npc(self, row):
249
  detail = row.strip("{}").split(",")
250
+ npc_id, employer_id = int(detail[0]), int(detail[3])
251
  if npc_id in self.id2name:
252
  return
253
 
254
  npc_name = detail[1]
255
  self.id2name[npc_id] = npc_name
256
  self.name2id[npc_name] = npc_id
257
+ if employer_id in self.players:
258
+ self.pet2employer[npc_id] = employer_id
259
 
260
  def parse_pet(self, row):
261
  detail = row.strip().strip("{}")
262
  pet_id = int(detail[0])
263
+ if player_id := self.pet2employer.get(pet_id):
264
+ if self.next_pet_buff_stacks[player_id]:
265
+ pet_buff_stacks = self.next_pet_buff_stacks[player_id].pop()
 
266
  else:
267
+ pet_buff_stacks = {}
268
+ self.buff_stacks[pet_id] = {**self.buff_stacks[player_id].copy(), **pet_buff_stacks}
269
 
270
  def parse_shift_buff(self, row):
271
  detail = row.strip("{}").split(",")
 
289
  for player_id, shift_buffs in self.frame_shift_buffs.pop(frame).items():
290
  for buff, buff_stack in shift_buffs.items():
291
  if buff_stack:
292
+ self.buff_stacks[player_id][buff] = buff_stack
293
  else:
294
+ self.buff_stacks[player_id].pop(buff, None)
295
 
296
  def parse_second_shift_status(self):
297
  for second in list(self.second_shift_buffs):
 
300
  for player_id, shift_buffs in self.second_shift_buffs.pop(second).items():
301
  for buff, buff_stack in shift_buffs.items():
302
  if buff_stack:
303
+ self.buff_stacks[player_id][buff] = buff_stack
304
  else:
305
+ self.buff_stacks[player_id].pop(buff, None)
306
+
307
+ def parse_buff_intervals(self):
308
+ for caster_id, buffs in self.buff_intervals.items():
309
+ pop_buffs = []
310
+ for buff, end_frame in buffs.items():
311
+ if end_frame < self.current_frame:
312
+ self.buff_stacks[caster_id].pop(buff, None)
313
+ pop_buffs.append(buff)
314
+ for pop_buff in pop_buffs:
315
+ buffs.pop(pop_buff)
316
+ for target_id in self.target_buff_intervals:
317
+ for caster_id, buffs in self.target_buff_intervals[target_id].items():
318
+ pop_buffs = []
319
+ for buff, end_frame in buffs.items():
320
  if end_frame < self.current_frame:
321
+ self.target_buff_stacks[target_id][caster_id].pop(buff, None)
322
+ pop_buffs.append(buff)
323
+ for pop_buff in pop_buffs:
324
+ buffs.pop(pop_buff)
325
 
326
  def parse_buff(self, row):
327
  detail = row.strip("{}").split(",")
328
  caster_id = int(detail[0])
329
+ if caster_id in self.pet2employer:
330
+ player_id = self.pet2employer[caster_id]
331
+ if caster_id in self.buff_stacks:
332
+ buff_stacks = self.buff_stacks[caster_id]
333
+ elif self.next_pet_buff_stacks[player_id]:
334
+ buff_stacks = self.next_pet_buff_stacks[player_id][0]
335
+ else:
336
+ buff_stacks = {}
337
+ self.next_pet_buff_stacks[player_id].append(buff_stacks)
338
  else:
339
  player_id = caster_id
340
+ buff_stacks = self.buff_stacks[player_id]
341
 
342
  if player_id not in self.players:
343
  return
 
351
  return
352
 
353
  if buff_stack:
354
+ buff_stacks[(buff_id, buff_level)] = buff_stack
355
  else:
356
+ buff_stacks.pop((buff_id, buff_level), None)
357
 
358
  def parse_skill(self, row):
359
  detail = row.strip("{}").split(",")
360
  caster_id, target_id = int(detail[0]), int(detail[1])
361
+ if caster_id in self.pet2employer:
362
+ player_id = self.pet2employer[caster_id]
363
  else:
364
  player_id = caster_id
365
 
 
381
  self.current_skill = skill_id
382
  skill = self.players[player_id].skills[skill_id]
383
  skill.skill_level = skill_level
384
+ skill.parse(critical, self)
385
 
386
  def status(self, skill_id):
387
  current_status = []
388
+ for (buff_id, buff_level), buff_stack in self.current_buff_stacks.items():
389
  buff = self.current_school.buffs[buff_id]
390
  if buff.gain_attributes:
391
  current_status.append((buff_id, buff_level, buff_stack))
 
402
  snapshot_status.append((buff_id, buff_level, buff_stack))
403
 
404
  target_status = []
405
+ for (buff_id, buff_level), buff_stack in self.current_target_buff_stacks.items():
406
  buff = self.current_school.buffs[buff_id]
407
  if buff.gain_attributes:
408
  target_status.append((buff_id, buff_level, buff_stack))
 
439
  if (current_frame := int(row[1])) != self.current_frame:
440
  self.current_frame = current_frame
441
  self.parse_frame_shift_status()
442
+ self.parse_buff_intervals()
443
  # if (current_second := int(row[3])) != self.current_second:
444
  # self.current_second = current_second
445
  # self.parse_frame_shift_status()