""" Handoff Protocol (帧交接协议) NovelMaster Core Engine - 状态管理器 特性: - LOD (视锥剔除): 只精确交接在场人物 - Tick (全局时钟): 多线程悬置动作记录 - 休眠背景人物惰性更新 """ from dataclasses import dataclass, field from typing import List, Optional, Dict, Any from enum import Enum import time class CharacterStatus(Enum): ACTIVE = "active" DORMANT = "dormant" SUSPENDED = "suspended" class ActionType(Enum): DIALOGUE = "dialogue" MOVEMENT = "movement" COMBAT = "combat" SKILL = "skill" MENTAL = "mental" class Urgency(Enum): CRITICAL = "critical" NORMAL = "normal" LOW = "low" @dataclass class Vector3D: """3D坐标""" x: float y: float z: float @dataclass class Character: """角色""" id: str name: str status: CharacterStatus = CharacterStatus.ACTIVE position: Optional[Vector3D] = None last_update_tick: int = 0 pending_actions: List['ImminentAction'] = field(default_factory=list) @dataclass class ImminentAction: """悬置动作""" id: str character_id: str type: ActionType description: str urgency: Urgency = Urgency.NORMAL tick: int = 0 @dataclass class WorldState: """世界状态""" location: str time_of_day: str weather: Optional[str] = None tension_level: int = 50 # 0-100 hook_pressure: int = 0 # 负债池压强 @dataclass class HandoffMetadata: """交接元数据""" chapter_number: int scene_number: int previous_chapter_summary: str next_chapter_hint: Optional[str] = None @dataclass class HandoffFrame: """交接帧""" tick: int timestamp: float active_characters: List[Character] suspended_characters: List[Character] imminent_actions: List[ImminentAction] world_state: WorldState metadata: HandoffMetadata def cull_to_viewshed(characters: List[Character], view_center: Vector3D, radius: float) -> List[Character]: """LOD视锥剔除 - 只保留视野内角色""" result = [] for char in characters: if not char.position: continue distance = ((char.position.x - view_center.x) ** 2 + (char.position.y - view_center.y) ** 2 + (char.position.z - view_center.z) ** 2) ** 0.5 if distance <= radius: result.append(char) return result def create_handoff_frame( current_tick: int, all_characters: List[Character], world_state: WorldState, metadata: HandoffMetadata ) -> HandoffFrame: """创建交接帧""" active_characters = [c for c in all_characters if c.status == CharacterStatus.ACTIVE] suspended_characters = [c for c in all_characters if c.status == CharacterStatus.SUSPENDED] # 收集所有悬置动作 imminent_actions = [] for char in all_characters: imminent_actions.extend(char.pending_actions) return HandoffFrame( tick=current_tick, timestamp=time.time(), active_characters=active_characters, suspended_characters=suspended_characters, imminent_actions=imminent_actions, world_state=world_state, metadata=metadata ) def merge_handoff_frames(frames: List[HandoffFrame]) -> HandoffFrame: """合并多个交接帧""" if not frames: raise ValueError("No frames to merge") latest_frame = max(frames, key=lambda f: f.tick) return HandoffFrame( tick=latest_frame.tick, timestamp=latest_frame.timestamp, active_characters=[c for f in frames for c in f.active_characters], suspended_characters=[c for f in frames for c in f.suspended_characters], imminent_actions=[a for f in frames for a in f.imminent_actions], world_state=latest_frame.world_state, metadata=latest_frame.metadata ) def to_dict(frame: HandoffFrame) -> Dict[str, Any]: """序列化为字典""" return { "tick": frame.tick, "timestamp": frame.timestamp, "active_characters": [ { "id": c.id, "name": c.name, "status": c.status.value, "position": {"x": c.position.x, "y": c.position.y, "z": c.position.z} if c.position else None } for c in frame.active_characters ], "suspended_characters": [c.id for c in frame.suspended_characters], "imminent_actions": [ { "id": a.id, "character_id": a.character_id, "type": a.type.value, "description": a.description, "urgency": a.urgency.value } for a in frame.imminent_actions ], "world_state": { "location": frame.world_state.location, "time_of_day": frame.world_state.time_of_day, "weather": frame.world_state.weather, "tension_level": frame.world_state.tension_level, "hook_pressure": frame.world_state.hook_pressure }, "metadata": { "chapter_number": frame.metadata.chapter_number, "scene_number": frame.metadata.scene_number, "previous_chapter_summary": frame.metadata.previous_chapter_summary, "next_chapter_hint": frame.metadata.next_chapter_hint } }