steel_utils/value_providers/
height.rs1use serde::{Deserialize, Deserializer, de::Error as _};
2
3use crate::random::Random;
4
5use super::VerticalAnchor;
6
7#[derive(Debug, Clone, Copy)]
12pub enum HeightProvider {
13 Constant(VerticalAnchor),
15 Uniform {
17 min_inclusive: VerticalAnchor,
19 max_inclusive: VerticalAnchor,
21 },
22 Trapezoid {
25 min_inclusive: VerticalAnchor,
27 max_inclusive: VerticalAnchor,
29 plateau: i32,
31 },
32 BiasedToBottom {
34 min_inclusive: VerticalAnchor,
36 max_inclusive: VerticalAnchor,
38 inner: i32,
40 },
41 VeryBiasedToBottom {
43 min_inclusive: VerticalAnchor,
45 max_inclusive: VerticalAnchor,
47 inner: i32,
49 },
50}
51
52impl HeightProvider {
53 pub fn sample<R: Random + ?Sized>(
59 self,
60 random: &mut R,
61 min_y: i32,
62 height: i32,
63 sea_level: i32,
64 ) -> i32 {
65 match self {
66 Self::Constant(anchor) => anchor.resolve_y(min_y, height, sea_level),
67 Self::Uniform {
68 min_inclusive,
69 max_inclusive,
70 } => {
71 let min = min_inclusive.resolve_y(min_y, height, sea_level);
72 let max = max_inclusive.resolve_y(min_y, height, sea_level);
73 if min > max {
74 min
75 } else {
76 random.next_i32_between(min, max)
77 }
78 }
79 Self::Trapezoid {
80 min_inclusive,
81 max_inclusive,
82 plateau,
83 } => {
84 let min = min_inclusive.resolve_y(min_y, height, sea_level);
85 let max = max_inclusive.resolve_y(min_y, height, sea_level);
86 if min > max {
87 min
88 } else {
89 let range = max - min;
90 if plateau >= range {
91 random.next_i32_between(min, max)
92 } else {
93 let plateau_start = (range - plateau) / 2;
94 let plateau_end = range - plateau_start;
95 min + random.next_i32_between(0, plateau_end)
96 + random.next_i32_between(0, plateau_start)
97 }
98 }
99 }
100 Self::BiasedToBottom {
101 min_inclusive,
102 max_inclusive,
103 inner,
104 } => {
105 let min = min_inclusive.resolve_y(min_y, height, sea_level);
106 let max = max_inclusive.resolve_y(min_y, height, sea_level);
107 if max - min - inner < 0 {
108 min
109 } else {
110 let limit = random.next_i32_bounded(max - min - inner + 1);
111 random.next_i32_bounded(limit + inner) + min
112 }
113 }
114 Self::VeryBiasedToBottom {
115 min_inclusive,
116 max_inclusive,
117 inner,
118 } => {
119 let min = min_inclusive.resolve_y(min_y, height, sea_level);
120 let max = max_inclusive.resolve_y(min_y, height, sea_level);
121 if max - min - inner < 0 {
122 min
123 } else {
124 let upper_inclusive = random.next_i32_between(min + inner, max);
125 let biased_upper_inclusive = random.next_i32_between(min, upper_inclusive - 1);
126 random.next_i32_between(min, biased_upper_inclusive - 1 + inner)
127 }
128 }
129 }
130 }
131}
132
133impl<'de> Deserialize<'de> for HeightProvider {
134 fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
135 #[derive(Deserialize)]
136 #[serde(tag = "type", deny_unknown_fields)]
137 enum Tagged {
138 #[serde(rename = "minecraft:constant")]
139 Constant { value: VerticalAnchor },
140 #[serde(rename = "minecraft:uniform")]
141 Uniform {
142 min_inclusive: VerticalAnchor,
143 max_inclusive: VerticalAnchor,
144 },
145 #[serde(rename = "minecraft:trapezoid")]
146 Trapezoid {
147 min_inclusive: VerticalAnchor,
148 max_inclusive: VerticalAnchor,
149 #[serde(default)]
150 plateau: i32,
151 },
152 #[serde(rename = "minecraft:biased_to_bottom")]
153 BiasedToBottom {
154 min_inclusive: VerticalAnchor,
155 max_inclusive: VerticalAnchor,
156 #[serde(default = "default_inner")]
157 inner: i32,
158 },
159 #[serde(rename = "minecraft:very_biased_to_bottom")]
160 VeryBiasedToBottom {
161 min_inclusive: VerticalAnchor,
162 max_inclusive: VerticalAnchor,
163 #[serde(default = "default_inner")]
164 inner: i32,
165 },
166 }
167
168 const fn default_inner() -> i32 {
169 1
170 }
171
172 let value = serde_json::Value::deserialize(d)?;
173 let has_type = value
174 .as_object()
175 .is_some_and(|object| object.contains_key("type"));
176
177 if !has_type {
178 let anchor = VerticalAnchor::deserialize(value).map_err(D::Error::custom)?;
179 return Ok(Self::Constant(anchor));
180 }
181
182 Ok(
183 match serde_json::from_value(value).map_err(D::Error::custom)? {
184 Tagged::Constant { value } => Self::Constant(value),
185 Tagged::Uniform {
186 min_inclusive,
187 max_inclusive,
188 } => Self::Uniform {
189 min_inclusive,
190 max_inclusive,
191 },
192 Tagged::Trapezoid {
193 min_inclusive,
194 max_inclusive,
195 plateau,
196 } => Self::Trapezoid {
197 min_inclusive,
198 max_inclusive,
199 plateau,
200 },
201 Tagged::BiasedToBottom {
202 min_inclusive,
203 max_inclusive,
204 inner,
205 } => Self::BiasedToBottom {
206 min_inclusive,
207 max_inclusive,
208 inner,
209 },
210 Tagged::VeryBiasedToBottom {
211 min_inclusive,
212 max_inclusive,
213 inner,
214 } => Self::VeryBiasedToBottom {
215 min_inclusive,
216 max_inclusive,
217 inner,
218 },
219 },
220 )
221 }
222}