Skip to content

Commit ce125f9

Browse files
committed
add rune reconstruction vibe coding demo
1 parent fb7b07a commit ce125f9

47 files changed

Lines changed: 3050 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

program/rune_reconstruction_vibe_demo/demo.html

Lines changed: 527 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
const fs = require('fs');
2+
let content = fs.readFileSync('src/js/logic.js', 'utf8');
3+
4+
// I'll just find the exact block and replace it correctly.
5+
content = content.replace(/ \}\);\n \}\);\n\n function traverse/g, " });\n\n function traverse");
6+
7+
fs.writeFileSync('src/js/logic.js', content, 'utf8');
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
const fs = require('fs');
2+
let content = fs.readFileSync('src/js/logic.js', 'utf8');
3+
4+
content = content.replace(
5+
/let connectsToSelf = false;\n\s*const effectOuts = state\.conns\.filter\(c => c\.fromId === currNode\.instId\);\n\s*effectOuts\.forEach\(c => \{\n\s*const n = state\.nodes\.find\(node => node\.instId === c\.toId\);\n\s*if \(n && n\.id === 'n_self'\) connectsToSelf = true;\n\s*\}\);\n\s*activeSpells\.push\(\{ \.\.\.currentContext, connectsToSelf \}\);/,
6+
`let connectsToSelf = false;
7+
const effectOuts = state.conns.filter(c => c.fromId === currNode.instId);
8+
effectOuts.forEach(c => {
9+
const n = state.nodes.find(node => node.instId === c.toId);
10+
if (n && n.id === 'n_self') connectsToSelf = true;
11+
});
12+
13+
if (!connectsToSelf) continue;
14+
15+
activeSpells.push({ ...currentContext, connectsToSelf });`
16+
);
17+
18+
fs.writeFileSync('src/js/logic.js', content, 'utf8');
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<!DOCTYPE html>
2+
<html lang="zh-CN">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>符文重构 (Rune Reconstruction)</title>
7+
<link rel="stylesheet" href="src/css/style.css">
8+
</head>
9+
<body>
10+
11+
<div id="game-container">
12+
<canvas id="gameCanvas"></canvas>
13+
14+
<div id="ui-layer">
15+
<div id="top-bar">
16+
<div style="min-width: 80px; font-weight: bold;">LV.<span id="txt-lv">1</span></div>
17+
<div id="hp-container" style="width: 150px; height: 8px; background: #400; border-radius: 4px; overflow: hidden; margin-right: 20px;">
18+
<div id="hp-bar" style="width: 100%; height: 100%; background: #f33; box-shadow: 0 0 10px #f33; transition: width 0.1s;"></div>
19+
</div>
20+
<div id="exp-container"><div id="exp-bar"></div></div>
21+
<div style="min-width: 120px; text-align: right; color: var(--accent);" id="txt-dmg">15</div>
22+
</div>
23+
24+
<!-- 进化选择界面 -->
25+
<div id="choice-panel">
26+
<div style="text-align: center; margin-bottom: 50px;">
27+
<h1 style="color: var(--accent); letter-spacing: 10px;">符文进化</h1>
28+
<p style="color: #666;">选择一个逻辑节点合并至你的法术架构</p>
29+
</div>
30+
<div class="choice-container" id="choice-list"></div>
31+
</div>
32+
33+
<!-- 节点重构编辑器 -->
34+
<div id="node-editor">
35+
<div id="editor-header">
36+
<div style="font-size: 24px; color: var(--accent); font-weight: bold;">架构解析器 / STRUCTURE PARSER</div>
37+
<button id="btn-close">同步并锁定架构</button>
38+
</div>
39+
<div id="inventory-panel" style="position: absolute; right: 0; top: 71px; width: 250px; height: calc(100% - 71px); background: #111; border-left: 1px solid #333; overflow-y: auto; z-index: 60; padding: 10px;">
40+
<div style="color: var(--accent); font-weight: bold; margin-bottom: 10px; border-bottom: 1px solid #333; padding-bottom: 5px;">节点储存库 / NODE REPOSITORY</div>
41+
<div id="inventory-list" style="display: flex; flex-direction: column; gap: 10px;"></div>
42+
</div>
43+
<div id="editor-main" style="width: calc(100% - 250px); overflow: hidden; position: relative; background-color: #222;">
44+
<div id="editor-canvas" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; transform-origin: 0 0;">
45+
<svg id="connections-svg" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; overflow: visible;"></svg>
46+
</div>
47+
</div>
48+
<div style="padding: 10px; text-align: center; background: #000; font-size: 12px; color: #444;">
49+
[提示] 点击输出端口并拖动到输入端口进行连接 | 右键点击连线断开 | 右键点击节点可移除
50+
</div>
51+
</div>
52+
</div>
53+
</div>
54+
55+
<script type="module">
56+
import { init } from './src/js/game.js';
57+
init();
58+
</script>
59+
</body>
60+
</html>
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
const fs = require('fs');
2+
let content = fs.readFileSync('src/js/editor.js', 'utf8');
3+
4+
content = content.replace(
5+
/function drawCurve\(x1, y1, x2, y2, color, connIdx\) \{[\s\S]*?svgLayer\.appendChild\(path\);\n\}/,
6+
`function drawCurve(x1, y1, x2, y2, color, connIdx) {
7+
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
8+
const d = \\\`M \\\${x1} \\\${y1} C \\\${x1 + 60} \\\${y1}, \\\${x2 - 60} \\\${y2}, \\\${x2} \\\${y2}\\\`;
9+
path.setAttribute('d', d);
10+
path.setAttribute('stroke', color);
11+
path.setAttribute('stroke-width', '3');
12+
path.setAttribute('fill', 'none');
13+
path.dataset.idx = connIdx;
14+
15+
if (connIdx !== -1) {
16+
path.style.cursor = 'pointer';
17+
path.oncontextmenu = (e) => {
18+
e.preventDefault();
19+
state.conns.splice(connIdx, 1);
20+
renderConnections();
21+
};
22+
path.onmouseover = () => path.setAttribute('stroke', '#ff3366');
23+
path.onmouseout = () => path.setAttribute('stroke', color);
24+
}
25+
26+
svgLayer.appendChild(path);
27+
}`
28+
);
29+
30+
fs.writeFileSync('src/js/editor.js', content, 'utf8');
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
const fs = require('fs');
2+
let content = fs.readFileSync('src/js/game.js', 'utf8');
3+
4+
content = content.replace(
5+
/area: s\.area \* 3\.0, gravity: true, \n\s*\}\);/,
6+
`area: s.area * 3.0, gravity: true, isSelf: s.connectsToSelf, payloads: s.payloads || []\n });`
7+
);
8+
9+
fs.writeFileSync('src/js/game.js', content, 'utf8');
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
const fs = require('fs');
2+
let content = fs.readFileSync('src/js/config.js', 'utf8');
3+
4+
// Update n_self to in: 1, out: 0
5+
content = content.replace(
6+
/\{ id: 'n_self', type: 'ENTITY', name: '', desc: '', in: 1, out: 1 \},/,
7+
"{ id: 'n_self', type: 'ENTITY', name: '自身', desc: '实体锚点。赋予连接至此的效果实体属性。', in: 1, out: 0 },"
8+
);
9+
10+
// Update all TRIGGERs to in: 0, out: 1
11+
content = content.replace(
12+
/\{ id: '(t_\w+)', type: 'TRIGGER', name: '([^']+)', desc: '([^']+)', in: 1, out: 1 \}/g,
13+
"{ id: '$1', type: 'TRIGGER', name: '$2', desc: '$3', in: 0, out: 1 }"
14+
);
15+
16+
// Update INITIAL_NODES positions
17+
content = content.replace(/export const INITIAL_NODES = \[([\s\S]*?)\];/, `export const INITIAL_NODES = [
18+
{ protoIdx: 1, instId: 'init-t', x: 50, y: 150 }, // t_pulsar
19+
{ protoIdx: 6, instId: 'init-a', x: 250, y: 150 }, // o_aim
20+
{ protoIdx: 20, instId: 'init-e', x: 450, y: 150 }, // e_photon
21+
{ protoIdx: 0, instId: 'init-self', x: 650, y: 150 } // n_self
22+
];`);
23+
24+
// Update INITIAL_CONNS
25+
content = content.replace(/export const INITIAL_CONNS = \[([\s\S]*?)\];/, `export const INITIAL_CONNS = [
26+
{ fromId: 'init-t', fromPort: 0, toId: 'init-a', toPort: 0 },
27+
{ fromId: 'init-a', fromPort: 0, toId: 'init-e', toPort: 0 },
28+
{ fromId: 'init-e', fromPort: 0, toId: 'init-self', toPort: 0 }
29+
];`);
30+
31+
fs.writeFileSync('src/js/config.js', content, 'utf8');
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
const fs = require('fs');
2+
let content = fs.readFileSync('src/js/config.js', 'utf8');
3+
4+
content = content.replace(
5+
/export const INITIAL_CONNS = \[\n\s*\{ fromId: 'init-t', fromPort: 0, toId: 'init-a', toPort: 0 \},\n\s*\{ fromId: 'init-a', fromPort: 0, toId: 'init-e', toPort: 0 \},\n\s*\{ fromId: 'init-e', fromPort: 0, toId: 'init-self', toPort: 0 \}\n\];/,
6+
`export const INITIAL_CONNS = [
7+
{ fromId: 'init-t', fromPort: 0, toId: 'init-a', toPort: 0 },
8+
{ fromId: 'init-a', fromPort: 0, toId: 'init-e', toPort: 0 }
9+
];`
10+
);
11+
12+
fs.writeFileSync('src/js/config.js', content, 'utf8');
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
const fs = require('fs');
2+
let content = fs.readFileSync('src/js/config.js', 'utf8');
3+
4+
content = content.replace(
5+
/export const INITIAL_CONNS = \[\n\s*\{ fromId: 'init-t', fromPort: 0, toId: 'init-a', toPort: 0 \},\n\s*\{ fromId: 'init-a', fromPort: 0, toId: 'init-e', toPort: 0 \}\n\];/,
6+
`export const INITIAL_CONNS = [
7+
{ fromId: 'init-t', fromPort: 0, toId: 'init-a', toPort: 0 },
8+
{ fromId: 'init-a', fromPort: 0, toId: 'init-e', toPort: 0 },
9+
{ fromId: 'init-e', fromPort: 0, toId: 'init-self', toPort: 0 }
10+
];`
11+
);
12+
13+
fs.writeFileSync('src/js/config.js', content, 'utf8');
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
const fs = require('fs');
2+
let content = fs.readFileSync('src/js/game.js', 'utf8');
3+
4+
const targetStr = ` // GC
5+
state.entities = state.entities.filter(e => (e.life > 0 && e.hp > 0) || e.type === 'player');`;
6+
7+
const replaceStr = ` // Payload On-Death Triggers
8+
state.entities.forEach(e => {
9+
if (!e.isDead && (e.hp <= 0 || e.life <= 0) && e.type !== 'player') {
10+
e.isDead = true;
11+
if (e.payloads && e.payloads.length > 0) {
12+
const nextPayloads = [...e.payloads];
13+
const activePayload = nextPayloads.pop();
14+
15+
const spell = {
16+
...e,
17+
...activePayload,
18+
payloads: nextPayloads,
19+
connectsToSelf: e.isSelf
20+
};
21+
22+
// Slightly randomize angle if there's no inherent autoAim or split?
23+
// Actually fireSpells handles angle distribution if split > 1
24+
fireSpells([spell], e);
25+
}
26+
}
27+
});
28+
29+
// GC
30+
state.entities = state.entities.filter(e => (e.life > 0 && e.hp > 0) || e.type === 'player');`;
31+
32+
content = content.replace(targetStr, replaceStr);
33+
fs.writeFileSync('src/js/game.js', content, 'utf8');

0 commit comments

Comments
 (0)