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