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,208 @@
1
+ // ============================================
2
+ // Capture the Flag Mini-Game
3
+ // ============================================
4
+ // 场景:两队对抗,抢夺对方旗帜带回己方基地得分
5
+ // Scenario: Two teams compete to capture enemy flag
6
+ // ============================================
7
+
8
+ import "../stdlib/teams.mcrs"
9
+ import "../stdlib/effects.mcrs"
10
+ import "../stdlib/world.mcrs"
11
+ import "../stdlib/inventory.mcrs"
12
+ import "../stdlib/particles.mcrs"
13
+
14
+ // ===== 配置 =====
15
+ const RED_BASE_X: int = -50;
16
+ const RED_BASE_Z: int = 0;
17
+ const BLUE_BASE_X: int = 50;
18
+ const BLUE_BASE_Z: int = 0;
19
+ const BASE_Y: int = 64;
20
+ const WIN_SCORE: int = 3;
21
+
22
+ // ===== 游戏状态 =====
23
+ struct GameState {
24
+ running: int,
25
+ red_score: int,
26
+ blue_score: int,
27
+ red_flag_taken: int,
28
+ blue_flag_taken: int
29
+ }
30
+
31
+ let game: GameState = GameState {
32
+ running: 0,
33
+ red_score: 0,
34
+ blue_score: 0,
35
+ red_flag_taken: 0,
36
+ blue_flag_taken: 0
37
+ };
38
+
39
+ // ===== 初始化 =====
40
+ @load
41
+ fn init() {
42
+ // 创建记分板
43
+ scoreboard_add_objective("ctf_team", "dummy");
44
+ scoreboard_add_objective("ctf_flag", "dummy");
45
+
46
+ // 创建队伍
47
+ setup_two_teams();
48
+
49
+ // 世界设置
50
+ set_day();
51
+ weather_clear();
52
+ enable_keep_inventory();
53
+
54
+ announce("§e[CTF] §f夺旗战已加载!使用 /trigger start 开始游戏");
55
+ }
56
+
57
+ // ===== 开始游戏 =====
58
+ fn start_game() {
59
+ game.running = 1;
60
+ game.red_score = 0;
61
+ game.blue_score = 0;
62
+
63
+ // 随机分配队伍
64
+ assign_teams();
65
+
66
+ // 传送到各自基地
67
+ tp(@a[team=red], RED_BASE_X, BASE_Y, RED_BASE_Z);
68
+ tp(@a[team=blue], BLUE_BASE_X, BASE_Y, BLUE_BASE_Z);
69
+
70
+ // 给装备
71
+ foreach (p in @a) {
72
+ clear_inventory(p);
73
+ give_kit_warrior(p);
74
+ }
75
+
76
+ // 放置旗帜(盔甲架)
77
+ place_flags();
78
+
79
+ announce("§e[CTF] §a游戏开始!抢夺敌方旗帜!");
80
+ title(@a, "§6夺旗战开始!");
81
+ }
82
+
83
+ fn place_flags() {
84
+ // 红方旗帜(红色旗帜方块)
85
+ setblock(RED_BASE_X, BASE_Y + 1, RED_BASE_Z, "minecraft:red_banner");
86
+ // 蓝方旗帜
87
+ setblock(BLUE_BASE_X, BASE_Y + 1, BLUE_BASE_Z, "minecraft:blue_banner");
88
+
89
+ game.red_flag_taken = 0;
90
+ game.blue_flag_taken = 0;
91
+ }
92
+
93
+ fn assign_teams() {
94
+ let count: int = 0;
95
+ foreach (p in @a) {
96
+ if (count % 2 == 0) {
97
+ team_join(p, "red");
98
+ scoreboard_set(p, "ctf_team", 1);
99
+ } else {
100
+ team_join(p, "blue");
101
+ scoreboard_set(p, "ctf_team", 2);
102
+ }
103
+ count = count + 1;
104
+ }
105
+ }
106
+
107
+ // ===== 每 tick 检查 =====
108
+ @tick
109
+ fn game_tick() {
110
+ if (game.running == 1) {
111
+ check_flag_pickup();
112
+ check_flag_capture();
113
+ check_win();
114
+ }
115
+ }
116
+
117
+ fn check_flag_pickup() {
118
+ // 检查蓝队玩家是否靠近红旗
119
+ foreach (p in @a[team=blue]) {
120
+ // 如果在红方基地附近且旗帜未被拿走
121
+ let dist: int = distance_to(p, RED_BASE_X, BASE_Y, RED_BASE_Z);
122
+ if (dist < 3) {
123
+ if (game.red_flag_taken == 0) {
124
+ game.red_flag_taken = 1;
125
+ tag_add(p, "has_flag");
126
+ setblock(RED_BASE_X, BASE_Y + 1, RED_BASE_Z, "minecraft:air");
127
+ announce("§e[CTF] §9蓝队 §f拿到了 §c红方旗帜§f!");
128
+ glow(p, 9999);
129
+ }
130
+ }
131
+ }
132
+
133
+ // 检查红队玩家是否靠近蓝旗
134
+ foreach (p in @a[team=red]) {
135
+ let dist: int = distance_to(p, BLUE_BASE_X, BASE_Y, BLUE_BASE_Z);
136
+ if (dist < 3) {
137
+ if (game.blue_flag_taken == 0) {
138
+ game.blue_flag_taken = 1;
139
+ tag_add(p, "has_flag");
140
+ setblock(BLUE_BASE_X, BASE_Y + 1, BLUE_BASE_Z, "minecraft:air");
141
+ announce("§e[CTF] §c红队 §f拿到了 §9蓝方旗帜§f!");
142
+ glow(p, 9999);
143
+ }
144
+ }
145
+ }
146
+ }
147
+
148
+ fn check_flag_capture() {
149
+ // 蓝队带红旗回蓝方基地
150
+ foreach (p in @a[team=blue, tag=has_flag]) {
151
+ let dist: int = distance_to(p, BLUE_BASE_X, BASE_Y, BLUE_BASE_Z);
152
+ if (dist < 5) {
153
+ game.blue_score = game.blue_score + 1;
154
+ announce("§e[CTF] §9蓝队 §a得分!§f (" + game.blue_score + "/" + WIN_SCORE + ")");
155
+ tag_remove(p, "has_flag");
156
+ effect_clear(p, "minecraft:glowing");
157
+ place_flags();
158
+ happy(p);
159
+ }
160
+ }
161
+
162
+ // 红队带蓝旗回红方基地
163
+ foreach (p in @a[team=red, tag=has_flag]) {
164
+ let dist: int = distance_to(p, RED_BASE_X, BASE_Y, RED_BASE_Z);
165
+ if (dist < 5) {
166
+ game.red_score = game.red_score + 1;
167
+ announce("§e[CTF] §c红队 §a得分!§f (" + game.red_score + "/" + WIN_SCORE + ")");
168
+ tag_remove(p, "has_flag");
169
+ effect_clear(p, "minecraft:glowing");
170
+ place_flags();
171
+ happy(p);
172
+ }
173
+ }
174
+ }
175
+
176
+ fn check_win() {
177
+ if (game.red_score >= WIN_SCORE) {
178
+ end_game("red");
179
+ }
180
+ if (game.blue_score >= WIN_SCORE) {
181
+ end_game("blue");
182
+ }
183
+ }
184
+
185
+ fn end_game(winner: string) {
186
+ game.running = 0;
187
+
188
+ if (winner == "red") {
189
+ title(@a, "§c红队获胜!");
190
+ announce("§e[CTF] §c红队 §6赢得了比赛!");
191
+ } else {
192
+ title(@a, "§9蓝队获胜!");
193
+ announce("§e[CTF] §9蓝队 §6赢得了比赛!");
194
+ }
195
+
196
+ // 清理
197
+ foreach (p in @a[tag=has_flag]) {
198
+ tag_remove(p, "has_flag");
199
+ effect_clear(p);
200
+ }
201
+ }
202
+
203
+ // ===== 辅助函数 =====
204
+ fn distance_to(target: selector, x: int, y: int, z: int) -> int {
205
+ // 简化的距离计算(使用记分板)
206
+ let dist: int = scoreboard_get(target, "ctf_dist");
207
+ return dist;
208
+ }
@@ -2,7 +2,7 @@
2
2
 
3
3
  @tick
4
4
  fn counter_tick() {
5
- let ticks: int = scoreboard_get("counter", #ticks);
5
+ let ticks = scoreboard_get("counter", #ticks);
6
6
  ticks = ticks + 1;
7
7
  scoreboard_set("counter", #ticks, ticks);
8
8
 
@@ -0,0 +1,301 @@
1
+ // ============================================
2
+ // Hunger Games - 饥饿游戏
3
+ // ============================================
4
+ // 场景:玩家从中央补给点开始,搜刮物资生存
5
+ // 边界逐渐缩小,最后存活者获胜
6
+ // Scenario: Battle royale with shrinking border
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 CENTER_X: int = 0;
16
+ const CENTER_Y: int = 64;
17
+ const CENTER_Z: int = 0;
18
+ const INITIAL_BORDER: int = 200;
19
+ const FINAL_BORDER: int = 20;
20
+ const GRACE_PERIOD: int = 600; // 30秒无敌时间
21
+ const SHRINK_INTERVAL: int = 1200; // 每60秒缩圈
22
+
23
+ // ===== 游戏状态 =====
24
+ struct HGState {
25
+ running: int,
26
+ phase: int, // 0=等待, 1=倒计时, 2=游戏中, 3=结束
27
+ countdown: int,
28
+ grace_timer: int,
29
+ border_timer: int,
30
+ current_border: int,
31
+ alive_count: int
32
+ }
33
+
34
+ let game: HGState = HGState {
35
+ running: 0,
36
+ phase: 0,
37
+ countdown: 0,
38
+ grace_timer: 0,
39
+ border_timer: 0,
40
+ current_border: INITIAL_BORDER,
41
+ alive_count: 0
42
+ };
43
+
44
+ @load
45
+ fn init() {
46
+ scoreboard_add_objective("hg_kills", "dummy");
47
+ scoreboard_add_objective("hg_alive", "dummy");
48
+
49
+ // 设置世界边界
50
+ // worldborder center 0 0
51
+ // worldborder set 200
52
+
53
+ announce("§c[饥饿游戏] §f已加载!等待玩家加入...");
54
+ }
55
+
56
+ // ===== 开始游戏 =====
57
+ fn start_countdown() {
58
+ game.phase = 1;
59
+ game.countdown = 200; // 10秒倒计时
60
+
61
+ // 传送所有玩家到中央
62
+ foreach (p in @a) {
63
+ tp(p, CENTER_X, CENTER_Y + 50, CENTER_Z); // 高空开始
64
+ scoreboard_set(p, "hg_kills", 0);
65
+ scoreboard_set(p, "hg_alive", 1);
66
+
67
+ // 清空背包
68
+ clear(p);
69
+
70
+ // 冻结玩家
71
+ effect(p, "minecraft:slowness", 15, 255);
72
+ effect(p, "minecraft:jump_boost", 15, 250); // 防止跳跃
73
+ }
74
+
75
+ // 创建边界 bossbar
76
+ create_progress_bar("hg_border", "§c边界缩小中", INITIAL_BORDER);
77
+
78
+ announce("§c[饥饿游戏] §e游戏即将开始!");
79
+ }
80
+
81
+ fn start_game() {
82
+ game.phase = 2;
83
+ game.running = 1;
84
+ game.grace_timer = GRACE_PERIOD;
85
+ game.border_timer = SHRINK_INTERVAL;
86
+ game.current_border = INITIAL_BORDER;
87
+
88
+ // 计算存活人数
89
+ game.alive_count = 0;
90
+ foreach (p in @a) {
91
+ game.alive_count = game.alive_count + 1;
92
+
93
+ // 清除效果
94
+ effect_clear(p);
95
+
96
+ // 缓降
97
+ slow_fall(p, 100);
98
+ }
99
+
100
+ title(@a, "§c游戏开始!");
101
+ subtitle(@a, "§7搜刮物资,成为最后的生存者!");
102
+
103
+ announce("§c[饥饿游戏] §a游戏开始!§730秒内无法互相伤害");
104
+ }
105
+
106
+ // ===== 每 tick =====
107
+ @tick
108
+ fn game_tick() {
109
+ if (game.phase == 1) {
110
+ countdown_tick();
111
+ }
112
+
113
+ if (game.phase == 2) {
114
+ main_game_tick();
115
+ }
116
+ }
117
+
118
+ fn countdown_tick() {
119
+ game.countdown = game.countdown - 1;
120
+
121
+ // 倒计时提示
122
+ if (game.countdown == 100) {
123
+ title(@a, "§e5");
124
+ playsound("minecraft:block.note_block.pling", "player", @a);
125
+ }
126
+ if (game.countdown == 80) {
127
+ title(@a, "§e4");
128
+ playsound("minecraft:block.note_block.pling", "player", @a);
129
+ }
130
+ if (game.countdown == 60) {
131
+ title(@a, "§e3");
132
+ playsound("minecraft:block.note_block.pling", "player", @a);
133
+ }
134
+ if (game.countdown == 40) {
135
+ title(@a, "§c2");
136
+ playsound("minecraft:block.note_block.pling", "player", @a);
137
+ }
138
+ if (game.countdown == 20) {
139
+ title(@a, "§c1");
140
+ playsound("minecraft:block.note_block.pling", "player", @a);
141
+ }
142
+
143
+ if (game.countdown <= 0) {
144
+ start_game();
145
+ }
146
+ }
147
+
148
+ fn main_game_tick() {
149
+ // 无敌时间
150
+ if (game.grace_timer > 0) {
151
+ game.grace_timer = game.grace_timer - 1;
152
+
153
+ if (game.grace_timer == 0) {
154
+ announce("§c[饥饿游戏] §4无敌时间结束!可以开始战斗!");
155
+ title(@a, "§c战斗开始!");
156
+ }
157
+ }
158
+
159
+ // 边界缩小
160
+ game.border_timer = game.border_timer - 1;
161
+ if (game.border_timer <= 0) {
162
+ shrink_border();
163
+ game.border_timer = SHRINK_INTERVAL;
164
+ }
165
+
166
+ // 更新 bossbar
167
+ update_bar("hg_border", game.current_border);
168
+
169
+ // 检查边界伤害
170
+ check_border_damage();
171
+
172
+ // 检查存活人数
173
+ check_alive();
174
+ }
175
+
176
+ fn shrink_border() {
177
+ if (game.current_border > FINAL_BORDER) {
178
+ game.current_border = game.current_border - 20;
179
+
180
+ if (game.current_border < FINAL_BORDER) {
181
+ game.current_border = FINAL_BORDER;
182
+ }
183
+
184
+ // worldborder set game.current_border 60
185
+ announce("§c[饥饿游戏] §e边界正在缩小!当前大小:" + game.current_border);
186
+
187
+ // 更新 bossbar
188
+ bossbar_set_max("hg_border", INITIAL_BORDER);
189
+ bossbar_set_value("hg_border", game.current_border);
190
+
191
+ // 颜色变化
192
+ if (game.current_border < 50) {
193
+ bossbar_set_color("hg_border", "red");
194
+ } else {
195
+ if (game.current_border < 100) {
196
+ bossbar_set_color("hg_border", "yellow");
197
+ }
198
+ }
199
+ }
200
+ }
201
+
202
+ fn check_border_damage() {
203
+ // 对边界外的玩家造成伤害
204
+ // 需要检查玩家是否在边界外
205
+ foreach (p in @a) {
206
+ // 简化:使用 execute positioned
207
+ let dist: int = 0; // 需要计算距离中心的距离
208
+ // 如果超出边界,造成伤害
209
+ // effect(p, "minecraft:wither", 1, 0);
210
+ }
211
+ }
212
+
213
+ fn check_alive() {
214
+ let alive: int = 0;
215
+ let winner: selector = @a;
216
+
217
+ foreach (p in @a[gamemode=survival]) {
218
+ let is_alive: int = scoreboard_get(p, "hg_alive");
219
+ if (is_alive == 1) {
220
+ alive = alive + 1;
221
+ winner = p;
222
+ }
223
+ }
224
+
225
+ game.alive_count = alive;
226
+
227
+ // 更新记分板显示
228
+ actionbar(@a, "§c存活: " + alive + " 人");
229
+
230
+ // 检查胜利
231
+ if (alive <= 1) {
232
+ if (alive == 1) {
233
+ declare_winner(winner);
234
+ } else {
235
+ // 无人存活
236
+ end_game_no_winner();
237
+ }
238
+ }
239
+ }
240
+
241
+ fn declare_winner(winner: selector) {
242
+ game.phase = 3;
243
+ game.running = 0;
244
+
245
+ let kills: int = scoreboard_get(winner, "hg_kills");
246
+
247
+ title(@a, "§6胜利者!");
248
+ announce("§c[饥饿游戏] §6游戏结束!");
249
+ announce("§a胜利者击杀数:" + kills);
250
+
251
+ // 胜利特效
252
+ totem_effect(winner);
253
+ buff_all(winner, 200);
254
+
255
+ remove_bar("hg_border");
256
+ }
257
+
258
+ fn end_game_no_winner() {
259
+ game.phase = 3;
260
+ game.running = 0;
261
+
262
+ title(@a, "§7游戏结束");
263
+ subtitle(@a, "§c无人存活");
264
+
265
+ remove_bar("hg_border");
266
+ }
267
+
268
+ // ===== 玩家死亡处理 =====
269
+ fn on_player_death(player: selector, killer: selector) {
270
+ scoreboard_set(player, "hg_alive", 0);
271
+
272
+ // 更新击杀数
273
+ scoreboard_add(killer, "hg_kills", 1);
274
+
275
+ // 切换观察模式
276
+ // gamemode spectator player
277
+
278
+ let kills: int = scoreboard_get(killer, "hg_kills");
279
+ announce("§c☠ §7玩家被击杀!击杀者已有 " + kills + " 杀");
280
+ }
281
+
282
+ // ===== 重置游戏 =====
283
+ fn reset_game() {
284
+ game.phase = 0;
285
+ game.running = 0;
286
+
287
+ foreach (p in @a) {
288
+ scoreboard_set(p, "hg_kills", 0);
289
+ scoreboard_set(p, "hg_alive", 0);
290
+ effect_clear(p);
291
+ // gamemode survival p
292
+ }
293
+
294
+ // 重置边界
295
+ game.current_border = INITIAL_BORDER;
296
+ // worldborder set 200
297
+
298
+ remove_bar("hg_border");
299
+
300
+ announce("§c[饥饿游戏] §7游戏已重置");
301
+ }
@@ -0,0 +1,193 @@
1
+ /**
2
+ * RedScript New Features Demo
3
+ * 展示 2026-03-12 新增的语言特性
4
+ */
5
+
6
+ // ============================================
7
+ // 1. 类型推断 (Type Inference)
8
+ // ============================================
9
+ fn type_inference_demo() {
10
+ // 不需要写 `: int`,编译器自动推断
11
+ let health = 100;
12
+ let name = "Steve";
13
+ let alive = true;
14
+ let speed = 1.5;
15
+
16
+ // NBT 后缀也能推断
17
+ let damage = 20b; // byte
18
+ let distance = 1000s; // short
19
+ let bignum = 999999L; // long
20
+ let precise = 3.14d; // double
21
+
22
+ say("Health: ${health}, Name: ${name}");
23
+ }
24
+
25
+ // ============================================
26
+ // 2. for-range 循环 (For-Range Loops)
27
+ // ============================================
28
+ fn for_range_demo() {
29
+ // 从 0 到 9 循环
30
+ for i in 0..10 {
31
+ say("Count: ${i}");
32
+ }
33
+
34
+ // 可以用于倒计时
35
+ for sec in 0..5 {
36
+ title(@a, "Starting in ${sec}...");
37
+ }
38
+ }
39
+
40
+ // ============================================
41
+ // 3. NBT 结构化参数 (NBT Structured Params)
42
+ // ============================================
43
+ fn nbt_params_demo() {
44
+ // 给予带 NBT 的物品
45
+ give(@s, "minecraft:diamond_sword", 1, {
46
+ display: { Name: "Excalibur" },
47
+ Enchantments: [
48
+ { id: "minecraft:sharpness", lvl: 5 },
49
+ { id: "minecraft:unbreaking", lvl: 3 }
50
+ ]
51
+ });
52
+
53
+ // 召唤带属性的实体
54
+ summon("minecraft:zombie", @s, {
55
+ CustomName: "Boss Zombie",
56
+ Health: 100.0,
57
+ Attributes: [
58
+ { Name: "generic.max_health", Base: 100.0 }
59
+ ]
60
+ });
61
+ }
62
+
63
+ // ============================================
64
+ // 4. Set 数据结构 (Runtime Set)
65
+ // ============================================
66
+ fn set_demo() {
67
+ // 创建集合
68
+ let visited = set_new();
69
+
70
+ // 使用方法语法添加元素
71
+ visited.add("spawn");
72
+ visited.add("castle");
73
+ visited.add("dungeon");
74
+
75
+ // 检查是否存在
76
+ if (visited.contains("castle")) {
77
+ say("You've been to the castle!");
78
+ }
79
+
80
+ // 删除元素
81
+ visited.remove("spawn");
82
+
83
+ // 清空集合
84
+ visited.clear();
85
+ }
86
+
87
+ // ============================================
88
+ // 5. 方法语法糖 (Method Syntax Sugar)
89
+ // ============================================
90
+ fn method_syntax_demo() {
91
+ let items: string[] = [];
92
+
93
+ // obj.method(args) → method(obj, args)
94
+ items.push("sword");
95
+ items.push("shield");
96
+ items.push("potion");
97
+
98
+ let count = items.len();
99
+ say("You have ${count} items");
100
+
101
+ // Set 也可以用方法语法
102
+ let tags = set_new();
103
+ tags.add("vip");
104
+ tags.add("admin");
105
+
106
+ if (tags.contains("admin")) {
107
+ say("Welcome, admin!");
108
+ }
109
+ }
110
+
111
+ // ============================================
112
+ // 6. #mc_name 语法 (MC Identifier Syntax)
113
+ // ============================================
114
+ fn mc_name_demo() {
115
+ // #name 编译为裸 MC 名称(不带引号)
116
+ scoreboard_set(@s, #kills, 0);
117
+ scoreboard_add(@s, #deaths, 1);
118
+
119
+ let score = scoreboard_get(@s, #points);
120
+
121
+ // 用于 tag
122
+ tag_add(@s, #vip);
123
+
124
+ // 用于 team
125
+ team_join(@s, #red);
126
+
127
+ // 对比:字符串仍需引号
128
+ give(@s, "minecraft:diamond", 1);
129
+ }
130
+
131
+ // ============================================
132
+ // 7. 块注释 (Block Comments)
133
+ // ============================================
134
+
135
+ /*
136
+ * 这是块注释
137
+ * 可以跨越多行
138
+ */
139
+
140
+ /**
141
+ * 这是文档注释
142
+ * @param player 目标玩家
143
+ */
144
+ fn documented_function() {
145
+ say("Hello!");
146
+ }
147
+
148
+ // ============================================
149
+ // 综合示例:简单游戏
150
+ // ============================================
151
+
152
+ struct Player {
153
+ score: int,
154
+ level: int,
155
+ visited: string // set ID
156
+ }
157
+
158
+ fn init_player() {
159
+ let visited_set = set_new();
160
+ let p: Player = {
161
+ score: 0,
162
+ level: 1,
163
+ visited: visited_set
164
+ };
165
+
166
+ // 记录出生点(使用 set_add,因为 visited 是 string ID)
167
+ set_add(p.visited, "spawn");
168
+
169
+ scoreboard_set(@s, #score, p.score);
170
+ scoreboard_set(@s, #level, p.level);
171
+ }
172
+
173
+ @tick(rate=20)
174
+ fn game_tick() {
175
+ // 每秒检查一次
176
+ for i in 0..1 {
177
+ let score = scoreboard_get(@s, #score);
178
+ if (score >= 100) {
179
+ title(@s, "Level Up!");
180
+ scoreboard_set(@s, #level, 2);
181
+ }
182
+ }
183
+ }
184
+
185
+ fn reward_player(amount: int) {
186
+ // 类型推断 + NBT 参数
187
+ let bonus = amount * 2;
188
+ scoreboard_add(@s, #score, bonus);
189
+
190
+ give(@s, "minecraft:gold_ingot", amount, {
191
+ display: { Name: "Reward Gold" }
192
+ });
193
+ }