1use super::{BlockStateId, DyeColor, Identifier, ItemStack, REGISTRY, RegistryExt, RngExt};
2use crate::equipment::EquipmentSlot;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum LootContextEntity {
7 This,
9 Killer,
11 DirectKiller,
13 KillerPlayer,
15 Interacting,
17}
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub enum LootType {
22 Block,
23 Entity,
24 Chest,
25 Fishing,
26 Gift,
27 Archaeology,
28 Vault,
29 Shearing,
30 Equipment,
31 Selector,
32 EntityInteract,
33 BlockInteract,
34 Barter,
35}
36
37#[derive(Debug, Clone)]
39pub enum NumberProvider {
40 Constant(f32),
41 Uniform {
42 min: f32,
43 max: f32,
44 },
45 Binomial {
46 n: i32,
47 p: f32,
48 },
49 Score {
51 target: ScoreboardTarget,
52 score: &'static str,
53 scale: f32,
54 },
55 Storage {
57 storage: Identifier,
58 path: &'static str,
59 },
60 EnchantmentLevel {
62 enchantment: Identifier,
63 },
64}
65
66#[derive(Debug, Clone, Copy)]
68pub enum ScoreboardTarget {
69 This,
71 Killer,
73 DirectKiller,
75 KillerPlayer,
77 Fixed(&'static str),
79}
80
81impl NumberProvider {
82 pub fn get<R: rand::Rng>(&self, rng: &mut R, ctx: Option<&LootContextRef<'_>>) -> f32 {
84 match self {
85 Self::Constant(v) => *v,
86 Self::Uniform { min, max } => rng.random_range(*min..=*max),
87 Self::Binomial { n, p } => {
88 let mut count = 0;
89 for _ in 0..*n {
90 if rng.random::<f32>() < *p {
91 count += 1;
92 }
93 }
94 count as f32
95 }
96 Self::Score { .. } => {
97 let _ = ctx;
99 0.0
100 }
101 Self::Storage { .. } => {
102 let _ = ctx;
104 0.0
105 }
106 Self::EnchantmentLevel { enchantment } => ctx
107 .and_then(|c| c.tool)
108 .map_or(0.0, |t| t.get_enchantment_level(enchantment) as f32),
109 }
110 }
111
112 pub fn get_float<R: rand::Rng>(&self, ctx: &mut LootContext<'_, R>) -> f32 {
114 let ctx_ref = LootContextRef { tool: ctx.tool };
115 self.get(ctx.rng, Some(&ctx_ref))
116 }
117
118 pub fn get_simple(&self, rng: &mut impl rand::Rng) -> f32 {
120 match self {
121 Self::Constant(v) => *v,
122 Self::Uniform { min, max } => rng.random_range(*min..=*max),
123 Self::Binomial { n, p } => {
124 let mut count = 0;
125 for _ in 0..*n {
126 if rng.random::<f32>() < *p {
127 count += 1;
128 }
129 }
130 count as f32
131 }
132 Self::Score { .. } | Self::Storage { .. } | Self::EnchantmentLevel { .. } => 0.0,
134 }
135 }
136
137 pub fn get_int(&self, rng: &mut impl rand::Rng) -> i32 {
139 match self {
140 Self::Uniform { min, max } => uniform_int(rng, math_round(*min), math_round(*max)),
141 other => math_round(other.get_simple(rng)),
142 }
143 }
144
145 pub fn get_int_with_ctx<R: rand::Rng>(
147 &self,
148 rng: &mut R,
149 ctx: Option<&LootContextRef<'_>>,
150 ) -> i32 {
151 match self {
152 Self::Uniform { min, max } => uniform_int(rng, math_round(*min), math_round(*max)),
153 other => math_round(other.get(rng, ctx)),
154 }
155 }
156}
157
158pub(crate) fn math_round(value: f32) -> i32 {
160 (f64::from(value) + 0.5).floor() as i32
161}
162
163fn uniform_int(rng: &mut impl rand::Rng, min: i32, max: i32) -> i32 {
166 if min >= max {
167 min
168 } else {
169 rng.random_range(min..=max)
170 }
171}
172
173#[derive(Debug, Clone)]
175pub struct NumberProviderRange {
176 pub min: Option<NumberProvider>,
177 pub max: Option<NumberProvider>,
178}
179
180impl NumberProviderRange {
181 pub fn test(&self, value: f32, rng: &mut impl rand::Rng) -> bool {
183 if let Some(min) = &self.min
184 && value < min.get_simple(rng)
185 {
186 return false;
187 }
188 if let Some(max) = &self.max
189 && value > max.get_simple(rng)
190 {
191 return false;
192 }
193 true
194 }
195
196 #[must_use]
198 pub const fn exact(value: f32) -> Self {
199 Self {
200 min: Some(NumberProvider::Constant(value)),
201 max: Some(NumberProvider::Constant(value)),
202 }
203 }
204
205 #[must_use]
207 pub const fn at_least(min: f32) -> Self {
208 Self {
209 min: Some(NumberProvider::Constant(min)),
210 max: None,
211 }
212 }
213
214 #[must_use]
216 pub const fn at_most(max: f32) -> Self {
217 Self {
218 min: None,
219 max: Some(NumberProvider::Constant(max)),
220 }
221 }
222
223 #[must_use]
225 pub const fn between(min: f32, max: f32) -> Self {
226 Self {
227 min: Some(NumberProvider::Constant(min)),
228 max: Some(NumberProvider::Constant(max)),
229 }
230 }
231}
232
233pub struct LootContextRef<'a> {
236 pub tool: Option<&'a ItemStack>,
237 }
239
240pub struct LootContext<'a, R: rand::Rng> {
244 pub rng: &'a mut R,
246 pub luck: f32,
248 pub block_state: Option<BlockStateId>,
250 pub tool: Option<&'a ItemStack>,
252 pub explosion_radius: Option<f32>,
254 pub killed_by_player: bool,
256
257 pub origin: Option<(f64, f64, f64)>,
259 pub game_time: Option<i64>,
261 pub weather: Option<WeatherState>,
263 pub this_entity: Option<EntityRef<'a>>,
266 pub killer_entity: Option<EntityRef<'a>>,
268 pub direct_killer_entity: Option<EntityRef<'a>>,
270 pub last_damage_player: Option<EntityRef<'a>>,
272 pub damage_source: Option<DamageSourceInfo<'a>>,
274 pub block_entity: Option<BlockEntityRef<'a>>,
276 pub interacting_entity: Option<EntityRef<'a>>,
278}
279
280#[derive(Debug, Clone, Copy, Default)]
282pub struct WeatherState {
283 pub raining: bool,
284 pub thundering: bool,
285}
286
287#[derive(Debug, Clone, Copy)]
290pub struct EntityRef<'a> {
291 pub entity_type: Option<&'a Identifier>,
293 pub flags: EntityRefFlags,
295 pub equipment: Option<EntityEquipmentRef<'a>>,
297 pub custom_name: Option<&'a str>,
299 pub sheep_color: Option<DyeColor>,
301 pub sheep_sheared: Option<bool>,
304 pub chicken_variant: Option<&'a Identifier>,
306}
307
308#[derive(Debug, Clone, Copy, Default)]
310pub struct EntityRefFlags {
311 pub is_on_fire: bool,
312 pub is_sneaking: bool,
313 pub is_sprinting: bool,
314 pub is_swimming: bool,
315 pub is_baby: bool,
316}
317
318pub type EntityEquipmentSlots = [ItemStack; EquipmentSlot::ALL.len()];
320
321#[derive(Debug, Clone, Copy)]
323pub struct EntityEquipmentRef<'a>(&'a EntityEquipmentSlots);
324
325impl<'a> EntityEquipmentRef<'a> {
326 #[must_use]
327 pub const fn new(slots: &'a EntityEquipmentSlots) -> Self {
328 Self(slots)
329 }
330
331 #[must_use]
333 pub const fn get(self, slot: EquipmentSlot) -> &'a ItemStack {
334 &self.0[slot.index()]
335 }
336
337 #[must_use]
339 pub fn get_enchantment_level(self, enchantment: &Identifier) -> i32 {
340 let Some(definition) = REGISTRY.enchantments.by_key(enchantment) else {
341 return 0;
342 };
343 EquipmentSlot::ALL
344 .into_iter()
345 .filter(|slot| definition.matching_slot(*slot))
346 .filter_map(|slot| {
347 let stack = self.get(slot);
348 (!stack.is_empty()).then(|| stack.get_enchantment_level(enchantment))
349 })
350 .max()
351 .unwrap_or(0)
352 }
353}
354
355#[derive(Debug, Clone, Copy)]
357pub struct DamageSourceInfo<'a> {
358 pub damage_type: Option<&'a Identifier>,
360 pub tags: &'a [Identifier],
362 pub is_direct: bool,
364}
365
366#[derive(Debug, Clone, Copy)]
368pub struct BlockEntityRef<'a> {
369 pub block_entity_type: Option<&'a Identifier>,
371 pub custom_name: Option<&'a str>,
373 pub inventory: Option<&'a [ItemStack]>,
375}
376
377impl<'a, R: rand::Rng> LootContext<'a, R> {
378 pub const fn new(rng: &'a mut R) -> Self {
380 Self {
381 rng,
382 luck: 0.0,
383 block_state: None,
384 tool: None,
385 explosion_radius: None,
386 killed_by_player: false,
387 origin: None,
388 game_time: None,
389 weather: None,
390 this_entity: None,
391 killer_entity: None,
392 direct_killer_entity: None,
393 last_damage_player: None,
394 damage_source: None,
395 block_entity: None,
396 interacting_entity: None,
397 }
398 }
399
400 #[must_use]
402 pub const fn with_luck(mut self, luck: f32) -> Self {
403 self.luck = luck;
404 self
405 }
406
407 #[must_use]
409 pub const fn with_block_state(mut self, state: BlockStateId) -> Self {
410 self.block_state = Some(state);
411 self
412 }
413
414 #[must_use]
416 pub const fn with_tool(mut self, tool: &'a ItemStack) -> Self {
417 self.tool = Some(tool);
418 self
419 }
420
421 #[must_use]
423 pub const fn with_explosion(mut self, radius: f32) -> Self {
424 self.explosion_radius = Some(radius);
425 self
426 }
427
428 #[must_use]
430 pub const fn with_killed_by_player(mut self, killed: bool) -> Self {
431 self.killed_by_player = killed;
432 self
433 }
434
435 #[must_use]
437 pub const fn with_origin(mut self, x: f64, y: f64, z: f64) -> Self {
438 self.origin = Some((x, y, z));
439 self
440 }
441
442 #[must_use]
444 pub const fn with_game_time(mut self, time: i64) -> Self {
445 self.game_time = Some(time);
446 self
447 }
448
449 #[must_use]
451 pub const fn with_weather(mut self, weather: WeatherState) -> Self {
452 self.weather = Some(weather);
453 self
454 }
455
456 #[must_use]
458 pub const fn with_this_entity(mut self, entity: EntityRef<'a>) -> Self {
459 self.this_entity = Some(entity);
460 self
461 }
462
463 #[must_use]
465 pub const fn with_killer_entity(mut self, entity: EntityRef<'a>) -> Self {
466 self.killer_entity = Some(entity);
467 self
468 }
469
470 #[must_use]
472 pub const fn with_direct_killer_entity(mut self, entity: EntityRef<'a>) -> Self {
473 self.direct_killer_entity = Some(entity);
474 self
475 }
476
477 #[must_use]
479 pub const fn with_last_damage_player(mut self, entity: EntityRef<'a>) -> Self {
480 self.last_damage_player = Some(entity);
481 self
482 }
483
484 #[must_use]
486 pub const fn with_damage_source(mut self, damage_source: DamageSourceInfo<'a>) -> Self {
487 self.damage_source = Some(damage_source);
488 self
489 }
490
491 #[must_use]
493 pub const fn with_block_entity(mut self, block_entity: BlockEntityRef<'a>) -> Self {
494 self.block_entity = Some(block_entity);
495 self
496 }
497
498 #[must_use]
500 pub const fn with_interacting_entity(mut self, entity: EntityRef<'a>) -> Self {
501 self.interacting_entity = Some(entity);
502 self
503 }
504
505 #[must_use]
507 pub fn get_enchantment_level_by_id(&self, enchantment: &Identifier) -> i32 {
508 self.tool
509 .map_or(0, |t| t.get_enchantment_level(enchantment))
510 }
511
512 #[must_use]
514 pub fn get_attacking_entity_enchantment_level(&self, enchantment: &Identifier) -> i32 {
515 self.killer_entity
516 .and_then(|entity| entity.equipment)
517 .map_or(0, |equipment| equipment.get_enchantment_level(enchantment))
518 }
519
520 #[must_use]
522 pub const fn get_entity(&self, target: LootContextEntity) -> Option<EntityRef<'a>> {
523 match target {
524 LootContextEntity::This => self.this_entity,
525 LootContextEntity::Killer => self.killer_entity,
526 LootContextEntity::DirectKiller => self.direct_killer_entity,
527 LootContextEntity::KillerPlayer => self.last_damage_player,
528 LootContextEntity::Interacting => self.interacting_entity,
529 }
530 }
531}