Skip to content

Commit 377c7a1

Browse files
committed
1
1 parent 58e44e5 commit 377c7a1

File tree

8 files changed

+1327
-81
lines changed

8 files changed

+1327
-81
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,5 @@ $ uv run main.py <需要转换的lua文件的完整路径>
1818
(仅在Linux下测试通过)之后Lua文件所在的同一级目录下会出现与lua文件同名文件夹。
1919
里面的文件就是按照新版本重构过的代码。
2020

21-
> **Warning**
21+
> [!WARNING]
2222
> AI生成的内容并不一定靠谱!请自己再另外进行测试

main.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,6 @@ def main():
1010

1111
path = args[1]
1212
pkg = FkPackage(path)
13-
obj = pkg.skills['jilei']
14-
print(obj['content'])
15-
print(obj['translations'])
1613
pkg.mkSkillDir()
1714

1815
if __name__ == "__main__":

src/llm.py

Whitespace-only changes.

src/package.py

Lines changed: 169 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,144 @@
11
import os
22
import re
3+
from openai import OpenAI
4+
5+
class Skill:
6+
name: str
7+
contents = []
8+
translations = []
9+
def __init__(self, name, contents, trans):
10+
self.name = name
11+
self.contents = contents
12+
self.translations = trans
13+
14+
class GPT:
15+
basic_prompt: str # 用来指示技能基本架构如何进行改变的prompt
16+
misc_prompt: str
17+
askto_prompt: str # 用来杀askFor系列的prompt
18+
model = "qwen2.5:32b_72Kctx" # 用ollama创建的context window增大版的model
19+
20+
def __init__(self):
21+
dir_path = os.path.dirname(os.path.abspath(__file__))
22+
base_path = os.path.join(dir_path, "prompts")
23+
with open(os.path.join(base_path, "01-basic.md")) as f:
24+
self.basic_prompt = f.read()
25+
with open(os.path.join(base_path, "02-misc.md")) as f:
26+
self.misc_prompt = f.read()
27+
with open(os.path.join(base_path, "03-askto.md")) as f:
28+
self.askto_prompt = f.read()
29+
30+
# 根据自己需求修改key
31+
# 不过如果用的是公开厂商的API key,那么绝对不要公布出来!
32+
# 这里是我本地部署的ai所以我就对key随便处理了
33+
api_end_point = "http://localhost:8080/api"
34+
api_key = "sk-cb7ee040c840447ca35cc6e63c25bebf"
35+
self.client = OpenAI(api_key=api_key, base_url=api_end_point)
36+
37+
def chat(self, sys_prompt, skill) -> str:
38+
return self.client.chat.completions.create(
39+
model=self.model,
40+
messages=[
41+
{"role": "system", "content": sys_prompt},
42+
{"role": "user", "content": skill},
43+
],
44+
stream=False
45+
).choices[0].message.content
346

447
# 先就解决一下提取技能和提取武将的问题
548
class FkPackage:
6-
skills = {}
49+
skills: dict[str, Skill] = {}
750
generals = []
851
translations = {}
952

1053
def __init__(self, filepath: str):
54+
self.gpt = GPT()
1155
self.filepath = filepath
1256
with open(filepath, 'r', encoding='utf-8') as f:
1357
self.file_content = f.read()
1458
self._extract_translations()
1559
self._extract_skills()
16-
self._extract_generals()
60+
61+
'''
62+
直接检索文件中的Fk:loadTranslationTable { ... }
63+
花括号中的内容必定是形如 ["xxx"] = "xxx2", 之类的表形式
64+
并且必定不产生嵌套 只有string-string的键值对
65+
可能利用了lua的字符串拼接符号..进行多行字符串,亦可能使用单引号
66+
将提取到的翻译表内容翻译成python字典语法,然后直接读取该字典
67+
'''
68+
def _extract_translations(self):
69+
self.translations = {}
70+
# 查找Fk:loadTranslationTable的位置
71+
start_pattern = re.compile(r'Fk:loadTranslationTable\s*{', re.DOTALL)
72+
for match in start_pattern.finditer(self.file_content):
73+
start_pos = match.end()
74+
75+
# 提取直到匹配的闭合花括号的内容
76+
brace_count = 1
77+
current_pos = start_pos
78+
content = []
79+
while current_pos < len(self.file_content) and brace_count > 0:
80+
char = self.file_content[current_pos]
81+
if char == '{':
82+
brace_count += 1
83+
elif char == '}':
84+
brace_count -= 1
85+
if brace_count > 0:
86+
content.append(char)
87+
current_pos += 1
88+
table_content = ''.join(content)
89+
90+
# 去除注释
91+
cleaned_content = re.sub(r'--.*', '', table_content, flags=re.MULTILINE)
92+
93+
# 分割键值对
94+
entries = re.split(r',\s*', cleaned_content)
95+
96+
for entry in entries:
97+
entry = entry.strip()
98+
if not entry:
99+
continue
100+
101+
# 解析键
102+
key_match = re.match(r'\s*\[\s*(["\'])(.*?)\1\s*\]\s*=\s*(.*)', entry, re.DOTALL)
103+
if not key_match:
104+
continue
105+
quote_type, key_content, value_expr = key_match.groups()
106+
107+
# 处理键的转义
108+
key = self._unescape_lua_string(key_content)
109+
110+
# 解析值中的字符串
111+
value = self._parse_lua_value(value_expr)
112+
113+
if key and value is not None:
114+
self.translations[key] = value
115+
116+
def _unescape_lua_string(self, s):
117+
# 处理Lua字符串中的转义字符
118+
replacements = {
119+
'\\\\': '\\',
120+
'\\"': '"',
121+
"\\'": "'",
122+
'\\n': '\n',
123+
'\\t': '\t',
124+
'\\r': '\r',
125+
'\\b': '\b',
126+
'\\f': '\f',
127+
'\\a': '\a',
128+
'\\v': '\v'
129+
}
130+
for escaped, unescaped in replacements.items():
131+
s = s.replace(escaped, unescaped)
132+
return s
133+
134+
def _parse_lua_value(self, expr):
135+
# 解析Lua字符串拼接表达式
136+
str_pattern = re.compile(r'''["']((?:[^\\"']|\\.)*)["']''', re.DOTALL)
137+
parts = []
138+
for match in str_pattern.finditer(expr):
139+
str_content = match.group(1)
140+
parts.append(self._unescape_lua_string(str_content))
141+
return ''.join(parts) if parts else None
17142

18143
'''
19144
把东西作为字典放入自己的skills里面
@@ -34,14 +159,6 @@ def __init__(self, filepath: str):
34159
35160
最后加入数组
36161
'''
37-
def _extract_translations(self):
38-
# 提取所有翻译项
39-
trans_pattern = re.compile(r'\[["\'](.*?)["\']\]\s*=\s*["\'](.*?)["\']', re.DOTALL)
40-
self.translations = {}
41-
for match in trans_pattern.finditer(self.file_content):
42-
key, value = match.groups()
43-
self.translations[key] = value
44-
45162
def _extract_skills(self):
46163
self.skills = {}
47164
skill_pattern = re.compile(r'local\s+(\w+)\s*=\s*fk\.Create\w+Skill\s*\{', re.IGNORECASE)
@@ -58,10 +175,8 @@ def _extract_skills(self):
58175
level -= 1
59176
end_pos += 1
60177
content = self.file_content[start_pos:end_pos] # 提取完整技能内容
61-
self.skills[skill_name] = ({
62-
'content': content,
63-
'translations': [],
64-
})
178+
skill_str_name = re.compile(r'name = ["\'](.*?)["\'],').findall(content)[0]
179+
self.skills[skill_name] = Skill(skill_str_name, [content], [])
65180

66181
related_pattern = re.compile(r'(\w+):addRelatedSkill\(\s*(\w+)\s*\)')
67182
for match in related_pattern.finditer(self.file_content):
@@ -71,67 +186,29 @@ def _extract_skills(self):
71186
continue
72187
main_skill_obj = self.skills[main_skill]
73188
sub_skill_obj = self.skills[sub_skill]
74-
main_skill_obj['content'] += "\n\n"
75-
main_skill_obj['content'] += sub_skill_obj['content']
76-
main_skill_obj['content'] += "\n"
77-
main_skill_obj['content'] += match.group()
189+
main_skill_obj.contents.append(sub_skill_obj.contents[0])
78190
del self.skills[sub_skill]
79191

80192
for k in self.skills:
81193
sk = self.skills[k]
82-
content = sk['content']
83-
strings = re.findall(r'["\'](.*?)["\']', content, re.DOTALL)
194+
content = '\n'.join(sk.contents)
195+
strings: list[str] = re.findall(r'["\'](.*?)["\']', content, re.DOTALL)
196+
for i in range(0, len(strings)):
197+
if strings[i].endswith(':'):
198+
strings[i] = strings[i].rstrip(':')
199+
strings.append(sk.name)
200+
strings.append(":" + sk.name)
201+
strings.append("$" + sk.name)
202+
for i in range(1, 9):
203+
strings.append("$" + sk.name + str(i))
84204
# 收集翻译并去重
85205
translations = []
86206
seen = set()
87-
for s in set(strings):
207+
for s in strings:
88208
if s in self.translations and s not in seen:
89209
seen.add(s)
90210
translations.append({'key': s, 'value': self.translations[s]})
91-
sk['translations'] = translations
92-
93-
def _extract_generals(self):
94-
self.generals = []
95-
general_pattern = re.compile(r'local\s+(\w+)\s*=\s*General(?::new)?\s*\(', re.IGNORECASE)
96-
for match in general_pattern.finditer(self.file_content):
97-
general_name = match.group(1)
98-
# 定位参数括号位置
99-
general_match = match.group(0)
100-
paren_pos = general_match.rfind('(')
101-
start_paren_pos = match.start() + paren_pos
102-
level = 1
103-
end_paren_pos = start_paren_pos + 1
104-
while end_paren_pos < len(self.file_content) and level > 0:
105-
char = self.file_content[end_paren_pos]
106-
if char == '(':
107-
level += 1
108-
elif char == ')':
109-
level -= 1
110-
end_paren_pos += 1
111-
creation_content = self.file_content[match.start():end_paren_pos]
112-
# 收集关联调用
113-
related_lines = []
114-
line_pattern = re.compile(r'^\s*{}\b\s*[:.]'.format(re.escape(general_name)), re.MULTILINE)
115-
for line_match in line_pattern.finditer(self.file_content, end_paren_pos):
116-
line_start = line_match.start()
117-
line_end = self.file_content.find('\n', line_start)
118-
line = self.file_content[line_start:line_end if line_end != -1 else None].strip()
119-
related_lines.append(line)
120-
# 合并内容
121-
content = '\n'.join([creation_content] + related_lines)
122-
# 处理翻译
123-
strings = re.findall(r'["\'](.*?)["\']', content, re.DOTALL)
124-
translations = []
125-
seen = set()
126-
for s in set(strings):
127-
if s in self.translations and s not in seen:
128-
seen.add(s)
129-
translations.append({'key': s, 'value': self.translations[s]})
130-
self.generals.append({
131-
'name': general_name,
132-
'content': content,
133-
'translations': translations
134-
})
211+
sk.translations = translations
135212

136213
def mkSkillDir(self):
137214
"""创建技能文件目录结构并写入技能内容"""
@@ -143,15 +220,41 @@ def mkSkillDir(self):
143220
target_dir = os.path.join(dir_path, "pkg", base_name, "skills")
144221
os.makedirs(target_dir, exist_ok=True)
145222

223+
i = 0
224+
n = len(self.skills.keys())
225+
146226
# 写入技能文件
147227
for skill_name in self.skills:
228+
i += 1
229+
print("[*] 正在处理第 %d/%d 个技能:%s" % (i, n, skill_name))
148230
skill = self.skills[skill_name]
149231
# 清理非法文件名字符
150232
safe_name = re.sub(r'[\\/*?:"<>|]', '_', skill_name)
151233
file_path = os.path.join(target_dir, f"{safe_name}.lua")
152234

153235
try:
154236
with open(file_path, 'w', encoding='utf-8') as f:
155-
f.write(skill['content'])
237+
f.write(self._refactor_skill(skill))
238+
156239
except Exception as e:
157240
print(f"写入技能文件失败: {file_path} | 错误: {str(e)}")
241+
242+
def _refactor_skill(self, skillObj: Skill) -> str:
243+
content = '\n\n'.join(skillObj.contents)
244+
content += "Fk:loadTranslationTable{\n"
245+
for kv in skillObj.translations:
246+
content += " [%s] = %s,\n" % (repr(kv['key']), repr(kv['value']))
247+
content += "}\n"
248+
gpt = self.gpt
249+
n = 2
250+
251+
print (" [1/%d] 将技能主体结构调整为新版" % n)
252+
content = gpt.chat(gpt.basic_prompt, content)
253+
print (" [2/%d] 调整self.name,cost_data等" % n)
254+
content = gpt.chat(gpt.misc_prompt, content)
255+
print (" [3/%d] 重构askFor系列函数" % n)
256+
content = gpt.chat(gpt.askto_prompt, content)
257+
258+
content = content.replace(' ', ' ')
259+
260+
return content

0 commit comments

Comments
 (0)