redscript-mc 1.2.28 → 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/__tests__/cli.test.js +12 -12
- package/dist/__tests__/optimizer-advanced.test.js +4 -4
- package/dist/compile.d.ts +3 -2
- package/dist/compile.js +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.js +1 -1
- package/dist/lexer/index.js +9 -1
- package/dist/lowering/index.js +17 -2
- package/editors/vscode/out/extension.js +1797 -1157
- package/editors/vscode/package-lock.json +3 -3
- package/editors/vscode/package.json +1 -1
- package/editors/vscode/src/hover.ts +18 -0
- package/editors/vscode/syntaxes/redscript.tmLanguage.json +12 -3
- package/examples/readme-demo.mcrs +44 -66
- package/package.json +1 -1
- package/src/__tests__/cli.test.ts +12 -12
- package/src/__tests__/optimizer-advanced.test.ts +4 -4
- package/src/compile.ts +4 -3
- package/src/index.ts +5 -4
- package/src/lexer/index.ts +9 -1
- package/src/lowering/index.ts +20 -4
|
@@ -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",
|
|
@@ -1009,6 +1009,24 @@ export function registerHoverProvider(context: vscode.ExtensionContext): void {
|
|
|
1009
1009
|
provideHover(document, position) {
|
|
1010
1010
|
const line = document.lineAt(position.line).text
|
|
1011
1011
|
|
|
1012
|
+
// ── #rs — special compiler token ────────────────────────
|
|
1013
|
+
const rsRange = document.getWordRangeAtPosition(position, /#rs\b/)
|
|
1014
|
+
if (rsRange) {
|
|
1015
|
+
const md = new vscode.MarkdownString('', true)
|
|
1016
|
+
md.isTrusted = true
|
|
1017
|
+
md.appendCodeblock('#rs', 'redscript')
|
|
1018
|
+
md.appendMarkdown('**RS Internal Scoreboard Objective** *(compiler token)*\n\n')
|
|
1019
|
+
md.appendMarkdown('Resolves to the current datapack\'s internal scoreboard objective at compile time.\n\n')
|
|
1020
|
+
md.appendMarkdown('Default: `__<namespace>` (e.g. `__mygame` for namespace `mygame`).\n\n')
|
|
1021
|
+
md.appendMarkdown('Use `#rs` in `scoreboard_get` / `scoreboard_set` when you need to read or write\n')
|
|
1022
|
+
md.appendMarkdown('the compiler\'s own variable slots — such as in stdlib implementations.\n\n')
|
|
1023
|
+
md.appendMarkdown('> ⚠️ Unlike other `#name` tokens, `#rs` does **not** compile to the literal string `rs`.\n')
|
|
1024
|
+
md.appendMarkdown('> It tracks the `--scoreboard` flag or the `__<namespace>` default.\n\n')
|
|
1025
|
+
md.appendMarkdown('**Example:**\n')
|
|
1026
|
+
md.appendCodeblock('scoreboard_set("timer_ticks", #rs, 0);\n// compiles to: scoreboard players set timer_ticks __mygame 0', 'redscript')
|
|
1027
|
+
return new vscode.Hover(md, rsRange)
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1012
1030
|
// ── #mc_name hover ──────────────────────────────────────
|
|
1013
1031
|
const mcRange = document.getWordRangeAtPosition(position, /#[a-zA-Z_][a-zA-Z0-9_]*/)
|
|
1014
1032
|
if (mcRange) {
|
|
@@ -307,9 +307,18 @@
|
|
|
307
307
|
},
|
|
308
308
|
|
|
309
309
|
"mc-name": {
|
|
310
|
-
"comment": "#objective, #tag, #team — unquoted MC identifier literal",
|
|
311
|
-
"
|
|
312
|
-
|
|
310
|
+
"comment": "#objective, #tag, #team — unquoted MC identifier literal. #rs is the special RS-internal scoreboard objective token.",
|
|
311
|
+
"patterns": [
|
|
312
|
+
{
|
|
313
|
+
"comment": "#rs — special compiler token: resolves to the current datapack's internal scoreboard objective (__<namespace>)",
|
|
314
|
+
"name": "support.constant.rs-objective.redscript",
|
|
315
|
+
"match": "#rs\\b"
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
"name": "variable.other.mc-name.redscript",
|
|
319
|
+
"match": "#[a-zA-Z_][a-zA-Z0-9_]*"
|
|
320
|
+
}
|
|
321
|
+
]
|
|
313
322
|
},
|
|
314
323
|
|
|
315
324
|
"operators": {
|
|
@@ -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
|
@@ -55,7 +55,7 @@ describe('CLI API', () => {
|
|
|
55
55
|
const result = compile(source, { namespace: 'mygame', filePath: mainPath })
|
|
56
56
|
const tickTimer = result.files.find(file => file.path.endsWith('/tick_timer.mcfunction'))
|
|
57
57
|
|
|
58
|
-
expect(tickTimer?.content).toContain('scoreboard players set #rs
|
|
58
|
+
expect(tickTimer?.content).toContain('scoreboard players set #rs __mygame.timer_ticks 1')
|
|
59
59
|
})
|
|
60
60
|
|
|
61
61
|
it('adds a call-site hash for stdlib internal scoreboard objectives', () => {
|
|
@@ -113,8 +113,8 @@ describe('CLI API', () => {
|
|
|
113
113
|
|
|
114
114
|
expect(result.typeErrors).toEqual([])
|
|
115
115
|
const newFn = result.files.find(file => file.path.endsWith('/Timer_new.mcfunction'))
|
|
116
|
-
expect(newFn?.content).toContain('scoreboard players set timer_ticks
|
|
117
|
-
expect(newFn?.content).toContain('scoreboard players set timer_active
|
|
116
|
+
expect(newFn?.content).toContain('scoreboard players set timer_ticks __timernew 0')
|
|
117
|
+
expect(newFn?.content).toContain('scoreboard players set timer_active __timernew 0')
|
|
118
118
|
})
|
|
119
119
|
|
|
120
120
|
it('Timer.start/pause/reset', () => {
|
|
@@ -142,9 +142,9 @@ describe('CLI API', () => {
|
|
|
142
142
|
const pauseFn = result.files.find(file => file.path.endsWith('/Timer_pause.mcfunction'))
|
|
143
143
|
const resetFn = result.files.find(file => file.path.endsWith('/Timer_reset.mcfunction'))
|
|
144
144
|
|
|
145
|
-
expect(startFn?.content).toContain('scoreboard players set timer_active
|
|
146
|
-
expect(pauseFn?.content).toContain('scoreboard players set timer_active
|
|
147
|
-
expect(resetFn?.content).toContain('scoreboard players set timer_ticks
|
|
145
|
+
expect(startFn?.content).toContain('scoreboard players set timer_active __timerstate 1')
|
|
146
|
+
expect(pauseFn?.content).toContain('scoreboard players set timer_active __timerstate 0')
|
|
147
|
+
expect(resetFn?.content).toContain('scoreboard players set timer_ticks __timerstate 0')
|
|
148
148
|
})
|
|
149
149
|
|
|
150
150
|
it('Timer.done returns bool', () => {
|
|
@@ -171,9 +171,9 @@ describe('CLI API', () => {
|
|
|
171
171
|
expect(result.typeErrors).toEqual([])
|
|
172
172
|
const doneFn = result.files.find(file => file.path.endsWith('/Timer_done.mcfunction'))
|
|
173
173
|
const mainFn = result.files.find(file => file.path.endsWith('/main.mcfunction'))
|
|
174
|
-
expect(doneFn?.content).toContain('scoreboard players get timer_ticks
|
|
174
|
+
expect(doneFn?.content).toContain('scoreboard players get timer_ticks __timerdone')
|
|
175
175
|
expect(doneFn?.content).toContain('return run scoreboard players get')
|
|
176
|
-
expect(mainFn?.content).toContain('execute if score $main_finished
|
|
176
|
+
expect(mainFn?.content).toContain('execute if score $main_finished __timerdone matches 1..')
|
|
177
177
|
})
|
|
178
178
|
|
|
179
179
|
it('Timer.tick increments', () => {
|
|
@@ -201,10 +201,10 @@ describe('CLI API', () => {
|
|
|
201
201
|
.map(file => file.content)
|
|
202
202
|
.join('\n')
|
|
203
203
|
|
|
204
|
-
expect(tickOutput).toContain('scoreboard players get timer_active
|
|
205
|
-
expect(tickOutput).toContain('scoreboard players get timer_ticks
|
|
206
|
-
expect(tickOutput).toContain(' += $const_1
|
|
207
|
-
expect(tickOutput).toContain('execute store result score timer_ticks
|
|
204
|
+
expect(tickOutput).toContain('scoreboard players get timer_active __timertick')
|
|
205
|
+
expect(tickOutput).toContain('scoreboard players get timer_ticks __timertick')
|
|
206
|
+
expect(tickOutput).toContain(' += $const_1 __timertick')
|
|
207
|
+
expect(tickOutput).toContain('execute store result score timer_ticks __timertick run scoreboard players get $_')
|
|
208
208
|
})
|
|
209
209
|
})
|
|
210
210
|
|
|
@@ -28,7 +28,7 @@ fn turret_tick() {
|
|
|
28
28
|
const parent = getFileContent(result.files, 'data/test/function/turret_tick.mcfunction')
|
|
29
29
|
const loopBody = getFileContent(result.files, 'data/test/function/turret_tick/foreach_0.mcfunction')
|
|
30
30
|
|
|
31
|
-
const hoistedRead = 'execute store result score $_0
|
|
31
|
+
const hoistedRead = 'execute store result score $_0 __test run scoreboard players get config test.turret_range'
|
|
32
32
|
const executeCall = 'execute as @e[tag=turret] run function test:turret_tick/foreach_0'
|
|
33
33
|
|
|
34
34
|
expect(parent).toContain(hoistedRead)
|
|
@@ -54,7 +54,7 @@ fn read_twice() {
|
|
|
54
54
|
const readMatches = fn.match(/scoreboard players get @s test\.coins/g) ?? []
|
|
55
55
|
|
|
56
56
|
expect(readMatches).toHaveLength(1)
|
|
57
|
-
expect(fn).toContain('scoreboard players operation $_1
|
|
57
|
+
expect(fn).toContain('scoreboard players operation $_1 __test = $_0 __test')
|
|
58
58
|
})
|
|
59
59
|
|
|
60
60
|
test('reuses duplicate arithmetic sequences', () => {
|
|
@@ -71,10 +71,10 @@ fn math() {
|
|
|
71
71
|
|
|
72
72
|
const result = compile(source, { namespace: 'test' })
|
|
73
73
|
const fn = getFileContent(result.files, 'data/test/function/math.mcfunction')
|
|
74
|
-
const addMatches = fn.match(/\+= \$const_2
|
|
74
|
+
const addMatches = fn.match(/\+= \$const_2 __test/g) ?? []
|
|
75
75
|
|
|
76
76
|
expect(addMatches).toHaveLength(1)
|
|
77
|
-
expect(fn).toContain('scoreboard players operation $_1
|
|
77
|
+
expect(fn).toContain('scoreboard players operation $_1 __test = $_0 __test')
|
|
78
78
|
})
|
|
79
79
|
})
|
|
80
80
|
|
package/src/compile.ts
CHANGED
|
@@ -28,8 +28,9 @@ export interface CompileOptions {
|
|
|
28
28
|
dce?: boolean
|
|
29
29
|
mangle?: boolean
|
|
30
30
|
/** Scoreboard objective used for all variable slots.
|
|
31
|
-
* Defaults to '
|
|
32
|
-
*
|
|
31
|
+
* Defaults to '__<namespace>' (e.g. '__mathshow') — the double-underscore
|
|
32
|
+
* prefix signals compiler-internal and avoids occupying the user's namespace.
|
|
33
|
+
* Each datapack gets a unique objective automatically; no manual setup needed. */
|
|
33
34
|
scoreboardObjective?: string
|
|
34
35
|
/** Additional source files that should be treated as *library* code.
|
|
35
36
|
* Functions in these files are DCE-eligible: they are only compiled into
|
|
@@ -269,7 +270,7 @@ export function compile(source: string, options: CompileOptions = {}): CompileRe
|
|
|
269
270
|
// Configure scoreboard objective for this compilation.
|
|
270
271
|
// Default: use the datapack namespace so each datapack gets its own objective
|
|
271
272
|
// automatically, preventing variable collisions when multiple datapacks coexist.
|
|
272
|
-
const scoreboardObj = options.scoreboardObjective ?? namespace
|
|
273
|
+
const scoreboardObj = options.scoreboardObjective ?? `__${namespace}`
|
|
273
274
|
setScoreboardObjective(scoreboardObj)
|
|
274
275
|
|
|
275
276
|
// Lowering
|
package/src/index.ts
CHANGED
|
@@ -36,9 +36,10 @@ export interface CompileOptions {
|
|
|
36
36
|
filePath?: string
|
|
37
37
|
dce?: boolean
|
|
38
38
|
mangle?: boolean
|
|
39
|
-
/** Scoreboard objective used for all variable slots
|
|
40
|
-
*
|
|
41
|
-
* RedScript datapacks are loaded simultaneously
|
|
39
|
+
/** Scoreboard objective used for all variable slots.
|
|
40
|
+
* Defaults to '__<namespace>' (e.g. '__mathshow') to avoid collisions when
|
|
41
|
+
* multiple RedScript datapacks are loaded simultaneously, without occupying
|
|
42
|
+
* the user's own namespace. Override only if you need a specific name. */
|
|
42
43
|
scoreboardObjective?: string
|
|
43
44
|
}
|
|
44
45
|
|
|
@@ -108,7 +109,7 @@ export function compile(source: string, options: CompileOptions = {}): CompileRe
|
|
|
108
109
|
// Configure scoreboard objective for this compilation.
|
|
109
110
|
// Default: use the datapack namespace so each datapack gets its own objective
|
|
110
111
|
// automatically, preventing variable collisions when multiple datapacks coexist.
|
|
111
|
-
const scoreboardObj = options.scoreboardObjective ?? namespace
|
|
112
|
+
const scoreboardObj = options.scoreboardObjective ?? `__${namespace}`
|
|
112
113
|
setScoreboardObjective(scoreboardObj)
|
|
113
114
|
|
|
114
115
|
// Lowering to IR
|
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
|
|