redscript-mc 1.2.29 → 1.2.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -28
- package/README.zh.md +28 -28
- package/demo.gif +0 -0
- package/dist/lexer/index.js +9 -1
- package/dist/lowering/index.js +17 -2
- package/editors/vscode/package-lock.json +3 -3
- package/editors/vscode/package.json +1 -1
- package/examples/readme-demo.mcrs +44 -66
- package/package.json +1 -1
- package/src/lexer/index.ts +9 -1
- package/src/lowering/index.ts +20 -4
package/README.md
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
<img src="./logo.png" alt="RedScript Logo" width="64" />
|
|
4
4
|
|
|
5
|
-
<img src="https://img.shields.io/badge/RedScript-1.2.
|
|
5
|
+
<img src="https://img.shields.io/badge/RedScript-1.2.29-red?style=for-the-badge&logo=minecraft&logoColor=white" alt="RedScript" />
|
|
6
6
|
|
|
7
7
|
**A typed scripting language that compiles to Minecraft datapacks.**
|
|
8
8
|
|
|
9
9
|
Write clean game logic. RedScript handles the scoreboard spaghetti.
|
|
10
10
|
|
|
11
11
|
[](https://github.com/bkmashiro/redscript/actions/workflows/ci.yml)
|
|
12
|
-
[](https://github.com/bkmashiro/redscript)
|
|
13
13
|
[](https://www.npmjs.com/package/redscript-mc)
|
|
14
14
|
[](https://www.npmjs.com/package/redscript-mc)
|
|
15
15
|
[](https://marketplace.visualstudio.com/items?itemName=bkmashiro.redscript-vscode)
|
|
@@ -20,9 +20,11 @@ Write clean game logic. RedScript handles the scoreboard spaghetti.
|
|
|
20
20
|
|
|
21
21
|
### 🚀 [Try it online — no install needed!](https://redscript-ide.pages.dev)
|
|
22
22
|
|
|
23
|
-
<img src="./demo.gif" alt="RedScript Demo" width="
|
|
23
|
+
<img src="./demo.gif" alt="RedScript Demo — math curves drawn with particles in Minecraft" width="520" />
|
|
24
24
|
|
|
25
|
-
*↑
|
|
25
|
+
*↑ Five mathematical curves rendered in real-time with particles — 100% vanilla, no mods!*
|
|
26
|
+
*`y = x·sin(x)` · `y = sin(x) + ½sin(2x)` · `y = e⁻ˣsin(4x)` · `y = tanh(2x)` · `r = cos(2θ)` rose curve*
|
|
27
|
+
*Each curve is computed tick-by-tick using RedScript's fixed-point math stdlib.*
|
|
26
28
|
|
|
27
29
|
</div>
|
|
28
30
|
|
|
@@ -32,34 +34,33 @@ Write clean game logic. RedScript handles the scoreboard spaghetti.
|
|
|
32
34
|
|
|
33
35
|
RedScript is a typed scripting language that compiles to vanilla Minecraft datapacks. Write clean code with variables, functions, loops, and events — RedScript handles the scoreboard commands and `.mcfunction` files for you.
|
|
34
36
|
|
|
35
|
-
**The demo above?**
|
|
37
|
+
**The demo above?** Five math curves drawn with 64 sample points each. The core logic:
|
|
36
38
|
|
|
37
39
|
```rs
|
|
38
|
-
|
|
39
|
-
let running: bool = false;
|
|
40
|
-
|
|
41
|
-
@tick fn demo_tick() {
|
|
42
|
-
if (!running) { return; }
|
|
43
|
-
counter = counter + 1;
|
|
44
|
-
|
|
45
|
-
foreach (p in @a) at @s {
|
|
46
|
-
particle("minecraft:end_rod", ~0, ~1, ~0, 0.5, 0.5, 0.5, 0.1, 5);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (counter % 20 == 0) {
|
|
50
|
-
say(f"Running for {counter} ticks");
|
|
51
|
-
}
|
|
52
|
-
}
|
|
40
|
+
import "stdlib/math.mcrs"
|
|
53
41
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
42
|
+
let phase: int = 0;
|
|
43
|
+
let frame: int = 0;
|
|
44
|
+
|
|
45
|
+
// 5 curves cycle every 128 ticks (~6.5 s each)
|
|
46
|
+
@tick fn _wave_tick() {
|
|
47
|
+
phase = (phase + 4) % 360;
|
|
48
|
+
frame = frame + 1;
|
|
49
|
+
|
|
50
|
+
let curve_id: int = (frame / 128) % 5;
|
|
51
|
+
|
|
52
|
+
// Compute sin at 9 column offsets (40° apart = full sine wave span)
|
|
53
|
+
let s0: int = sin_fixed((phase + 0) % 360);
|
|
54
|
+
let s1: int = sin_fixed((phase + 40) % 360);
|
|
55
|
+
// ... s2 through s8 ...
|
|
56
|
+
|
|
57
|
+
// Draw bar chart: each column height = sin value
|
|
58
|
+
// (64 fixed particle positions per curve, all respawned each tick)
|
|
59
|
+
if (curve_id == 0) { _draw_xsinx(); }
|
|
60
|
+
if (curve_id == 1) { _draw_harmonic(); }
|
|
61
|
+
// ...
|
|
59
62
|
|
|
60
|
-
|
|
61
|
-
running = false;
|
|
62
|
-
say(f"Demo stopped at {counter} ticks.");
|
|
63
|
+
actionbar(@a, f"§e y = x·sin(x) phase: {phase}° center: {s0}‰");
|
|
63
64
|
}
|
|
64
65
|
```
|
|
65
66
|
|
package/README.zh.md
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
<img src="./logo.png" alt="RedScript Logo" width="64" />
|
|
4
4
|
|
|
5
|
-
<img src="https://img.shields.io/badge/RedScript-1.2.
|
|
5
|
+
<img src="https://img.shields.io/badge/RedScript-1.2.29-red?style=for-the-badge&logo=minecraft&logoColor=white" alt="RedScript" />
|
|
6
6
|
|
|
7
7
|
**一个编译到 Minecraft Datapack 的类型化脚本语言。**
|
|
8
8
|
|
|
9
9
|
写干净的游戏逻辑,把记分板的面条代码交给 RedScript 处理。
|
|
10
10
|
|
|
11
11
|
[](https://github.com/bkmashiro/redscript/actions/workflows/ci.yml)
|
|
12
|
-
[](https://github.com/bkmashiro/redscript)
|
|
13
13
|
[](https://www.npmjs.com/package/redscript-mc)
|
|
14
14
|
[](https://www.npmjs.com/package/redscript-mc)
|
|
15
15
|
[](https://marketplace.visualstudio.com/items?itemName=bkmashiro.redscript-vscode)
|
|
@@ -20,9 +20,11 @@
|
|
|
20
20
|
|
|
21
21
|
### 🚀 [在线试用 — 无需安装!](https://redscript-ide.pages.dev)
|
|
22
22
|
|
|
23
|
-
<img src="./demo.gif" alt="RedScript Demo" width="
|
|
23
|
+
<img src="./demo.gif" alt="RedScript Demo — 用粒子在 Minecraft 中绘制数学曲线" width="520" />
|
|
24
24
|
|
|
25
|
-
*↑
|
|
25
|
+
*↑ 五条数学函数图像实时粒子渲染 — 纯原版,无需 MOD!*
|
|
26
|
+
*`y = x·sin(x)` · `y = sin(x) + ½sin(2x)` · `y = e⁻ˣsin(4x)` · `y = tanh(2x)` · 玫瑰曲线 `r = cos(2θ)`*
|
|
27
|
+
*每条曲线由 RedScript 定点数学库逐 tick 计算,64 个采样点动态绘制。*
|
|
26
28
|
|
|
27
29
|
</div>
|
|
28
30
|
|
|
@@ -32,34 +34,32 @@
|
|
|
32
34
|
|
|
33
35
|
RedScript 是一门编译到原版 Minecraft 数据包的脚本语言。用变量、函数、循环、事件写代码,RedScript 帮你生成记分板命令和 `.mcfunction` 文件。
|
|
34
36
|
|
|
35
|
-
**上面的演示?**
|
|
37
|
+
**上面的演示?** 五条数学曲线,每条 64 个采样点,核心逻辑:
|
|
36
38
|
|
|
37
39
|
```rs
|
|
38
|
-
|
|
39
|
-
let running: bool = false;
|
|
40
|
-
|
|
41
|
-
@tick fn demo_tick() {
|
|
42
|
-
if (!running) { return; }
|
|
43
|
-
counter = counter + 1;
|
|
44
|
-
|
|
45
|
-
foreach (p in @a) at @s {
|
|
46
|
-
particle("minecraft:end_rod", ~0, ~1, ~0, 0.5, 0.5, 0.5, 0.1, 5);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (counter % 20 == 0) {
|
|
50
|
-
say(f"已运行 {counter} ticks");
|
|
51
|
-
}
|
|
52
|
-
}
|
|
40
|
+
import "stdlib/math.mcrs"
|
|
53
41
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
42
|
+
let phase: int = 0;
|
|
43
|
+
let frame: int = 0;
|
|
44
|
+
|
|
45
|
+
// 5 条曲线每 128 tick (~6.5 秒) 自动切换
|
|
46
|
+
@tick fn _wave_tick() {
|
|
47
|
+
phase = (phase + 4) % 360;
|
|
48
|
+
frame = frame + 1;
|
|
49
|
+
|
|
50
|
+
let curve_id: int = (frame / 128) % 5;
|
|
51
|
+
|
|
52
|
+
// 计算 9 列的 sin 值(每列相差 40°,刚好覆盖完整周期)
|
|
53
|
+
let s0: int = sin_fixed((phase + 0) % 360);
|
|
54
|
+
let s1: int = sin_fixed((phase + 40) % 360);
|
|
55
|
+
// ... s2 到 s8 ...
|
|
56
|
+
|
|
57
|
+
// 绘制图像:每条曲线有 64 个固定坐标粒子,每 tick 全部重绘
|
|
58
|
+
if (curve_id == 0) { _draw_xsinx(); }
|
|
59
|
+
if (curve_id == 1) { _draw_harmonic(); }
|
|
60
|
+
// ...
|
|
59
61
|
|
|
60
|
-
@
|
|
61
|
-
running = false;
|
|
62
|
-
say(f"Demo 已停止,共运行 {counter} ticks。");
|
|
62
|
+
actionbar(@a, f"§e y = x·sin(x) phase: {phase}° center: {s0}‰");
|
|
63
63
|
}
|
|
64
64
|
```
|
|
65
65
|
|
package/demo.gif
CHANGED
|
Binary file
|
package/dist/lexer/index.js
CHANGED
|
@@ -236,9 +236,17 @@ class Lexer {
|
|
|
236
236
|
this.addToken('rel_coord', value, startLine, startCol);
|
|
237
237
|
return;
|
|
238
238
|
}
|
|
239
|
-
// Local coordinate: ^ or ^5 or ^-3 or ^0.5
|
|
239
|
+
// Local coordinate: ^ or ^5 or ^-3 or ^0.5 or ^varname (macro variable)
|
|
240
240
|
if (char === '^') {
|
|
241
241
|
let value = '^';
|
|
242
|
+
// Check for identifier (variable name for macro substitution, e.g. ^px, ^height)
|
|
243
|
+
if (/[a-zA-Z_]/.test(this.peek())) {
|
|
244
|
+
while (/[a-zA-Z0-9_]/.test(this.peek())) {
|
|
245
|
+
value += this.advance();
|
|
246
|
+
}
|
|
247
|
+
this.addToken('local_coord', value, startLine, startCol);
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
242
250
|
// Check for optional sign
|
|
243
251
|
if (this.peek() === '-' || this.peek() === '+') {
|
|
244
252
|
value += this.advance();
|
package/dist/lowering/index.js
CHANGED
|
@@ -495,16 +495,31 @@ class Lowering {
|
|
|
495
495
|
}
|
|
496
496
|
}
|
|
497
497
|
// Set up NBT storage for each macro param
|
|
498
|
+
// float-typed params are stored as `double 0.01` so that an integer value N
|
|
499
|
+
// becomes N/100.0 in the command (e.g. scoreboard value 975 → NBT 9.75d → ^9.75)
|
|
498
500
|
for (const macroParam of macroParamNames) {
|
|
499
501
|
const paramIdx = params.findIndex(p => p.name === macroParam);
|
|
500
502
|
if (paramIdx < 0 || paramIdx >= loweredArgs.length)
|
|
501
503
|
continue;
|
|
502
504
|
const operand = loweredArgs[paramIdx];
|
|
505
|
+
const paramType = params[paramIdx]?.type;
|
|
506
|
+
const isFloat = paramType?.kind === 'named' && paramType.name === 'float';
|
|
503
507
|
if (operand.kind === 'const') {
|
|
504
|
-
|
|
508
|
+
if (isFloat) {
|
|
509
|
+
const floatVal = (operand.value / 100).toFixed(6);
|
|
510
|
+
this.builder.emitRaw(`data modify storage rs:macro_args ${macroParam} set value ${floatVal}d`);
|
|
511
|
+
}
|
|
512
|
+
else {
|
|
513
|
+
this.builder.emitRaw(`data modify storage rs:macro_args ${macroParam} set value ${operand.value}`);
|
|
514
|
+
}
|
|
505
515
|
}
|
|
506
516
|
else if (operand.kind === 'var') {
|
|
507
|
-
|
|
517
|
+
if (isFloat) {
|
|
518
|
+
this.builder.emitRaw(`execute store result storage rs:macro_args ${macroParam} double 0.01 run scoreboard players get ${operand.name} ${exports.LOWERING_OBJ}`);
|
|
519
|
+
}
|
|
520
|
+
else {
|
|
521
|
+
this.builder.emitRaw(`execute store result storage rs:macro_args ${macroParam} int 1 run scoreboard players get ${operand.name} ${exports.LOWERING_OBJ}`);
|
|
522
|
+
}
|
|
508
523
|
}
|
|
509
524
|
}
|
|
510
525
|
// Call with macro storage
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "redscript-vscode",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "redscript-vscode",
|
|
9
|
-
"version": "1.0
|
|
9
|
+
"version": "1.2.0",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"redscript": "file:../../"
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
},
|
|
24
24
|
"../..": {
|
|
25
25
|
"name": "redscript-mc",
|
|
26
|
-
"version": "1.2.
|
|
26
|
+
"version": "1.2.29",
|
|
27
27
|
"license": "MIT",
|
|
28
28
|
"bin": {
|
|
29
29
|
"redscript": "dist/cli.js",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "redscript-vscode",
|
|
3
3
|
"displayName": "RedScript for Minecraft",
|
|
4
4
|
"description": "Syntax highlighting, error diagnostics, and language support for RedScript — a compiler targeting Minecraft Java Edition",
|
|
5
|
-
"version": "1.0
|
|
5
|
+
"version": "1.2.0",
|
|
6
6
|
"publisher": "bkmashiro",
|
|
7
7
|
"icon": "icon.png",
|
|
8
8
|
"license": "MIT",
|
|
@@ -1,92 +1,70 @@
|
|
|
1
|
-
// readme-demo.mcrs —
|
|
2
|
-
//
|
|
1
|
+
// readme-demo.mcrs — real-time 2D sine wave
|
|
2
|
+
// Uses sin_fixed() to compute y = sin(x + t) at runtime.
|
|
3
|
+
// _draw(px, py) is a macro function: float params are stored as double×0.01
|
|
4
|
+
// so an integer value N → coordinate N/100 blocks (e.g. 975 → 9.75 blocks).
|
|
5
|
+
// No lookup tables. No precomputed positions. Pure runtime computation.
|
|
3
6
|
//
|
|
4
|
-
// Compile:
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
+
// Compile:
|
|
8
|
+
// node dist/cli.js compile examples/readme-demo.mcrs \
|
|
9
|
+
// -o <out> --namespace rsdemo
|
|
10
|
+
// In-game:
|
|
11
|
+
// /function rsdemo:start (face forward, stand ~10 blocks back)
|
|
12
|
+
// /function rsdemo:stop
|
|
7
13
|
|
|
8
14
|
import "../src/stdlib/math.mcrs"
|
|
9
15
|
|
|
10
|
-
let
|
|
11
|
-
let
|
|
16
|
+
let phase: int = 0;
|
|
17
|
+
let t: int = 0;
|
|
12
18
|
let on: bool = false;
|
|
13
19
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
20
|
+
// Macro function: place one particle at (^px/100, ^py/100, ^5).
|
|
21
|
+
// RedScript stores float params as `double 0.01` in rs:macro_args,
|
|
22
|
+
// so integer values become sub-block coordinates at call time.
|
|
23
|
+
fn _draw(px: float, py: float) {
|
|
24
|
+
particle("minecraft:end_rod", ^px, ^py, ^5, 0.02, 0.02, 0.02, 0.0, 10);
|
|
17
25
|
}
|
|
18
26
|
|
|
27
|
+
@load fn _init() { phase = 0; t = 0; }
|
|
28
|
+
|
|
19
29
|
@tick fn _wave_tick() {
|
|
20
30
|
if (!on) { return; }
|
|
21
31
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
let s: int = sin_fixed(t); // -1000 .. 1000
|
|
26
|
-
let c: int = cos_fixed(t); // -1000 .. 1000
|
|
27
|
-
let mag: int = abs(s); // 0 .. 1000
|
|
32
|
+
// Advance: 4 new points this tick, phase scrolls at 1°/tick
|
|
33
|
+
t = (t + 4) % 40;
|
|
34
|
+
phase = (phase + 1) % 360;
|
|
28
35
|
|
|
29
|
-
// ── Particle rings: spread values scale with sin(t) ─────────────────
|
|
30
|
-
// Inner ring — always visible, small tight shimmer
|
|
31
36
|
foreach (p in @a) at @s {
|
|
32
|
-
|
|
33
|
-
|
|
37
|
+
// Draw 4 consecutive points (loop-unrolled)
|
|
38
|
+
// px: index 0..39 → -975..975 (÷100 = -9.75..9.75 blocks)
|
|
39
|
+
// py: sin_fixed → -1000..1000 → ÷4 → -250..250 (÷100 = -2.5..2.5 blocks)
|
|
34
40
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
particle("minecraft:end_rod", ~0, ~1, ~0, 0.9, 0.25, 0.9, 0.02, 12);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
+
let i0: int = t;
|
|
42
|
+
let s0: int = sin_fixed((i0 * 9 + phase) % 360);
|
|
43
|
+
_draw(i0 * 50 - 975, s0 / 4);
|
|
41
44
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
particle("minecraft:end_rod", ~0, ~1, ~0, 1.8, 0.4, 1.8, 0.02, 18);
|
|
46
|
-
particle("minecraft:soul_fire_flame", ~0, ~2, ~0, 0.9, 0.3, 0.9, 0.03, 8);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
45
|
+
let i1: int = (t + 1) % 40;
|
|
46
|
+
let s1: int = sin_fixed((i1 * 9 + phase) % 360);
|
|
47
|
+
_draw(i1 * 50 - 975, s1 / 4);
|
|
49
48
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
particle("minecraft:flash", ~0, ~3, ~0, 0.4, 0.3, 0.4, 0.1, 3);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
49
|
+
let i2: int = (t + 2) % 40;
|
|
50
|
+
let s2: int = sin_fixed((i2 * 9 + phase) % 360);
|
|
51
|
+
_draw(i2 * 50 - 975, s2 / 4);
|
|
56
52
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
particle("minecraft:portal", ~0, ~0.5, ~0, 1.2, 0.4, 1.2, 0.05, 12);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// ── Actionbar: live math rotating every 3 s ──────────────────────────
|
|
65
|
-
let seg: int = (frame / 45) % 4;
|
|
66
|
-
if (seg == 0) {
|
|
67
|
-
actionbar(@a, f"§3⚡ §bsin({t}°) = §e{s}‰ cos({t}°) = §e{c}‰");
|
|
68
|
-
}
|
|
69
|
-
if (seg == 1) {
|
|
70
|
-
actionbar(@a, f"§3⚡ §bsmoothstep(|sin|) = §e{smoothstep(0, 1000, mag)}‰ isqrt({frame}) = §e{isqrt(frame)}");
|
|
71
|
-
}
|
|
72
|
-
if (seg == 2) {
|
|
73
|
-
actionbar(@a, f"§3⚡ §bgcd(360, {t + 1}) = §e{gcd(360, t + 1)} log2({mag + 1}) = §e{log2_int(mag + 1)}");
|
|
74
|
-
}
|
|
75
|
-
if (seg == 3) {
|
|
76
|
-
actionbar(@a, f"§3⚡ §bpow(2, {(frame / 30) % 10 + 1}) = §e{pow_int(2, (frame / 30) % 10 + 1)} clamp(sin) = §e{clamp(s, -500, 500)}");
|
|
53
|
+
let i3: int = (t + 3) % 40;
|
|
54
|
+
let s3: int = sin_fixed((i3 * 9 + phase) % 360);
|
|
55
|
+
_draw(i3 * 50 - 975, s3 / 4);
|
|
77
56
|
}
|
|
78
57
|
}
|
|
79
58
|
|
|
80
59
|
fn start() {
|
|
81
|
-
on
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
title(@a,
|
|
85
|
-
say("§a▶
|
|
60
|
+
on = true;
|
|
61
|
+
phase = 0;
|
|
62
|
+
t = 0;
|
|
63
|
+
title(@a, "§e y = sin(x + t)", "§7sin_fixed · macro · real-time");
|
|
64
|
+
say("§a▶ sine wave — face forward, stand back ~10 blocks");
|
|
86
65
|
}
|
|
87
66
|
|
|
88
67
|
fn stop() {
|
|
89
68
|
on = false;
|
|
90
|
-
|
|
91
|
-
say("§c■ readme-demo stopped");
|
|
69
|
+
say("§c■ stopped");
|
|
92
70
|
}
|
package/package.json
CHANGED
package/src/lexer/index.ts
CHANGED
|
@@ -313,9 +313,17 @@ export class Lexer {
|
|
|
313
313
|
return
|
|
314
314
|
}
|
|
315
315
|
|
|
316
|
-
// Local coordinate: ^ or ^5 or ^-3 or ^0.5
|
|
316
|
+
// Local coordinate: ^ or ^5 or ^-3 or ^0.5 or ^varname (macro variable)
|
|
317
317
|
if (char === '^') {
|
|
318
318
|
let value = '^'
|
|
319
|
+
// Check for identifier (variable name for macro substitution, e.g. ^px, ^height)
|
|
320
|
+
if (/[a-zA-Z_]/.test(this.peek())) {
|
|
321
|
+
while (/[a-zA-Z0-9_]/.test(this.peek())) {
|
|
322
|
+
value += this.advance()
|
|
323
|
+
}
|
|
324
|
+
this.addToken('local_coord', value, startLine, startCol)
|
|
325
|
+
return
|
|
326
|
+
}
|
|
319
327
|
// Check for optional sign
|
|
320
328
|
if (this.peek() === '-' || this.peek() === '+') {
|
|
321
329
|
value += this.advance()
|
package/src/lowering/index.ts
CHANGED
|
@@ -515,17 +515,33 @@ export class Lowering {
|
|
|
515
515
|
}
|
|
516
516
|
|
|
517
517
|
// Set up NBT storage for each macro param
|
|
518
|
+
// float-typed params are stored as `double 0.01` so that an integer value N
|
|
519
|
+
// becomes N/100.0 in the command (e.g. scoreboard value 975 → NBT 9.75d → ^9.75)
|
|
518
520
|
for (const macroParam of macroParamNames) {
|
|
519
521
|
const paramIdx = params.findIndex(p => p.name === macroParam)
|
|
520
522
|
if (paramIdx < 0 || paramIdx >= loweredArgs.length) continue
|
|
521
523
|
|
|
522
524
|
const operand = loweredArgs[paramIdx]
|
|
525
|
+
const paramType = params[paramIdx]?.type
|
|
526
|
+
const isFloat = paramType?.kind === 'named' && (paramType as any).name === 'float'
|
|
527
|
+
|
|
523
528
|
if (operand.kind === 'const') {
|
|
524
|
-
|
|
529
|
+
if (isFloat) {
|
|
530
|
+
const floatVal = (operand.value / 100).toFixed(6)
|
|
531
|
+
this.builder.emitRaw(`data modify storage rs:macro_args ${macroParam} set value ${floatVal}d`)
|
|
532
|
+
} else {
|
|
533
|
+
this.builder.emitRaw(`data modify storage rs:macro_args ${macroParam} set value ${operand.value}`)
|
|
534
|
+
}
|
|
525
535
|
} else if (operand.kind === 'var') {
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
536
|
+
if (isFloat) {
|
|
537
|
+
this.builder.emitRaw(
|
|
538
|
+
`execute store result storage rs:macro_args ${macroParam} double 0.01 run scoreboard players get ${operand.name} ${LOWERING_OBJ}`
|
|
539
|
+
)
|
|
540
|
+
} else {
|
|
541
|
+
this.builder.emitRaw(
|
|
542
|
+
`execute store result storage rs:macro_args ${macroParam} int 1 run scoreboard players get ${operand.name} ${LOWERING_OBJ}`
|
|
543
|
+
)
|
|
544
|
+
}
|
|
529
545
|
}
|
|
530
546
|
}
|
|
531
547
|
|