169 lines
5.9 KiB
Python
169 lines
5.9 KiB
Python
import math
|
|
import time
|
|
|
|
from hardware_control import hw_ctrl
|
|
|
|
|
|
class DeathManager:
|
|
ROUTE_SELECTION_EPSILON = 0.3
|
|
|
|
def __init__(
|
|
self,
|
|
patrol_system,
|
|
resurrection_waypoints_path=None,
|
|
resurrection_route_a_path=None,
|
|
resurrection_route_b_path=None,
|
|
release_spirit_key='9',
|
|
resurrect_key='0',
|
|
):
|
|
self.patrol_system = patrol_system
|
|
self.release_spirit_key = str(release_spirit_key).strip() or '9'
|
|
self.resurrect_key = str(resurrect_key).strip() or '0'
|
|
self.resurrection_route_a = self._load_waypoints(
|
|
resurrection_route_a_path or resurrection_waypoints_path
|
|
)
|
|
self.resurrection_route_b = self._load_waypoints(resurrection_route_b_path)
|
|
|
|
self.corpse_pos = None
|
|
self.graveyard_pos = None
|
|
self.is_running_to_corpse = False
|
|
self.selected_resurrection_route = None
|
|
self.selected_resurrection_route_name = None
|
|
|
|
self._spirit_release_sent = False
|
|
self._route_selected = False
|
|
self._resurrection_completed = False
|
|
|
|
def _load_waypoints(self, path):
|
|
"""Load a resurrection route JSON file, allowing empty configuration."""
|
|
if not path:
|
|
return None
|
|
|
|
import json
|
|
import os
|
|
|
|
if not os.path.exists(path):
|
|
return None
|
|
try:
|
|
with open(path, 'r', encoding='utf-8') as f:
|
|
data = json.load(f)
|
|
if isinstance(data, list) and data:
|
|
return [(float(point[0]), float(point[1])) for point in data]
|
|
except Exception:
|
|
pass
|
|
return None
|
|
|
|
def _clear_death_run_state(self):
|
|
self.graveyard_pos = None
|
|
self.selected_resurrection_route = None
|
|
self.selected_resurrection_route_name = None
|
|
self._route_selected = False
|
|
self._resurrection_completed = False
|
|
|
|
def _build_resurrection_route_candidates(self):
|
|
candidates = []
|
|
if self.resurrection_route_a:
|
|
candidates.append(("A", self.resurrection_route_a))
|
|
if self.resurrection_route_b:
|
|
candidates.append(("B", self.resurrection_route_b))
|
|
return candidates
|
|
|
|
def _record_graveyard_pos(self, state):
|
|
if self.graveyard_pos is not None:
|
|
return True
|
|
|
|
x = state.get('x')
|
|
y = state.get('y')
|
|
if x is None or y is None:
|
|
return False
|
|
|
|
self.graveyard_pos = (float(x), float(y))
|
|
print(f">>> [系统] 记录墓地坐标: {self.graveyard_pos}")
|
|
return True
|
|
|
|
def _choose_resurrection_route(self):
|
|
if self.graveyard_pos is None:
|
|
return None, None
|
|
|
|
candidates = self._build_resurrection_route_candidates()
|
|
if not candidates:
|
|
return None, None
|
|
|
|
best_name = None
|
|
best_points = None
|
|
best_dist = None
|
|
for name, points in candidates:
|
|
start_pos = points[0]
|
|
dist = math.dist(self.graveyard_pos, start_pos)
|
|
print(f">>> [系统] 复活路线{name} 起点距离墓地: {dist:.3f} | 起点: {start_pos}")
|
|
if best_dist is None or dist + self.ROUTE_SELECTION_EPSILON < best_dist:
|
|
best_name = name
|
|
best_points = points
|
|
best_dist = dist
|
|
return best_name, best_points
|
|
|
|
def reset_when_alive(self):
|
|
"""Clear death-related state after the character is alive again."""
|
|
self._spirit_release_sent = False
|
|
self.corpse_pos = None
|
|
self.is_running_to_corpse = False
|
|
self._clear_death_run_state()
|
|
|
|
def on_death(self, state):
|
|
"""Record corpse coordinates once, then release spirit."""
|
|
if self._spirit_release_sent:
|
|
return
|
|
|
|
self._spirit_release_sent = True
|
|
self._clear_death_run_state()
|
|
self.corpse_pos = (state['x'], state['y'])
|
|
self.is_running_to_corpse = True
|
|
print(f">>> [系统] 记录死亡坐标: {self.corpse_pos},准备释放灵魂...")
|
|
hw_ctrl.press(self.release_spirit_key)
|
|
time.sleep(5)
|
|
|
|
def run_to_corpse(self, state, get_state=None):
|
|
"""
|
|
Run the configured resurrection route first, then navigate back to the corpse.
|
|
"""
|
|
if not self.corpse_pos:
|
|
return
|
|
|
|
if not self._record_graveyard_pos(state):
|
|
return
|
|
|
|
if not self._route_selected:
|
|
route_name, route_points = self._choose_resurrection_route()
|
|
self.selected_resurrection_route_name = route_name
|
|
self.selected_resurrection_route = route_points
|
|
self._route_selected = True
|
|
if route_name:
|
|
print(f">>> [系统] 本次死亡选择复活路线: {route_name}")
|
|
else:
|
|
print(">>> [系统] 未配置可用复活路线,直接前往尸体...")
|
|
|
|
if self.selected_resurrection_route and not self._resurrection_completed:
|
|
if get_state is None:
|
|
return
|
|
if self.patrol_system.navigate_path(get_state, self.selected_resurrection_route):
|
|
self._resurrection_completed = True
|
|
print(
|
|
f">>> [系统] 复活路线{self.selected_resurrection_route_name}跑完,开始前往尸体..."
|
|
)
|
|
return
|
|
|
|
is_arrived = self.patrol_system.navigate_to_point(state, self.corpse_pos)
|
|
if is_arrived:
|
|
print(">>> 已到达尸体附近,尝试复活...")
|
|
hw_ctrl.press(self.resurrect_key)
|
|
time.sleep(5)
|
|
if get_state:
|
|
new_state = get_state()
|
|
if new_state and new_state.get('death_state') == 2:
|
|
print(">>> 仍为灵魂状态,再次尝试复活...")
|
|
hw_ctrl.press(self.resurrect_key)
|
|
time.sleep(5)
|
|
self.is_running_to_corpse = False
|
|
self.corpse_pos = None
|
|
self._clear_death_run_state()
|