redscript-mc 1.0.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 (272) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
  2. package/.github/ISSUE_TEMPLATE/feature_request.md +31 -0
  3. package/.github/ISSUE_TEMPLATE/wrong_output.md +33 -0
  4. package/.github/PULL_REQUEST_TEMPLATE.md +34 -0
  5. package/.github/workflows/ci.yml +29 -0
  6. package/.github/workflows/publish-extension.yml +35 -0
  7. package/LICENSE +21 -0
  8. package/README.md +261 -0
  9. package/README.zh.md +261 -0
  10. package/dist/__tests__/cli.test.d.ts +1 -0
  11. package/dist/__tests__/cli.test.js +140 -0
  12. package/dist/__tests__/codegen.test.d.ts +1 -0
  13. package/dist/__tests__/codegen.test.js +121 -0
  14. package/dist/__tests__/diagnostics.test.d.ts +4 -0
  15. package/dist/__tests__/diagnostics.test.js +149 -0
  16. package/dist/__tests__/e2e.test.d.ts +6 -0
  17. package/dist/__tests__/e2e.test.js +1528 -0
  18. package/dist/__tests__/lexer.test.d.ts +1 -0
  19. package/dist/__tests__/lexer.test.js +316 -0
  20. package/dist/__tests__/lowering.test.d.ts +1 -0
  21. package/dist/__tests__/lowering.test.js +819 -0
  22. package/dist/__tests__/mc-integration.test.d.ts +12 -0
  23. package/dist/__tests__/mc-integration.test.js +395 -0
  24. package/dist/__tests__/mc-syntax.test.d.ts +1 -0
  25. package/dist/__tests__/mc-syntax.test.js +112 -0
  26. package/dist/__tests__/nbt.test.d.ts +1 -0
  27. package/dist/__tests__/nbt.test.js +82 -0
  28. package/dist/__tests__/optimizer-advanced.test.d.ts +1 -0
  29. package/dist/__tests__/optimizer-advanced.test.js +124 -0
  30. package/dist/__tests__/optimizer.test.d.ts +1 -0
  31. package/dist/__tests__/optimizer.test.js +118 -0
  32. package/dist/__tests__/parser.test.d.ts +1 -0
  33. package/dist/__tests__/parser.test.js +717 -0
  34. package/dist/__tests__/repl.test.d.ts +1 -0
  35. package/dist/__tests__/repl.test.js +27 -0
  36. package/dist/__tests__/runtime.test.d.ts +1 -0
  37. package/dist/__tests__/runtime.test.js +276 -0
  38. package/dist/__tests__/structure-optimizer.test.d.ts +1 -0
  39. package/dist/__tests__/structure-optimizer.test.js +33 -0
  40. package/dist/__tests__/typechecker.test.d.ts +1 -0
  41. package/dist/__tests__/typechecker.test.js +364 -0
  42. package/dist/ast/types.d.ts +357 -0
  43. package/dist/ast/types.js +9 -0
  44. package/dist/cli.d.ts +11 -0
  45. package/dist/cli.js +407 -0
  46. package/dist/codegen/cmdblock/index.d.ts +26 -0
  47. package/dist/codegen/cmdblock/index.js +45 -0
  48. package/dist/codegen/mcfunction/index.d.ts +34 -0
  49. package/dist/codegen/mcfunction/index.js +413 -0
  50. package/dist/codegen/structure/index.d.ts +18 -0
  51. package/dist/codegen/structure/index.js +249 -0
  52. package/dist/compile.d.ts +30 -0
  53. package/dist/compile.js +152 -0
  54. package/dist/data/arena/function/__load.mcfunction +6 -0
  55. package/dist/data/arena/function/__tick.mcfunction +2 -0
  56. package/dist/data/arena/function/announce_leaders/else_1.mcfunction +3 -0
  57. package/dist/data/arena/function/announce_leaders/foreach_0/merge_2.mcfunction +1 -0
  58. package/dist/data/arena/function/announce_leaders/foreach_0/then_0.mcfunction +3 -0
  59. package/dist/data/arena/function/announce_leaders/foreach_0.mcfunction +7 -0
  60. package/dist/data/arena/function/announce_leaders/foreach_1/merge_2.mcfunction +1 -0
  61. package/dist/data/arena/function/announce_leaders/foreach_1/then_0.mcfunction +4 -0
  62. package/dist/data/arena/function/announce_leaders/foreach_1.mcfunction +6 -0
  63. package/dist/data/arena/function/announce_leaders/merge_2.mcfunction +1 -0
  64. package/dist/data/arena/function/announce_leaders/then_0.mcfunction +4 -0
  65. package/dist/data/arena/function/announce_leaders.mcfunction +6 -0
  66. package/dist/data/arena/function/arena_tick/merge_2.mcfunction +1 -0
  67. package/dist/data/arena/function/arena_tick/then_0.mcfunction +4 -0
  68. package/dist/data/arena/function/arena_tick.mcfunction +11 -0
  69. package/dist/data/counter/function/__load.mcfunction +5 -0
  70. package/dist/data/counter/function/__tick.mcfunction +2 -0
  71. package/dist/data/counter/function/counter_tick/merge_2.mcfunction +1 -0
  72. package/dist/data/counter/function/counter_tick/then_0.mcfunction +3 -0
  73. package/dist/data/counter/function/counter_tick.mcfunction +11 -0
  74. package/dist/data/minecraft/tags/function/load.json +5 -0
  75. package/dist/data/minecraft/tags/function/tick.json +5 -0
  76. package/dist/data/quiz/function/__load.mcfunction +16 -0
  77. package/dist/data/quiz/function/__tick.mcfunction +6 -0
  78. package/dist/data/quiz/function/__trigger_quiz_a_dispatch.mcfunction +4 -0
  79. package/dist/data/quiz/function/__trigger_quiz_b_dispatch.mcfunction +4 -0
  80. package/dist/data/quiz/function/__trigger_quiz_c_dispatch.mcfunction +4 -0
  81. package/dist/data/quiz/function/__trigger_quiz_start_dispatch.mcfunction +4 -0
  82. package/dist/data/quiz/function/answer_a.mcfunction +4 -0
  83. package/dist/data/quiz/function/answer_b.mcfunction +4 -0
  84. package/dist/data/quiz/function/answer_c.mcfunction +4 -0
  85. package/dist/data/quiz/function/ask_question/else_1.mcfunction +5 -0
  86. package/dist/data/quiz/function/ask_question/else_4.mcfunction +5 -0
  87. package/dist/data/quiz/function/ask_question/else_7.mcfunction +4 -0
  88. package/dist/data/quiz/function/ask_question/merge_2.mcfunction +1 -0
  89. package/dist/data/quiz/function/ask_question/merge_5.mcfunction +2 -0
  90. package/dist/data/quiz/function/ask_question/merge_8.mcfunction +2 -0
  91. package/dist/data/quiz/function/ask_question/then_0.mcfunction +4 -0
  92. package/dist/data/quiz/function/ask_question/then_3.mcfunction +4 -0
  93. package/dist/data/quiz/function/ask_question/then_6.mcfunction +4 -0
  94. package/dist/data/quiz/function/ask_question.mcfunction +7 -0
  95. package/dist/data/quiz/function/finish_quiz.mcfunction +6 -0
  96. package/dist/data/quiz/function/handle_answer/else_1.mcfunction +5 -0
  97. package/dist/data/quiz/function/handle_answer/else_10.mcfunction +3 -0
  98. package/dist/data/quiz/function/handle_answer/else_16.mcfunction +3 -0
  99. package/dist/data/quiz/function/handle_answer/else_4.mcfunction +3 -0
  100. package/dist/data/quiz/function/handle_answer/else_7.mcfunction +5 -0
  101. package/dist/data/quiz/function/handle_answer/merge_11.mcfunction +2 -0
  102. package/dist/data/quiz/function/handle_answer/merge_14.mcfunction +2 -0
  103. package/dist/data/quiz/function/handle_answer/merge_17.mcfunction +2 -0
  104. package/dist/data/quiz/function/handle_answer/merge_2.mcfunction +8 -0
  105. package/dist/data/quiz/function/handle_answer/merge_5.mcfunction +2 -0
  106. package/dist/data/quiz/function/handle_answer/merge_8.mcfunction +2 -0
  107. package/dist/data/quiz/function/handle_answer/then_0.mcfunction +5 -0
  108. package/dist/data/quiz/function/handle_answer/then_12.mcfunction +5 -0
  109. package/dist/data/quiz/function/handle_answer/then_15.mcfunction +6 -0
  110. package/dist/data/quiz/function/handle_answer/then_3.mcfunction +6 -0
  111. package/dist/data/quiz/function/handle_answer/then_6.mcfunction +5 -0
  112. package/dist/data/quiz/function/handle_answer/then_9.mcfunction +6 -0
  113. package/dist/data/quiz/function/handle_answer.mcfunction +11 -0
  114. package/dist/data/quiz/function/start_quiz.mcfunction +5 -0
  115. package/dist/data/shop/function/__load.mcfunction +7 -0
  116. package/dist/data/shop/function/__tick.mcfunction +3 -0
  117. package/dist/data/shop/function/__trigger_shop_buy_dispatch.mcfunction +4 -0
  118. package/dist/data/shop/function/complete_purchase/else_1.mcfunction +5 -0
  119. package/dist/data/shop/function/complete_purchase/else_4.mcfunction +5 -0
  120. package/dist/data/shop/function/complete_purchase/else_7.mcfunction +3 -0
  121. package/dist/data/shop/function/complete_purchase/merge_2.mcfunction +2 -0
  122. package/dist/data/shop/function/complete_purchase/merge_5.mcfunction +2 -0
  123. package/dist/data/shop/function/complete_purchase/merge_8.mcfunction +2 -0
  124. package/dist/data/shop/function/complete_purchase/then_0.mcfunction +4 -0
  125. package/dist/data/shop/function/complete_purchase/then_3.mcfunction +4 -0
  126. package/dist/data/shop/function/complete_purchase/then_6.mcfunction +4 -0
  127. package/dist/data/shop/function/complete_purchase.mcfunction +7 -0
  128. package/dist/data/shop/function/handle_shop_trigger.mcfunction +3 -0
  129. package/dist/data/turret/function/__load.mcfunction +5 -0
  130. package/dist/data/turret/function/__tick.mcfunction +4 -0
  131. package/dist/data/turret/function/__trigger_deploy_turret_dispatch.mcfunction +4 -0
  132. package/dist/data/turret/function/deploy_turret.mcfunction +8 -0
  133. package/dist/data/turret/function/turret_tick/at_1.mcfunction +2 -0
  134. package/dist/data/turret/function/turret_tick/foreach_0.mcfunction +2 -0
  135. package/dist/data/turret/function/turret_tick/foreach_2.mcfunction +2 -0
  136. package/dist/data/turret/function/turret_tick/tick_body.mcfunction +3 -0
  137. package/dist/data/turret/function/turret_tick/tick_skip.mcfunction +1 -0
  138. package/dist/data/turret/function/turret_tick.mcfunction +5 -0
  139. package/dist/diagnostics/index.d.ts +44 -0
  140. package/dist/diagnostics/index.js +140 -0
  141. package/dist/index.d.ts +53 -0
  142. package/dist/index.js +126 -0
  143. package/dist/ir/builder.d.ts +32 -0
  144. package/dist/ir/builder.js +99 -0
  145. package/dist/ir/types.d.ts +117 -0
  146. package/dist/ir/types.js +15 -0
  147. package/dist/lexer/index.d.ts +36 -0
  148. package/dist/lexer/index.js +458 -0
  149. package/dist/lowering/index.d.ts +106 -0
  150. package/dist/lowering/index.js +2041 -0
  151. package/dist/mc-test/client.d.ts +128 -0
  152. package/dist/mc-test/client.js +174 -0
  153. package/dist/mc-test/runner.d.ts +28 -0
  154. package/dist/mc-test/runner.js +150 -0
  155. package/dist/mc-test/setup.d.ts +11 -0
  156. package/dist/mc-test/setup.js +98 -0
  157. package/dist/mc-validator/index.d.ts +17 -0
  158. package/dist/mc-validator/index.js +322 -0
  159. package/dist/nbt/index.d.ts +86 -0
  160. package/dist/nbt/index.js +250 -0
  161. package/dist/optimizer/commands.d.ts +36 -0
  162. package/dist/optimizer/commands.js +349 -0
  163. package/dist/optimizer/passes.d.ts +34 -0
  164. package/dist/optimizer/passes.js +227 -0
  165. package/dist/optimizer/structure.d.ts +8 -0
  166. package/dist/optimizer/structure.js +344 -0
  167. package/dist/pack.mcmeta +6 -0
  168. package/dist/parser/index.d.ts +76 -0
  169. package/dist/parser/index.js +1193 -0
  170. package/dist/repl.d.ts +16 -0
  171. package/dist/repl.js +165 -0
  172. package/dist/runtime/index.d.ts +101 -0
  173. package/dist/runtime/index.js +1288 -0
  174. package/dist/typechecker/index.d.ts +42 -0
  175. package/dist/typechecker/index.js +629 -0
  176. package/docs/COMPILATION_STATS.md +142 -0
  177. package/docs/IMPLEMENTATION_GUIDE.md +512 -0
  178. package/docs/LANGUAGE_REFERENCE.md +415 -0
  179. package/docs/MC_MAPPING.md +280 -0
  180. package/docs/STRUCTURE_TARGET.md +80 -0
  181. package/docs/mc-reference/commands.md +259 -0
  182. package/editors/vscode/.vscodeignore +10 -0
  183. package/editors/vscode/LICENSE +21 -0
  184. package/editors/vscode/README.md +78 -0
  185. package/editors/vscode/build.mjs +28 -0
  186. package/editors/vscode/icon.png +0 -0
  187. package/editors/vscode/mcfunction-language-configuration.json +28 -0
  188. package/editors/vscode/out/extension.js +7236 -0
  189. package/editors/vscode/package-lock.json +566 -0
  190. package/editors/vscode/package.json +137 -0
  191. package/editors/vscode/redscript-language-configuration.json +28 -0
  192. package/editors/vscode/snippets/redscript.json +114 -0
  193. package/editors/vscode/src/codeactions.ts +89 -0
  194. package/editors/vscode/src/completion.ts +130 -0
  195. package/editors/vscode/src/extension.ts +239 -0
  196. package/editors/vscode/src/hover.ts +1120 -0
  197. package/editors/vscode/src/symbols.ts +207 -0
  198. package/editors/vscode/syntaxes/mcfunction.tmLanguage.json +740 -0
  199. package/editors/vscode/syntaxes/redscript.tmLanguage.json +357 -0
  200. package/editors/vscode/tsconfig.json +13 -0
  201. package/jest.config.js +5 -0
  202. package/package.json +38 -0
  203. package/src/__tests__/cli.test.ts +130 -0
  204. package/src/__tests__/codegen.test.ts +128 -0
  205. package/src/__tests__/diagnostics.test.ts +195 -0
  206. package/src/__tests__/e2e.test.ts +1721 -0
  207. package/src/__tests__/fixtures/mc-commands-1.21.4.json +18734 -0
  208. package/src/__tests__/formatter.test.ts +46 -0
  209. package/src/__tests__/lexer.test.ts +356 -0
  210. package/src/__tests__/lowering.test.ts +962 -0
  211. package/src/__tests__/mc-integration.test.ts +409 -0
  212. package/src/__tests__/mc-syntax.test.ts +96 -0
  213. package/src/__tests__/nbt.test.ts +58 -0
  214. package/src/__tests__/optimizer-advanced.test.ts +144 -0
  215. package/src/__tests__/optimizer.test.ts +129 -0
  216. package/src/__tests__/parser.test.ts +800 -0
  217. package/src/__tests__/repl.test.ts +33 -0
  218. package/src/__tests__/runtime.test.ts +289 -0
  219. package/src/__tests__/structure-optimizer.test.ts +38 -0
  220. package/src/__tests__/typechecker.test.ts +395 -0
  221. package/src/ast/types.ts +248 -0
  222. package/src/cli.ts +445 -0
  223. package/src/codegen/cmdblock/index.ts +63 -0
  224. package/src/codegen/mcfunction/index.ts +471 -0
  225. package/src/codegen/structure/index.ts +305 -0
  226. package/src/compile.ts +188 -0
  227. package/src/diagnostics/index.ts +186 -0
  228. package/src/examples/README.md +77 -0
  229. package/src/examples/SHOWCASE_GAME.md +43 -0
  230. package/src/examples/arena.rs +44 -0
  231. package/src/examples/counter.rs +12 -0
  232. package/src/examples/pvp_arena.rs +131 -0
  233. package/src/examples/quiz.rs +90 -0
  234. package/src/examples/rpg.rs +13 -0
  235. package/src/examples/shop.rs +30 -0
  236. package/src/examples/showcase_game.rs +552 -0
  237. package/src/examples/stdlib_demo.rs +181 -0
  238. package/src/examples/turret.rs +27 -0
  239. package/src/examples/world_manager.rs +23 -0
  240. package/src/formatter/index.ts +22 -0
  241. package/src/index.ts +161 -0
  242. package/src/ir/builder.ts +114 -0
  243. package/src/ir/types.ts +119 -0
  244. package/src/lexer/index.ts +555 -0
  245. package/src/lowering/index.ts +2406 -0
  246. package/src/mc-test/client.ts +259 -0
  247. package/src/mc-test/runner.ts +140 -0
  248. package/src/mc-test/setup.ts +70 -0
  249. package/src/mc-validator/index.ts +367 -0
  250. package/src/nbt/index.ts +321 -0
  251. package/src/optimizer/commands.ts +416 -0
  252. package/src/optimizer/passes.ts +233 -0
  253. package/src/optimizer/structure.ts +441 -0
  254. package/src/parser/index.ts +1437 -0
  255. package/src/repl.ts +165 -0
  256. package/src/runtime/index.ts +1403 -0
  257. package/src/stdlib/README.md +156 -0
  258. package/src/stdlib/combat.rs +20 -0
  259. package/src/stdlib/cooldown.rs +45 -0
  260. package/src/stdlib/math.rs +49 -0
  261. package/src/stdlib/mobs.rs +99 -0
  262. package/src/stdlib/player.rs +29 -0
  263. package/src/stdlib/strings.rs +7 -0
  264. package/src/stdlib/timer.rs +51 -0
  265. package/src/templates/README.md +126 -0
  266. package/src/templates/combat.rs +96 -0
  267. package/src/templates/economy.rs +40 -0
  268. package/src/templates/mini-game-framework.rs +117 -0
  269. package/src/templates/quest.rs +78 -0
  270. package/src/test_programs/zombie_game.rs +25 -0
  271. package/src/typechecker/index.ts +737 -0
  272. package/tsconfig.json +16 -0
@@ -0,0 +1,552 @@
1
+ import "../stdlib/math.rs"
2
+ import "../stdlib/player.rs"
3
+ import "../stdlib/cooldown.rs"
4
+
5
+ const WIN_SCORE: int = 15
6
+ const CRYSTAL_VALUE: int = 3
7
+ const ENEMY_DAMAGE: int = 2
8
+ const MIN_PLAYERS: int = 2
9
+ const COUNTDOWN_SECONDS: int = 10
10
+ const ROUND_SECONDS: int = 90
11
+ const RESET_SECONDS: int = 8
12
+ const DASH_DISTANCE: int = 3
13
+ const DASH_COOLDOWN: int = 80
14
+
15
+ enum Phase { Waiting, Countdown, Playing, Ended }
16
+ enum Lane { North, South, East, West }
17
+
18
+ struct GameState {
19
+ phase: int,
20
+ countdown: int,
21
+ timer: int,
22
+ joined: int,
23
+ reset_timer: int
24
+ }
25
+
26
+ struct PlayerState {
27
+ score: int,
28
+ crystals: int,
29
+ streak: int,
30
+ alive: int
31
+ }
32
+
33
+ fn snapshot_game() -> GameState {
34
+ let phase: int = scoreboard_get(#crystal, #phase);
35
+ let countdown: int = scoreboard_get(#crystal, #countdown);
36
+ let timer: int = scoreboard_get(#crystal, #timer);
37
+ let joined: int = scoreboard_get(#crystal, #joined);
38
+ let reset_timer: int = scoreboard_get(#crystal, #reset_timer);
39
+ let state: GameState = {
40
+ phase: phase,
41
+ countdown: countdown,
42
+ timer: timer,
43
+ joined: joined,
44
+ reset_timer: reset_timer
45
+ };
46
+ return state;
47
+ }
48
+
49
+ fn snapshot_player() -> PlayerState {
50
+ let score: int = scoreboard_get(@s, #score);
51
+ let crystals: int = scoreboard_get(@s, #crystals);
52
+ let streak: int = scoreboard_get(@s, #streak);
53
+ let alive: int = scoreboard_get(@s, #alive);
54
+ let state: PlayerState = { score: score, crystals: crystals, streak: streak, alive: alive };
55
+ return state;
56
+ }
57
+
58
+ fn save_player(state: PlayerState) {
59
+ scoreboard_set(@s, #score, state.score);
60
+ scoreboard_set(@s, #crystals, state.crystals);
61
+ scoreboard_set(@s, #streak, state.streak);
62
+ scoreboard_set(@s, #alive, state.alive);
63
+ }
64
+
65
+ fn crystal_reward(base: int, streak: int = 0) -> int {
66
+ let bonus: int = clamp(streak, 0, 2);
67
+ return base + bonus;
68
+ }
69
+
70
+ fn count_joined_players() -> int {
71
+ let total: int = 0;
72
+
73
+ foreach (player in @a[tag=cr_joined]) {
74
+ total += 1;
75
+ }
76
+
77
+ scoreboard_set(#crystal, #joined, total);
78
+ return total;
79
+ }
80
+
81
+ fn set_phase(phase: int) {
82
+ scoreboard_set(#crystal, #phase, phase);
83
+ }
84
+
85
+ fn lane_live_score(lane: int) -> int {
86
+ match (lane) {
87
+ Lane.North => {
88
+ return scoreboard_get(#crystal, #north_live);
89
+ }
90
+ Lane.South => {
91
+ return scoreboard_get(#crystal, #south_live);
92
+ }
93
+ Lane.East => {
94
+ return scoreboard_get(#crystal, #east_live);
95
+ }
96
+ _ => {
97
+ return scoreboard_get(#crystal, #west_live);
98
+ }
99
+ }
100
+ }
101
+
102
+ fn set_lane_live(lane: int, live: int) {
103
+ match (lane) {
104
+ Lane.North => {
105
+ scoreboard_set(#crystal, #north_live, live);
106
+ }
107
+ Lane.South => {
108
+ scoreboard_set(#crystal, #south_live, live);
109
+ }
110
+ Lane.East => {
111
+ scoreboard_set(#crystal, #east_live, live);
112
+ }
113
+ _ => {
114
+ scoreboard_set(#crystal, #west_live, live);
115
+ }
116
+ }
117
+ }
118
+
119
+ fn lane_label(lane: int) -> string {
120
+ match (lane) {
121
+ Lane.North => {
122
+ return "North";
123
+ }
124
+ Lane.South => {
125
+ return "South";
126
+ }
127
+ Lane.East => {
128
+ return "East";
129
+ }
130
+ _ => {
131
+ return "West";
132
+ }
133
+ }
134
+ }
135
+
136
+ fn detect_lane() -> int {
137
+ let px: int = data_get("entity", @s, "Pos[0]");
138
+ let pz: int = data_get("entity", @s, "Pos[2]");
139
+ let ax: int = abs(px);
140
+ let az: int = abs(pz);
141
+
142
+ if (az >= ax) {
143
+ if (pz < 0) {
144
+ return Lane.North;
145
+ } else {
146
+ return Lane.South;
147
+ }
148
+ }
149
+
150
+ if (px >= 0) {
151
+ return Lane.East;
152
+ }
153
+
154
+ return Lane.West;
155
+ }
156
+
157
+ fn respawn_lane(lane: int) {
158
+ let live: int = lane_live_score(lane);
159
+
160
+ if (live == 1) {
161
+ return;
162
+ }
163
+
164
+ match (lane) {
165
+ Lane.North => {
166
+ let pos: BlockPos = (0, 65, -8);
167
+ setblock(pos, "minecraft:emerald_block");
168
+ }
169
+ Lane.South => {
170
+ let pos: BlockPos = (0, 65, 8);
171
+ setblock(pos, "minecraft:emerald_block");
172
+ }
173
+ Lane.East => {
174
+ let pos: BlockPos = (8, 65, 0);
175
+ setblock(pos, "minecraft:emerald_block");
176
+ }
177
+ _ => {
178
+ let pos: BlockPos = (-8, 65, 0);
179
+ setblock(pos, "minecraft:emerald_block");
180
+ }
181
+ }
182
+
183
+ set_lane_live(lane, 1);
184
+ }
185
+
186
+ fn clear_lane(lane: int) {
187
+ match (lane) {
188
+ Lane.North => {
189
+ let pos: BlockPos = (0, 65, -8);
190
+ setblock(pos, "minecraft:air");
191
+ }
192
+ Lane.South => {
193
+ let pos: BlockPos = (0, 65, 8);
194
+ setblock(pos, "minecraft:air");
195
+ }
196
+ Lane.East => {
197
+ let pos: BlockPos = (8, 65, 0);
198
+ setblock(pos, "minecraft:air");
199
+ }
200
+ _ => {
201
+ let pos: BlockPos = (-8, 65, 0);
202
+ setblock(pos, "minecraft:air");
203
+ }
204
+ }
205
+ }
206
+
207
+ fn build_arena() {
208
+ let floor_a: BlockPos = (-12, 64, -12);
209
+ let floor_b: BlockPos = (12, 64, 12);
210
+ let air_a: BlockPos = (-12, 65, -12);
211
+ let air_b: BlockPos = (12, 72, 12);
212
+ let spawn: BlockPos = (0, 65, 0);
213
+ fill(floor_a, floor_b, "minecraft:quartz_block");
214
+ fill(air_a, air_b, "minecraft:air");
215
+ setblock(spawn, "minecraft:beacon");
216
+ setblock((0, 64, -8), "minecraft:sea_lantern");
217
+ setblock((0, 64, 8), "minecraft:sea_lantern");
218
+ setblock((8, 64, 0), "minecraft:sea_lantern");
219
+ setblock((-8, 64, 0), "minecraft:sea_lantern");
220
+ }
221
+
222
+ fn mark_player_spawn_pads() {
223
+ as @a[tag=cr_joined] at @s {
224
+ setblock((~0, 64, ~0), "minecraft:light_blue_stained_glass");
225
+ }
226
+ }
227
+
228
+ fn spawn_enemy_wave() {
229
+ let lanes: int[] = [0, 1, 2, 3];
230
+
231
+ foreach (lane in lanes) {
232
+ match (lane) {
233
+ Lane.North => {
234
+ summon("minecraft:zombie", (0, 65, -10));
235
+ }
236
+ Lane.South => {
237
+ summon("minecraft:zombie", (0, 65, 10));
238
+ }
239
+ Lane.East => {
240
+ summon("minecraft:zombie", (10, 65, 0));
241
+ }
242
+ _ => {
243
+ summon("minecraft:zombie", (-10, 65, 0));
244
+ }
245
+ }
246
+ }
247
+
248
+ announce("A fresh zombie wave crashes into the arena.");
249
+ }
250
+
251
+ fn reset_player_scores() {
252
+ scoreboard_set(@a[tag=cr_joined], #score, 0);
253
+ scoreboard_set(@a[tag=cr_joined], #crystals, 0);
254
+ scoreboard_set(@a[tag=cr_joined], #streak, 0);
255
+ scoreboard_set(@a[tag=cr_joined], #alive, 1);
256
+ scoreboard_set(@a[tag=cr_joined], #health, 20);
257
+ }
258
+
259
+ fn respawn_all_crystals() {
260
+ let lanes: int[] = [0, 1, 2, 3];
261
+
262
+ foreach (lane in lanes) {
263
+ respawn_lane(lane);
264
+ }
265
+ }
266
+
267
+ fn start_game() {
268
+ build_arena();
269
+ respawn_all_crystals();
270
+ reset_player_scores();
271
+ scoreboard_set(#crystal, #timer, ROUND_SECONDS);
272
+ scoreboard_set(#crystal, #countdown, 0);
273
+ scoreboard_set(#crystal, #reset_timer, 0);
274
+ set_phase(Phase.Playing);
275
+ tp(@a[tag=cr_joined], (0, 65, 0));
276
+ mark_player_spawn_pads();
277
+ cooldown_start("dash", 0);
278
+ title_times(@a, 10, 50, 10);
279
+ title(@a, "Crystal Rush");
280
+ subtitle(@a, "Collect crystals, survive zombies, race to 15");
281
+ announce("Crystal Rush has begun.");
282
+ }
283
+
284
+ fn announce_waiting() {
285
+ let joined: int = count_joined_players();
286
+ actionbar(@a, "Crystal Rush lobby: ${joined}/${MIN_PLAYERS} players ready");
287
+ }
288
+
289
+ fn tick_waiting() {
290
+ announce_waiting();
291
+ }
292
+
293
+ fn tick_countdown() {
294
+ let state: GameState = snapshot_game();
295
+ title(@a, "${state.countdown}");
296
+ subtitle(@a, "Crystal Rush starts soon");
297
+ actionbar(@a, "Use /trigger crystal_dash after the round begins");
298
+ }
299
+
300
+ fn tick_playing() {
301
+ cooldown_tick("dash");
302
+
303
+ foreach (player in @a[tag=cr_joined]) {
304
+ let state: PlayerState = snapshot_player();
305
+ actionbar(@s, "Crystals ${state.score}/${WIN_SCORE} | Streak ${state.streak}");
306
+
307
+ execute as @s at @s if entity @e[type=zombie, distance=..2] run {
308
+ damage(ENEMY_DAMAGE);
309
+ }
310
+
311
+ let health: int = scoreboard_get(@s, #health);
312
+ if (health <= 0) {
313
+ respawn_player_after_knockout();
314
+ }
315
+
316
+ if (state.score >= WIN_SCORE) {
317
+ end_game();
318
+ }
319
+ }
320
+ }
321
+
322
+ fn tick_ended() {
323
+ let state: GameState = snapshot_game();
324
+ actionbar(@a, "Resetting arena in ${state.reset_timer}s");
325
+ }
326
+
327
+ fn tick_fast() {
328
+ let phase: int = scoreboard_get(#crystal, #phase);
329
+
330
+ match (phase) {
331
+ Phase.Waiting => {
332
+ tick_waiting();
333
+ }
334
+ Phase.Countdown => {
335
+ tick_countdown();
336
+ }
337
+ Phase.Playing => {
338
+ tick_playing();
339
+ }
340
+ _ => {
341
+ tick_ended();
342
+ }
343
+ }
344
+ }
345
+
346
+ fn second_waiting() {
347
+ count_joined_players();
348
+ }
349
+
350
+ fn second_countdown() {
351
+ let cd: int = scoreboard_get(#crystal, #countdown);
352
+
353
+ if (cd > 1) {
354
+ scoreboard_set(#crystal, #countdown, cd - 1);
355
+ } else {
356
+ start_game();
357
+ }
358
+ }
359
+
360
+ fn second_playing() {
361
+ let timer: int = scoreboard_get(#crystal, #timer);
362
+ let next: int = max(timer - 1, 0);
363
+ scoreboard_set(#crystal, #timer, next);
364
+
365
+ if (next % 10 == 0) {
366
+ spawn_enemy_wave();
367
+ }
368
+
369
+ respawn_all_crystals();
370
+
371
+ if (next <= 0) {
372
+ end_game();
373
+ }
374
+ }
375
+
376
+ fn second_ended() {
377
+ let reset_timer: int = scoreboard_get(#crystal, #reset_timer);
378
+
379
+ if (reset_timer > 1) {
380
+ scoreboard_set(#crystal, #reset_timer, reset_timer - 1);
381
+ } else {
382
+ reset_match();
383
+ }
384
+ }
385
+
386
+ @tick
387
+ fn crystal_rush_tick() {
388
+ tick_fast();
389
+ }
390
+
391
+ @tick(rate=20)
392
+ fn crystal_rush_second() {
393
+ let phase: int = scoreboard_get(#crystal, #phase);
394
+
395
+ match (phase) {
396
+ Phase.Waiting => {
397
+ second_waiting();
398
+ }
399
+ Phase.Countdown => {
400
+ second_countdown();
401
+ }
402
+ Phase.Playing => {
403
+ second_playing();
404
+ }
405
+ _ => {
406
+ second_ended();
407
+ }
408
+ }
409
+ }
410
+
411
+ fn award_crystal(lane: int) {
412
+ let player: PlayerState = snapshot_player();
413
+ let reward: int = crystal_reward(CRYSTAL_VALUE, player.streak);
414
+ player.score = player.score + reward;
415
+ player.crystals = player.crystals + 1;
416
+ player.streak = player.streak + 1;
417
+ save_player(player);
418
+ clear_lane(lane);
419
+ set_lane_live(lane, 0);
420
+ title(@s, "+${reward} crystals");
421
+ subtitle(@s, "${lane_label(lane)} crystal secured");
422
+ xp_add(@s, reward, "points");
423
+ }
424
+
425
+ fn respawn_player_after_knockout() {
426
+ let player: PlayerState = snapshot_player();
427
+ player.streak = 0;
428
+ player.alive = 1;
429
+
430
+ if (player.score > 0) {
431
+ player.score = player.score - 1;
432
+ }
433
+
434
+ save_player(player);
435
+ scoreboard_set(@s, #health, 20);
436
+ title(@s, "Knocked out");
437
+ subtitle(@s, "You lost 1 crystal and returned to spawn");
438
+ tp(@s, (0, 65, 0));
439
+ }
440
+
441
+ fn end_game() {
442
+ set_phase(Phase.Ended);
443
+ scoreboard_set(#crystal, #reset_timer, RESET_SECONDS);
444
+ effect(@a, "minecraft:slowness", 5, 2);
445
+
446
+ foreach (player in @a[tag=cr_joined]) {
447
+ let score: int = scoreboard_get(@s, #score);
448
+ if (score >= WIN_SCORE) {
449
+ title(@a, "Game Over");
450
+ subtitle(@a, "${@s} wins Crystal Rush");
451
+ announce("${@s} reached ${score} crystals and won Crystal Rush.");
452
+ }
453
+ }
454
+ }
455
+
456
+ fn reset_match() {
457
+ set_phase(Phase.Waiting);
458
+ scoreboard_set(#crystal, #countdown, 0);
459
+ scoreboard_set(#crystal, #timer, 0);
460
+ scoreboard_set(#crystal, #reset_timer, 0);
461
+ clear_lane(Lane.North);
462
+ clear_lane(Lane.South);
463
+ clear_lane(Lane.East);
464
+ clear_lane(Lane.West);
465
+ announce("Crystal Rush reset. Waiting for more players.");
466
+ }
467
+
468
+ @on_trigger("crystal_join")
469
+ fn crystal_join() {
470
+ if (@s.has_tag("cr_joined")) {
471
+ actionbar(@s, "You are already in the Crystal Rush lobby");
472
+ return;
473
+ }
474
+
475
+ @s.tag("cr_joined");
476
+ scoreboard_set(@s, #score, 0);
477
+ scoreboard_set(@s, #crystals, 0);
478
+ scoreboard_set(@s, #streak, 0);
479
+ scoreboard_set(@s, #alive, 1);
480
+ scoreboard_set(@s, #health, 20);
481
+ tp(@s, (0, 65, 0));
482
+ title(@s, "Crystal Rush");
483
+ subtitle(@s, "Joined the lobby");
484
+
485
+ if (is_op() == 1) {
486
+ actionbar(@s, "Operator joined. You can start the round any time.");
487
+ } else {
488
+ actionbar(@s, "Use /trigger crystal_start once at least 2 players join");
489
+ }
490
+
491
+ count_joined_players();
492
+ }
493
+
494
+ @on_trigger("crystal_start")
495
+ fn crystal_start() {
496
+ let joined: int = count_joined_players();
497
+ let phase: int = scoreboard_get(#crystal, #phase);
498
+
499
+ if (phase != Phase.Waiting) {
500
+ actionbar(@s, "Crystal Rush is already running");
501
+ return;
502
+ }
503
+
504
+ if (joined < MIN_PLAYERS) {
505
+ actionbar(@s, "Need at least ${MIN_PLAYERS} joined players");
506
+ return;
507
+ }
508
+
509
+ scoreboard_set(#crystal, #countdown, COUNTDOWN_SECONDS);
510
+ set_phase(Phase.Countdown);
511
+ announce("Crystal Rush countdown has started.");
512
+ }
513
+
514
+ @on_trigger("crystal_claim")
515
+ fn crystal_claim() {
516
+ let phase: int = scoreboard_get(#crystal, #phase);
517
+
518
+ if (phase != Phase.Playing) {
519
+ actionbar(@s, "You can only claim crystals during the round");
520
+ return;
521
+ }
522
+
523
+ let lane: int = detect_lane();
524
+ let live: int = lane_live_score(lane);
525
+
526
+ if (live == 0) {
527
+ actionbar(@s, "${lane_label(lane)} crystal is still recharging");
528
+ return;
529
+ }
530
+
531
+ award_crystal(lane);
532
+ }
533
+
534
+ @on_trigger("crystal_dash")
535
+ fn crystal_dash() {
536
+ let phase: int = scoreboard_get(#crystal, #phase);
537
+
538
+ if (phase != Phase.Playing) {
539
+ actionbar(@s, "Dash unlocks once the match is live");
540
+ return;
541
+ }
542
+
543
+ if (cooldown_ready("dash") == 1) {
544
+ tp(@s, (^0, ^0, ^3));
545
+ cooldown_start("dash", DASH_COOLDOWN);
546
+ title(@s, "Crystal Dash");
547
+ subtitle(@s, "You lunged ${DASH_DISTANCE} blocks forward");
548
+ } else {
549
+ let ticks_left: int = scoreboard_get("cooldown_ticks", #rs);
550
+ actionbar(@s, "Dash cooling down: ${ticks_left} ticks");
551
+ }
552
+ }
@@ -0,0 +1,181 @@
1
+ // Stdlib pattern demo: a simple survival loop.
2
+ //
3
+ // This file is standalone on purpose. Since RedScript has no import system yet,
4
+ // copy the helpers you need from src/stdlib/ into your project file.
5
+
6
+ fn abs(x: int) -> int {
7
+ if (x < 0) {
8
+ return -x;
9
+ } else {
10
+ return x;
11
+ }
12
+ }
13
+
14
+ fn clamp(x: int, lo: int, hi: int) -> int {
15
+ if (x < lo) {
16
+ return lo;
17
+ } else {
18
+ if (x > hi) {
19
+ return hi;
20
+ } else {
21
+ return x;
22
+ }
23
+ }
24
+ }
25
+
26
+ fn heal(amount: int) {
27
+ let health: int = scoreboard_get(@p, #health);
28
+ let next: int = health + amount;
29
+ scoreboard_set(@p, #health, next);
30
+ }
31
+
32
+ fn damage(amount: int) {
33
+ let health: int = scoreboard_get(@p, #health);
34
+ let next: int = health - amount;
35
+
36
+ if (next < 0) {
37
+ scoreboard_set(@p, #health, 0);
38
+ } else {
39
+ scoreboard_set(@p, #health, next);
40
+ }
41
+ }
42
+
43
+ fn is_op() -> int {
44
+ let result: int = 0;
45
+
46
+ execute if entity @p[tag=op] run {
47
+ result = 1;
48
+ }
49
+
50
+ return result;
51
+ }
52
+
53
+ fn timer_start(name: string, duration: int) {
54
+ scoreboard_set("demo_timer_ticks", #rs, duration);
55
+ scoreboard_set("demo_timer_active", #rs, 1);
56
+ }
57
+
58
+ fn timer_tick(name: string) -> int {
59
+ let active: int = scoreboard_get("demo_timer_active", #rs);
60
+ let ticks: int = scoreboard_get("demo_timer_ticks", #rs);
61
+
62
+ if (active == 0) {
63
+ return 0;
64
+ }
65
+
66
+ if (ticks > 0) {
67
+ let next: int = ticks - 1;
68
+ scoreboard_set("demo_timer_ticks", #rs, next);
69
+
70
+ if (next == 0) {
71
+ scoreboard_set("demo_timer_active", #rs, 0);
72
+ }
73
+
74
+ return next;
75
+ }
76
+
77
+ scoreboard_set("demo_timer_active", #rs, 0);
78
+ return 0;
79
+ }
80
+
81
+ fn timer_done(name: string) -> int {
82
+ let active: int = scoreboard_get("demo_timer_active", #rs);
83
+ let ticks: int = scoreboard_get("demo_timer_ticks", #rs);
84
+
85
+ if (active == 0) {
86
+ if (ticks <= 0) {
87
+ return 1;
88
+ }
89
+ }
90
+
91
+ return 0;
92
+ }
93
+
94
+ fn cooldown_start(name: string, ticks: int) {
95
+ scoreboard_set("demo_dash_ticks", #rs, ticks);
96
+ scoreboard_set("demo_dash_active", #rs, 1);
97
+ }
98
+
99
+ fn cooldown_ready(name: string) -> int {
100
+ let active: int = scoreboard_get("demo_dash_active", #rs);
101
+ let ticks_left: int = scoreboard_get("demo_dash_ticks", #rs);
102
+
103
+ if (active == 0) {
104
+ return 1;
105
+ }
106
+
107
+ if (ticks_left <= 0) {
108
+ return 1;
109
+ }
110
+
111
+ return 0;
112
+ }
113
+
114
+ fn cooldown_tick(name: string) {
115
+ let active: int = scoreboard_get("demo_dash_active", #rs);
116
+ let ticks_left: int = scoreboard_get("demo_dash_ticks", #rs);
117
+
118
+ if (active == 0) {
119
+ return;
120
+ }
121
+
122
+ if (ticks_left > 0) {
123
+ let next: int = ticks_left - 1;
124
+ scoreboard_set("demo_dash_ticks", #rs, next);
125
+
126
+ if (next == 0) {
127
+ scoreboard_set("demo_dash_active", #rs, 0);
128
+ }
129
+ } else {
130
+ scoreboard_set("demo_dash_active", #rs, 0);
131
+ }
132
+ }
133
+
134
+ @on_trigger("arena_start")
135
+ fn arena_start() {
136
+ scoreboard_set("arena_zone_center", #rs, 0);
137
+ scoreboard_set(@p, #health, 20);
138
+ timer_start("wave", 200);
139
+ cooldown_start("dash", 0);
140
+ title(@p, "Arena started");
141
+ }
142
+
143
+ @tick
144
+ fn arena_tick() {
145
+ let remaining: int = timer_tick("wave");
146
+ cooldown_tick("dash");
147
+
148
+ if (timer_done("wave") == 1) {
149
+ title(@p, "Next wave");
150
+ timer_start("wave", 200);
151
+ }
152
+
153
+ let player_x: int = data_get("entity", @p, "Pos[0]");
154
+ let delta: int = player_x - scoreboard_get("arena_zone_center", #rs);
155
+ let distance: int = abs(delta);
156
+ let pressure: int = clamp(distance, 0, 8);
157
+
158
+ if (pressure > 4) {
159
+ damage(1);
160
+ } else {
161
+ heal(1);
162
+ }
163
+
164
+ if (remaining <= 40) {
165
+ tell(@p, "Wave nearly over.");
166
+ }
167
+ }
168
+
169
+ @on_trigger("dash")
170
+ fn dash_trigger() {
171
+ if (cooldown_ready("dash") == 1) {
172
+ raw("effect give @p speed 1 3 true");
173
+ cooldown_start("dash", 80);
174
+ } else {
175
+ tell(@p, "Dash is cooling down.");
176
+ }
177
+
178
+ if (is_op() == 1) {
179
+ tell(@p, "Operator override available.");
180
+ }
181
+ }