使用Antlr4来解析P社游戏群星[stellaris]的科技树
发表于|更新于
|阅读量:
背景
群星更新了4.0,然后新版本的科技树不知道长什么样,科技树mod也坏了,就打算自己修一下,顺便学习一下Antlr4
方案
1. 解析游戏的配置文件
科技树的配置文件在common/technology/
目录下
本地化文件在localization/
目录下,这次只考虑中文
变量在common/scripted_variables
目录下
还有个scripted_trigger在common/scripted_triggers
目录下,这次没有用到
每个配置文件的配置项都可以看做是一个对象,对象中有赋值语句和条件计算语句,计划是在grammar中定义整个结构,这样可以在Visitor中读取到每个Property的数据
如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| technology_body : technology_body_start ( area | tier | category | icon | modifier | cost | cost_by_script | cost_per_level | is_rare | is_dangerous | weight | levels | prerequisites | technology_swap | potential | gateway | repeatable | weight_groups | mod_weight_if_group_picked | start_tech | is_reverse_engineerable | ai_update_type | is_insight | feature_flags | prereqfor_desc | weight_modifier | ai_weight | starting_potential)+ technology_body_end ; area: 'area' ASSIGN area_val; area_val: val; tier: 'tier' ASSIGN tier_val; tier_val: val; category: 'category' ASSIGN category_val ; category_val: LBRACE val RBRACE; icon: 'icon' ASSIGN icon_val; icon_val: val; cost: 'cost' ASSIGN cost_val; cost_val: val; ... 以下忽略
|
每个Property的val使用单独的rule来定义,这样可以方便在visitor中获取结构简单的值,减少visitor的复杂度
具体的grammar我放在下面的项目中的dev分支:
https://github.com/codexvn/antlr4-stellaris
2. 输出科技树
计划是通过mermaid来输出科技树,原因是语法比较简单
使用类图来进行展示
一方面有依赖属性
另一方面类的属性和方法可以和科技树的属性和解锁条件对应上
具体的流程放在以下的项目中的dev分支
https://github.com/codexvn/stellaris-parser
按照以下模版进行渲染,由于我不懂前端,所以这个模版使用的是chatgpt生成的,支持滚动和缩放
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
| <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Mermaid Graph - Left to Right with Zoom</title> <style> body { margin: 0; overflow: hidden; } #container { width: 100vw; height: 100vh; overflow: hidden; background: #f9f9f9; position: relative; } #zoom-area { width: 100%; height: 100%; transform-origin: 0 0; cursor: grab; } .mermaid { font-family: 'Segoe UI', sans-serif; } </style> </head> <body> <div id="container"> <div id="zoom-area" class="mermaid"> classDiagram direction LR <mermaid_content> </div> </div>
<script type="module"> import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs"; mermaid.initialize({ startOnLoad: true, maxEdges: 5000, maxTextSize: 1000000 }); </script>
<script> const zoomArea = document.getElementById("zoom-area"); let scale = 1; let originX = 0, originY = 0; let isDragging = false; let startX, startY;
document.addEventListener("wheel", function (e) { e.preventDefault(); const zoomFactor = 0.1; if (e.deltaY < 0) { scale *= (1 + zoomFactor); } else { scale *= (1 - zoomFactor); } zoomArea.style.transform = `translate(${originX}px, ${originY}px) scale(${scale})`; }, { passive: false });
zoomArea.addEventListener("mousedown", (e) => { isDragging = true; startX = e.clientX; startY = e.clientY; zoomArea.style.cursor = "grabbing"; });
document.addEventListener("mouseup", () => { isDragging = false; zoomArea.style.cursor = "grab"; });
document.addEventListener("mousemove", (e) => { if (!isDragging) return; originX += e.clientX - startX; originY += e.clientY - startY; startX = e.clientX; startY = e.clientY; zoomArea.style.transform = `translate(${originX}px, ${originY}px) scale(${scale})`; }); </script> </body> </html>
|
最终效果
点击以下链接
构建于20250518