feat: initial commit
This commit is contained in:
@@ -0,0 +1,194 @@
|
||||
"""
|
||||
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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user