Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions src/char/BaseChar.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,20 @@ def __eq__(self, other):
return self.name == other.name and self.index == other.index
return False

def set_chain_action(self, method_name):
self._chain_method = method_name

def perform(self):
"""执行当前角色的主要战斗行动序列。"""
self.last_perform = time.time()
if hasattr(self, '_chain_method') and self._chain_method:
method_name = self._chain_method
self._chain_method = None
if hasattr(self, method_name):
getattr(self, method_name)()
else:
self.task.chain_executor.step_complete()
return
if self.has_intro:
self.add_intro_motion_freeze(self.last_perform)
if self.need_fast_perform():
Expand Down Expand Up @@ -591,6 +602,10 @@ def on_combat_end(self, chars):
"""
pass

def on_chain_step_complete(self):
"""链式步骤完成回调;默认无操作,子类可按需覆盖。"""
return None

@property
def add_freeze_duration(self):
"""添加冻结持续时间 (代理到 task.add_freeze_duration)。"""
Expand Down Expand Up @@ -880,3 +895,25 @@ def switch_other_char(self):
self.send_key(next_char)
self.sleep(0.2, sleep_check=False)
self.logger.debug(f"switch_other_char on_combat_end {self.index} switch end")

def is_anchor(self) -> bool:
return False

def _get_char_key(self, char_name):
for c in self.task.chars:
if c is not None and c.__class__.__name__ == char_name:
return c.index + 1
return None

def _send_chain_key(self):
if self.task.chain_executor.active:
target_char, _ = self.task.chain_executor.target
if target_char is not None and target_char != self:
self.task.send_key(target_char.index + 1)
return True
else:
anchor = self.task.chain_executor.pending_anchor
if anchor is not None and anchor != self:
self.task.send_key(anchor.index + 1)
return True
return False
8 changes: 8 additions & 0 deletions src/char/CharFactory.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,15 @@
from src.char.BaseChar import BaseChar, Element
from src.char.Chiz import Chiz
from src.char.Hotori import Hotori
from src.char.HotoriChain import HotoriChain
from src.char.Jiuyuan import Jiuyuan
from src.char.JiuyuanChain import JiuyuanChain
from src.char.Mint import Mint
from src.char.Nanally import Nanally
from src.char.NanallyChain import NanallyChain
from src.char.Sakiri import Sakiri
from src.char.Zero import Zero
from src.char.ZeroChain import ZeroChain

if TYPE_CHECKING:
import numpy as np
Expand All @@ -28,6 +32,10 @@
"char_nanally": {"cls": Nanally, "cn_name": "娜娜莉", "element": Element.GREEN},
"char_hotori": {"cls": Hotori, "cn_name": "浔", "element": Element.WHITE},
"char_chiz": {"cls": Chiz, "cn_name": "小吱", "element": Element.WHITE},
"char_chain_hotori": {"cls": HotoriChain, "cn_name": "浔创生链式-浔", "element": Element.WHITE},
"char_chain_zero": {"cls": ZeroChain, "cn_name": "浔创生链式-零", "element": Element.WHITE},
"char_chain_jiuyuan": {"cls": JiuyuanChain, "cn_name": "浔创生链式-九原", "element": Element.GREEN},
"char_chain_nanally": {"cls": NanallyChain, "cn_name": "浔创生链式-娜娜莉", "element": Element.GREEN},
}

char_names = char_dict.keys()
Expand Down
38 changes: 37 additions & 1 deletion src/char/Hotori.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,23 @@ class Hotori(BaseChar):
TEAM_SKILL_WINDOW = 5
MAX_TEAM_SKILL_RECORDS = 3
ULT_ATTACK_DURATION = 6
E_RECOVERY = 10
Q_RECOVERY = 20
SLOP = 0

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.start_combat = True
self.team_skill_window_start = 0
self._e_cast_time = 0
self._q_cast_time = 0

def do_perform(self):
self.wait_intro()

if self.can_ultimate_with_records():
if self.click_ultimate():
self._q_cast_time = time.time()
self.clear_team_skill_records()
else:
self.continues_normal_attack(0.2)
Expand All @@ -35,6 +41,7 @@ def do_perform(self):
return

if self.click_skill(time_out=1.5)[0]:
self._e_cast_time = time.time()
self.start_team_skill_window()

def do_get_switch_priority(self, current_char, has_intro=False):
Expand All @@ -59,6 +66,28 @@ def required_team_skill_records(self):
def team_skill_window_elapsed(self):
return self.time_elapsed_accounting_for_freeze(self.team_skill_window_start)

@property
def e_remaining(self):
if self._e_cast_time <= 0:
return 0
return self.E_RECOVERY + self.TEAM_SKILL_WINDOW + self.SLOP - self.time_elapsed_accounting_for_freeze(self._e_cast_time)

@property
def q_remaining(self):
if self._q_cast_time <= 0:
return 0
return self.Q_RECOVERY + self.SLOP - self.time_elapsed_accounting_for_freeze(self._q_cast_time)

def time_to_cashout(self):
if self._e_cast_time > 0:
return self.e_remaining
return 999

def time_to_startup(self):
if self._q_cast_time > 0:
return self.q_remaining
return 999

def expire_team_skill_window(self):
self.team_skill_window_start = 0

Expand Down Expand Up @@ -110,18 +139,25 @@ def can_ultimate_with_records(self):
def waiting_for_team_skills(self):
if self.team_skill_window_start <= 0 or self.ready_for_ultimate():
return False
if self.team_skill_window_elapsed() > self.TEAM_SKILL_WINDOW:
if self.time_elapsed_accounting_for_freeze(self.team_skill_window_start) > self.TEAM_SKILL_WINDOW:
self.expire_team_skill_window()
return False
return True

def reset_state(self):
super().reset_state()
self.clear_team_skill_records()
self._e_cast_time = 0
self._q_cast_time = 0

def on_combat_end(self, chars):
self.clear_team_skill_records()

def on_chain_step_complete(self):
if self.team_skill_window_start > 0:
# 基类中无需处理,由子类 HotoriChain 覆盖实现
pass

# def skill_available(self, check_color=True):
# available = super().skill_available(check_color=check_color)
# box = self.task.box_of_screen(0.3590, 0.9299, 0.3641, 0.9444,
Expand Down
Loading
Loading