steel_core/command/builtins/
weather.rs1use steel_utils::{Identifier, translations};
4use text_components::TextComponent;
5
6use super::super::{
7 brigadier::{CommandNodeBuilder, CommandSyntaxError},
8 execution::{
9 CommandSource, SteelArgumentType, SteelCommandContext, SteelCommandRuntime, argument,
10 literal,
11 },
12 registration::CommandRegistration,
13};
14
15const DEFAULT_DURATION: i32 = -1;
16
17pub(super) fn registration() -> CommandRegistration<CommandSource> {
18 CommandRegistration::new(Identifier::vanilla_static("weather"), |_| command())
19}
20
21fn command() -> CommandNodeBuilder<CommandSource, SteelCommandRuntime> {
22 literal("weather")
23 .then(weather_literal("clear", WeatherKind::Clear))
24 .then(weather_literal("rain", WeatherKind::Rain))
25 .then(weather_literal("thunder", WeatherKind::Thunder))
26}
27
28fn weather_literal(
29 name: &'static str,
30 weather: WeatherKind,
31) -> CommandNodeBuilder<CommandSource, SteelCommandRuntime> {
32 literal(name)
33 .executes(move |context| set_weather(context, weather, DEFAULT_DURATION))
34 .then(
35 argument("duration", SteelArgumentType::time(1)).executes(move |context| {
36 let Some(duration) = context.time("duration") else {
37 return Err(CommandSyntaxError::dynamic(
38 "Parsed weather duration is missing from the command context",
39 ));
40 };
41 set_weather(context, weather, duration)
42 }),
43 )
44}
45
46#[derive(Clone, Copy)]
47enum WeatherKind {
48 Clear,
49 Rain,
50 Thunder,
51}
52
53impl WeatherKind {
54 fn random_duration(self) -> i32 {
55 match self {
56 Self::Clear => rand::random_range(12_000..=180_000),
57 Self::Rain => rand::random_range(12_000..=24_000),
58 Self::Thunder => rand::random_range(3_600..=15_600),
59 }
60 }
61
62 const fn parameters(self, duration: i32) -> (i32, i32, bool, bool) {
63 match self {
64 Self::Clear => (duration, 0, false, false),
65 Self::Rain => (0, duration, true, false),
66 Self::Thunder => (0, duration, true, true),
67 }
68 }
69
70 fn success_message(self) -> TextComponent {
71 match self {
72 Self::Clear => TextComponent::from(&translations::COMMANDS_WEATHER_SET_CLEAR),
73 Self::Rain => TextComponent::from(&translations::COMMANDS_WEATHER_SET_RAIN),
74 Self::Thunder => TextComponent::from(&translations::COMMANDS_WEATHER_SET_THUNDER),
75 }
76 }
77}
78
79#[expect(
80 clippy::unnecessary_wraps,
81 reason = "Command executors use a shared fallible callback signature."
82)]
83fn set_weather(
84 context: &SteelCommandContext<CommandSource>,
85 weather: WeatherKind,
86 requested_duration: i32,
87) -> Result<i32, CommandSyntaxError> {
88 let duration = if requested_duration == DEFAULT_DURATION {
89 weather.random_duration()
90 } else {
91 requested_duration
92 };
93 let (clear_time, rain_time, raining, thundering) = weather.parameters(duration);
94 context
95 .source()
96 .world()
97 .set_weather_parameters(clear_time, rain_time, raining, thundering);
98 context
99 .source()
100 .send_success(&weather.success_message(), true);
101 Ok(requested_duration)
102}
103
104#[cfg(test)]
105mod tests {
106 use super::WeatherKind;
107
108 #[test]
109 fn weather_kinds_map_to_vanilla_parameter_sets() {
110 assert_eq!(WeatherKind::Clear.parameters(40), (40, 0, false, false));
111 assert_eq!(WeatherKind::Rain.parameters(40), (0, 40, true, false));
112 assert_eq!(WeatherKind::Thunder.parameters(40), (0, 40, true, true));
113 }
114}