1use super::{BlockStateId, DyeColor, Identifier, ItemStack, RngExt};
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub enum LootContextEntity {
6 This,
8 Killer,
10 DirectKiller,
12 KillerPlayer,
14 Interacting,
16}
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum LootType {
21 Block,
22 Entity,
23 Chest,
24 Fishing,
25 Gift,
26 Archaeology,
27 Vault,
28 Shearing,
29 Equipment,
30 Selector,
31 EntityInteract,
32 BlockInteract,
33 Barter,
34}
35
36#[derive(Debug, Clone)]
38pub enum NumberProvider {
39 Constant(f32),
40 Uniform {
41 min: f32,
42 max: f32,
43 },
44 Binomial {
45 n: i32,
46 p: f32,
47 },
48 Score {
50 target: ScoreboardTarget,
51 score: &'static str,
52 scale: f32,
53 },
54 Storage {
56 storage: Identifier,
57 path: &'static str,
58 },
59 EnchantmentLevel {
61 enchantment: Identifier,
62 },
63}
64
65#[derive(Debug, Clone, Copy)]
67pub enum ScoreboardTarget {
68 This,
70 Killer,
72 DirectKiller,
74 KillerPlayer,
76 Fixed(&'static str),
78}
79
80impl NumberProvider {
81 pub fn get<R: rand::Rng>(&self, rng: &mut R, ctx: Option<&LootContextRef<'_>>) -> f32 {
83 match self {
84 Self::Constant(v) => *v,
85 Self::Uniform { min, max } => rng.random_range(*min..=*max),
86 Self::Binomial { n, p } => {
87 let mut count = 0;
88 for _ in 0..*n {
89 if rng.random::<f32>() < *p {
90 count += 1;
91 }
92 }
93 count as f32
94 }
95 Self::Score { .. } => {
96 let _ = ctx;
98 0.0
99 }
100 Self::Storage { .. } => {
101 let _ = ctx;
103 0.0
104 }
105 Self::EnchantmentLevel { enchantment } => ctx
106 .and_then(|c| c.tool)
107 .map_or(0.0, |t| t.get_enchantment_level(enchantment) as f32),
108 }
109 }
110
111 pub fn get_simple(&self, rng: &mut impl rand::Rng) -> f32 {
113 match self {
114 Self::Constant(v) => *v,
115 Self::Uniform { min, max } => rng.random_range(*min..=*max),
116 Self::Binomial { n, p } => {
117 let mut count = 0;
118 for _ in 0..*n {
119 if rng.random::<f32>() < *p {
120 count += 1;
121 }
122 }
123 count as f32
124 }
125 Self::Score { .. } | Self::Storage { .. } | Self::EnchantmentLevel { .. } => 0.0,
127 }
128 }
129
130 pub fn get_int(&self, rng: &mut impl rand::Rng) -> i32 {
132 match self {
133 Self::Uniform { min, max } => uniform_int(rng, math_round(*min), math_round(*max)),
134 other => math_round(other.get_simple(rng)),
135 }
136 }
137
138 pub fn get_int_with_ctx<R: rand::Rng>(
140 &self,
141 rng: &mut R,
142 ctx: Option<&LootContextRef<'_>>,
143 ) -> i32 {
144 match self {
145 Self::Uniform { min, max } => uniform_int(rng, math_round(*min), math_round(*max)),
146 other => math_round(other.get(rng, ctx)),
147 }
148 }
149}
150
151fn math_round(value: f32) -> i32 {
153 (value + 0.5).floor() as i32
154}
155
156fn uniform_int(rng: &mut impl rand::Rng, min: i32, max: i32) -> i32 {
159 if min >= max {
160 min
161 } else {
162 rng.random_range(min..=max)
163 }
164}
165
166#[derive(Debug, Clone)]
168pub struct NumberProviderRange {
169 pub min: Option<NumberProvider>,
170 pub max: Option<NumberProvider>,
171}
172
173impl NumberProviderRange {
174 pub fn test(&self, value: f32, rng: &mut impl rand::Rng) -> bool {
176 if let Some(min) = &self.min
177 && value < min.get_simple(rng)
178 {
179 return false;
180 }
181 if let Some(max) = &self.max
182 && value > max.get_simple(rng)
183 {
184 return false;
185 }
186 true
187 }
188
189 #[must_use]
191 pub const fn exact(value: f32) -> Self {
192 Self {
193 min: Some(NumberProvider::Constant(value)),
194 max: Some(NumberProvider::Constant(value)),
195 }
196 }
197
198 #[must_use]
200 pub const fn at_least(min: f32) -> Self {
201 Self {
202 min: Some(NumberProvider::Constant(min)),
203 max: None,
204 }
205 }
206
207 #[must_use]
209 pub const fn at_most(max: f32) -> Self {
210 Self {
211 min: None,
212 max: Some(NumberProvider::Constant(max)),
213 }
214 }
215
216 #[must_use]
218 pub const fn between(min: f32, max: f32) -> Self {
219 Self {
220 min: Some(NumberProvider::Constant(min)),
221 max: Some(NumberProvider::Constant(max)),
222 }
223 }
224}
225
226pub struct LootContextRef<'a> {
229 pub tool: Option<&'a ItemStack>,
230 }
232
233pub struct LootContext<'a, R: rand::Rng> {
237 pub rng: &'a mut R,
239 pub luck: f32,
241 pub block_state: Option<BlockStateId>,
243 pub tool: Option<&'a ItemStack>,
245 pub explosion_radius: Option<f32>,
247 pub killed_by_player: bool,
249
250 pub origin: Option<(f64, f64, f64)>,
252 pub game_time: Option<i64>,
254 pub weather: Option<WeatherState>,
256 pub this_entity: Option<EntityRef<'a>>,
259 pub killer_entity: Option<EntityRef<'a>>,
261 pub direct_killer_entity: Option<EntityRef<'a>>,
263 pub last_damage_player: Option<EntityRef<'a>>,
265 pub damage_source: Option<DamageSourceInfo<'a>>,
267 pub block_entity: Option<BlockEntityRef<'a>>,
269 pub interacting_entity: Option<EntityRef<'a>>,
271}
272
273#[derive(Debug, Clone, Copy, Default)]
275pub struct WeatherState {
276 pub raining: bool,
277 pub thundering: bool,
278}
279
280#[derive(Debug, Clone, Copy)]
283pub struct EntityRef<'a> {
284 pub entity_type: Option<&'a Identifier>,
286 pub flags: EntityRefFlags,
288 pub equipment: Option<&'a EntityEquipmentRef<'a>>,
290 pub custom_name: Option<&'a str>,
292 pub sheep_color: Option<DyeColor>,
294 pub sheep_sheared: Option<bool>,
297 pub chicken_variant: Option<&'a Identifier>,
299}
300
301#[derive(Debug, Clone, Copy, Default)]
303pub struct EntityRefFlags {
304 pub is_on_fire: bool,
305 pub is_sneaking: bool,
306 pub is_sprinting: bool,
307 pub is_swimming: bool,
308 pub is_baby: bool,
309}
310
311#[derive(Debug, Clone, Copy)]
313pub struct EntityEquipmentRef<'a> {
314 pub mainhand: Option<&'a ItemStack>,
315 pub offhand: Option<&'a ItemStack>,
316 pub head: Option<&'a ItemStack>,
317 pub chest: Option<&'a ItemStack>,
318 pub legs: Option<&'a ItemStack>,
319 pub feet: Option<&'a ItemStack>,
320}
321
322#[derive(Debug, Clone, Copy)]
324pub struct DamageSourceInfo<'a> {
325 pub damage_type: Option<&'a Identifier>,
327 pub tags: &'a [Identifier],
329 pub is_direct: bool,
331}
332
333#[derive(Debug, Clone, Copy)]
335pub struct BlockEntityRef<'a> {
336 pub block_entity_type: Option<&'a Identifier>,
338 pub custom_name: Option<&'a str>,
340 pub inventory: Option<&'a [ItemStack]>,
342}
343
344impl<'a, R: rand::Rng> LootContext<'a, R> {
345 pub const fn new(rng: &'a mut R) -> Self {
347 Self {
348 rng,
349 luck: 0.0,
350 block_state: None,
351 tool: None,
352 explosion_radius: None,
353 killed_by_player: false,
354 origin: None,
355 game_time: None,
356 weather: None,
357 this_entity: None,
358 killer_entity: None,
359 direct_killer_entity: None,
360 last_damage_player: None,
361 damage_source: None,
362 block_entity: None,
363 interacting_entity: None,
364 }
365 }
366
367 #[must_use]
369 pub const fn with_luck(mut self, luck: f32) -> Self {
370 self.luck = luck;
371 self
372 }
373
374 #[must_use]
376 pub const fn with_block_state(mut self, state: BlockStateId) -> Self {
377 self.block_state = Some(state);
378 self
379 }
380
381 #[must_use]
383 pub const fn with_tool(mut self, tool: &'a ItemStack) -> Self {
384 self.tool = Some(tool);
385 self
386 }
387
388 #[must_use]
390 pub const fn with_explosion(mut self, radius: f32) -> Self {
391 self.explosion_radius = Some(radius);
392 self
393 }
394
395 #[must_use]
397 pub const fn with_killed_by_player(mut self, killed: bool) -> Self {
398 self.killed_by_player = killed;
399 self
400 }
401
402 #[must_use]
404 pub const fn with_origin(mut self, x: f64, y: f64, z: f64) -> Self {
405 self.origin = Some((x, y, z));
406 self
407 }
408
409 #[must_use]
411 pub const fn with_game_time(mut self, time: i64) -> Self {
412 self.game_time = Some(time);
413 self
414 }
415
416 #[must_use]
418 pub const fn with_weather(mut self, weather: WeatherState) -> Self {
419 self.weather = Some(weather);
420 self
421 }
422
423 #[must_use]
425 pub const fn with_this_entity(mut self, entity: EntityRef<'a>) -> Self {
426 self.this_entity = Some(entity);
427 self
428 }
429
430 #[must_use]
432 pub const fn with_killer_entity(mut self, entity: EntityRef<'a>) -> Self {
433 self.killer_entity = Some(entity);
434 self
435 }
436
437 #[must_use]
439 pub const fn with_direct_killer_entity(mut self, entity: EntityRef<'a>) -> Self {
440 self.direct_killer_entity = Some(entity);
441 self
442 }
443
444 #[must_use]
446 pub const fn with_last_damage_player(mut self, entity: EntityRef<'a>) -> Self {
447 self.last_damage_player = Some(entity);
448 self
449 }
450
451 #[must_use]
453 pub const fn with_damage_source(mut self, damage_source: DamageSourceInfo<'a>) -> Self {
454 self.damage_source = Some(damage_source);
455 self
456 }
457
458 #[must_use]
460 pub const fn with_block_entity(mut self, block_entity: BlockEntityRef<'a>) -> Self {
461 self.block_entity = Some(block_entity);
462 self
463 }
464
465 #[must_use]
467 pub const fn with_interacting_entity(mut self, entity: EntityRef<'a>) -> Self {
468 self.interacting_entity = Some(entity);
469 self
470 }
471
472 #[must_use]
474 pub fn get_enchantment_level_by_id(&self, enchantment: &Identifier) -> i32 {
475 self.tool
476 .map_or(0, |t| t.get_enchantment_level(enchantment))
477 }
478
479 #[must_use]
481 pub const fn get_entity(&self, target: LootContextEntity) -> Option<EntityRef<'a>> {
482 match target {
483 LootContextEntity::This => self.this_entity,
484 LootContextEntity::Killer => self.killer_entity,
485 LootContextEntity::DirectKiller => self.direct_killer_entity,
486 LootContextEntity::KillerPlayer => self.last_damage_player,
487 LootContextEntity::Interacting => self.interacting_entity,
488 }
489 }
490}