redscript-mc 1.2.26 → 1.2.28

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.
Files changed (243) hide show
  1. package/README.md +78 -9
  2. package/README.zh.md +72 -4
  3. package/dist/__tests__/cli.test.js +13 -13
  4. package/dist/__tests__/optimizer-advanced.test.js +4 -4
  5. package/dist/__tests__/stdlib-advanced.test.js +114 -0
  6. package/dist/__tests__/stdlib-bigint.test.d.ts +7 -0
  7. package/dist/__tests__/stdlib-bigint.test.js +428 -0
  8. package/dist/cli.js +13 -5
  9. package/dist/codegen/mcfunction/index.d.ts +4 -0
  10. package/dist/codegen/mcfunction/index.js +9 -4
  11. package/dist/compile.d.ts +4 -0
  12. package/dist/compile.js +9 -1
  13. package/dist/data/arena/function/__load.mcfunction +6 -0
  14. package/dist/data/arena/function/__tick.mcfunction +2 -0
  15. package/dist/data/arena/function/announce_leaders/else_1.mcfunction +3 -0
  16. package/dist/data/arena/function/announce_leaders/foreach_0/merge_2.mcfunction +1 -0
  17. package/dist/data/arena/function/announce_leaders/foreach_0/then_0.mcfunction +3 -0
  18. package/dist/data/arena/function/announce_leaders/foreach_0.mcfunction +7 -0
  19. package/dist/data/arena/function/announce_leaders/foreach_1/merge_2.mcfunction +1 -0
  20. package/dist/data/arena/function/announce_leaders/foreach_1/then_0.mcfunction +4 -0
  21. package/dist/data/arena/function/announce_leaders/foreach_1.mcfunction +6 -0
  22. package/dist/data/arena/function/announce_leaders/merge_2.mcfunction +1 -0
  23. package/dist/data/arena/function/announce_leaders/then_0.mcfunction +4 -0
  24. package/dist/data/arena/function/announce_leaders.mcfunction +6 -0
  25. package/dist/data/arena/function/arena_tick/merge_2.mcfunction +1 -0
  26. package/dist/data/arena/function/arena_tick/then_0.mcfunction +4 -0
  27. package/dist/data/arena/function/arena_tick.mcfunction +11 -0
  28. package/dist/data/counter/function/__load.mcfunction +5 -0
  29. package/dist/data/counter/function/__tick.mcfunction +2 -0
  30. package/dist/data/counter/function/counter_tick/merge_2.mcfunction +1 -0
  31. package/dist/data/counter/function/counter_tick/then_0.mcfunction +3 -0
  32. package/dist/data/counter/function/counter_tick.mcfunction +11 -0
  33. package/dist/data/gcd2/function/__load.mcfunction +3 -0
  34. package/dist/data/gcd2/function/abs/merge_2.mcfunction +3 -0
  35. package/dist/data/gcd2/function/abs/then_0.mcfunction +5 -0
  36. package/dist/data/gcd2/function/abs.mcfunction +7 -0
  37. package/dist/data/gcd2/function/gcd/loop_body_1.mcfunction +7 -0
  38. package/dist/data/gcd2/function/gcd/loop_check_0.mcfunction +5 -0
  39. package/dist/data/gcd2/function/gcd/loop_exit_2.mcfunction +3 -0
  40. package/dist/data/gcd2/function/gcd.mcfunction +14 -0
  41. package/dist/data/gcd3/function/__load.mcfunction +3 -0
  42. package/dist/data/gcd3/function/abs/merge_2.mcfunction +3 -0
  43. package/dist/data/gcd3/function/abs/then_0.mcfunction +5 -0
  44. package/dist/data/gcd3/function/abs.mcfunction +7 -0
  45. package/dist/data/gcd3/function/gcd/loop_body_1.mcfunction +7 -0
  46. package/dist/data/gcd3/function/gcd/loop_check_0.mcfunction +5 -0
  47. package/dist/data/gcd3/function/gcd/loop_exit_2.mcfunction +3 -0
  48. package/dist/data/gcd3/function/gcd.mcfunction +14 -0
  49. package/dist/data/gcd3/function/test.mcfunction +7 -0
  50. package/dist/data/gcd3nm/function/__load.mcfunction +3 -0
  51. package/dist/data/gcd3nm/function/abs/merge_2.mcfunction +3 -0
  52. package/dist/data/gcd3nm/function/abs/then_0.mcfunction +5 -0
  53. package/dist/data/gcd3nm/function/abs.mcfunction +7 -0
  54. package/dist/data/gcd3nm/function/gcd/loop_body_1.mcfunction +7 -0
  55. package/dist/data/gcd3nm/function/gcd/loop_check_0.mcfunction +5 -0
  56. package/dist/data/gcd3nm/function/gcd/loop_exit_2.mcfunction +3 -0
  57. package/dist/data/gcd3nm/function/gcd.mcfunction +14 -0
  58. package/dist/data/gcd3nm/function/test.mcfunction +7 -0
  59. package/dist/data/gcd_test/function/__load.mcfunction +3 -0
  60. package/dist/data/gcd_test/function/abs/merge_2.mcfunction +3 -0
  61. package/dist/data/gcd_test/function/abs/then_0.mcfunction +5 -0
  62. package/dist/data/gcd_test/function/abs.mcfunction +7 -0
  63. package/dist/data/gcd_test/function/gcd/loop_body_1.mcfunction +7 -0
  64. package/dist/data/gcd_test/function/gcd/loop_check_0.mcfunction +5 -0
  65. package/dist/data/gcd_test/function/gcd/loop_exit_2.mcfunction +3 -0
  66. package/dist/data/gcd_test/function/gcd.mcfunction +14 -0
  67. package/dist/data/isqrttest/function/__load.mcfunction +6 -0
  68. package/dist/data/isqrttest/function/isqrt/loop_body_4.mcfunction +12 -0
  69. package/dist/data/isqrttest/function/isqrt/loop_check_3.mcfunction +5 -0
  70. package/dist/data/isqrttest/function/isqrt/loop_exit_5.mcfunction +3 -0
  71. package/dist/data/isqrttest/function/isqrt/merge_2.mcfunction +4 -0
  72. package/dist/data/isqrttest/function/isqrt/merge_8.mcfunction +6 -0
  73. package/dist/data/isqrttest/function/isqrt/then_0.mcfunction +3 -0
  74. package/dist/data/isqrttest/function/isqrt/then_6.mcfunction +3 -0
  75. package/dist/data/isqrttest/function/isqrt.mcfunction +7 -0
  76. package/dist/data/isqrttest/function/test.mcfunction +6 -0
  77. package/dist/data/mathtest/function/__load.mcfunction +3 -0
  78. package/dist/data/mathtest/function/abs/merge_2.mcfunction +3 -0
  79. package/dist/data/mathtest/function/abs/then_0.mcfunction +5 -0
  80. package/dist/data/mathtest/function/abs.mcfunction +6 -0
  81. package/dist/data/mathtest/function/test.mcfunction +5 -0
  82. package/dist/data/minecraft/tags/function/load.json +5 -0
  83. package/dist/data/minecraft/tags/function/tick.json +5 -0
  84. package/dist/data/mypack/function/__load.mcfunction +13 -0
  85. package/dist/data/mypack/function/_atan_init.mcfunction +2 -0
  86. package/dist/data/mypack/function/abs/merge_2.mcfunction +3 -0
  87. package/dist/data/mypack/function/abs/then_0.mcfunction +5 -0
  88. package/dist/data/mypack/function/abs.mcfunction +6 -0
  89. package/dist/data/mypack/function/atan2_fixed/__sgi_1.mcfunction +2 -0
  90. package/dist/data/mypack/function/atan2_fixed/else_34.mcfunction +3 -0
  91. package/dist/data/mypack/function/atan2_fixed/loop_body_31.mcfunction +19 -0
  92. package/dist/data/mypack/function/atan2_fixed/loop_check_30.mcfunction +5 -0
  93. package/dist/data/mypack/function/atan2_fixed/loop_exit_32.mcfunction +6 -0
  94. package/dist/data/mypack/function/atan2_fixed/merge_11.mcfunction +6 -0
  95. package/dist/data/mypack/function/atan2_fixed/merge_14.mcfunction +3 -0
  96. package/dist/data/mypack/function/atan2_fixed/merge_17.mcfunction +6 -0
  97. package/dist/data/mypack/function/atan2_fixed/merge_2.mcfunction +5 -0
  98. package/dist/data/mypack/function/atan2_fixed/merge_20.mcfunction +5 -0
  99. package/dist/data/mypack/function/atan2_fixed/merge_23.mcfunction +5 -0
  100. package/dist/data/mypack/function/atan2_fixed/merge_26.mcfunction +6 -0
  101. package/dist/data/mypack/function/atan2_fixed/merge_29.mcfunction +4 -0
  102. package/dist/data/mypack/function/atan2_fixed/merge_38.mcfunction +5 -0
  103. package/dist/data/mypack/function/atan2_fixed/merge_41.mcfunction +5 -0
  104. package/dist/data/mypack/function/atan2_fixed/merge_44.mcfunction +5 -0
  105. package/dist/data/mypack/function/atan2_fixed/merge_47.mcfunction +5 -0
  106. package/dist/data/mypack/function/atan2_fixed/merge_5.mcfunction +5 -0
  107. package/dist/data/mypack/function/atan2_fixed/merge_8.mcfunction +3 -0
  108. package/dist/data/mypack/function/atan2_fixed/then_0.mcfunction +5 -0
  109. package/dist/data/mypack/function/atan2_fixed/then_12.mcfunction +3 -0
  110. package/dist/data/mypack/function/atan2_fixed/then_15.mcfunction +5 -0
  111. package/dist/data/mypack/function/atan2_fixed/then_18.mcfunction +5 -0
  112. package/dist/data/mypack/function/atan2_fixed/then_21.mcfunction +3 -0
  113. package/dist/data/mypack/function/atan2_fixed/then_24.mcfunction +3 -0
  114. package/dist/data/mypack/function/atan2_fixed/then_27.mcfunction +6 -0
  115. package/dist/data/mypack/function/atan2_fixed/then_3.mcfunction +3 -0
  116. package/dist/data/mypack/function/atan2_fixed/then_33.mcfunction +5 -0
  117. package/dist/data/mypack/function/atan2_fixed/then_36.mcfunction +5 -0
  118. package/dist/data/mypack/function/atan2_fixed/then_39.mcfunction +5 -0
  119. package/dist/data/mypack/function/atan2_fixed/then_42.mcfunction +3 -0
  120. package/dist/data/mypack/function/atan2_fixed/then_45.mcfunction +5 -0
  121. package/dist/data/mypack/function/atan2_fixed/then_6.mcfunction +3 -0
  122. package/dist/data/mypack/function/atan2_fixed/then_9.mcfunction +5 -0
  123. package/dist/data/mypack/function/atan2_fixed.mcfunction +7 -0
  124. package/dist/data/mypack/function/my_game.mcfunction +10 -0
  125. package/dist/data/quiz/function/__load.mcfunction +16 -0
  126. package/dist/data/quiz/function/__tick.mcfunction +6 -0
  127. package/dist/data/quiz/function/__trigger_quiz_a_dispatch.mcfunction +4 -0
  128. package/dist/data/quiz/function/__trigger_quiz_b_dispatch.mcfunction +4 -0
  129. package/dist/data/quiz/function/__trigger_quiz_c_dispatch.mcfunction +4 -0
  130. package/dist/data/quiz/function/__trigger_quiz_start_dispatch.mcfunction +4 -0
  131. package/dist/data/quiz/function/answer_a.mcfunction +4 -0
  132. package/dist/data/quiz/function/answer_b.mcfunction +4 -0
  133. package/dist/data/quiz/function/answer_c.mcfunction +4 -0
  134. package/dist/data/quiz/function/ask_question/else_1.mcfunction +5 -0
  135. package/dist/data/quiz/function/ask_question/else_4.mcfunction +5 -0
  136. package/dist/data/quiz/function/ask_question/else_7.mcfunction +4 -0
  137. package/dist/data/quiz/function/ask_question/merge_2.mcfunction +1 -0
  138. package/dist/data/quiz/function/ask_question/merge_5.mcfunction +2 -0
  139. package/dist/data/quiz/function/ask_question/merge_8.mcfunction +2 -0
  140. package/dist/data/quiz/function/ask_question/then_0.mcfunction +4 -0
  141. package/dist/data/quiz/function/ask_question/then_3.mcfunction +4 -0
  142. package/dist/data/quiz/function/ask_question/then_6.mcfunction +4 -0
  143. package/dist/data/quiz/function/ask_question.mcfunction +7 -0
  144. package/dist/data/quiz/function/finish_quiz.mcfunction +6 -0
  145. package/dist/data/quiz/function/handle_answer/else_1.mcfunction +5 -0
  146. package/dist/data/quiz/function/handle_answer/else_10.mcfunction +3 -0
  147. package/dist/data/quiz/function/handle_answer/else_16.mcfunction +3 -0
  148. package/dist/data/quiz/function/handle_answer/else_4.mcfunction +3 -0
  149. package/dist/data/quiz/function/handle_answer/else_7.mcfunction +5 -0
  150. package/dist/data/quiz/function/handle_answer/merge_11.mcfunction +2 -0
  151. package/dist/data/quiz/function/handle_answer/merge_14.mcfunction +2 -0
  152. package/dist/data/quiz/function/handle_answer/merge_17.mcfunction +2 -0
  153. package/dist/data/quiz/function/handle_answer/merge_2.mcfunction +8 -0
  154. package/dist/data/quiz/function/handle_answer/merge_5.mcfunction +2 -0
  155. package/dist/data/quiz/function/handle_answer/merge_8.mcfunction +2 -0
  156. package/dist/data/quiz/function/handle_answer/then_0.mcfunction +5 -0
  157. package/dist/data/quiz/function/handle_answer/then_12.mcfunction +5 -0
  158. package/dist/data/quiz/function/handle_answer/then_15.mcfunction +6 -0
  159. package/dist/data/quiz/function/handle_answer/then_3.mcfunction +6 -0
  160. package/dist/data/quiz/function/handle_answer/then_6.mcfunction +5 -0
  161. package/dist/data/quiz/function/handle_answer/then_9.mcfunction +6 -0
  162. package/dist/data/quiz/function/handle_answer.mcfunction +11 -0
  163. package/dist/data/quiz/function/start_quiz.mcfunction +5 -0
  164. package/dist/data/reqtest/function/__load.mcfunction +4 -0
  165. package/dist/data/reqtest/function/_table_init.mcfunction +2 -0
  166. package/dist/data/reqtest/function/no_trig.mcfunction +3 -0
  167. package/dist/data/reqtest/function/use_table.mcfunction +4 -0
  168. package/dist/data/reqtest2/function/__load.mcfunction +3 -0
  169. package/dist/data/reqtest2/function/no_trig.mcfunction +3 -0
  170. package/dist/data/runtime/function/__load.mcfunction +5 -0
  171. package/dist/data/runtime/function/__tick.mcfunction +2 -0
  172. package/dist/data/runtime/function/counter_tick/then_0.mcfunction +3 -0
  173. package/dist/data/runtime/function/counter_tick.mcfunction +13 -0
  174. package/dist/data/shop/function/__load.mcfunction +7 -0
  175. package/dist/data/shop/function/__tick.mcfunction +3 -0
  176. package/dist/data/shop/function/__trigger_shop_buy_dispatch.mcfunction +4 -0
  177. package/dist/data/shop/function/complete_purchase/else_1.mcfunction +5 -0
  178. package/dist/data/shop/function/complete_purchase/else_4.mcfunction +5 -0
  179. package/dist/data/shop/function/complete_purchase/else_7.mcfunction +3 -0
  180. package/dist/data/shop/function/complete_purchase/merge_2.mcfunction +2 -0
  181. package/dist/data/shop/function/complete_purchase/merge_5.mcfunction +2 -0
  182. package/dist/data/shop/function/complete_purchase/merge_8.mcfunction +2 -0
  183. package/dist/data/shop/function/complete_purchase/then_0.mcfunction +4 -0
  184. package/dist/data/shop/function/complete_purchase/then_3.mcfunction +4 -0
  185. package/dist/data/shop/function/complete_purchase/then_6.mcfunction +4 -0
  186. package/dist/data/shop/function/complete_purchase.mcfunction +7 -0
  187. package/dist/data/shop/function/handle_shop_trigger.mcfunction +3 -0
  188. package/dist/data/swap_test/function/__load.mcfunction +3 -0
  189. package/dist/data/swap_test/function/gcd_old/loop_body_1.mcfunction +7 -0
  190. package/dist/data/swap_test/function/gcd_old/loop_check_0.mcfunction +5 -0
  191. package/dist/data/swap_test/function/gcd_old/loop_exit_2.mcfunction +3 -0
  192. package/dist/data/swap_test/function/gcd_old.mcfunction +8 -0
  193. package/dist/data/turret/function/__load.mcfunction +5 -0
  194. package/dist/data/turret/function/__tick.mcfunction +4 -0
  195. package/dist/data/turret/function/__trigger_deploy_turret_dispatch.mcfunction +4 -0
  196. package/dist/data/turret/function/deploy_turret.mcfunction +8 -0
  197. package/dist/data/turret/function/turret_tick/at_1.mcfunction +2 -0
  198. package/dist/data/turret/function/turret_tick/foreach_0.mcfunction +2 -0
  199. package/dist/data/turret/function/turret_tick/foreach_2.mcfunction +2 -0
  200. package/dist/data/turret/function/turret_tick/tick_body.mcfunction +3 -0
  201. package/dist/data/turret/function/turret_tick/tick_skip.mcfunction +1 -0
  202. package/dist/data/turret/function/turret_tick.mcfunction +5 -0
  203. package/dist/gcd2.map.json +15 -0
  204. package/dist/gcd3.map.json +17 -0
  205. package/dist/gcd_test.map.json +15 -0
  206. package/dist/index.d.ts +4 -0
  207. package/dist/index.js +11 -6
  208. package/dist/isqrttest.map.json +15 -0
  209. package/dist/lowering/index.d.ts +3 -0
  210. package/dist/lowering/index.js +161 -64
  211. package/dist/mathtest.map.json +6 -0
  212. package/dist/mypack.map.json +27 -0
  213. package/dist/optimizer/commands.d.ts +1 -0
  214. package/dist/optimizer/commands.js +18 -11
  215. package/dist/optimizer/structure.d.ts +1 -0
  216. package/dist/optimizer/structure.js +6 -1
  217. package/dist/pack.mcmeta +6 -0
  218. package/dist/reqtest.map.json +4 -0
  219. package/dist/reqtest2.map.json +4 -0
  220. package/dist/runtime/index.js +26 -5
  221. package/dist/runtime.map.json +7 -0
  222. package/dist/swap_test.map.json +14 -0
  223. package/editors/vscode/package-lock.json +3 -3
  224. package/editors/vscode/package.json +1 -1
  225. package/examples/math-showcase.mcrs +146 -0
  226. package/examples/readme-demo.mcrs +92 -0
  227. package/examples/showcase.mcrs +505 -0
  228. package/package.json +1 -1
  229. package/src/__tests__/cli.test.ts +13 -13
  230. package/src/__tests__/optimizer-advanced.test.ts +4 -4
  231. package/src/__tests__/stdlib-advanced.test.ts +120 -0
  232. package/src/__tests__/stdlib-bigint.test.ts +427 -0
  233. package/src/cli.ts +14 -5
  234. package/src/codegen/mcfunction/index.ts +14 -5
  235. package/src/compile.ts +15 -2
  236. package/src/index.ts +17 -7
  237. package/src/lowering/index.ts +165 -63
  238. package/src/optimizer/commands.ts +18 -12
  239. package/src/optimizer/structure.ts +6 -2
  240. package/src/runtime/index.ts +27 -5
  241. package/src/stdlib/advanced.mcrs +81 -0
  242. package/src/stdlib/bigint.mcrs +205 -0
  243. package/src/stdlib/math.mcrs +19 -6
@@ -39,7 +39,8 @@ var __importStar = (this && this.__importStar) || (function () {
39
39
  };
40
40
  })();
41
41
  Object.defineProperty(exports, "__esModule", { value: true });
42
- exports.Lowering = void 0;
42
+ exports.Lowering = exports.LOWERING_OBJ = void 0;
43
+ exports.setScoreboardObjective = setScoreboardObjective;
43
44
  const builder_1 = require("../ir/builder");
44
45
  const diagnostics_1 = require("../diagnostics");
45
46
  const path = __importStar(require("path"));
@@ -54,6 +55,13 @@ const entity_hierarchy_1 = require("../types/entity-hierarchy");
54
55
  // All builtins support macro parameters - any arg that's a function param
55
56
  // will automatically use MC 1.20.2+ macro syntax when needed
56
57
  // ---------------------------------------------------------------------------
58
+ // Scoreboard Objective
59
+ // Set per-compilation via setScoreboardObjective() — defaults to 'rs'.
60
+ // ---------------------------------------------------------------------------
61
+ /** Current scoreboard objective. Set once per compile() call. */
62
+ exports.LOWERING_OBJ = 'rs';
63
+ function setScoreboardObjective(obj) { exports.LOWERING_OBJ = obj; }
64
+ // ---------------------------------------------------------------------------
57
65
  // Builtin Functions
58
66
  // ---------------------------------------------------------------------------
59
67
  const BUILTINS = {
@@ -73,9 +81,13 @@ const BUILTINS = {
73
81
  const pos = [x ?? '~', y ?? '~', z ?? '~'].join(' ');
74
82
  return nbt ? `summon ${type} ${pos} ${nbt}` : `summon ${type} ${pos}`;
75
83
  },
76
- particle: ([name, x, y, z]) => {
84
+ particle: ([name, x, y, z, dx, dy, dz, speed, count]) => {
77
85
  const pos = [x ?? '~', y ?? '~', z ?? '~'].join(' ');
78
- return `particle ${name} ${pos}`;
86
+ // dx/dy/dz/speed/count are optional; omit trailing undefineds
87
+ const extra = [dx, dy, dz, speed, count].filter(v => v !== undefined && v !== null);
88
+ return extra.length > 0
89
+ ? `particle ${name} ${pos} ${extra.join(' ')}`
90
+ : `particle ${name} ${pos}`;
79
91
  },
80
92
  playsound: ([sound, source, sel, x, y, z, volume, pitch, minVolume]) => ['playsound', sound, source, sel, x, y, z, volume, pitch, minVolume].filter(Boolean).join(' '),
81
93
  tp: () => null, // Special handling
@@ -130,6 +142,7 @@ const BUILTINS = {
130
142
  clearInterval: () => null, // Special handling
131
143
  storage_get_int: () => null, // Special handling (dynamic NBT array read via macro)
132
144
  storage_set_array: () => null, // Special handling (write literal NBT array to storage)
145
+ storage_set_int: () => null, // Special handling (dynamic NBT array write via macro)
133
146
  };
134
147
  function getSpan(node) {
135
148
  return node?.span;
@@ -339,12 +352,30 @@ class Lowering {
339
352
  }
340
353
  preScanExpr(expr, paramNames, macroParams) {
341
354
  if (expr.kind === 'call' && BUILTINS[expr.fn] !== undefined) {
342
- // All ident args to macro-aware builtins that are params → macro params
343
- for (const arg of expr.args) {
344
- if (arg.kind === 'ident' && paramNames.has(arg.name)) {
345
- macroParams.add(arg.name);
355
+ // Only trigger macro param detection for builtins that actually emit
356
+ // MC commands with $(param) inline in the current function body.
357
+ // Special-handled builtins (storage_get_int / storage_set_int / etc.) are
358
+ // declared as `() => null` — they create their own sub-functions for macro
359
+ // indirection and do NOT require the surrounding function to be a macro.
360
+ const handler = BUILTINS[expr.fn];
361
+ const isSpecialHandled = (() => {
362
+ try {
363
+ return handler() === null;
364
+ }
365
+ catch {
366
+ return false;
367
+ }
368
+ })();
369
+ if (!isSpecialHandled) {
370
+ for (const arg of expr.args) {
371
+ if (arg.kind === 'ident' && paramNames.has(arg.name)) {
372
+ macroParams.add(arg.name);
373
+ }
346
374
  }
347
375
  }
376
+ // Always recurse into args for nested calls/expressions
377
+ for (const arg of expr.args)
378
+ this.preScanExpr(arg, paramNames, macroParams);
348
379
  return;
349
380
  }
350
381
  // Recurse into sub-expressions for other call types
@@ -434,6 +465,16 @@ class Lowering {
434
465
  if (expr.kind === 'struct_lit' || expr.kind === 'array_lit') {
435
466
  return { str: this.exprToSnbt(expr) };
436
467
  }
468
+ // Float literals: preserve the float value for MC commands that accept floats
469
+ // (particle spread, playsound volume/pitch, etc.)
470
+ // We do NOT truncate here — that only applies to scoreboard/IR contexts.
471
+ if (expr.kind === 'float_lit') {
472
+ return { str: expr.value.toString() };
473
+ }
474
+ // Unary minus applied to a float literal (e.g. -0.5)
475
+ if (expr.kind === 'unary' && expr.op === '-' && expr.operand.kind === 'float_lit') {
476
+ return { str: (-expr.operand.value).toString() };
477
+ }
437
478
  return { str: this.exprToString(expr) };
438
479
  }
439
480
  /**
@@ -447,10 +488,10 @@ class Lowering {
447
488
  for (let i = 0; i < loweredArgs.length; i++) {
448
489
  const operand = loweredArgs[i];
449
490
  if (operand.kind === 'const') {
450
- this.builder.emitRaw(`scoreboard players set $p${i} rs ${operand.value}`);
491
+ this.builder.emitRaw(`scoreboard players set $p${i} ${exports.LOWERING_OBJ} ${operand.value}`);
451
492
  }
452
493
  else if (operand.kind === 'var') {
453
- this.builder.emitRaw(`scoreboard players operation $p${i} rs = ${operand.name} rs`);
494
+ this.builder.emitRaw(`scoreboard players operation $p${i} ${exports.LOWERING_OBJ} = ${operand.name} ${exports.LOWERING_OBJ}`);
454
495
  }
455
496
  }
456
497
  // Set up NBT storage for each macro param
@@ -463,14 +504,14 @@ class Lowering {
463
504
  this.builder.emitRaw(`data modify storage rs:macro_args ${macroParam} set value ${operand.value}`);
464
505
  }
465
506
  else if (operand.kind === 'var') {
466
- this.builder.emitRaw(`execute store result storage rs:macro_args ${macroParam} int 1 run scoreboard players get ${operand.name} rs`);
507
+ this.builder.emitRaw(`execute store result storage rs:macro_args ${macroParam} int 1 run scoreboard players get ${operand.name} ${exports.LOWERING_OBJ}`);
467
508
  }
468
509
  }
469
510
  // Call with macro storage
470
511
  this.builder.emitRaw(`function ${this.namespace}:${fnName} with storage rs:macro_args`);
471
512
  // Copy return value (callers may use it)
472
513
  const dst = this.builder.freshTemp();
473
- this.builder.emitRaw(`scoreboard players operation ${dst} rs = $ret rs`);
514
+ this.builder.emitRaw(`scoreboard players operation ${dst} ${exports.LOWERING_OBJ} = $ret ${exports.LOWERING_OBJ}`);
474
515
  return { kind: 'var', name: dst };
475
516
  }
476
517
  lower(program) {
@@ -704,7 +745,7 @@ class Lowering {
704
745
  const originalInstrs = [...entry.instrs];
705
746
  const originalTerm = entry.term;
706
747
  entry.instrs = [
707
- { op: 'raw', cmd: `scoreboard players add ${counterVar} rs 1` },
748
+ { op: 'raw', cmd: `scoreboard players add ${counterVar} ${exports.LOWERING_OBJ} 1` },
708
749
  ];
709
750
  // Create conditional jump
710
751
  const bodyLabel = 'tick_body';
@@ -718,13 +759,13 @@ class Lowering {
718
759
  // Add check instruction
719
760
  entry.instrs.push({
720
761
  op: 'raw',
721
- cmd: `execute store success score ${counterVar}_check rs if score ${counterVar} rs matches ${rate}..`,
762
+ cmd: `execute store success score ${counterVar}_check ${exports.LOWERING_OBJ} if score ${counterVar} ${exports.LOWERING_OBJ} matches ${rate}..`,
722
763
  });
723
764
  // Body block (original logic + counter reset)
724
765
  fn.blocks.push({
725
766
  label: bodyLabel,
726
767
  instrs: [
727
- { op: 'raw', cmd: `scoreboard players set ${counterVar} rs 0` },
768
+ { op: 'raw', cmd: `scoreboard players set ${counterVar} ${exports.LOWERING_OBJ} 0` },
728
769
  ...originalInstrs,
729
770
  ],
730
771
  term: originalTerm,
@@ -838,7 +879,7 @@ class Lowering {
838
879
  }
839
880
  else if (fieldValue.kind === 'var') {
840
881
  // Copy from scoreboard to NBT
841
- this.builder.emitRaw(`execute store result storage ${path} int 1 run scoreboard players get ${fieldValue.name} rs`);
882
+ this.builder.emitRaw(`execute store result storage ${path} int 1 run scoreboard players get ${fieldValue.name} ${exports.LOWERING_OBJ}`);
842
883
  }
843
884
  }
844
885
  return;
@@ -871,7 +912,7 @@ class Lowering {
871
912
  }
872
913
  else if (elemValue.kind === 'var') {
873
914
  this.builder.emitRaw(`data modify storage rs:heap ${stmt.name} append value 0`);
874
- this.builder.emitRaw(`execute store result storage rs:heap ${stmt.name}[-1] int 1 run scoreboard players get ${elemValue.name} rs`);
915
+ this.builder.emitRaw(`execute store result storage rs:heap ${stmt.name}[-1] int 1 run scoreboard players get ${elemValue.name} ${exports.LOWERING_OBJ}`);
875
916
  }
876
917
  }
877
918
  return;
@@ -917,7 +958,7 @@ class Lowering {
917
958
  this.builder.emitRaw(`data modify storage ${path} set value ${fieldValue.value}`);
918
959
  }
919
960
  else if (fieldValue.kind === 'var') {
920
- this.builder.emitRaw(`execute store result storage ${path} int 1 run scoreboard players get ${fieldValue.name} rs`);
961
+ this.builder.emitRaw(`execute store result storage ${path} int 1 run scoreboard players get ${fieldValue.name} ${exports.LOWERING_OBJ}`);
921
962
  }
922
963
  }
923
964
  this.builder.emitReturn({ kind: 'const', value: 0 });
@@ -1084,10 +1125,10 @@ class Lowering {
1084
1125
  this.varMap.set(stmt.varName, loopVar);
1085
1126
  const startVal = this.lowerExpr(stmt.start);
1086
1127
  if (startVal.kind === 'const') {
1087
- this.builder.emitRaw(`scoreboard players set ${loopVar} rs ${startVal.value}`);
1128
+ this.builder.emitRaw(`scoreboard players set ${loopVar} ${exports.LOWERING_OBJ} ${startVal.value}`);
1088
1129
  }
1089
1130
  else if (startVal.kind === 'var') {
1090
- this.builder.emitRaw(`scoreboard players operation ${loopVar} rs = ${startVal.name} rs`);
1131
+ this.builder.emitRaw(`scoreboard players operation ${loopVar} ${exports.LOWERING_OBJ} = ${startVal.name} ${exports.LOWERING_OBJ}`);
1091
1132
  }
1092
1133
  // Call loop function
1093
1134
  this.builder.emitRaw(`function ${this.namespace}:${subFnName}`);
@@ -1104,11 +1145,11 @@ class Lowering {
1104
1145
  // Body
1105
1146
  this.lowerBlock(stmt.body);
1106
1147
  // Increment
1107
- this.builder.emitRaw(`scoreboard players add ${loopVar} rs 1`);
1148
+ this.builder.emitRaw(`scoreboard players add ${loopVar} ${exports.LOWERING_OBJ} 1`);
1108
1149
  // Loop condition: execute if score matches ..<end-1> run function
1109
1150
  const endVal = this.lowerExpr(stmt.end);
1110
1151
  const endNum = endVal.kind === 'const' ? endVal.value - 1 : '?';
1111
- this.builder.emitRaw(`execute if score ${loopVar} rs matches ..${endNum} run function ${this.namespace}:${subFnName}`);
1152
+ this.builder.emitRaw(`execute if score ${loopVar} ${exports.LOWERING_OBJ} matches ..${endNum} run function ${this.namespace}:${subFnName}`);
1112
1153
  if (!this.builder.isBlockSealed()) {
1113
1154
  this.builder.emitReturn();
1114
1155
  }
@@ -1198,12 +1239,12 @@ class Lowering {
1198
1239
  matchCondition = String(patternValue.value);
1199
1240
  }
1200
1241
  const subFnName = `${this.currentFn}/match_${this.foreachCounter++}`;
1201
- this.builder.emitRaw(`execute if score ${matchedVar} rs matches ..0 if score ${subject} rs matches ${matchCondition} run function ${this.namespace}:${subFnName}`);
1242
+ this.builder.emitRaw(`execute if score ${matchedVar} ${exports.LOWERING_OBJ} matches ..0 if score ${subject} ${exports.LOWERING_OBJ} matches ${matchCondition} run function ${this.namespace}:${subFnName}`);
1202
1243
  this.emitMatchArmSubFunction(subFnName, matchedVar, arm.body, true);
1203
1244
  }
1204
1245
  if (defaultArm) {
1205
1246
  const subFnName = `${this.currentFn}/match_${this.foreachCounter++}`;
1206
- this.builder.emitRaw(`execute if score ${matchedVar} rs matches ..0 run function ${this.namespace}:${subFnName}`);
1247
+ this.builder.emitRaw(`execute if score ${matchedVar} ${exports.LOWERING_OBJ} matches ..0 run function ${this.namespace}:${subFnName}`);
1207
1248
  this.emitMatchArmSubFunction(subFnName, matchedVar, defaultArm.body, false);
1208
1249
  }
1209
1250
  }
@@ -1218,7 +1259,7 @@ class Lowering {
1218
1259
  this.blockPosVars = new Map(savedBlockPosVars);
1219
1260
  this.builder.startBlock('entry');
1220
1261
  if (setMatched) {
1221
- this.builder.emitRaw(`scoreboard players set ${matchedVar} rs 1`);
1262
+ this.builder.emitRaw(`scoreboard players set ${matchedVar} ${exports.LOWERING_OBJ} 1`);
1222
1263
  }
1223
1264
  this.lowerBlock(body);
1224
1265
  if (!this.builder.isBlockSealed()) {
@@ -1250,7 +1291,7 @@ class Lowering {
1250
1291
  }
1251
1292
  this.builder.emitAssign(indexVar, { kind: 'const', value: 0 });
1252
1293
  this.builder.emitAssign(oneVar, { kind: 'const', value: 1 });
1253
- this.builder.emitRaw(`execute store result score ${lengthVar} rs run data get storage rs:heap ${arrayName}`);
1294
+ this.builder.emitRaw(`execute store result score ${lengthVar} ${exports.LOWERING_OBJ} run data get storage rs:heap ${arrayName}`);
1254
1295
  const checkLabel = this.builder.freshLabel('foreach_array_check');
1255
1296
  const bodyLabel = this.builder.freshLabel('foreach_array_body');
1256
1297
  const exitLabel = this.builder.freshLabel('foreach_array_exit');
@@ -1263,7 +1304,7 @@ class Lowering {
1263
1304
  this.builder.emitAssign(bindingVar, element);
1264
1305
  this.lowerBlock(stmt.body);
1265
1306
  if (!this.builder.isBlockSealed()) {
1266
- this.builder.emitRaw(`scoreboard players operation ${indexVar} rs += ${oneVar} rs`);
1307
+ this.builder.emitRaw(`scoreboard players operation ${indexVar} ${exports.LOWERING_OBJ} += ${oneVar} ${exports.LOWERING_OBJ}`);
1267
1308
  this.builder.emitJump(checkLabel);
1268
1309
  }
1269
1310
  this.builder.startBlock(exitLabel);
@@ -1577,7 +1618,7 @@ class Lowering {
1577
1618
  if (mapped && mapped.startsWith('@e[tag=__rs_obj_')) {
1578
1619
  // World object field access → scoreboard get
1579
1620
  const dst = this.builder.freshTemp();
1580
- this.builder.emitRaw(`scoreboard players operation ${dst} rs = ${mapped} rs`);
1621
+ this.builder.emitRaw(`scoreboard players operation ${dst} ${exports.LOWERING_OBJ} = ${mapped} ${exports.LOWERING_OBJ}`);
1581
1622
  return { kind: 'var', name: dst };
1582
1623
  }
1583
1624
  if (varType?.kind === 'struct') {
@@ -1585,13 +1626,13 @@ class Lowering {
1585
1626
  const path = `rs:heap ${structName}_${expr.obj.name}.${expr.field}`;
1586
1627
  const dst = this.builder.freshTemp();
1587
1628
  // Read from NBT storage into scoreboard
1588
- this.builder.emitRaw(`execute store result score ${dst} rs run data get storage ${path}`);
1629
+ this.builder.emitRaw(`execute store result score ${dst} ${exports.LOWERING_OBJ} run data get storage ${path}`);
1589
1630
  return { kind: 'var', name: dst };
1590
1631
  }
1591
1632
  // Array length property
1592
1633
  if (varType?.kind === 'array' && expr.field === 'len') {
1593
1634
  const dst = this.builder.freshTemp();
1594
- this.builder.emitRaw(`execute store result score ${dst} rs run data get storage rs:heap ${expr.obj.name}`);
1635
+ this.builder.emitRaw(`execute store result score ${dst} ${exports.LOWERING_OBJ} run data get storage rs:heap ${expr.obj.name}`);
1595
1636
  return { kind: 'var', name: dst };
1596
1637
  }
1597
1638
  }
@@ -1607,10 +1648,10 @@ class Lowering {
1607
1648
  const value = this.lowerExpr(expr.value);
1608
1649
  if (expr.op === '=') {
1609
1650
  if (value.kind === 'const') {
1610
- this.builder.emitRaw(`scoreboard players set ${mapped} rs ${value.value}`);
1651
+ this.builder.emitRaw(`scoreboard players set ${mapped} ${exports.LOWERING_OBJ} ${value.value}`);
1611
1652
  }
1612
1653
  else if (value.kind === 'var') {
1613
- this.builder.emitRaw(`scoreboard players operation ${mapped} rs = ${value.name} rs`);
1654
+ this.builder.emitRaw(`scoreboard players operation ${mapped} ${exports.LOWERING_OBJ} = ${value.name} ${exports.LOWERING_OBJ}`);
1614
1655
  }
1615
1656
  }
1616
1657
  else {
@@ -1620,10 +1661,10 @@ class Lowering {
1620
1661
  if (value.kind === 'const') {
1621
1662
  const constTemp = this.builder.freshTemp();
1622
1663
  this.builder.emitAssign(constTemp, value);
1623
- this.builder.emitRaw(`scoreboard players operation ${mapped} rs ${opMap[binOp]} ${constTemp} rs`);
1664
+ this.builder.emitRaw(`scoreboard players operation ${mapped} ${exports.LOWERING_OBJ} ${opMap[binOp]} ${constTemp} ${exports.LOWERING_OBJ}`);
1624
1665
  }
1625
1666
  else if (value.kind === 'var') {
1626
- this.builder.emitRaw(`scoreboard players operation ${mapped} rs ${opMap[binOp]} ${value.name} rs`);
1667
+ this.builder.emitRaw(`scoreboard players operation ${mapped} ${exports.LOWERING_OBJ} ${opMap[binOp]} ${value.name} ${exports.LOWERING_OBJ}`);
1627
1668
  }
1628
1669
  }
1629
1670
  return { kind: 'const', value: 0 };
@@ -1637,16 +1678,16 @@ class Lowering {
1637
1678
  this.builder.emitRaw(`data modify storage ${path} set value ${value.value}`);
1638
1679
  }
1639
1680
  else if (value.kind === 'var') {
1640
- this.builder.emitRaw(`execute store result storage ${path} int 1 run scoreboard players get ${value.name} rs`);
1681
+ this.builder.emitRaw(`execute store result storage ${path} int 1 run scoreboard players get ${value.name} ${exports.LOWERING_OBJ}`);
1641
1682
  }
1642
1683
  }
1643
1684
  else {
1644
1685
  // Compound assignment: read, modify, write back
1645
1686
  const dst = this.builder.freshTemp();
1646
- this.builder.emitRaw(`execute store result score ${dst} rs run data get storage ${path}`);
1687
+ this.builder.emitRaw(`execute store result score ${dst} ${exports.LOWERING_OBJ} run data get storage ${path}`);
1647
1688
  const binOp = expr.op.slice(0, -1);
1648
1689
  this.builder.emitBinop(dst, { kind: 'var', name: dst }, binOp, value);
1649
- this.builder.emitRaw(`execute store result storage ${path} int 1 run scoreboard players get ${dst} rs`);
1690
+ this.builder.emitRaw(`execute store result storage ${path} int 1 run scoreboard players get ${dst} ${exports.LOWERING_OBJ}`);
1650
1691
  }
1651
1692
  return { kind: 'const', value: 0 };
1652
1693
  }
@@ -1675,14 +1716,14 @@ class Lowering {
1675
1716
  this.builder.emitAssign(dst, left);
1676
1717
  const rightVar = this.operandToVar(right);
1677
1718
  // dst = dst && right → if dst != 0 then dst = right
1678
- this.builder.emitRaw(`execute if score ${dst} rs matches 1.. run scoreboard players operation ${dst} rs = ${rightVar} rs`);
1719
+ this.builder.emitRaw(`execute if score ${dst} ${exports.LOWERING_OBJ} matches 1.. run scoreboard players operation ${dst} ${exports.LOWERING_OBJ} = ${rightVar} ${exports.LOWERING_OBJ}`);
1679
1720
  }
1680
1721
  else {
1681
1722
  // Short-circuit OR
1682
1723
  this.builder.emitAssign(dst, left);
1683
1724
  const rightVar = this.operandToVar(right);
1684
1725
  // dst = dst || right → if dst == 0 then dst = right
1685
- this.builder.emitRaw(`execute if score ${dst} rs matches ..0 run scoreboard players operation ${dst} rs = ${rightVar} rs`);
1726
+ this.builder.emitRaw(`execute if score ${dst} ${exports.LOWERING_OBJ} matches ..0 run scoreboard players operation ${dst} ${exports.LOWERING_OBJ} = ${rightVar} ${exports.LOWERING_OBJ}`);
1686
1727
  }
1687
1728
  return { kind: 'var', name: dst };
1688
1729
  }
@@ -1700,16 +1741,16 @@ class Lowering {
1700
1741
  // Divide by 1000 to correct for double scaling
1701
1742
  const constDiv = this.builder.freshTemp();
1702
1743
  this.builder.emitAssign(constDiv, { kind: 'const', value: 1000 });
1703
- this.builder.emitRaw(`scoreboard players operation ${dst} rs /= ${constDiv} rs`);
1744
+ this.builder.emitRaw(`scoreboard players operation ${dst} ${exports.LOWERING_OBJ} /= ${constDiv} ${exports.LOWERING_OBJ}`);
1704
1745
  }
1705
1746
  else {
1706
1747
  // Division: a * 1000 / b
1707
1748
  const constMul = this.builder.freshTemp();
1708
1749
  this.builder.emitAssign(constMul, { kind: 'const', value: 1000 });
1709
1750
  this.builder.emitAssign(dst, left);
1710
- this.builder.emitRaw(`scoreboard players operation ${dst} rs *= ${constMul} rs`);
1751
+ this.builder.emitRaw(`scoreboard players operation ${dst} ${exports.LOWERING_OBJ} *= ${constMul} ${exports.LOWERING_OBJ}`);
1711
1752
  const rightVar = this.operandToVar(right);
1712
- this.builder.emitRaw(`scoreboard players operation ${dst} rs /= ${rightVar} rs`);
1753
+ this.builder.emitRaw(`scoreboard players operation ${dst} ${exports.LOWERING_OBJ} /= ${rightVar} ${exports.LOWERING_OBJ}`);
1713
1754
  }
1714
1755
  return { kind: 'var', name: dst };
1715
1756
  }
@@ -1779,7 +1820,7 @@ class Lowering {
1779
1820
  const storagePath = this.getStringStoragePath(expr.args[0]);
1780
1821
  if (storagePath) {
1781
1822
  const dst = this.builder.freshTemp();
1782
- this.builder.emitRaw(`execute store result score ${dst} rs run data get storage ${storagePath}`);
1823
+ this.builder.emitRaw(`execute store result score ${dst} ${exports.LOWERING_OBJ} run data get storage ${storagePath}`);
1783
1824
  return { kind: 'var', name: dst };
1784
1825
  }
1785
1826
  const staticString = this.resolveStaticString(expr.args[0]);
@@ -1813,7 +1854,7 @@ class Lowering {
1813
1854
  const entity = this.exprToString(expr.args[0]);
1814
1855
  const tagName = this.exprToString(expr.args[1]);
1815
1856
  const dst = this.builder.freshTemp();
1816
- this.builder.emitRaw(`execute store result score ${dst} rs if entity ${entity}[tag=${tagName}]`);
1857
+ this.builder.emitRaw(`execute store result score ${dst} ${exports.LOWERING_OBJ} if entity ${entity}[tag=${tagName}]`);
1817
1858
  return { kind: 'var', name: dst };
1818
1859
  }
1819
1860
  // Handle array push
@@ -1828,7 +1869,7 @@ class Lowering {
1828
1869
  }
1829
1870
  else if (value.kind === 'var') {
1830
1871
  this.builder.emitRaw(`data modify storage rs:heap ${arrName} append value 0`);
1831
- this.builder.emitRaw(`execute store result storage rs:heap ${arrName}[-1] int 1 run scoreboard players get ${value.name} rs`);
1872
+ this.builder.emitRaw(`execute store result storage rs:heap ${arrName}[-1] int 1 run scoreboard players get ${value.name} ${exports.LOWERING_OBJ}`);
1832
1873
  }
1833
1874
  }
1834
1875
  return { kind: 'const', value: 0 };
@@ -1837,7 +1878,7 @@ class Lowering {
1837
1878
  const arrName = this.getArrayStorageName(expr.args[0]);
1838
1879
  const dst = this.builder.freshTemp();
1839
1880
  if (arrName) {
1840
- this.builder.emitRaw(`execute store result score ${dst} rs run data get storage rs:heap ${arrName}[-1]`);
1881
+ this.builder.emitRaw(`execute store result score ${dst} ${exports.LOWERING_OBJ} run data get storage rs:heap ${arrName}[-1]`);
1841
1882
  this.builder.emitRaw(`data remove storage rs:heap ${arrName}[-1]`);
1842
1883
  }
1843
1884
  else {
@@ -2101,7 +2142,7 @@ class Lowering {
2101
2142
  const dst = this.builder.freshTemp();
2102
2143
  const min = args[0] ? this.exprToLiteral(args[0]) : '0';
2103
2144
  const max = args[1] ? this.exprToLiteral(args[1]) : '100';
2104
- this.builder.emitRaw(`scoreboard players random ${dst} rs ${min} ${max}`);
2145
+ this.builder.emitRaw(`scoreboard players random ${dst} ${exports.LOWERING_OBJ} ${min} ${max}`);
2105
2146
  return { kind: 'var', name: dst };
2106
2147
  }
2107
2148
  // Special case: random_native - /random value (MC 1.20.3+)
@@ -2109,7 +2150,7 @@ class Lowering {
2109
2150
  const dst = this.builder.freshTemp();
2110
2151
  const min = args[0] ? this.exprToLiteral(args[0]) : '0';
2111
2152
  const max = args[1] ? this.exprToLiteral(args[1]) : '100';
2112
- this.builder.emitRaw(`execute store result score ${dst} rs run random value ${min} ${max}`);
2153
+ this.builder.emitRaw(`execute store result score ${dst} ${exports.LOWERING_OBJ} run random value ${min} ${max}`);
2113
2154
  return { kind: 'var', name: dst };
2114
2155
  }
2115
2156
  // Special case: random_sequence - /random reset (MC 1.20.3+)
@@ -2124,7 +2165,7 @@ class Lowering {
2124
2165
  const dst = this.builder.freshTemp();
2125
2166
  const player = this.exprToTargetString(args[0]);
2126
2167
  const objective = this.resolveScoreboardObjective(args[0], args[1], callSpan);
2127
- this.builder.emitRaw(`execute store result score ${dst} rs run scoreboard players get ${player} ${objective}`);
2168
+ this.builder.emitRaw(`execute store result score ${dst} ${exports.LOWERING_OBJ} run scoreboard players get ${player} ${objective}`);
2128
2169
  return { kind: 'var', name: dst };
2129
2170
  }
2130
2171
  // Special case: scoreboard_set — write to vanilla MC scoreboard
@@ -2138,7 +2179,7 @@ class Lowering {
2138
2179
  else if (value.kind === 'var') {
2139
2180
  // Read directly from the computed scoreboard temp. Routing through a fresh
2140
2181
  // temp here breaks once optimization removes the apparently-dead assign.
2141
- this.builder.emitRaw(`execute store result score ${player} ${objective} run scoreboard players get ${value.name} rs`);
2182
+ this.builder.emitRaw(`execute store result score ${player} ${objective} run scoreboard players get ${value.name} ${exports.LOWERING_OBJ}`);
2142
2183
  }
2143
2184
  return { kind: 'const', value: 0 };
2144
2185
  }
@@ -2201,7 +2242,7 @@ class Lowering {
2201
2242
  }
2202
2243
  if (name === 'bossbar_get_value') {
2203
2244
  const dst = this.builder.freshTemp();
2204
- this.builder.emitRaw(`execute store result score ${dst} rs run bossbar get ${this.exprToString(args[0])} value`);
2245
+ this.builder.emitRaw(`execute store result score ${dst} ${exports.LOWERING_OBJ} run bossbar get ${this.exprToString(args[0])} value`);
2205
2246
  return { kind: 'var', name: dst };
2206
2247
  }
2207
2248
  if (name === 'team_add') {
@@ -2242,7 +2283,7 @@ class Lowering {
2242
2283
  : this.exprToString(args[1]);
2243
2284
  const path = this.exprToString(args[2]);
2244
2285
  const scale = args[3] ? this.exprToString(args[3]) : '1';
2245
- this.builder.emitRaw(`execute store result score ${dst} rs run data get ${targetType} ${target} ${path} ${scale}`);
2286
+ this.builder.emitRaw(`execute store result score ${dst} ${exports.LOWERING_OBJ} run data get ${targetType} ${target} ${path} ${scale}`);
2246
2287
  return { kind: 'var', name: dst };
2247
2288
  }
2248
2289
  // storage_get_int(storage_ns, array_key, index) -> int
@@ -2251,7 +2292,7 @@ class Lowering {
2251
2292
  // array_key : e.g. "sin"
2252
2293
  // index : integer index (const or runtime)
2253
2294
  //
2254
- // Const index: execute store result score $dst rs run data get storage math:tables sin[N] 1
2295
+ // Const index: execute store result score $dst ${LOWERING_OBJ} run data get storage math:tables sin[N] 1
2255
2296
  // Runtime index: macro sub-function via rs:heap, mirrors readArrayElement.
2256
2297
  if (name === 'storage_get_int') {
2257
2298
  const storageNs = this.exprToString(args[0]); // "math:tables"
@@ -2259,7 +2300,7 @@ class Lowering {
2259
2300
  const indexOperand = this.lowerExpr(args[2]);
2260
2301
  const dst = this.builder.freshTemp();
2261
2302
  if (indexOperand.kind === 'const') {
2262
- this.builder.emitRaw(`execute store result score ${dst} rs run data get storage ${storageNs} ${arrayKey}[${indexOperand.value}] 1`);
2303
+ this.builder.emitRaw(`execute store result score ${dst} ${exports.LOWERING_OBJ} run data get storage ${storageNs} ${arrayKey}[${indexOperand.value}] 1`);
2263
2304
  }
2264
2305
  else {
2265
2306
  // Runtime index: store the index into rs:heap under a unique key,
@@ -2269,13 +2310,13 @@ class Lowering {
2269
2310
  const indexVar = indexOperand.kind === 'var'
2270
2311
  ? indexOperand.name
2271
2312
  : this.operandToVar(indexOperand);
2272
- this.builder.emitRaw(`execute store result storage rs:heap ${macroKey} int 1 run scoreboard players get ${indexVar} rs`);
2313
+ this.builder.emitRaw(`execute store result storage rs:heap ${macroKey} int 1 run scoreboard players get ${indexVar} ${exports.LOWERING_OBJ}`);
2273
2314
  this.builder.emitRaw(`function ${this.namespace}:${subFnName} with storage rs:heap`);
2274
2315
  // Prefix \x01 is a sentinel for the MC macro '$' line-start marker.
2275
2316
  // We avoid using literal '$execute' here so the pre-alloc pass
2276
2317
  // doesn't mistakenly register 'execute' as a scoreboard variable.
2277
2318
  // Codegen replaces \x01 → '$' when emitting the mc function file.
2278
- this.emitRawSubFunction(subFnName, `\x01execute store result score ${dst} rs run data get storage ${storageNs} ${arrayKey}[$(${macroKey})] 1`);
2319
+ this.emitRawSubFunction(subFnName, `\x01execute store result score ${dst} ${exports.LOWERING_OBJ} run data get storage ${storageNs} ${arrayKey}[$(${macroKey})] 1`);
2279
2320
  }
2280
2321
  return { kind: 'var', name: dst };
2281
2322
  }
@@ -2289,6 +2330,54 @@ class Lowering {
2289
2330
  this.builder.emitRaw(`data modify storage ${storageNs} ${arrayKey} set value ${nbtLiteral}`);
2290
2331
  return { kind: 'const', value: 0 };
2291
2332
  }
2333
+ // storage_set_int(storage_ns, array_key, index, value) -> void
2334
+ // Writes one integer element into an NBT int-array stored in data storage.
2335
+ // storage_ns : e.g. "rs:bigint"
2336
+ // array_key : e.g. "a"
2337
+ // index : element index (const or runtime)
2338
+ // value : integer value to write (const or runtime)
2339
+ //
2340
+ // Const index + const value:
2341
+ // execute store result storage <ns> <key>[N] int 1 run scoreboard players set $const_V ${LOWERING_OBJ} V
2342
+ // Runtime index or value: macro sub-function via rs:heap
2343
+ if (name === 'storage_set_int') {
2344
+ const storageNs = this.exprToString(args[0]);
2345
+ const arrayKey = this.exprToString(args[1]);
2346
+ const indexOperand = this.lowerExpr(args[2]);
2347
+ const valueOperand = this.lowerExpr(args[3]);
2348
+ if (indexOperand.kind === 'const') {
2349
+ // Static index — use execute store result to write to the fixed slot
2350
+ const valVar = valueOperand.kind === 'var'
2351
+ ? valueOperand.name
2352
+ : this.operandToVar(valueOperand);
2353
+ this.builder.emitRaw(`execute store result storage ${storageNs} ${arrayKey}[${indexOperand.value}] int 1 run scoreboard players get ${valVar} ${exports.LOWERING_OBJ}`);
2354
+ }
2355
+ else {
2356
+ // Runtime index: we need a macro sub-function.
2357
+ // Store index + value into rs:heap, call macro that does:
2358
+ // $data modify storage <ns> <key>[$(idx_key)] set value $(val_key)
2359
+ const macroIdxKey = `__ssi_i_${this.foreachCounter++}`;
2360
+ const macroValKey = `__ssi_v_${this.foreachCounter++}`; // kept to pin valVar in optimizer
2361
+ const subFnName = `${this.currentFn}/__ssi_${this.foreachCounter++}`;
2362
+ const indexVar = indexOperand.kind === 'var'
2363
+ ? indexOperand.name
2364
+ : this.operandToVar(indexOperand);
2365
+ const valVar = valueOperand.kind === 'var'
2366
+ ? valueOperand.name
2367
+ : this.operandToVar(valueOperand);
2368
+ this.builder.emitRaw(`execute store result storage rs:heap ${macroIdxKey} int 1 run scoreboard players get ${indexVar} ${exports.LOWERING_OBJ}`);
2369
+ // Pin valVar in the optimizer's read-set so the assignment is not dead-code-eliminated.
2370
+ // The value is stored to rs:heap but NOT used by the macro (the macro reads the scoreboard
2371
+ // slot directly to avoid the MC 'data modify set value $(n)' macro substitution bug).
2372
+ this.builder.emitRaw(`execute store result storage rs:heap ${macroValKey} int 1 run scoreboard players get ${valVar} ${exports.LOWERING_OBJ}`);
2373
+ this.builder.emitRaw(`function ${this.namespace}:${subFnName} with storage rs:heap`);
2374
+ // Use execute store result (not 'data modify set value $(val)') to avoid MC macro
2375
+ // substitution bugs with numeric values. The scoreboard slot ${valVar} is hardcoded
2376
+ // into the macro sub-function at compile time — only the array index is macro-substituted.
2377
+ this.emitRawSubFunction(subFnName, `\x01execute store result storage ${storageNs} ${arrayKey}[$(${macroIdxKey})] int 1 run scoreboard players get ${valVar} ${exports.LOWERING_OBJ}`);
2378
+ }
2379
+ return { kind: 'const', value: 0 };
2380
+ }
2292
2381
  // data_merge(target, nbt) — merge NBT into entity/block/storage
2293
2382
  // data_merge(@s, { Invisible: 1b, Silent: 1b })
2294
2383
  if (name === 'data_merge') {
@@ -2331,7 +2420,7 @@ class Lowering {
2331
2420
  const dst = this.builder.freshTemp();
2332
2421
  const setId = this.exprToString(args[0]);
2333
2422
  const value = this.exprToString(args[1]);
2334
- this.builder.emitRaw(`execute store result score ${dst} rs if data storage rs:sets ${setId}[{value:${value}}]`);
2423
+ this.builder.emitRaw(`execute store result score ${dst} ${exports.LOWERING_OBJ} if data storage rs:sets ${setId}[{value:${value}}]`);
2335
2424
  return { kind: 'var', name: dst };
2336
2425
  }
2337
2426
  if (name === 'set_remove') {
@@ -2596,7 +2685,7 @@ class Lowering {
2596
2685
  components.push({ text: operand.value.toString() });
2597
2686
  return;
2598
2687
  }
2599
- components.push({ score: { name: this.operandToVar(operand), objective: 'rs' } });
2688
+ components.push({ score: { name: this.operandToVar(operand), objective: exports.LOWERING_OBJ } });
2600
2689
  }
2601
2690
  exprToString(expr) {
2602
2691
  switch (expr.kind) {
@@ -2749,11 +2838,19 @@ class Lowering {
2749
2838
  }
2750
2839
  exprToScoreboardObjective(expr, span) {
2751
2840
  if (expr.kind === 'mc_name') {
2752
- return expr.value;
2841
+ // 'rs' is the canonical token for the current RS scoreboard objective.
2842
+ // Resolve to LOWERING_OBJ so it respects --scoreboard / namespace default.
2843
+ return expr.value === 'rs' ? exports.LOWERING_OBJ : expr.value;
2753
2844
  }
2754
2845
  const objective = this.exprToString(expr);
2755
2846
  if (objective.startsWith('#') || objective.includes('.')) {
2756
- return objective.startsWith('#') ? objective.slice(1) : objective;
2847
+ if (objective.startsWith('#')) {
2848
+ const name = objective.slice(1);
2849
+ // '#rs' is the canonical way to reference the current RS scoreboard objective.
2850
+ // Resolve to LOWERING_OBJ so it tracks the --scoreboard option.
2851
+ return name === 'rs' ? exports.LOWERING_OBJ : name;
2852
+ }
2853
+ return objective;
2757
2854
  }
2758
2855
  return `${this.getObjectiveNamespace(span)}.${objective}`;
2759
2856
  }
@@ -2769,7 +2866,7 @@ class Lowering {
2769
2866
  if (!filePath) {
2770
2867
  return this.namespace;
2771
2868
  }
2772
- return this.isStdlibFile(filePath) ? 'rs' : this.namespace;
2869
+ return this.isStdlibFile(filePath) ? exports.LOWERING_OBJ : this.namespace;
2773
2870
  }
2774
2871
  tryGetStdlibInternalObjective(playerExpr, objectiveExpr, span) {
2775
2872
  if (!span || !this.currentStdlibCallSite || objectiveExpr.kind !== 'mc_name' || objectiveExpr.value !== 'rs') {
@@ -2784,7 +2881,7 @@ class Lowering {
2784
2881
  return null;
2785
2882
  }
2786
2883
  const hash = this.shortHash(this.serializeCallSite(this.currentStdlibCallSite));
2787
- return `rs._${resourceBase}_${hash}`;
2884
+ return `${exports.LOWERING_OBJ}._${resourceBase}_${hash}`;
2788
2885
  }
2789
2886
  getStdlibInternalResourceBase(playerExpr) {
2790
2887
  if (!playerExpr || playerExpr.kind !== 'str_lit') {
@@ -3071,15 +3168,15 @@ class Lowering {
3071
3168
  readArrayElement(arrayName, index) {
3072
3169
  const dst = this.builder.freshTemp();
3073
3170
  if (index.kind === 'const') {
3074
- this.builder.emitRaw(`execute store result score ${dst} rs run data get storage rs:heap ${arrayName}[${index.value}]`);
3171
+ this.builder.emitRaw(`execute store result score ${dst} ${exports.LOWERING_OBJ} run data get storage rs:heap ${arrayName}[${index.value}]`);
3075
3172
  return { kind: 'var', name: dst };
3076
3173
  }
3077
3174
  const macroKey = `__rs_index_${this.foreachCounter++}`;
3078
3175
  const subFnName = `${this.currentFn}/array_get_${this.foreachCounter++}`;
3079
3176
  const indexVar = index.kind === 'var' ? index.name : this.operandToVar(index);
3080
- this.builder.emitRaw(`execute store result storage rs:heap ${macroKey} int 1 run scoreboard players get ${indexVar} rs`);
3177
+ this.builder.emitRaw(`execute store result storage rs:heap ${macroKey} int 1 run scoreboard players get ${indexVar} ${exports.LOWERING_OBJ}`);
3081
3178
  this.builder.emitRaw(`function ${this.namespace}:${subFnName} with storage rs:heap`);
3082
- this.emitRawSubFunction(subFnName, `\x01execute store result score ${dst} rs run data get storage rs:heap ${arrayName}[$(${macroKey})]`);
3179
+ this.emitRawSubFunction(subFnName, `\x01execute store result score ${dst} ${exports.LOWERING_OBJ} run data get storage rs:heap ${arrayName}[$(${macroKey})]`);
3083
3180
  return { kind: 'var', name: dst };
3084
3181
  }
3085
3182
  emitRawSubFunction(name, ...commands) {
@@ -0,0 +1,6 @@
1
+ {
2
+ "$b": "x",
3
+ "$a": "const:0",
4
+ "$c": "internal:p0",
5
+ "$f": "internal:ret"
6
+ }
@@ -0,0 +1,27 @@
1
+ {
2
+ "$o": "x",
3
+ "$r": "execute",
4
+ "$u": "y",
5
+ "$aa": "ax",
6
+ "$ad": "ay",
7
+ "$ai": "swapped",
8
+ "$ak": "tmp",
9
+ "$al": "lo",
10
+ "$am": "hi",
11
+ "$ar": "mid",
12
+ "$as": "t",
13
+ "$ax": "theta",
14
+ "$a": "const:0",
15
+ "$b": "const:1",
16
+ "$c": "const:2",
17
+ "$d": "const:45",
18
+ "$e": "const:90",
19
+ "$f": "const:180",
20
+ "$g": "const:270",
21
+ "$h": "const:360",
22
+ "$i": "const:1000",
23
+ "$j": "const:46340",
24
+ "$k": "internal:ret",
25
+ "$n": "internal:p0",
26
+ "$t": "internal:p1"
27
+ }
@@ -17,6 +17,7 @@ export interface CommandFunction {
17
17
  name: string;
18
18
  commands: IRCommand[];
19
19
  }
20
+ export declare function setOptimizerObjective(obj: string): void;
20
21
  export declare function createEmptyOptimizationStats(): OptimizationStats;
21
22
  export declare function mergeOptimizationStats(base: OptimizationStats, delta: Partial<OptimizationStats>): void;
22
23
  export declare function applyLICM(functions: CommandFunction[]): {