添加DSL引擎迁移和验证功能

- 新增最终验证脚本和迁移测试脚本,确保DSL引擎的完整性和功能
- 实现故事模块的音频配置、角色定义和情感故事模块
- 迁移现有故事数据到新的DSL格式,支持动态内容和条件导航
- 更新主题和UI组件,确保一致的黑色背景和暗色主题
- 添加音频管理器和性能监控工具,提升游戏体验和调试能力
This commit is contained in:
2025-09-10 17:36:55 +08:00
parent a90cc1c5a9
commit 93400900c0
9 changed files with 6938 additions and 1202 deletions

View File

@@ -0,0 +1,114 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import argparse
import os
import re
from typing import Dict, List, Tuple
NODE_RE = re.compile(r'^\s*@node\s+(?P<id>[A-Za-z0-9_]+)\s*$')
TITLE_RE = re.compile(r'^\s*@title\s+"(?P<title>.*)"\s*$')
CHOICE_RE = re.compile(r'^\s*choice_\d+\s*:\s*"(?P<label>.*?)"\s*->\s*(?P<target>[A-Za-z0-9_]+)')
ENDING_ID_PREFIXES = (
'ending_',
)
ENDING_TITLE_KEYWORDS = (
'结局', '最终', '终极', '拯救', '守护', '和谐', '文明', '宽恕', '宇宙', '完美'
)
def parse_story(path: str) -> Tuple[Dict[str, str], Dict[str, List[Tuple[str, str]]]]:
with open(path, 'r', encoding='utf-8') as f:
lines = f.readlines()
nodes: Dict[str, str] = {}
edges: Dict[str, List[Tuple[str, str]]] = {}
current_id: str = ''
i = 0
n = len(lines)
while i < n:
line = lines[i].rstrip('\n')
m_node = NODE_RE.match(line)
if m_node:
current_id = m_node.group('id')
if current_id not in nodes:
nodes[current_id] = current_id
if current_id not in edges:
edges[current_id] = []
j = i + 1
while j < n:
l2 = lines[j].rstrip('\n')
if NODE_RE.match(l2):
break
m_title = TITLE_RE.match(l2)
if m_title:
title = m_title.group('title').strip()
if title:
nodes[current_id] = title
m_choice = CHOICE_RE.match(l2)
if m_choice:
label = m_choice.group('label').strip()
target = m_choice.group('target').strip()
edges.setdefault(current_id, []).append((label, target))
j += 1
i = j
continue
i += 1
return nodes, edges
def is_ending(node_id: str, title: str) -> bool:
if any(node_id.startswith(p) for p in ENDING_ID_PREFIXES):
return True
return any(k in (title or '') for k in ENDING_TITLE_KEYWORDS)
def main():
ap = argparse.ArgumentParser()
ap.add_argument('--input', required=True)
ap.add_argument('--output', required=True)
args = ap.parse_args()
nodes, edges = parse_story(args.input)
# Compute out-degree
outdeg = {nid: len(edges.get(nid, [])) for nid in nodes.keys()}
leaf_nodes = [nid for nid, deg in outdeg.items() if deg == 0]
endings = []
non_endings = []
for nid in sorted(leaf_nodes):
title = nodes.get(nid, '')
(endings if is_ending(nid, title) else non_endings).append((nid, title))
os.makedirs(os.path.dirname(args.output), exist_ok=True)
with open(args.output, 'w', encoding='utf-8') as f:
f.write('# 无后续(无 choices节点清单\n\n')
f.write(f'- 总节点数: {len(nodes)}\n')
f.write(f'- 无后续节点数: {len(leaf_nodes)}\n')
f.write(f'- 结局型: {len(endings)}\n')
f.write(f'- 可能未接续: {len(non_endings)}\n\n')
f.write('## 结局型Leaf Endings\n')
for nid, title in endings:
f.write(f'- {title} | id: `{nid}`\n')
f.write('\n')
f.write('## 可能未接续(需人工确认是否应有后续)\n')
for nid, title in non_endings:
f.write(f'- {title} | id: `{nid}`\n')
print(f"Wrote report to {args.output}. Endings={len(endings)} NonEndings={len(non_endings)}")
if __name__ == '__main__':
main()