redscript-mc 1.0.0 → 1.1.0

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 (106) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.yml +72 -0
  2. package/.github/ISSUE_TEMPLATE/feature_request.yml +57 -0
  3. package/.github/PULL_REQUEST_TEMPLATE.md +17 -25
  4. package/CHANGELOG.md +58 -0
  5. package/CONTRIBUTING.md +140 -0
  6. package/README.md +28 -19
  7. package/README.zh.md +28 -19
  8. package/dist/__tests__/cli.test.js +10 -10
  9. package/dist/__tests__/codegen.test.js +1 -1
  10. package/dist/__tests__/diagnostics.test.js +5 -5
  11. package/dist/__tests__/e2e.test.js +146 -5
  12. package/dist/__tests__/formatter.test.d.ts +1 -0
  13. package/dist/__tests__/formatter.test.js +40 -0
  14. package/dist/__tests__/lowering.test.js +36 -3
  15. package/dist/__tests__/mc-integration.test.js +255 -10
  16. package/dist/__tests__/mc-syntax.test.js +3 -3
  17. package/dist/__tests__/nbt.test.js +2 -2
  18. package/dist/__tests__/optimizer-advanced.test.js +3 -3
  19. package/dist/__tests__/runtime.test.js +1 -1
  20. package/dist/ast/types.d.ts +21 -3
  21. package/dist/cli.js +25 -7
  22. package/dist/codegen/mcfunction/index.d.ts +1 -1
  23. package/dist/codegen/mcfunction/index.js +8 -2
  24. package/dist/codegen/structure/index.js +7 -1
  25. package/dist/formatter/index.d.ts +1 -0
  26. package/dist/formatter/index.js +26 -0
  27. package/dist/ir/builder.d.ts +2 -1
  28. package/dist/ir/types.d.ts +7 -2
  29. package/dist/ir/types.js +1 -1
  30. package/dist/lowering/index.d.ts +2 -0
  31. package/dist/lowering/index.js +183 -8
  32. package/dist/mc-test/runner.d.ts +2 -2
  33. package/dist/mc-test/runner.js +3 -3
  34. package/dist/mc-test/setup.js +2 -2
  35. package/dist/parser/index.d.ts +2 -0
  36. package/dist/parser/index.js +75 -7
  37. package/docs/COMPILATION_STATS.md +24 -24
  38. package/docs/IMPLEMENTATION_GUIDE.md +1 -1
  39. package/docs/STRUCTURE_TARGET.md +1 -1
  40. package/editors/vscode/.vscodeignore +1 -0
  41. package/editors/vscode/icons/mcrs.svg +7 -0
  42. package/editors/vscode/icons/redscript-icons.json +10 -0
  43. package/editors/vscode/out/extension.js +152 -9
  44. package/editors/vscode/package.json +10 -3
  45. package/editors/vscode/src/hover.ts +55 -2
  46. package/editors/vscode/src/symbols.ts +42 -0
  47. package/package.json +1 -1
  48. package/src/__tests__/cli.test.ts +10 -10
  49. package/src/__tests__/codegen.test.ts +1 -1
  50. package/src/__tests__/diagnostics.test.ts +5 -5
  51. package/src/__tests__/e2e.test.ts +134 -5
  52. package/src/__tests__/lowering.test.ts +48 -3
  53. package/src/__tests__/mc-integration.test.ts +285 -10
  54. package/src/__tests__/mc-syntax.test.ts +3 -3
  55. package/src/__tests__/nbt.test.ts +2 -2
  56. package/src/__tests__/optimizer-advanced.test.ts +3 -3
  57. package/src/__tests__/runtime.test.ts +1 -1
  58. package/src/ast/types.ts +20 -3
  59. package/src/cli.ts +10 -10
  60. package/src/codegen/mcfunction/index.ts +9 -2
  61. package/src/codegen/structure/index.ts +8 -1
  62. package/src/examples/capture_the_flag.mcrs +208 -0
  63. package/src/examples/{counter.rs → counter.mcrs} +1 -1
  64. package/src/examples/hunger_games.mcrs +301 -0
  65. package/src/examples/new_features_demo.mcrs +193 -0
  66. package/src/examples/parkour_race.mcrs +233 -0
  67. package/src/examples/rpg.mcrs +13 -0
  68. package/src/examples/{shop.rs → shop.mcrs} +1 -1
  69. package/src/examples/{showcase_game.rs → showcase_game.mcrs} +3 -3
  70. package/src/examples/{turret.rs → turret.mcrs} +1 -1
  71. package/src/examples/zombie_survival.mcrs +314 -0
  72. package/src/ir/builder.ts +3 -1
  73. package/src/ir/types.ts +8 -2
  74. package/src/lowering/index.ts +156 -8
  75. package/src/mc-test/runner.ts +3 -3
  76. package/src/mc-test/setup.ts +2 -2
  77. package/src/parser/index.ts +81 -8
  78. package/src/stdlib/README.md +155 -147
  79. package/src/stdlib/bossbar.mcrs +68 -0
  80. package/src/stdlib/{cooldown.rs → cooldown.mcrs} +1 -1
  81. package/src/stdlib/effects.mcrs +64 -0
  82. package/src/stdlib/interactions.mcrs +195 -0
  83. package/src/stdlib/inventory.mcrs +38 -0
  84. package/src/stdlib/mobs.mcrs +99 -0
  85. package/src/stdlib/particles.mcrs +52 -0
  86. package/src/stdlib/sets.mcrs +20 -0
  87. package/src/stdlib/spawn.mcrs +41 -0
  88. package/src/stdlib/teams.mcrs +68 -0
  89. package/src/stdlib/world.mcrs +92 -0
  90. package/src/examples/rpg.rs +0 -13
  91. package/src/stdlib/mobs.rs +0 -99
  92. /package/src/examples/{arena.rs → arena.mcrs} +0 -0
  93. /package/src/examples/{pvp_arena.rs → pvp_arena.mcrs} +0 -0
  94. /package/src/examples/{quiz.rs → quiz.mcrs} +0 -0
  95. /package/src/examples/{stdlib_demo.rs → stdlib_demo.mcrs} +0 -0
  96. /package/src/examples/{world_manager.rs → world_manager.mcrs} +0 -0
  97. /package/src/stdlib/{combat.rs → combat.mcrs} +0 -0
  98. /package/src/stdlib/{math.rs → math.mcrs} +0 -0
  99. /package/src/stdlib/{player.rs → player.mcrs} +0 -0
  100. /package/src/stdlib/{strings.rs → strings.mcrs} +0 -0
  101. /package/src/stdlib/{timer.rs → timer.mcrs} +0 -0
  102. /package/src/templates/{combat.rs → combat.mcrs} +0 -0
  103. /package/src/templates/{economy.rs → economy.mcrs} +0 -0
  104. /package/src/templates/{mini-game-framework.rs → mini-game-framework.mcrs} +0 -0
  105. /package/src/templates/{quest.rs → quest.mcrs} +0 -0
  106. /package/src/test_programs/{zombie_game.rs → zombie_game.mcrs} +0 -0
@@ -0,0 +1,233 @@
1
+ // ============================================
2
+ // Parkour Race - 跑酷竞速
3
+ // ============================================
4
+ // 场景:玩家从起点跑到终点,记录最快时间
5
+ // 设有多个检查点,掉落会传送回最近检查点
6
+ // Scenario: Race from start to finish with checkpoints
7
+ // ============================================
8
+
9
+ import "../stdlib/effects.mcrs"
10
+ import "../stdlib/world.mcrs"
11
+ import "../stdlib/bossbar.mcrs"
12
+ import "../stdlib/particles.mcrs"
13
+
14
+ // ===== 配置 =====
15
+ const START_X: int = 0;
16
+ const START_Y: int = 64;
17
+ const START_Z: int = 0;
18
+ const FALL_Y: int = 50; // 低于此高度视为掉落
19
+
20
+ const CHECKPOINT_COUNT: int = 5;
21
+
22
+ // ===== 游戏状态 =====
23
+ // 记分板:
24
+ // - pk_time: 当前用时 (ticks)
25
+ // - pk_best: 最佳记录
26
+ // - pk_checkpoint: 当前检查点
27
+ // - pk_running: 是否在比赛中
28
+
29
+ // 检查点坐标 (简化为直线跑酷)
30
+ // CP0: 0,64,0 (起点)
31
+ // CP1: 0,64,50
32
+ // CP2: 0,70,100
33
+ // CP3: 0,75,150
34
+ // CP4: 0,80,200
35
+ // CP5: 0,64,250 (终点)
36
+
37
+ @load
38
+ fn init() {
39
+ scoreboard_add_objective("pk_time", "dummy");
40
+ scoreboard_add_objective("pk_best", "dummy");
41
+ scoreboard_add_objective("pk_checkpoint", "dummy");
42
+ scoreboard_add_objective("pk_running", "dummy");
43
+
44
+ // 显示最佳时间
45
+ scoreboard_display("sidebar", "pk_best");
46
+
47
+ set_day();
48
+ weather_clear();
49
+
50
+ announce("§b[跑酷] §f已加载!踩压力板开始");
51
+ }
52
+
53
+ // ===== 开始比赛 =====
54
+ fn start_race(player: selector) {
55
+ // 检查是否已在比赛中
56
+ let running: int = scoreboard_get(player, "pk_running");
57
+ if (running == 1) {
58
+ tell(player, "§c你已经在比赛中了!");
59
+ return;
60
+ }
61
+
62
+ scoreboard_set(player, "pk_running", 1);
63
+ scoreboard_set(player, "pk_time", 0);
64
+ scoreboard_set(player, "pk_checkpoint", 0);
65
+
66
+ // 传送到起点
67
+ tp(player, START_X, START_Y, START_Z);
68
+
69
+ // 清除效果,给速度
70
+ effect_clear(player);
71
+
72
+ title(player, "§b开始!");
73
+ subtitle(player, "§7跑向终点!");
74
+
75
+ // 创建计时 bossbar
76
+ tell(player, "§b[跑酷] §a比赛开始!到达终点完成比赛");
77
+
78
+ sparkles(player);
79
+ }
80
+
81
+ // ===== 每 tick =====
82
+ @tick
83
+ fn race_tick() {
84
+ // 对每个比赛中的玩家
85
+ foreach (p in @a) {
86
+ let running: int = scoreboard_get(p, "pk_running");
87
+ if (running == 1) {
88
+ // 增加时间
89
+ scoreboard_add(p, "pk_time", 1);
90
+
91
+ // 显示当前时间
92
+ let time: int = scoreboard_get(p, "pk_time");
93
+ let seconds: int = time / 20;
94
+ let ms: int = (time % 20) * 5;
95
+ actionbar(p, "§e⏱ " + seconds + "." + ms + " 秒");
96
+
97
+ // 检查掉落
98
+ check_fall(p);
99
+
100
+ // 检查检查点
101
+ check_checkpoints(p);
102
+ }
103
+ }
104
+ }
105
+
106
+ fn check_fall(player: selector) {
107
+ // 如果玩家低于 FALL_Y,传送回检查点
108
+ // 使用 execute positioned 检查
109
+ execute if entity player[y=..50] run {
110
+ let cp: int = scoreboard_get(player, "pk_checkpoint");
111
+ respawn_at_checkpoint(player, cp);
112
+ tell(player, "§c掉落!返回检查点 " + cp);
113
+ angry(player);
114
+ }
115
+ }
116
+
117
+ fn check_checkpoints(player: selector) {
118
+ let current_cp: int = scoreboard_get(player, "pk_checkpoint");
119
+
120
+ // 检查点 1 (0, 64, 50)
121
+ if (current_cp == 0) {
122
+ execute if entity player[x=-2..2, y=62..68, z=48..52] run {
123
+ reach_checkpoint(player, 1);
124
+ }
125
+ }
126
+
127
+ // 检查点 2 (0, 70, 100)
128
+ if (current_cp == 1) {
129
+ execute if entity player[x=-2..2, y=68..74, z=98..102] run {
130
+ reach_checkpoint(player, 2);
131
+ }
132
+ }
133
+
134
+ // 检查点 3 (0, 75, 150)
135
+ if (current_cp == 2) {
136
+ execute if entity player[x=-2..2, y=73..79, z=148..152] run {
137
+ reach_checkpoint(player, 3);
138
+ }
139
+ }
140
+
141
+ // 检查点 4 (0, 80, 200)
142
+ if (current_cp == 3) {
143
+ execute if entity player[x=-2..2, y=78..84, z=198..202] run {
144
+ reach_checkpoint(player, 4);
145
+ }
146
+ }
147
+
148
+ // 终点 (0, 64, 250)
149
+ if (current_cp == 4) {
150
+ execute if entity player[x=-2..2, y=62..68, z=248..252] run {
151
+ finish_race(player);
152
+ }
153
+ }
154
+ }
155
+
156
+ fn reach_checkpoint(player: selector, cp: int) {
157
+ scoreboard_set(player, "pk_checkpoint", cp);
158
+
159
+ title(player, "");
160
+ subtitle(player, "§a检查点 " + cp + " / " + CHECKPOINT_COUNT);
161
+
162
+ happy(player);
163
+ playsound("minecraft:entity.experience_orb.pickup", "player", player);
164
+ }
165
+
166
+ fn respawn_at_checkpoint(player: selector, cp: int) {
167
+ if (cp == 0) {
168
+ tp(player, 0, 64, 0);
169
+ }
170
+ if (cp == 1) {
171
+ tp(player, 0, 64, 50);
172
+ }
173
+ if (cp == 2) {
174
+ tp(player, 0, 70, 100);
175
+ }
176
+ if (cp == 3) {
177
+ tp(player, 0, 75, 150);
178
+ }
179
+ if (cp == 4) {
180
+ tp(player, 0, 80, 200);
181
+ }
182
+ }
183
+
184
+ fn finish_race(player: selector) {
185
+ scoreboard_set(player, "pk_running", 0);
186
+
187
+ let time: int = scoreboard_get(player, "pk_time");
188
+ let best: int = scoreboard_get(player, "pk_best");
189
+ let seconds: int = time / 20;
190
+ let ms: int = (time % 20) * 5;
191
+
192
+ // 检查是否是新纪录
193
+ if (best == 0) {
194
+ // 第一次完成
195
+ scoreboard_set(player, "pk_best", time);
196
+ title(player, "§6完成!");
197
+ subtitle(player, "§e" + seconds + "." + ms + " 秒 §7(首次记录)");
198
+ announce("§b[跑酷] §f玩家完成比赛!用时 §e" + seconds + "." + ms + " 秒");
199
+ } else {
200
+ if (time < best) {
201
+ // 新纪录!
202
+ scoreboard_set(player, "pk_best", time);
203
+ title(player, "§6新纪录!");
204
+ subtitle(player, "§e" + seconds + "." + ms + " 秒");
205
+ announce("§b[跑酷] §6新纪录!§f用时 §e" + seconds + "." + ms + " 秒");
206
+ totem_effect(player);
207
+ } else {
208
+ title(player, "§a完成!");
209
+ subtitle(player, "§e" + seconds + "." + ms + " 秒");
210
+ }
211
+ }
212
+
213
+ // 特效
214
+ sparkles(player);
215
+ playsound("minecraft:ui.toast.challenge_complete", "player", player);
216
+ }
217
+
218
+ // ===== 放弃比赛 =====
219
+ fn quit_race(player: selector) {
220
+ let running: int = scoreboard_get(player, "pk_running");
221
+ if (running == 1) {
222
+ scoreboard_set(player, "pk_running", 0);
223
+ tell(player, "§c已放弃比赛");
224
+ tp(player, START_X, START_Y, START_Z);
225
+ }
226
+ }
227
+
228
+ // ===== 查看排行榜 =====
229
+ fn show_leaderboard() {
230
+ announce("§b===== 跑酷排行榜 =====");
231
+ // 显示前 5 名 (需要记分板排序)
232
+ scoreboard_display("sidebar", "pk_best");
233
+ }
@@ -0,0 +1,13 @@
1
+ import "../stdlib/math.mcrs"
2
+ import "../stdlib/combat.mcrs"
3
+
4
+ fn attack(enemy: string, base: int, bonus: int) {
5
+ let raw_damage = weapon_damage(base, bonus);
6
+ let damage = clamp(raw_damage, 1, 20);
7
+ apply_damage(enemy, damage);
8
+ }
9
+
10
+ @tick
11
+ fn battle_tick() {
12
+ attack("goblin", 4, 2);
13
+ }
@@ -2,7 +2,7 @@
2
2
  // Players set shop_choice, then run /trigger shop_buy.
3
3
 
4
4
  fn complete_purchase() {
5
- let choice: int = scoreboard_get(@s, #shop_choice);
5
+ let choice = scoreboard_get(@s, #shop_choice);
6
6
 
7
7
  if (choice == 1) {
8
8
  give(@s, "minecraft:bread", 1);
@@ -1,6 +1,6 @@
1
- import "../stdlib/math.rs"
2
- import "../stdlib/player.rs"
3
- import "../stdlib/cooldown.rs"
1
+ import "../stdlib/math.mcrs"
2
+ import "../stdlib/player.mcrs"
3
+ import "../stdlib/cooldown.mcrs"
4
4
 
5
5
  const WIN_SCORE: int = 15
6
6
  const CRYSTAL_VALUE: int = 3
@@ -8,7 +8,7 @@ fn deploy_turret() {
8
8
  turret.health = 40;
9
9
  turret.tag("turret");
10
10
 
11
- let state: TurretState = { health: 40 };
11
+ let state = { health: 40 };
12
12
  let hp = state.health;
13
13
  scoreboard_set("turret", #health, hp);
14
14
 
@@ -0,0 +1,314 @@
1
+ // ============================================
2
+ // Zombie Survival - 僵尸生存模式
3
+ // ============================================
4
+ // 场景:玩家在竞技场中心抵御一波波僵尸
5
+ // 每波结束后可以购买装备升级
6
+ // Scenario: Survive zombie waves, buy upgrades between rounds
7
+ // ============================================
8
+
9
+ import "../stdlib/effects.mcrs"
10
+ import "../stdlib/world.mcrs"
11
+ import "../stdlib/inventory.mcrs"
12
+ import "../stdlib/bossbar.mcrs"
13
+ import "../stdlib/particles.mcrs"
14
+
15
+ // ===== 配置 =====
16
+ const ARENA_X: int = 0;
17
+ const ARENA_Y: int = 64;
18
+ const ARENA_Z: int = 0;
19
+ const ARENA_RADIUS: int = 30;
20
+ const WAVE_DELAY: int = 200; // 10秒准备时间
21
+
22
+ // ===== 游戏状态 =====
23
+ struct SurvivalState {
24
+ running: int,
25
+ wave: int,
26
+ zombies_left: int,
27
+ phase: int, // 0=准备, 1=战斗
28
+ prep_timer: int,
29
+ total_kills: int
30
+ }
31
+
32
+ let state: SurvivalState = SurvivalState {
33
+ running: 0,
34
+ wave: 0,
35
+ zombies_left: 0,
36
+ phase: 0,
37
+ prep_timer: 0,
38
+ total_kills: 0
39
+ };
40
+
41
+ // ===== 玩家数据 =====
42
+ // 金币记分板: zs_coins
43
+ // 击杀数: zs_kills
44
+
45
+ // ===== 初始化 =====
46
+ @load
47
+ fn init() {
48
+ scoreboard_add_objective("zs_coins", "dummy");
49
+ scoreboard_add_objective("zs_kills", "dummy");
50
+ scoreboard_add_objective("zs_display", "dummy");
51
+
52
+ // 显示记分板
53
+ scoreboard_display("sidebar", "zs_display");
54
+
55
+ set_night();
56
+ disable_mob_griefing();
57
+
58
+ announce("§4[僵尸生存] §f已加载!输入 /trigger start 开始");
59
+ }
60
+
61
+ // ===== 开始游戏 =====
62
+ fn start_game() {
63
+ state.running = 1;
64
+ state.wave = 0;
65
+ state.total_kills = 0;
66
+
67
+ // 初始化玩家
68
+ foreach (p in @a) {
69
+ scoreboard_set(p, "zs_coins", 0);
70
+ scoreboard_set(p, "zs_kills", 0);
71
+ clear_inventory(p);
72
+ give(p, "minecraft:wooden_sword", 1);
73
+ give(p, "minecraft:leather_chestplate", 1);
74
+ tp(p, ARENA_X, ARENA_Y, ARENA_Z);
75
+ }
76
+
77
+ // 创建 bossbar
78
+ create_progress_bar("zs_wave", "§c僵尸剩余", 10);
79
+
80
+ title(@a, "§4僵尸生存");
81
+ subtitle(@a, "§7准备战斗...");
82
+
83
+ // 开始第一波
84
+ start_prep_phase();
85
+ }
86
+
87
+ // ===== 阶段控制 =====
88
+ fn start_prep_phase() {
89
+ state.phase = 0;
90
+ state.prep_timer = WAVE_DELAY;
91
+ state.wave = state.wave + 1;
92
+
93
+ announce("§4[僵尸生存] §e第 " + state.wave + " 波即将来袭!");
94
+ announce("§7准备时间 10 秒...");
95
+
96
+ // 商店提示
97
+ if (state.wave > 1) {
98
+ announce("§a[商店] §f输入 /trigger buy 购买装备");
99
+ }
100
+ }
101
+
102
+ fn start_combat_phase() {
103
+ state.phase = 1;
104
+
105
+ // 计算僵尸数量 (每波增加)
106
+ let zombie_count: int = 3 + (state.wave * 2);
107
+ state.zombies_left = zombie_count;
108
+
109
+ // 更新 bossbar
110
+ bossbar_set_max("zs_wave", zombie_count);
111
+ bossbar_set_value("zs_wave", zombie_count);
112
+
113
+ title(@a, "§c第 " + state.wave + " 波");
114
+
115
+ // 生成僵尸
116
+ spawn_zombies(zombie_count);
117
+ }
118
+
119
+ fn spawn_zombies(count: int) {
120
+ for i in 0..count {
121
+ // 在竞技场边缘随机生成
122
+ let angle: int = i * 30; // 分散生成
123
+ let spawn_x: int = ARENA_X + (ARENA_RADIUS - 5);
124
+ let spawn_z: int = ARENA_Z;
125
+
126
+ // 根据波数增加僵尸强度
127
+ if (state.wave < 3) {
128
+ summon("minecraft:zombie", spawn_x, ARENA_Y, spawn_z);
129
+ } else {
130
+ if (state.wave < 5) {
131
+ // 穿盔甲的僵尸
132
+ summon("minecraft:zombie", spawn_x, ARENA_Y, spawn_z,
133
+ {ArmorItems: [{}, {}, {id: "iron_chestplate", Count: 1}, {}]});
134
+ } else {
135
+ // 快速僵尸
136
+ summon("minecraft:husk", spawn_x, ARENA_Y, spawn_z);
137
+ }
138
+ }
139
+ }
140
+
141
+ announce("§c" + count + " 只僵尸出现了!");
142
+ }
143
+
144
+ // ===== 每 tick =====
145
+ @tick
146
+ fn game_tick() {
147
+ if (state.running == 0) {
148
+ return;
149
+ }
150
+
151
+ if (state.phase == 0) {
152
+ // 准备阶段
153
+ prep_tick();
154
+ } else {
155
+ // 战斗阶段
156
+ combat_tick();
157
+ }
158
+
159
+ update_scoreboard();
160
+ }
161
+
162
+ fn prep_tick() {
163
+ state.prep_timer = state.prep_timer - 1;
164
+
165
+ // 倒计时提示
166
+ if (state.prep_timer == 100) {
167
+ actionbar(@a, "§e5 秒...");
168
+ }
169
+ if (state.prep_timer == 60) {
170
+ actionbar(@a, "§e3 秒...");
171
+ }
172
+ if (state.prep_timer == 20) {
173
+ actionbar(@a, "§c1 秒...");
174
+ }
175
+
176
+ if (state.prep_timer <= 0) {
177
+ start_combat_phase();
178
+ }
179
+ }
180
+
181
+ fn combat_tick() {
182
+ // 检查僵尸数量
183
+ let zombies: int = count_entities(@e[type=zombie, distance=..50]);
184
+ let husks: int = count_entities(@e[type=husk, distance=..50]);
185
+ state.zombies_left = zombies + husks;
186
+
187
+ // 更新 bossbar
188
+ update_bar("zs_wave", state.zombies_left);
189
+
190
+ // 检查波次完成
191
+ if (state.zombies_left <= 0) {
192
+ wave_complete();
193
+ }
194
+
195
+ // 检查玩家存活
196
+ let alive: int = count_entities(@a[gamemode=survival]);
197
+ if (alive <= 0) {
198
+ game_over();
199
+ }
200
+ }
201
+
202
+ fn wave_complete() {
203
+ announce("§4[僵尸生存] §a第 " + state.wave + " 波完成!");
204
+
205
+ // 奖励金币
206
+ let reward: int = 50 + (state.wave * 25);
207
+ foreach (p in @a) {
208
+ scoreboard_add(p, "zs_coins", reward);
209
+ happy(p);
210
+ }
211
+ announce("§6+" + reward + " 金币");
212
+
213
+ // 检查特殊波
214
+ if (state.wave == 10) {
215
+ victory();
216
+ return;
217
+ }
218
+
219
+ start_prep_phase();
220
+ }
221
+
222
+ // ===== 商店系统 =====
223
+ fn buy_item(player: selector, item_id: int) {
224
+ let coins: int = scoreboard_get(player, "zs_coins");
225
+
226
+ if (item_id == 1) {
227
+ // 铁剑 - 100 金币
228
+ if (coins >= 100) {
229
+ scoreboard_add(player, "zs_coins", -100);
230
+ give(player, "minecraft:iron_sword", 1);
231
+ tell(player, "§a购买成功:铁剑");
232
+ } else {
233
+ tell(player, "§c金币不足!需要 100");
234
+ }
235
+ }
236
+
237
+ if (item_id == 2) {
238
+ // 铁甲 - 200 金币
239
+ if (coins >= 200) {
240
+ scoreboard_add(player, "zs_coins", -200);
241
+ give(player, "minecraft:iron_chestplate", 1);
242
+ give(player, "minecraft:iron_leggings", 1);
243
+ give(player, "minecraft:iron_boots", 1);
244
+ tell(player, "§a购买成功:铁甲套装");
245
+ } else {
246
+ tell(player, "§c金币不足!需要 200");
247
+ }
248
+ }
249
+
250
+ if (item_id == 3) {
251
+ // 弓箭 - 150 金币
252
+ if (coins >= 150) {
253
+ scoreboard_add(player, "zs_coins", -150);
254
+ give(player, "minecraft:bow", 1);
255
+ give(player, "minecraft:arrow", 32);
256
+ tell(player, "§a购买成功:弓 + 32 箭");
257
+ } else {
258
+ tell(player, "§c金币不足!需要 150");
259
+ }
260
+ }
261
+
262
+ if (item_id == 4) {
263
+ // 金苹果 - 75 金币
264
+ if (coins >= 75) {
265
+ scoreboard_add(player, "zs_coins", -75);
266
+ give(player, "minecraft:golden_apple", 2);
267
+ tell(player, "§a购买成功:金苹果 x2");
268
+ } else {
269
+ tell(player, "§c金币不足!需要 75");
270
+ }
271
+ }
272
+ }
273
+
274
+ // ===== 结束 =====
275
+ fn victory() {
276
+ state.running = 0;
277
+
278
+ title(@a, "§6胜利!");
279
+ subtitle(@a, "§a你们生存了 10 波!");
280
+ announce("§4[僵尸生存] §6恭喜!完成全部 10 波!");
281
+
282
+ foreach (p in @a) {
283
+ totem_effect(p);
284
+ buff_all(p, 200);
285
+ }
286
+
287
+ remove_bar("zs_wave");
288
+ }
289
+
290
+ fn game_over() {
291
+ state.running = 0;
292
+
293
+ title(@a, "§c游戏结束");
294
+ subtitle(@a, "§7生存了 " + state.wave + " 波");
295
+ announce("§4[僵尸生存] §c全员阵亡!最高波数:" + state.wave);
296
+
297
+ // 清理僵尸
298
+ kill(@e[type=zombie]);
299
+ kill(@e[type=husk]);
300
+
301
+ remove_bar("zs_wave");
302
+ }
303
+
304
+ fn update_scoreboard() {
305
+ scoreboard_set("$wave", "zs_display", state.wave);
306
+ scoreboard_set("$zombies", "zs_display", state.zombies_left);
307
+ }
308
+
309
+ fn count_entities(sel: selector) -> int {
310
+ // 使用 execute store 计算实体数量
311
+ let count: int = 0;
312
+ // 实际实现需要 execute store
313
+ return count;
314
+ }
package/src/ir/builder.ts CHANGED
@@ -109,6 +109,8 @@ export class IRBuilder {
109
109
  }
110
110
  }
111
111
 
112
- export function buildModule(namespace: string, fns: IRFunction[], globals: string[] = []): IRModule {
112
+ import type { GlobalVar } from './types'
113
+
114
+ export function buildModule(namespace: string, fns: IRFunction[], globals: GlobalVar[] = []): IRModule {
113
115
  return { namespace, functions: fns, globals }
114
116
  }
package/src/ir/types.ts CHANGED
@@ -8,7 +8,7 @@
8
8
  * - Integer vars → scoreboard fake player ($name on objective "rs_vars")
9
9
  * - Complex data → NBT storage (redscript:stack / redscript:heap)
10
10
  * - Return value → fake player $ret
11
- * - Temporaries → $t0, $t1, ...
11
+ * - Temporaries → $_0, $_1, ...
12
12
  */
13
13
 
14
14
  // ---------------------------------------------------------------------------
@@ -100,6 +100,7 @@ export interface IRFunction {
100
100
  blocks: IRBlock[] // blocks[0] = entry block
101
101
  commands?: IRCommand[] // structure target command stream
102
102
  isTickLoop?: boolean // true → Repeat command block (runs every tick)
103
+ isLoadInit?: boolean // true → called from __load.mcfunction
103
104
  isTriggerHandler?: boolean // true → handles a trigger event
104
105
  triggerName?: string // the trigger objective name
105
106
  eventTrigger?: {
@@ -112,8 +113,13 @@ export interface IRFunction {
112
113
  // Module — top-level compilation unit
113
114
  // ---------------------------------------------------------------------------
114
115
 
116
+ export interface GlobalVar {
117
+ name: string
118
+ init: number
119
+ }
120
+
115
121
  export interface IRModule {
116
122
  namespace: string // datapack namespace (e.g. "mypack")
117
123
  functions: IRFunction[]
118
- globals: string[] // global variable names
124
+ globals: GlobalVar[] // global variable declarations with init values
119
125
  }