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
@@ -29,6 +29,15 @@ import { getBaseSelectorType, areCompatibleTypes, getConcreteSubtypes } from '..
29
29
  // All builtins support macro parameters - any arg that's a function param
30
30
  // will automatically use MC 1.20.2+ macro syntax when needed
31
31
 
32
+ // ---------------------------------------------------------------------------
33
+ // Scoreboard Objective
34
+ // Set per-compilation via setScoreboardObjective() — defaults to 'rs'.
35
+ // ---------------------------------------------------------------------------
36
+
37
+ /** Current scoreboard objective. Set once per compile() call. */
38
+ export let LOWERING_OBJ = 'rs'
39
+ export function setScoreboardObjective(obj: string): void { LOWERING_OBJ = obj }
40
+
32
41
  // ---------------------------------------------------------------------------
33
42
  // Builtin Functions
34
43
  // ---------------------------------------------------------------------------
@@ -50,9 +59,13 @@ const BUILTINS: Record<string, (args: string[]) => string | null> = {
50
59
  const pos = [x ?? '~', y ?? '~', z ?? '~'].join(' ')
51
60
  return nbt ? `summon ${type} ${pos} ${nbt}` : `summon ${type} ${pos}`
52
61
  },
53
- particle: ([name, x, y, z]) => {
62
+ particle: ([name, x, y, z, dx, dy, dz, speed, count]) => {
54
63
  const pos = [x ?? '~', y ?? '~', z ?? '~'].join(' ')
55
- return `particle ${name} ${pos}`
64
+ // dx/dy/dz/speed/count are optional; omit trailing undefineds
65
+ const extra = [dx, dy, dz, speed, count].filter(v => v !== undefined && v !== null)
66
+ return extra.length > 0
67
+ ? `particle ${name} ${pos} ${extra.join(' ')}`
68
+ : `particle ${name} ${pos}`
56
69
  },
57
70
  playsound: ([sound, source, sel, x, y, z, volume, pitch, minVolume]) =>
58
71
  ['playsound', sound, source, sel, x, y, z, volume, pitch, minVolume].filter(Boolean).join(' '),
@@ -108,6 +121,7 @@ const BUILTINS: Record<string, (args: string[]) => string | null> = {
108
121
  clearInterval: () => null, // Special handling
109
122
  storage_get_int: () => null, // Special handling (dynamic NBT array read via macro)
110
123
  storage_set_array: () => null, // Special handling (write literal NBT array to storage)
124
+ storage_set_int: () => null, // Special handling (dynamic NBT array write via macro)
111
125
  }
112
126
 
113
127
  export interface Warning {
@@ -364,12 +378,24 @@ export class Lowering {
364
378
 
365
379
  private preScanExpr(expr: Expr, paramNames: Set<string>, macroParams: Set<string>): void {
366
380
  if (expr.kind === 'call' && BUILTINS[expr.fn] !== undefined) {
367
- // All ident args to macro-aware builtins that are params → macro params
368
- for (const arg of expr.args) {
369
- if (arg.kind === 'ident' && paramNames.has(arg.name)) {
370
- macroParams.add(arg.name)
381
+ // Only trigger macro param detection for builtins that actually emit
382
+ // MC commands with $(param) inline in the current function body.
383
+ // Special-handled builtins (storage_get_int / storage_set_int / etc.) are
384
+ // declared as `() => null` — they create their own sub-functions for macro
385
+ // indirection and do NOT require the surrounding function to be a macro.
386
+ const handler = BUILTINS[expr.fn]!
387
+ const isSpecialHandled: boolean = (() => {
388
+ try { return (handler as () => string | null)() === null } catch { return false }
389
+ })()
390
+ if (!isSpecialHandled) {
391
+ for (const arg of expr.args) {
392
+ if (arg.kind === 'ident' && paramNames.has(arg.name)) {
393
+ macroParams.add(arg.name)
394
+ }
371
395
  }
372
396
  }
397
+ // Always recurse into args for nested calls/expressions
398
+ for (const arg of expr.args) this.preScanExpr(arg, paramNames, macroParams)
373
399
  return
374
400
  }
375
401
  // Recurse into sub-expressions for other call types
@@ -452,6 +478,16 @@ export class Lowering {
452
478
  if (expr.kind === 'struct_lit' || expr.kind === 'array_lit') {
453
479
  return { str: this.exprToSnbt(expr) }
454
480
  }
481
+ // Float literals: preserve the float value for MC commands that accept floats
482
+ // (particle spread, playsound volume/pitch, etc.)
483
+ // We do NOT truncate here — that only applies to scoreboard/IR contexts.
484
+ if (expr.kind === 'float_lit') {
485
+ return { str: expr.value.toString() }
486
+ }
487
+ // Unary minus applied to a float literal (e.g. -0.5)
488
+ if (expr.kind === 'unary' && expr.op === '-' && expr.operand.kind === 'float_lit') {
489
+ return { str: (-expr.operand.value).toString() }
490
+ }
455
491
  return { str: this.exprToString(expr) }
456
492
  }
457
493
 
@@ -472,9 +508,9 @@ export class Lowering {
472
508
  for (let i = 0; i < loweredArgs.length; i++) {
473
509
  const operand = loweredArgs[i]
474
510
  if (operand.kind === 'const') {
475
- this.builder.emitRaw(`scoreboard players set $p${i} rs ${operand.value}`)
511
+ this.builder.emitRaw(`scoreboard players set $p${i} ${LOWERING_OBJ} ${operand.value}`)
476
512
  } else if (operand.kind === 'var') {
477
- this.builder.emitRaw(`scoreboard players operation $p${i} rs = ${operand.name} rs`)
513
+ this.builder.emitRaw(`scoreboard players operation $p${i} ${LOWERING_OBJ} = ${operand.name} ${LOWERING_OBJ}`)
478
514
  }
479
515
  }
480
516
 
@@ -488,7 +524,7 @@ export class Lowering {
488
524
  this.builder.emitRaw(`data modify storage rs:macro_args ${macroParam} set value ${operand.value}`)
489
525
  } else if (operand.kind === 'var') {
490
526
  this.builder.emitRaw(
491
- `execute store result storage rs:macro_args ${macroParam} int 1 run scoreboard players get ${operand.name} rs`
527
+ `execute store result storage rs:macro_args ${macroParam} int 1 run scoreboard players get ${operand.name} ${LOWERING_OBJ}`
492
528
  )
493
529
  }
494
530
  }
@@ -498,7 +534,7 @@ export class Lowering {
498
534
 
499
535
  // Copy return value (callers may use it)
500
536
  const dst = this.builder.freshTemp()
501
- this.builder.emitRaw(`scoreboard players operation ${dst} rs = $ret rs`)
537
+ this.builder.emitRaw(`scoreboard players operation ${dst} ${LOWERING_OBJ} = $ret ${LOWERING_OBJ}`)
502
538
  return { kind: 'var', name: dst }
503
539
  }
504
540
 
@@ -779,7 +815,7 @@ export class Lowering {
779
815
  const originalTerm = entry.term
780
816
 
781
817
  entry.instrs = [
782
- { op: 'raw', cmd: `scoreboard players add ${counterVar} rs 1` },
818
+ { op: 'raw', cmd: `scoreboard players add ${counterVar} ${LOWERING_OBJ} 1` },
783
819
  ]
784
820
 
785
821
  // Create conditional jump
@@ -796,14 +832,14 @@ export class Lowering {
796
832
  // Add check instruction
797
833
  entry.instrs.push({
798
834
  op: 'raw',
799
- cmd: `execute store success score ${counterVar}_check rs if score ${counterVar} rs matches ${rate}..`,
835
+ cmd: `execute store success score ${counterVar}_check ${LOWERING_OBJ} if score ${counterVar} ${LOWERING_OBJ} matches ${rate}..`,
800
836
  })
801
837
 
802
838
  // Body block (original logic + counter reset)
803
839
  fn.blocks.push({
804
840
  label: bodyLabel,
805
841
  instrs: [
806
- { op: 'raw', cmd: `scoreboard players set ${counterVar} rs 0` },
842
+ { op: 'raw', cmd: `scoreboard players set ${counterVar} ${LOWERING_OBJ} 0` },
807
843
  ...originalInstrs,
808
844
  ],
809
845
  term: originalTerm,
@@ -930,7 +966,7 @@ export class Lowering {
930
966
  this.builder.emitRaw(`data modify storage ${path} set value ${fieldValue.value}`)
931
967
  } else if (fieldValue.kind === 'var') {
932
968
  // Copy from scoreboard to NBT
933
- this.builder.emitRaw(`execute store result storage ${path} int 1 run scoreboard players get ${fieldValue.name} rs`)
969
+ this.builder.emitRaw(`execute store result storage ${path} int 1 run scoreboard players get ${fieldValue.name} ${LOWERING_OBJ}`)
934
970
  }
935
971
  }
936
972
  return
@@ -964,7 +1000,7 @@ export class Lowering {
964
1000
  this.builder.emitRaw(`data modify storage rs:heap ${stmt.name} append value ${elemValue.value}`)
965
1001
  } else if (elemValue.kind === 'var') {
966
1002
  this.builder.emitRaw(`data modify storage rs:heap ${stmt.name} append value 0`)
967
- this.builder.emitRaw(`execute store result storage rs:heap ${stmt.name}[-1] int 1 run scoreboard players get ${elemValue.name} rs`)
1003
+ this.builder.emitRaw(`execute store result storage rs:heap ${stmt.name}[-1] int 1 run scoreboard players get ${elemValue.name} ${LOWERING_OBJ}`)
968
1004
  }
969
1005
  }
970
1006
  return
@@ -1015,7 +1051,7 @@ export class Lowering {
1015
1051
  if (fieldValue.kind === 'const') {
1016
1052
  this.builder.emitRaw(`data modify storage ${path} set value ${fieldValue.value}`)
1017
1053
  } else if (fieldValue.kind === 'var') {
1018
- this.builder.emitRaw(`execute store result storage ${path} int 1 run scoreboard players get ${fieldValue.name} rs`)
1054
+ this.builder.emitRaw(`execute store result storage ${path} int 1 run scoreboard players get ${fieldValue.name} ${LOWERING_OBJ}`)
1019
1055
  }
1020
1056
  }
1021
1057
  this.builder.emitReturn({ kind: 'const', value: 0 })
@@ -1234,9 +1270,9 @@ export class Lowering {
1234
1270
  this.varMap.set(stmt.varName, loopVar)
1235
1271
  const startVal = this.lowerExpr(stmt.start)
1236
1272
  if (startVal.kind === 'const') {
1237
- this.builder.emitRaw(`scoreboard players set ${loopVar} rs ${startVal.value}`)
1273
+ this.builder.emitRaw(`scoreboard players set ${loopVar} ${LOWERING_OBJ} ${startVal.value}`)
1238
1274
  } else if (startVal.kind === 'var') {
1239
- this.builder.emitRaw(`scoreboard players operation ${loopVar} rs = ${startVal.name} rs`)
1275
+ this.builder.emitRaw(`scoreboard players operation ${loopVar} ${LOWERING_OBJ} = ${startVal.name} ${LOWERING_OBJ}`)
1240
1276
  }
1241
1277
 
1242
1278
  // Call loop function
@@ -1259,12 +1295,12 @@ export class Lowering {
1259
1295
  this.lowerBlock(stmt.body)
1260
1296
 
1261
1297
  // Increment
1262
- this.builder.emitRaw(`scoreboard players add ${loopVar} rs 1`)
1298
+ this.builder.emitRaw(`scoreboard players add ${loopVar} ${LOWERING_OBJ} 1`)
1263
1299
 
1264
1300
  // Loop condition: execute if score matches ..<end-1> run function
1265
1301
  const endVal = this.lowerExpr(stmt.end)
1266
1302
  const endNum = endVal.kind === 'const' ? endVal.value - 1 : '?'
1267
- this.builder.emitRaw(`execute if score ${loopVar} rs matches ..${endNum} run function ${this.namespace}:${subFnName}`)
1303
+ this.builder.emitRaw(`execute if score ${loopVar} ${LOWERING_OBJ} matches ..${endNum} run function ${this.namespace}:${subFnName}`)
1268
1304
 
1269
1305
  if (!this.builder.isBlockSealed()) {
1270
1306
  this.builder.emitReturn()
@@ -1369,13 +1405,13 @@ export class Lowering {
1369
1405
  }
1370
1406
 
1371
1407
  const subFnName = `${this.currentFn}/match_${this.foreachCounter++}`
1372
- this.builder.emitRaw(`execute if score ${matchedVar} rs matches ..0 if score ${subject} rs matches ${matchCondition} run function ${this.namespace}:${subFnName}`)
1408
+ this.builder.emitRaw(`execute if score ${matchedVar} ${LOWERING_OBJ} matches ..0 if score ${subject} ${LOWERING_OBJ} matches ${matchCondition} run function ${this.namespace}:${subFnName}`)
1373
1409
  this.emitMatchArmSubFunction(subFnName, matchedVar, arm.body, true)
1374
1410
  }
1375
1411
 
1376
1412
  if (defaultArm) {
1377
1413
  const subFnName = `${this.currentFn}/match_${this.foreachCounter++}`
1378
- this.builder.emitRaw(`execute if score ${matchedVar} rs matches ..0 run function ${this.namespace}:${subFnName}`)
1414
+ this.builder.emitRaw(`execute if score ${matchedVar} ${LOWERING_OBJ} matches ..0 run function ${this.namespace}:${subFnName}`)
1379
1415
  this.emitMatchArmSubFunction(subFnName, matchedVar, defaultArm.body, false)
1380
1416
  }
1381
1417
  }
@@ -1393,7 +1429,7 @@ export class Lowering {
1393
1429
 
1394
1430
  this.builder.startBlock('entry')
1395
1431
  if (setMatched) {
1396
- this.builder.emitRaw(`scoreboard players set ${matchedVar} rs 1`)
1432
+ this.builder.emitRaw(`scoreboard players set ${matchedVar} ${LOWERING_OBJ} 1`)
1397
1433
  }
1398
1434
  this.lowerBlock(body)
1399
1435
  if (!this.builder.isBlockSealed()) {
@@ -1432,7 +1468,7 @@ export class Lowering {
1432
1468
 
1433
1469
  this.builder.emitAssign(indexVar, { kind: 'const', value: 0 })
1434
1470
  this.builder.emitAssign(oneVar, { kind: 'const', value: 1 })
1435
- this.builder.emitRaw(`execute store result score ${lengthVar} rs run data get storage rs:heap ${arrayName}`)
1471
+ this.builder.emitRaw(`execute store result score ${lengthVar} ${LOWERING_OBJ} run data get storage rs:heap ${arrayName}`)
1436
1472
 
1437
1473
  const checkLabel = this.builder.freshLabel('foreach_array_check')
1438
1474
  const bodyLabel = this.builder.freshLabel('foreach_array_body')
@@ -1449,7 +1485,7 @@ export class Lowering {
1449
1485
  this.builder.emitAssign(bindingVar, element)
1450
1486
  this.lowerBlock(stmt.body)
1451
1487
  if (!this.builder.isBlockSealed()) {
1452
- this.builder.emitRaw(`scoreboard players operation ${indexVar} rs += ${oneVar} rs`)
1488
+ this.builder.emitRaw(`scoreboard players operation ${indexVar} ${LOWERING_OBJ} += ${oneVar} ${LOWERING_OBJ}`)
1453
1489
  this.builder.emitJump(checkLabel)
1454
1490
  }
1455
1491
 
@@ -1828,7 +1864,7 @@ export class Lowering {
1828
1864
  if (mapped && mapped.startsWith('@e[tag=__rs_obj_')) {
1829
1865
  // World object field access → scoreboard get
1830
1866
  const dst = this.builder.freshTemp()
1831
- this.builder.emitRaw(`scoreboard players operation ${dst} rs = ${mapped} rs`)
1867
+ this.builder.emitRaw(`scoreboard players operation ${dst} ${LOWERING_OBJ} = ${mapped} ${LOWERING_OBJ}`)
1832
1868
  return { kind: 'var', name: dst }
1833
1869
  }
1834
1870
 
@@ -1837,14 +1873,14 @@ export class Lowering {
1837
1873
  const path = `rs:heap ${structName}_${expr.obj.name}.${expr.field}`
1838
1874
  const dst = this.builder.freshTemp()
1839
1875
  // Read from NBT storage into scoreboard
1840
- this.builder.emitRaw(`execute store result score ${dst} rs run data get storage ${path}`)
1876
+ this.builder.emitRaw(`execute store result score ${dst} ${LOWERING_OBJ} run data get storage ${path}`)
1841
1877
  return { kind: 'var', name: dst }
1842
1878
  }
1843
1879
 
1844
1880
  // Array length property
1845
1881
  if (varType?.kind === 'array' && expr.field === 'len') {
1846
1882
  const dst = this.builder.freshTemp()
1847
- this.builder.emitRaw(`execute store result score ${dst} rs run data get storage rs:heap ${expr.obj.name}`)
1883
+ this.builder.emitRaw(`execute store result score ${dst} ${LOWERING_OBJ} run data get storage rs:heap ${expr.obj.name}`)
1848
1884
  return { kind: 'var', name: dst }
1849
1885
  }
1850
1886
  }
@@ -1863,9 +1899,9 @@ export class Lowering {
1863
1899
  const value = this.lowerExpr(expr.value)
1864
1900
  if (expr.op === '=') {
1865
1901
  if (value.kind === 'const') {
1866
- this.builder.emitRaw(`scoreboard players set ${mapped} rs ${value.value}`)
1902
+ this.builder.emitRaw(`scoreboard players set ${mapped} ${LOWERING_OBJ} ${value.value}`)
1867
1903
  } else if (value.kind === 'var') {
1868
- this.builder.emitRaw(`scoreboard players operation ${mapped} rs = ${value.name} rs`)
1904
+ this.builder.emitRaw(`scoreboard players operation ${mapped} ${LOWERING_OBJ} = ${value.name} ${LOWERING_OBJ}`)
1869
1905
  }
1870
1906
  } else {
1871
1907
  // Compound assignment
@@ -1874,9 +1910,9 @@ export class Lowering {
1874
1910
  if (value.kind === 'const') {
1875
1911
  const constTemp = this.builder.freshTemp()
1876
1912
  this.builder.emitAssign(constTemp, value)
1877
- this.builder.emitRaw(`scoreboard players operation ${mapped} rs ${opMap[binOp]} ${constTemp} rs`)
1913
+ this.builder.emitRaw(`scoreboard players operation ${mapped} ${LOWERING_OBJ} ${opMap[binOp]} ${constTemp} ${LOWERING_OBJ}`)
1878
1914
  } else if (value.kind === 'var') {
1879
- this.builder.emitRaw(`scoreboard players operation ${mapped} rs ${opMap[binOp]} ${value.name} rs`)
1915
+ this.builder.emitRaw(`scoreboard players operation ${mapped} ${LOWERING_OBJ} ${opMap[binOp]} ${value.name} ${LOWERING_OBJ}`)
1880
1916
  }
1881
1917
  }
1882
1918
  return { kind: 'const', value: 0 }
@@ -1891,15 +1927,15 @@ export class Lowering {
1891
1927
  if (value.kind === 'const') {
1892
1928
  this.builder.emitRaw(`data modify storage ${path} set value ${value.value}`)
1893
1929
  } else if (value.kind === 'var') {
1894
- this.builder.emitRaw(`execute store result storage ${path} int 1 run scoreboard players get ${value.name} rs`)
1930
+ this.builder.emitRaw(`execute store result storage ${path} int 1 run scoreboard players get ${value.name} ${LOWERING_OBJ}`)
1895
1931
  }
1896
1932
  } else {
1897
1933
  // Compound assignment: read, modify, write back
1898
1934
  const dst = this.builder.freshTemp()
1899
- this.builder.emitRaw(`execute store result score ${dst} rs run data get storage ${path}`)
1935
+ this.builder.emitRaw(`execute store result score ${dst} ${LOWERING_OBJ} run data get storage ${path}`)
1900
1936
  const binOp = expr.op.slice(0, -1)
1901
1937
  this.builder.emitBinop(dst, { kind: 'var', name: dst }, binOp as any, value)
1902
- this.builder.emitRaw(`execute store result storage ${path} int 1 run scoreboard players get ${dst} rs`)
1938
+ this.builder.emitRaw(`execute store result storage ${path} int 1 run scoreboard players get ${dst} ${LOWERING_OBJ}`)
1903
1939
  }
1904
1940
  return { kind: 'const', value: 0 }
1905
1941
  }
@@ -1932,13 +1968,13 @@ export class Lowering {
1932
1968
  this.builder.emitAssign(dst, left)
1933
1969
  const rightVar = this.operandToVar(right)
1934
1970
  // dst = dst && right → if dst != 0 then dst = right
1935
- this.builder.emitRaw(`execute if score ${dst} rs matches 1.. run scoreboard players operation ${dst} rs = ${rightVar} rs`)
1971
+ this.builder.emitRaw(`execute if score ${dst} ${LOWERING_OBJ} matches 1.. run scoreboard players operation ${dst} ${LOWERING_OBJ} = ${rightVar} ${LOWERING_OBJ}`)
1936
1972
  } else {
1937
1973
  // Short-circuit OR
1938
1974
  this.builder.emitAssign(dst, left)
1939
1975
  const rightVar = this.operandToVar(right)
1940
1976
  // dst = dst || right → if dst == 0 then dst = right
1941
- this.builder.emitRaw(`execute if score ${dst} rs matches ..0 run scoreboard players operation ${dst} rs = ${rightVar} rs`)
1977
+ this.builder.emitRaw(`execute if score ${dst} ${LOWERING_OBJ} matches ..0 run scoreboard players operation ${dst} ${LOWERING_OBJ} = ${rightVar} ${LOWERING_OBJ}`)
1942
1978
  }
1943
1979
  return { kind: 'var', name: dst }
1944
1980
  }
@@ -1957,15 +1993,15 @@ export class Lowering {
1957
1993
  // Divide by 1000 to correct for double scaling
1958
1994
  const constDiv = this.builder.freshTemp()
1959
1995
  this.builder.emitAssign(constDiv, { kind: 'const', value: 1000 })
1960
- this.builder.emitRaw(`scoreboard players operation ${dst} rs /= ${constDiv} rs`)
1996
+ this.builder.emitRaw(`scoreboard players operation ${dst} ${LOWERING_OBJ} /= ${constDiv} ${LOWERING_OBJ}`)
1961
1997
  } else {
1962
1998
  // Division: a * 1000 / b
1963
1999
  const constMul = this.builder.freshTemp()
1964
2000
  this.builder.emitAssign(constMul, { kind: 'const', value: 1000 })
1965
2001
  this.builder.emitAssign(dst, left)
1966
- this.builder.emitRaw(`scoreboard players operation ${dst} rs *= ${constMul} rs`)
2002
+ this.builder.emitRaw(`scoreboard players operation ${dst} ${LOWERING_OBJ} *= ${constMul} ${LOWERING_OBJ}`)
1967
2003
  const rightVar = this.operandToVar(right)
1968
- this.builder.emitRaw(`scoreboard players operation ${dst} rs /= ${rightVar} rs`)
2004
+ this.builder.emitRaw(`scoreboard players operation ${dst} ${LOWERING_OBJ} /= ${rightVar} ${LOWERING_OBJ}`)
1969
2005
  }
1970
2006
  return { kind: 'var', name: dst }
1971
2007
  }
@@ -2044,7 +2080,7 @@ export class Lowering {
2044
2080
  const storagePath = this.getStringStoragePath(expr.args[0])
2045
2081
  if (storagePath) {
2046
2082
  const dst = this.builder.freshTemp()
2047
- this.builder.emitRaw(`execute store result score ${dst} rs run data get storage ${storagePath}`)
2083
+ this.builder.emitRaw(`execute store result score ${dst} ${LOWERING_OBJ} run data get storage ${storagePath}`)
2048
2084
  return { kind: 'var', name: dst }
2049
2085
  }
2050
2086
 
@@ -2082,7 +2118,7 @@ export class Lowering {
2082
2118
  const entity = this.exprToString(expr.args[0])
2083
2119
  const tagName = this.exprToString(expr.args[1])
2084
2120
  const dst = this.builder.freshTemp()
2085
- this.builder.emitRaw(`execute store result score ${dst} rs if entity ${entity}[tag=${tagName}]`)
2121
+ this.builder.emitRaw(`execute store result score ${dst} ${LOWERING_OBJ} if entity ${entity}[tag=${tagName}]`)
2086
2122
  return { kind: 'var', name: dst }
2087
2123
  }
2088
2124
 
@@ -2097,7 +2133,7 @@ export class Lowering {
2097
2133
  this.builder.emitRaw(`data modify storage rs:heap ${arrName} append value ${value.value}`)
2098
2134
  } else if (value.kind === 'var') {
2099
2135
  this.builder.emitRaw(`data modify storage rs:heap ${arrName} append value 0`)
2100
- this.builder.emitRaw(`execute store result storage rs:heap ${arrName}[-1] int 1 run scoreboard players get ${value.name} rs`)
2136
+ this.builder.emitRaw(`execute store result storage rs:heap ${arrName}[-1] int 1 run scoreboard players get ${value.name} ${LOWERING_OBJ}`)
2101
2137
  }
2102
2138
  }
2103
2139
  return { kind: 'const', value: 0 }
@@ -2107,7 +2143,7 @@ export class Lowering {
2107
2143
  const arrName = this.getArrayStorageName(expr.args[0])
2108
2144
  const dst = this.builder.freshTemp()
2109
2145
  if (arrName) {
2110
- this.builder.emitRaw(`execute store result score ${dst} rs run data get storage rs:heap ${arrName}[-1]`)
2146
+ this.builder.emitRaw(`execute store result score ${dst} ${LOWERING_OBJ} run data get storage rs:heap ${arrName}[-1]`)
2111
2147
  this.builder.emitRaw(`data remove storage rs:heap ${arrName}[-1]`)
2112
2148
  } else {
2113
2149
  this.builder.emitAssign(dst, { kind: 'const', value: 0 })
@@ -2407,7 +2443,7 @@ export class Lowering {
2407
2443
  const dst = this.builder.freshTemp()
2408
2444
  const min = args[0] ? this.exprToLiteral(args[0]) : '0'
2409
2445
  const max = args[1] ? this.exprToLiteral(args[1]) : '100'
2410
- this.builder.emitRaw(`scoreboard players random ${dst} rs ${min} ${max}`)
2446
+ this.builder.emitRaw(`scoreboard players random ${dst} ${LOWERING_OBJ} ${min} ${max}`)
2411
2447
  return { kind: 'var', name: dst }
2412
2448
  }
2413
2449
 
@@ -2416,7 +2452,7 @@ export class Lowering {
2416
2452
  const dst = this.builder.freshTemp()
2417
2453
  const min = args[0] ? this.exprToLiteral(args[0]) : '0'
2418
2454
  const max = args[1] ? this.exprToLiteral(args[1]) : '100'
2419
- this.builder.emitRaw(`execute store result score ${dst} rs run random value ${min} ${max}`)
2455
+ this.builder.emitRaw(`execute store result score ${dst} ${LOWERING_OBJ} run random value ${min} ${max}`)
2420
2456
  return { kind: 'var', name: dst }
2421
2457
  }
2422
2458
 
@@ -2433,7 +2469,7 @@ export class Lowering {
2433
2469
  const dst = this.builder.freshTemp()
2434
2470
  const player = this.exprToTargetString(args[0])
2435
2471
  const objective = this.resolveScoreboardObjective(args[0], args[1], callSpan)
2436
- this.builder.emitRaw(`execute store result score ${dst} rs run scoreboard players get ${player} ${objective}`)
2472
+ this.builder.emitRaw(`execute store result score ${dst} ${LOWERING_OBJ} run scoreboard players get ${player} ${objective}`)
2437
2473
  return { kind: 'var', name: dst }
2438
2474
  }
2439
2475
 
@@ -2447,7 +2483,7 @@ export class Lowering {
2447
2483
  } else if (value.kind === 'var') {
2448
2484
  // Read directly from the computed scoreboard temp. Routing through a fresh
2449
2485
  // temp here breaks once optimization removes the apparently-dead assign.
2450
- this.builder.emitRaw(`execute store result score ${player} ${objective} run scoreboard players get ${value.name} rs`)
2486
+ this.builder.emitRaw(`execute store result score ${player} ${objective} run scoreboard players get ${value.name} ${LOWERING_OBJ}`)
2451
2487
  }
2452
2488
  return { kind: 'const', value: 0 }
2453
2489
  }
@@ -2523,7 +2559,7 @@ export class Lowering {
2523
2559
 
2524
2560
  if (name === 'bossbar_get_value') {
2525
2561
  const dst = this.builder.freshTemp()
2526
- this.builder.emitRaw(`execute store result score ${dst} rs run bossbar get ${this.exprToString(args[0])} value`)
2562
+ this.builder.emitRaw(`execute store result score ${dst} ${LOWERING_OBJ} run bossbar get ${this.exprToString(args[0])} value`)
2527
2563
  return { kind: 'var', name: dst }
2528
2564
  }
2529
2565
 
@@ -2570,7 +2606,7 @@ export class Lowering {
2570
2606
  : this.exprToString(args[1])
2571
2607
  const path = this.exprToString(args[2])
2572
2608
  const scale = args[3] ? this.exprToString(args[3]) : '1'
2573
- this.builder.emitRaw(`execute store result score ${dst} rs run data get ${targetType} ${target} ${path} ${scale}`)
2609
+ this.builder.emitRaw(`execute store result score ${dst} ${LOWERING_OBJ} run data get ${targetType} ${target} ${path} ${scale}`)
2574
2610
  return { kind: 'var', name: dst }
2575
2611
  }
2576
2612
 
@@ -2580,7 +2616,7 @@ export class Lowering {
2580
2616
  // array_key : e.g. "sin"
2581
2617
  // index : integer index (const or runtime)
2582
2618
  //
2583
- // Const index: execute store result score $dst rs run data get storage math:tables sin[N] 1
2619
+ // Const index: execute store result score $dst ${LOWERING_OBJ} run data get storage math:tables sin[N] 1
2584
2620
  // Runtime index: macro sub-function via rs:heap, mirrors readArrayElement.
2585
2621
  if (name === 'storage_get_int') {
2586
2622
  const storageNs = this.exprToString(args[0]) // "math:tables"
@@ -2590,7 +2626,7 @@ export class Lowering {
2590
2626
 
2591
2627
  if (indexOperand.kind === 'const') {
2592
2628
  this.builder.emitRaw(
2593
- `execute store result score ${dst} rs run data get storage ${storageNs} ${arrayKey}[${indexOperand.value}] 1`
2629
+ `execute store result score ${dst} ${LOWERING_OBJ} run data get storage ${storageNs} ${arrayKey}[${indexOperand.value}] 1`
2594
2630
  )
2595
2631
  } else {
2596
2632
  // Runtime index: store the index into rs:heap under a unique key,
@@ -2601,7 +2637,7 @@ export class Lowering {
2601
2637
  ? indexOperand.name
2602
2638
  : this.operandToVar(indexOperand)
2603
2639
  this.builder.emitRaw(
2604
- `execute store result storage rs:heap ${macroKey} int 1 run scoreboard players get ${indexVar} rs`
2640
+ `execute store result storage rs:heap ${macroKey} int 1 run scoreboard players get ${indexVar} ${LOWERING_OBJ}`
2605
2641
  )
2606
2642
  this.builder.emitRaw(`function ${this.namespace}:${subFnName} with storage rs:heap`)
2607
2643
  // Prefix \x01 is a sentinel for the MC macro '$' line-start marker.
@@ -2610,7 +2646,7 @@ export class Lowering {
2610
2646
  // Codegen replaces \x01 → '$' when emitting the mc function file.
2611
2647
  this.emitRawSubFunction(
2612
2648
  subFnName,
2613
- `\x01execute store result score ${dst} rs run data get storage ${storageNs} ${arrayKey}[$(${macroKey})] 1`
2649
+ `\x01execute store result score ${dst} ${LOWERING_OBJ} run data get storage ${storageNs} ${arrayKey}[$(${macroKey})] 1`
2614
2650
  )
2615
2651
  }
2616
2652
  return { kind: 'var', name: dst }
@@ -2629,6 +2665,64 @@ export class Lowering {
2629
2665
  return { kind: 'const', value: 0 }
2630
2666
  }
2631
2667
 
2668
+ // storage_set_int(storage_ns, array_key, index, value) -> void
2669
+ // Writes one integer element into an NBT int-array stored in data storage.
2670
+ // storage_ns : e.g. "rs:bigint"
2671
+ // array_key : e.g. "a"
2672
+ // index : element index (const or runtime)
2673
+ // value : integer value to write (const or runtime)
2674
+ //
2675
+ // Const index + const value:
2676
+ // execute store result storage <ns> <key>[N] int 1 run scoreboard players set $const_V ${LOWERING_OBJ} V
2677
+ // Runtime index or value: macro sub-function via rs:heap
2678
+ if (name === 'storage_set_int') {
2679
+ const storageNs = this.exprToString(args[0])
2680
+ const arrayKey = this.exprToString(args[1])
2681
+ const indexOperand = this.lowerExpr(args[2])
2682
+ const valueOperand = this.lowerExpr(args[3])
2683
+
2684
+ if (indexOperand.kind === 'const') {
2685
+ // Static index — use execute store result to write to the fixed slot
2686
+ const valVar = valueOperand.kind === 'var'
2687
+ ? valueOperand.name
2688
+ : this.operandToVar(valueOperand)
2689
+ this.builder.emitRaw(
2690
+ `execute store result storage ${storageNs} ${arrayKey}[${indexOperand.value}] int 1 run scoreboard players get ${valVar} ${LOWERING_OBJ}`
2691
+ )
2692
+ } else {
2693
+ // Runtime index: we need a macro sub-function.
2694
+ // Store index + value into rs:heap, call macro that does:
2695
+ // $data modify storage <ns> <key>[$(idx_key)] set value $(val_key)
2696
+ const macroIdxKey = `__ssi_i_${this.foreachCounter++}`
2697
+ const macroValKey = `__ssi_v_${this.foreachCounter++}` // kept to pin valVar in optimizer
2698
+ const subFnName = `${this.currentFn}/__ssi_${this.foreachCounter++}`
2699
+ const indexVar = indexOperand.kind === 'var'
2700
+ ? indexOperand.name
2701
+ : this.operandToVar(indexOperand)
2702
+ const valVar = valueOperand.kind === 'var'
2703
+ ? valueOperand.name
2704
+ : this.operandToVar(valueOperand)
2705
+ this.builder.emitRaw(
2706
+ `execute store result storage rs:heap ${macroIdxKey} int 1 run scoreboard players get ${indexVar} ${LOWERING_OBJ}`
2707
+ )
2708
+ // Pin valVar in the optimizer's read-set so the assignment is not dead-code-eliminated.
2709
+ // The value is stored to rs:heap but NOT used by the macro (the macro reads the scoreboard
2710
+ // slot directly to avoid the MC 'data modify set value $(n)' macro substitution bug).
2711
+ this.builder.emitRaw(
2712
+ `execute store result storage rs:heap ${macroValKey} int 1 run scoreboard players get ${valVar} ${LOWERING_OBJ}`
2713
+ )
2714
+ this.builder.emitRaw(`function ${this.namespace}:${subFnName} with storage rs:heap`)
2715
+ // Use execute store result (not 'data modify set value $(val)') to avoid MC macro
2716
+ // substitution bugs with numeric values. The scoreboard slot ${valVar} is hardcoded
2717
+ // into the macro sub-function at compile time — only the array index is macro-substituted.
2718
+ this.emitRawSubFunction(
2719
+ subFnName,
2720
+ `\x01execute store result storage ${storageNs} ${arrayKey}[$(${macroIdxKey})] int 1 run scoreboard players get ${valVar} ${LOWERING_OBJ}`
2721
+ )
2722
+ }
2723
+ return { kind: 'const', value: 0 }
2724
+ }
2725
+
2632
2726
  // data_merge(target, nbt) — merge NBT into entity/block/storage
2633
2727
  // data_merge(@s, { Invisible: 1b, Silent: 1b })
2634
2728
  if (name === 'data_merge') {
@@ -2673,7 +2767,7 @@ export class Lowering {
2673
2767
  const dst = this.builder.freshTemp()
2674
2768
  const setId = this.exprToString(args[0])
2675
2769
  const value = this.exprToString(args[1])
2676
- this.builder.emitRaw(`execute store result score ${dst} rs if data storage rs:sets ${setId}[{value:${value}}]`)
2770
+ this.builder.emitRaw(`execute store result score ${dst} ${LOWERING_OBJ} if data storage rs:sets ${setId}[{value:${value}}]`)
2677
2771
  return { kind: 'var', name: dst }
2678
2772
  }
2679
2773
 
@@ -2990,7 +3084,7 @@ export class Lowering {
2990
3084
  return
2991
3085
  }
2992
3086
 
2993
- components.push({ score: { name: this.operandToVar(operand), objective: 'rs' } })
3087
+ components.push({ score: { name: this.operandToVar(operand), objective: LOWERING_OBJ } })
2994
3088
  }
2995
3089
 
2996
3090
  private exprToString(expr: Expr): string {
@@ -3156,12 +3250,20 @@ export class Lowering {
3156
3250
 
3157
3251
  private exprToScoreboardObjective(expr: Expr, span?: Span): string {
3158
3252
  if (expr.kind === 'mc_name') {
3159
- return expr.value
3253
+ // 'rs' is the canonical token for the current RS scoreboard objective.
3254
+ // Resolve to LOWERING_OBJ so it respects --scoreboard / namespace default.
3255
+ return expr.value === 'rs' ? LOWERING_OBJ : expr.value
3160
3256
  }
3161
3257
 
3162
3258
  const objective = this.exprToString(expr)
3163
3259
  if (objective.startsWith('#') || objective.includes('.')) {
3164
- return objective.startsWith('#') ? objective.slice(1) : objective
3260
+ if (objective.startsWith('#')) {
3261
+ const name = objective.slice(1)
3262
+ // '#rs' is the canonical way to reference the current RS scoreboard objective.
3263
+ // Resolve to LOWERING_OBJ so it tracks the --scoreboard option.
3264
+ return name === 'rs' ? LOWERING_OBJ : name
3265
+ }
3266
+ return objective
3165
3267
  }
3166
3268
 
3167
3269
  return `${this.getObjectiveNamespace(span)}.${objective}`
@@ -3181,7 +3283,7 @@ export class Lowering {
3181
3283
  return this.namespace
3182
3284
  }
3183
3285
 
3184
- return this.isStdlibFile(filePath) ? 'rs' : this.namespace
3286
+ return this.isStdlibFile(filePath) ? LOWERING_OBJ : this.namespace
3185
3287
  }
3186
3288
 
3187
3289
  private tryGetStdlibInternalObjective(playerExpr: Expr | undefined, objectiveExpr: Expr, span?: Span): string | null {
@@ -3200,7 +3302,7 @@ export class Lowering {
3200
3302
  }
3201
3303
 
3202
3304
  const hash = this.shortHash(this.serializeCallSite(this.currentStdlibCallSite))
3203
- return `rs._${resourceBase}_${hash}`
3305
+ return `${LOWERING_OBJ}._${resourceBase}_${hash}`
3204
3306
  }
3205
3307
 
3206
3308
  private getStdlibInternalResourceBase(playerExpr: Expr | undefined): string | null {
@@ -3522,18 +3624,18 @@ export class Lowering {
3522
3624
  const dst = this.builder.freshTemp()
3523
3625
 
3524
3626
  if (index.kind === 'const') {
3525
- this.builder.emitRaw(`execute store result score ${dst} rs run data get storage rs:heap ${arrayName}[${index.value}]`)
3627
+ this.builder.emitRaw(`execute store result score ${dst} ${LOWERING_OBJ} run data get storage rs:heap ${arrayName}[${index.value}]`)
3526
3628
  return { kind: 'var', name: dst }
3527
3629
  }
3528
3630
 
3529
3631
  const macroKey = `__rs_index_${this.foreachCounter++}`
3530
3632
  const subFnName = `${this.currentFn}/array_get_${this.foreachCounter++}`
3531
3633
  const indexVar = index.kind === 'var' ? index.name : this.operandToVar(index)
3532
- this.builder.emitRaw(`execute store result storage rs:heap ${macroKey} int 1 run scoreboard players get ${indexVar} rs`)
3634
+ this.builder.emitRaw(`execute store result storage rs:heap ${macroKey} int 1 run scoreboard players get ${indexVar} ${LOWERING_OBJ}`)
3533
3635
  this.builder.emitRaw(`function ${this.namespace}:${subFnName} with storage rs:heap`)
3534
3636
  this.emitRawSubFunction(
3535
3637
  subFnName,
3536
- `\x01execute store result score ${dst} rs run data get storage rs:heap ${arrayName}[$(${macroKey})]`
3638
+ `\x01execute store result score ${dst} ${LOWERING_OBJ} run data get storage rs:heap ${arrayName}[$(${macroKey})]`
3537
3639
  )
3538
3640
  return { kind: 'var', name: dst }
3539
3641
  }