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
@@ -1,156 +1,164 @@
1
- # RedScript Stdlib
1
+ # RedScript Standard Library
2
2
 
3
- This folder contains source-only utility modules for RedScript projects. There is
4
- no import system yet, so stdlib usage is intentionally simple:
3
+ Ready-to-use utility functions for common Minecraft operations.
5
4
 
6
- 1. Copy the functions you need into your project file.
7
- 2. Compile a stdlib file on its own to inspect the generated `.mcfunction` output.
8
- 3. Recreate the same pattern with `raw("function namespace:name")` if you prefer
9
- to wire precompiled datapack functions manually.
5
+ ## Usage
10
6
 
11
- ## Modules
12
-
13
- ### `math.rs`
14
-
15
- Integer helpers:
16
-
17
- - `abs(x: int) -> int`
18
- - `min(a: int, b: int) -> int`
19
- - `max(a: int, b: int) -> int`
20
- - `clamp(x: int, lo: int, hi: int) -> int`
21
- - `sign(x: int) -> int`
22
-
23
- Example:
24
-
25
- ```rs
26
- let offset: int = abs(delta);
27
- let limited: int = clamp(offset, 0, 20);
28
- ```
29
-
30
- ### `player.rs`
31
-
32
- Nearest-player helpers:
33
-
34
- - `heal(amount: int)` adds to the nearest player's `health` scoreboard value
35
- - `damage(amount: int)` subtracts from the nearest player's `health` scoreboard
36
- - `is_op() -> int` returns `1` when the nearest player has the `op` tag
37
-
38
- Example:
39
-
40
- ```rs
41
- if (is_op() == 1) {
42
- heal(4);
43
- } else {
44
- damage(2);
45
- }
46
- ```
47
-
48
- ### `strings.rs`
49
-
50
- String helpers:
51
-
52
- - `str_len(s: string) -> int`
53
-
54
- Important limitation:
55
-
56
- RedScript still does not support general runtime string manipulation. Today the
57
- compiler only lowers `str_len(...)` cleanly when the input is a string literal,
58
- a `const` string, or a string variable initialized from a literal-backed value.
59
- That path works by storing the string in `storage rs:strings` and reading its
60
- character count with `data get storage`.
61
-
62
- Example:
63
-
64
- ```rs
65
- let name: string = "Player";
66
- let n: int = str_len(name);
67
- tell(@s, "${n}");
68
- ```
69
-
70
- ### `mobs.rs`
71
-
72
- Vanilla Java Edition entity type constants for selectors, summon helpers, and
73
- command wrappers. The file groups hostile, passive, neutral, boss, and misc
74
- entity IDs so you can copy stable names instead of repeating raw strings.
75
-
76
- Example:
77
-
78
- ```rs
79
- summon(ALLAY, "~", "~", "~");
80
- summon(ZOMBIE, "~", "~", "~");
81
- ```
82
- ```
83
-
84
- ### `timer.rs`
85
-
86
- Countdown helpers plus a `Timer` struct shape:
87
-
88
- - `struct Timer { ticks: int, active: int }`
89
- - `timer_start(name: string, duration: int)`
90
- - `timer_tick(name: string) -> int`
91
- - `timer_done(name: string) -> int`
92
-
93
- Important limitation:
94
-
95
- Current RedScript lowering does not preserve string arguments when they are passed
96
- into user-defined functions. Because of that, `timer.rs` is implemented as a
97
- single-slot template backed by `timer_ticks` and `timer_active` fake players on
98
- the `rs` objective. Keep one copy per named timer, or replace those fake-player
99
- names with your own concrete identifiers.
100
-
101
- Example:
102
-
103
- ```rs
104
- timer_start("wave", 200);
105
- let remaining: int = timer_tick("wave");
106
- if (timer_done("wave") == 1) {
107
- say("Next wave.");
108
- }
109
- ```
110
-
111
- ### `cooldown.rs`
112
-
113
- Cooldown helpers:
7
+ ```mcrs
8
+ import "stdlib/effects.mcrs"
9
+ import "stdlib/world.mcrs"
114
10
 
115
- - `cooldown_start(name: string, ticks: int)`
116
- - `cooldown_ready(name: string) -> int`
117
- - `cooldown_tick(name: string)`
118
-
119
- This file has the same single-slot limitation as `timer.rs`. Copy it and rename
120
- the scoreboard fake players if you need multiple independent cooldowns.
121
-
122
- Example:
123
-
124
- ```rs
125
- if (cooldown_ready("dash") == 1) {
126
- cooldown_start("dash", 40);
11
+ fn start() {
12
+ set_day();
13
+ buff_all(@a, 600); // 30 second buff
127
14
  }
128
- cooldown_tick("dash");
129
15
  ```
130
16
 
131
- ## Incorporation Patterns
132
-
133
- ### Copy into your project
134
-
135
- This is the simplest path today. Paste the helper functions above your gameplay
136
- functions and compile one `.rs` file.
137
-
138
- ### Compile separately
139
-
140
- You can inspect or ship a stdlib datapack directly:
141
-
142
- ```bash
143
- npx ts-node src/cli.ts compile src/stdlib/math.rs -o dist/stdlib-math
144
- ```
145
-
146
- ### Call precompiled helpers with `raw()`
147
-
148
- If you compile helper files into their own namespace, you can call them from raw
149
- Minecraft commands:
17
+ ## Modules
150
18
 
151
- ```rs
152
- raw("function stdlib_math:abs");
19
+ ### math.mcrs
20
+ Basic math utilities.
21
+ - `abs(x)` — Absolute value
22
+ - `min(a, b)` — Minimum of two values
23
+ - `max(a, b)` — Maximum of two values
24
+ - `clamp(x, min, max)` — Clamp value to range
25
+ - `sign(x)` — Sign of number (-1, 0, 1)
26
+
27
+ ### effects.mcrs
28
+ Effect shortcuts.
29
+ - `speed(target, duration, level)`
30
+ - `jump(target, duration, level)`
31
+ - `regen(target, duration, level)`
32
+ - `resistance(target, duration, level)`
33
+ - `strength(target, duration, level)`
34
+ - `invisible(target, duration)`
35
+ - `night_vision(target, duration)`
36
+ - `slow_fall(target, duration)`
37
+ - `glow(target, duration)`
38
+ - `clear_effects(target)`
39
+ - `buff_all(target, duration)` — Speed + strength + regen + resistance
40
+
41
+ ### world.mcrs
42
+ World and game rule helpers.
43
+ - `set_day()`, `set_night()`, `set_noon()`, `set_midnight()`
44
+ - `weather_clear()`, `weather_rain()`, `weather_thunder()`
45
+ - `enable_keep_inventory()`, `disable_keep_inventory()`
46
+ - `disable_mob_griefing()`, `disable_fire_spread()`
47
+ - `set_peaceful()`, `set_easy()`, `set_normal()`, `set_hard()`
48
+ - `barrier_wall(x1,y1,z1,x2,y2,z2)`
49
+ - `clear_area(x1,y1,z1,x2,y2,z2)`
50
+ - `glass_box(x1,y1,z1,x2,y2,z2)`
51
+
52
+ ### inventory.mcrs
53
+ Inventory management.
54
+ - `clear_inventory(target)`
55
+ - `give_kit_warrior(target)`
56
+ - `give_kit_archer(target)`
57
+ - `give_kit_mage(target)`
58
+ - `remove_item(target, item)`
59
+
60
+ ### player.mcrs
61
+ Player state management.
62
+ - `heal(amount)`
63
+ - `damage(amount)`
64
+ - `is_op()` — Check if player has op tag
65
+
66
+ ### cooldown.mcrs
67
+ Cooldown system using scoreboards.
68
+ - `cooldown_start(target, ticks)`
69
+ - `cooldown_tick()`
70
+ - `is_on_cooldown(target)` → int
71
+
72
+ ### timer.mcrs
73
+ Timer utilities.
74
+ - `timer_start(name, ticks)`
75
+ - `timer_tick()`
76
+ - `timer_done(name)` → int
77
+
78
+ ### combat.mcrs
79
+ Combat helpers.
80
+ - `apply_damage(target, amount)`
81
+ - `knockback(target, strength)`
82
+
83
+ ### mobs.mcrs
84
+ Mob spawning utilities.
85
+ - `spawn_zombie(x, y, z)`
86
+ - `spawn_skeleton(x, y, z)`
87
+ - `spawn_creeper(x, y, z)`
88
+
89
+ ### sets.mcrs
90
+ Runtime set operations (NBT-based).
91
+ - `make_set()` → string
92
+ - `add_to_set(set, value)`
93
+ - `in_set(set, value)` → int
94
+ - `remove_from_set(set, value)`
95
+
96
+ ### strings.mcrs
97
+ String formatting helpers.
98
+ - `broadcast(msg)` — Announce to all players
99
+ - `whisper(target, msg)` — Private message
100
+
101
+ ### particles.mcrs
102
+ Particle effect shortcuts.
103
+ - `hearts(target)` — Heart particles above target
104
+ - `flames(x, y, z)` — Fire particles
105
+ - `smoke(x, y, z)` — Smoke effect
106
+ - `explosion_effect(x, y, z)` — Explosion particles
107
+ - `sparkles(target)` — Enchantment sparkles
108
+ - `angry(target)` — Angry villager particles
109
+ - `happy(target)` — Happy villager particles
110
+ - `portal_effect(x, y, z)` — Portal particles
111
+ - `totem_effect(target)` — Totem of undying particles
112
+ - `end_sparkles(target)` — End rod particles
113
+
114
+ ### spawn.mcrs
115
+ Teleport and spawn utilities.
116
+ - `teleport_to(target, x, y, z)` — TP to coordinates
117
+ - `teleport_to_entity(target, dest)` — TP to entity
118
+ - `gather_all(x, y, z)` — TP all players
119
+ - `launch_up(target, height)` — Launch player upward
120
+ - `goto_lobby(target)` — TP to lobby
121
+ - `goto_arena(target)` — TP to arena
122
+
123
+ ### teams.mcrs
124
+ Team management.
125
+ - `create_team(name, color)` — Create colored team
126
+ - `create_red_team()`, `create_blue_team()` — Quick team setup
127
+ - `create_green_team()`, `create_yellow_team()`
128
+ - `add_to_team(target, team)` — Add player to team
129
+ - `remove_from_teams(target)` — Remove from all teams
130
+ - `setup_two_teams()` — Quick 2-team setup
131
+ - `setup_four_teams()` — Quick 4-team setup
132
+ - `cleanup_teams()` — Remove all teams
133
+
134
+ ### bossbar.mcrs
135
+ Bossbar for timers and progress.
136
+ - `create_timer_bar(id, name, seconds)` — Timer bossbar
137
+ - `create_health_bar(id, name, max)` — Red health bar
138
+ - `create_progress_bar(id, name, max)` — Blue progress bar
139
+ - `update_bar(id, value)` — Update value
140
+ - `hide_bar(id)`, `show_bar(id)` — Visibility
141
+ - `remove_bar(id)` — Delete bossbar
142
+ - `update_bar_color(id, percent)` — Color by percentage
143
+
144
+ ### interactions.mcrs
145
+ Player input detection (right click, sneak, look direction).
146
+ - `interactions_init()` — Setup scoreboards (call on @load)
147
+ - `on_right_click()` — Detect carrot-on-stick right click
148
+ - `is_sneaking(target)` — Check if sneaking
149
+ - `on_sneak_start()` — Detect sneak start (first tick)
150
+ - `check_look_up()` — Tag players looking up
151
+ - `check_look_down()` — Tag players looking down
152
+ - `on_sneak_click()` — Detect sneak + right click combo
153
+ - `on_double_sneak()` — Detect double-tap sneak
154
+
155
+ **New selector filters:**
156
+ ```mcrs
157
+ // Rotation (pitch/yaw)
158
+ @a[x_rotation=-90..-45] // Looking up
159
+ @a[x_rotation=45..90] // Looking down
160
+ @a[y_rotation=0..90] // Facing east
161
+
162
+ // Position ranges
163
+ @a[x=-5..5, y=62..68, z=-5..5] // In specific area
153
164
  ```
154
-
155
- That pattern is most useful for command wrappers or tick/load entrypoints where
156
- the datapack interface is already fixed.
@@ -0,0 +1,68 @@
1
+ // Bossbar helpers for progress bars and timers
2
+ // Usage: import "stdlib/bossbar.mcrs"
3
+
4
+ /// Create and show a timer bossbar
5
+ fn create_timer_bar(id: string, name: string, max_seconds: int) {
6
+ bossbar_add(id, name);
7
+ bossbar_set_max(id, max_seconds * 20);
8
+ bossbar_set_value(id, max_seconds * 20);
9
+ bossbar_set_color(id, "green");
10
+ bossbar_set_style(id, "progress");
11
+ bossbar_set_players(id, @a);
12
+ bossbar_set_visible(id, 1);
13
+ }
14
+
15
+ /// Create a health-style bossbar (red)
16
+ fn create_health_bar(id: string, name: string, max_val: int) {
17
+ bossbar_add(id, name);
18
+ bossbar_set_max(id, max_val);
19
+ bossbar_set_value(id, max_val);
20
+ bossbar_set_color(id, "red");
21
+ bossbar_set_style(id, "progress");
22
+ bossbar_set_players(id, @a);
23
+ bossbar_set_visible(id, 1);
24
+ }
25
+
26
+ /// Create a progress bossbar (blue)
27
+ fn create_progress_bar(id: string, name: string, max_val: int) {
28
+ bossbar_add(id, name);
29
+ bossbar_set_max(id, max_val);
30
+ bossbar_set_value(id, 0);
31
+ bossbar_set_color(id, "blue");
32
+ bossbar_set_style(id, "progress");
33
+ bossbar_set_players(id, @a);
34
+ bossbar_set_visible(id, 1);
35
+ }
36
+
37
+ /// Update bossbar value
38
+ fn update_bar(id: string, value: int) {
39
+ bossbar_set_value(id, value);
40
+ }
41
+
42
+ /// Hide bossbar
43
+ fn hide_bar(id: string) {
44
+ bossbar_set_visible(id, 0);
45
+ }
46
+
47
+ /// Show bossbar
48
+ fn show_bar(id: string) {
49
+ bossbar_set_visible(id, 1);
50
+ }
51
+
52
+ /// Remove bossbar
53
+ fn remove_bar(id: string) {
54
+ bossbar_remove(id);
55
+ }
56
+
57
+ /// Set bossbar color based on percentage
58
+ fn update_bar_color(id: string, percent: int) {
59
+ if (percent > 66) {
60
+ bossbar_set_color(id, "green");
61
+ } else {
62
+ if (percent > 33) {
63
+ bossbar_set_color(id, "yellow");
64
+ } else {
65
+ bossbar_set_color(id, "red");
66
+ }
67
+ }
68
+ }
@@ -1,6 +1,6 @@
1
1
  // Cooldown helpers.
2
2
  //
3
- // Like timer.rs, the `name` parameter is reserved for a future compiler/runtime
3
+ // Like timer.mcrs, the `name` parameter is reserved for a future compiler/runtime
4
4
  // that can route string literals through user-defined calls. Today this file
5
5
  // manages one cooldown slot on the `rs` objective.
6
6
 
@@ -0,0 +1,64 @@
1
+ // Effect helper functions
2
+
3
+ /// Give speed boost
4
+ fn speed(target: selector, duration: int, level: int) {
5
+ effect(target, "minecraft:speed", duration, level);
6
+ }
7
+
8
+ /// Give jump boost
9
+ fn jump(target: selector, duration: int, level: int) {
10
+ effect(target, "minecraft:jump_boost", duration, level);
11
+ }
12
+
13
+ /// Give regeneration
14
+ fn regen(target: selector, duration: int, level: int) {
15
+ effect(target, "minecraft:regeneration", duration, level);
16
+ }
17
+
18
+ /// Give resistance
19
+ fn resistance(target: selector, duration: int, level: int) {
20
+ effect(target, "minecraft:resistance", duration, level);
21
+ }
22
+
23
+ /// Give strength
24
+ fn strength(target: selector, duration: int, level: int) {
25
+ effect(target, "minecraft:strength", duration, level);
26
+ }
27
+
28
+ /// Give invisibility
29
+ fn invisible(target: selector, duration: int) {
30
+ effect(target, "minecraft:invisibility", duration, 0);
31
+ }
32
+
33
+ /// Give night vision
34
+ fn night_vision(target: selector, duration: int) {
35
+ effect(target, "minecraft:night_vision", duration, 0);
36
+ }
37
+
38
+ /// Give slow falling
39
+ fn slow_fall(target: selector, duration: int) {
40
+ effect(target, "minecraft:slow_falling", duration, 0);
41
+ }
42
+
43
+ /// Give glowing effect
44
+ fn glow(target: selector, duration: int) {
45
+ effect(target, "minecraft:glowing", duration, 0);
46
+ }
47
+
48
+ /// Clear all effects from target
49
+ fn clear_effects(target: selector) {
50
+ effect_clear(target);
51
+ }
52
+
53
+ /// Clear specific effect from target
54
+ fn clear_effect(target: selector, eff: string) {
55
+ effect_clear(target, eff);
56
+ }
57
+
58
+ /// Full buff package (speed, strength, regen)
59
+ fn buff_all(target: selector, duration: int) {
60
+ speed(target, duration, 1);
61
+ strength(target, duration, 1);
62
+ regen(target, duration, 1);
63
+ resistance(target, duration, 0);
64
+ }
@@ -0,0 +1,195 @@
1
+ // Player interaction detection helpers
2
+ // Usage: import "stdlib/interactions.mcrs"
3
+ //
4
+ // Common patterns for detecting player input:
5
+ // - Right click (carrot on stick)
6
+ // - Sneak/crouch
7
+ // - Look direction
8
+ // - Item in hand
9
+
10
+ // =============================================
11
+ // SETUP - Call once on @load
12
+ // =============================================
13
+
14
+ /// Initialize interaction scoreboards
15
+ fn interactions_init() {
16
+ // Right click detection (carrot on a stick)
17
+ scoreboard_add_objective("rs.click", "minecraft.used:minecraft.carrot_on_a_stick");
18
+
19
+ // Sneak time tracking
20
+ scoreboard_add_objective("rs.sneak", "minecraft.custom:minecraft.sneak_time");
21
+
22
+ // Attack detection (left click on entity)
23
+ scoreboard_add_objective("rs.attack", "minecraft.custom:minecraft.damage_dealt");
24
+ }
25
+
26
+ // =============================================
27
+ // RIGHT CLICK DETECTION
28
+ // =============================================
29
+ // Uses Carrot on a Stick item
30
+ // Give players: /give @p carrot_on_a_stick{CustomModelData:1234}
31
+
32
+ /// Check if player right-clicked (carrot on stick)
33
+ /// Call this in @tick, resets after detection
34
+ fn on_right_click(callback_fn: string) {
35
+ // Players with click score > 0 have clicked
36
+ foreach (p in @a) {
37
+ let clicks: int = scoreboard_get(p, "rs.click");
38
+ if (clicks > 0) {
39
+ // Reset counter
40
+ scoreboard_set(p, "rs.click", 0);
41
+ // Trigger callback by tag
42
+ tag_add(p, "rs.clicked");
43
+ }
44
+ }
45
+ }
46
+
47
+ /// Example: detect right click and do something
48
+ fn example_right_click() {
49
+ foreach (p in @a) {
50
+ let clicks: int = scoreboard_get(p, "rs.click");
51
+ if (clicks > 0) {
52
+ scoreboard_set(p, "rs.click", 0);
53
+ // Do your action here
54
+ say("Player right clicked!");
55
+ }
56
+ }
57
+ }
58
+
59
+ // =============================================
60
+ // SNEAK DETECTION
61
+ // =============================================
62
+
63
+ /// Check if player just started sneaking
64
+ /// Returns 1 if sneaking, 0 otherwise
65
+ fn is_sneaking(target: selector) -> int {
66
+ let sneak_time: int = scoreboard_get(target, "rs.sneak");
67
+ if (sneak_time > 0) {
68
+ return 1;
69
+ }
70
+ return 0;
71
+ }
72
+
73
+ /// Detect sneak start (first tick of sneaking)
74
+ fn on_sneak_start() {
75
+ foreach (p in @a) {
76
+ let sneak_time: int = scoreboard_get(p, "rs.sneak");
77
+ // Sneak time == 1 means just started
78
+ if (sneak_time == 1) {
79
+ tag_add(p, "rs.sneak_start");
80
+ } else {
81
+ tag_remove(p, "rs.sneak_start");
82
+ }
83
+ }
84
+ }
85
+
86
+ // =============================================
87
+ // LOOK DIRECTION
88
+ // =============================================
89
+ // Uses execute with rotation checks
90
+
91
+ /// Check if player is looking up (pitch < -45)
92
+ fn check_look_up() {
93
+ // Now supports p[filters] syntax sugar!
94
+ foreach (p in @a) {
95
+ execute if entity p[x_rotation=-90..-45] run {
96
+ tag_add(@s, "rs.look_up");
97
+ }
98
+ execute unless entity p[x_rotation=-90..-45] run {
99
+ tag_remove(@s, "rs.look_up");
100
+ }
101
+ }
102
+ }
103
+
104
+ /// Check if player is looking down (pitch > 45)
105
+ fn check_look_down() {
106
+ foreach (p in @a) {
107
+ execute if entity p[x_rotation=45..90] run {
108
+ tag_add(@s, "rs.look_down");
109
+ }
110
+ execute unless entity p[x_rotation=45..90] run {
111
+ tag_remove(@s, "rs.look_down");
112
+ }
113
+ }
114
+ }
115
+
116
+ /// Check if player is looking straight (pitch -45 to 45)
117
+ fn check_look_straight() {
118
+ foreach (p in @a) {
119
+ execute if entity p[x_rotation=-45..45] run {
120
+ tag_add(@s, "rs.look_straight");
121
+ }
122
+ execute unless entity p[x_rotation=-45..45] run {
123
+ tag_remove(@s, "rs.look_straight");
124
+ }
125
+ }
126
+ }
127
+
128
+ // =============================================
129
+ // ITEM IN HAND
130
+ // =============================================
131
+ // Use execute if data or predicates
132
+
133
+ /// Check if holding specific item (by tag)
134
+ /// Usage: check_holding_tag("my_wand") checks for tag rs.holding.my_wand
135
+ fn check_holding_item(item_id: string) {
136
+ // This requires predicate files in datapack
137
+ // Example predicate: data/ns/predicates/holding_diamond_sword.json
138
+ // { "condition": "minecraft:entity_properties",
139
+ // "entity": "this",
140
+ // "predicate": { "equipment": { "mainhand": { "items": ["minecraft:diamond_sword"] }}}}
141
+
142
+ // For now, use NBT check via execute
143
+ say("Use execute if data for item checks");
144
+ }
145
+
146
+ // =============================================
147
+ // COMBINED: SNEAK + RIGHT CLICK
148
+ // =============================================
149
+ // Common pattern: Sneak + Use for special ability
150
+
151
+ /// Detect sneak + right click combo
152
+ fn on_sneak_click() {
153
+ foreach (p in @a) {
154
+ let clicks: int = scoreboard_get(p, "rs.click");
155
+ let sneak: int = scoreboard_get(p, "rs.sneak");
156
+
157
+ if (clicks > 0) {
158
+ if (sneak > 0) {
159
+ // Sneak + Click!
160
+ scoreboard_set(p, "rs.click", 0);
161
+ tag_add(p, "rs.sneak_click");
162
+ } else {
163
+ // Normal click
164
+ scoreboard_set(p, "rs.click", 0);
165
+ tag_add(p, "rs.clicked");
166
+ }
167
+ }
168
+ }
169
+ }
170
+
171
+ // =============================================
172
+ // DOUBLE TAP SNEAK
173
+ // =============================================
174
+ // Detect double-tap sneak (toggle fly, etc)
175
+
176
+ let last_sneak_tick: int = 0;
177
+ const DOUBLE_TAP_WINDOW: int = 10; // 0.5 second
178
+
179
+ fn on_double_sneak() {
180
+ foreach (p in @a) {
181
+ let sneak: int = scoreboard_get(p, "rs.sneak");
182
+ let last: int = scoreboard_get(p, "rs.last_sneak");
183
+ let now: int = scoreboard_get(p, "rs.tick");
184
+
185
+ // Just started sneaking
186
+ if (sneak == 1) {
187
+ let diff: int = now - last;
188
+ if (diff < DOUBLE_TAP_WINDOW) {
189
+ // Double sneak detected!
190
+ tag_add(p, "rs.double_sneak");
191
+ }
192
+ scoreboard_set(p, "rs.last_sneak", now);
193
+ }
194
+ }
195
+ }
@@ -0,0 +1,38 @@
1
+ // Inventory management helpers
2
+
3
+ /// Clear all items from a player's inventory
4
+ fn clear_inventory(target: selector) {
5
+ clear(target);
6
+ }
7
+
8
+ /// Give a kit of items to a player
9
+ fn give_kit_warrior(target: selector) {
10
+ give(target, "minecraft:iron_sword", 1);
11
+ give(target, "minecraft:iron_chestplate", 1);
12
+ give(target, "minecraft:iron_leggings", 1);
13
+ give(target, "minecraft:iron_boots", 1);
14
+ give(target, "minecraft:shield", 1);
15
+ give(target, "minecraft:cooked_beef", 16);
16
+ }
17
+
18
+ fn give_kit_archer(target: selector) {
19
+ give(target, "minecraft:bow", 1);
20
+ give(target, "minecraft:arrow", 64);
21
+ give(target, "minecraft:leather_chestplate", 1);
22
+ give(target, "minecraft:leather_leggings", 1);
23
+ give(target, "minecraft:leather_boots", 1);
24
+ give(target, "minecraft:cooked_beef", 16);
25
+ }
26
+
27
+ fn give_kit_mage(target: selector) {
28
+ give(target, "minecraft:wooden_sword", 1);
29
+ give(target, "minecraft:golden_apple", 8);
30
+ give(target, "minecraft:ender_pearl", 16);
31
+ give(target, "minecraft:splash_potion{Potion:\"minecraft:harming\"}", 8);
32
+ give(target, "minecraft:cooked_beef", 16);
33
+ }
34
+
35
+ /// Remove a specific item from player
36
+ fn remove_item(target: selector, item: string) {
37
+ clear(target, item);
38
+ }