Pumpkin/crates/pumpkin-util/src/loot_table.rs
2026-08-06 12:56:40 +02:00

370 lines
12 KiB
Rust

#[cfg(feature = "codegen")]
use proc_macro2::TokenStream;
#[cfg(feature = "codegen")]
use quote::{ToTokens, quote};
use serde::Deserialize;
use crate::random::RandomImpl;
/// Represents a loot table used to generate items or rewards.
#[derive(Clone, Copy, Debug)]
pub struct LootTable {
/// The type of loot table (e.g., `Entity`, `Block`, `Chest`).
pub r#type: LootTableType,
/// Optional random sequence identifier for deterministic rolls.
pub random_sequence: Option<&'static str>,
/// Optional array of loot pools that define item generation.
pub pools: Option<&'static [LootPool]>,
}
/// Defines a loot pool containing entries, rolls, and conditions.
#[derive(Clone, Debug)]
pub struct LootPool {
/// Entries contained in this loot pool.
pub entries: &'static [LootPoolEntry],
/// Number of rolls, specified using a number provider.
pub rolls: LootNumberProviderTypes,
/// Additional bonus rolls to apply.
pub bonus_rolls: LootNumberProviderTypes,
/// Optional conditions that must be met for this pool to be applied.
pub conditions: Option<&'static [LootCondition]>,
/// Optional functions applied to each entry in the pool.
pub functions: Option<&'static [LootFunction]>,
}
/// Represents an individual item entry in a loot pool.
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct ItemEntry {
/// Name of the item.
pub name: &'static str,
}
/// Represents an alternative loot entry, which chooses one of its children.
#[derive(Clone, Debug)]
pub struct AlternativeEntry {
/// Child entries for this alternative.
pub children: &'static [LootPoolEntry],
}
#[derive(Clone, Debug)]
pub struct TagEntry {
pub name: &'static str,
pub expand: bool,
}
#[derive(Clone, Debug)]
pub struct SequenceEntry {
pub children: &'static [LootPoolEntry],
}
#[derive(Clone, Debug)]
pub struct GroupEntry {
pub children: &'static [LootPoolEntry],
}
#[derive(Clone, Debug)]
pub struct LootTableEntry {
pub value: &'static str,
}
#[derive(Clone, Debug)]
pub struct DynamicEntry {
pub name: &'static str,
}
#[derive(Clone, Debug)]
pub enum LootPoolEntryTypes {
Empty,
Item(ItemEntry),
LootTable(LootTableEntry),
Dynamic(DynamicEntry),
Tag(TagEntry),
Alternatives(AlternativeEntry),
Sequence(SequenceEntry),
Group(GroupEntry),
}
/// Conditions that can modify whether loot entries are applied.
#[derive(Clone, PartialEq, Debug)]
pub enum LootCondition {
/// Inverts the result of another condition.
Inverted(&'static Self),
/// Passes if any of the given conditions are met.
AnyOf(&'static [Self]),
/// Passes only if all the given conditions are met.
AllOf(&'static [Self]),
/// Passes based on a random chance.
RandomChance { chance: f32 },
/// Passes based on a random chance modified by item enchantments.
RandomChanceWithEnchantedBonus {
enchantment: &'static str,
chances: Option<&'static [f32]>,
},
/// Checks properties of the entity (e.g. type, flags, equipment).
/// Mirrors vanilla `LootItemEntityPropertyCondition`.
EntityProperties {
/// The entity target to check: "this", "attacker", or "`direct_attacker`".
entity: &'static str,
/// Expected entity type (e.g. "minecraft:cow").
expected_type: Option<&'static str>,
/// When `Some`, the entity must be on fire for the condition to pass.
/// Mirrors vanilla `EntityFlagsPredicate.isOnFire`.
is_on_fire: Option<bool>,
/// When `Some`, the entity's mainhand item must have an enchantment in this tag.
/// Mirrors vanilla `EntityEquipmentPredicate.mainhand` with enchantment tag lookup.
mainhand_enchantment_tag: Option<&'static str>,
},
/// Requires the entity to have been killed by a player.
KilledByPlayer,
/// Checks entity scores against specified criteria.
EntityScores { entity: &'static str },
/// Checks block state properties.
BlockStateProperty {
/// The block to check.
block: &'static str,
/// Key-value pairs of properties and their expected values.
properties: &'static [(&'static str, &'static str)],
},
/// Requires a specific tool to match certain criteria.
MatchTool {
items: Option<&'static [&'static str]>,
},
/// Applies a bonus based on a table.
TableBonus {
enchantment: &'static str,
chances: &'static [f32],
},
/// Survives explosion damage.
SurvivesExplosion,
/// Checks properties of the damage source.
DamageSourceProperties {
expected_source_type: Option<&'static str>,
expected_direct_type: Option<&'static str>,
},
/// Checks the location of the entity.
LocationCheck {
offset_x: i32,
offset_y: i32,
offset_z: i32,
expected_biome: Option<&'static str>,
},
/// Checks weather conditions.
WeatherCheck {
raining: Option<bool>,
thundering: Option<bool>,
},
/// References to another condition or table.
Reference { name: &'static str },
/// Checks the in-game time.
TimeCheck {
range: (Option<f32>, Option<f32>),
period: Option<u64>,
},
/// Checks against a specific value or range.
ValueCheck {
value: LootNumberProviderTypes,
range: (Option<f32>, Option<f32>),
},
/// Checks if a specific enchantment is active.
EnchantmentActiveCheck { active: bool },
}
/// Functions applied to loot entries.
#[derive(Clone, PartialEq, Debug)]
pub struct LootFunction {
/// The type of function to apply.
pub content: LootFunctionTypes,
/// Optional conditions for the function.
pub conditions: Option<&'static [LootCondition]>,
}
/// Specific types of loot functions.
#[derive(Clone, PartialEq, Debug)]
pub enum LootFunctionTypes {
/// Sets the number of items in the stack, optionally adding to an existing count.
SetCount {
count: LootFunctionNumberProvider,
add: bool,
},
/// Increases the count of items based on the item's enchantments.
EnchantedCountIncrease {
enchantment: &'static str,
count: LootFunctionNumberProvider,
limit: Option<f32>,
},
/// Smelts the item as if it passed through a furnace.
FurnaceSmelt,
/// Sets the potion type for potion items.
SetPotion { id: &'static str },
/// Sets the amplifier for ominous bottles.
SetOminousBottleAmplifier,
/// Limits the count of items to a minimum and/or maximum value.
LimitCount { min: Option<f32>, max: Option<f32> },
/// Applies a bonus based on an enchantment and a formula, optionally with extra parameters.
ApplyBonus {
enchantment: &'static str,
formula: &'static str,
parameters: Option<LootFunctionBonusParameter>,
},
/// Copies specified components from a source entity or item.
CopyComponents {
source: &'static str,
include: &'static [&'static str],
},
/// Copies state or properties from a block.
CopyState {
block: &'static str,
properties: &'static [&'static str],
},
/// Applies decay to items in case of an explosion.
ExplosionDecay,
}
/// Numeric providers for loot function counts.
#[derive(Clone, PartialEq, Debug)]
pub enum LootFunctionNumberProvider {
/// Constant value.
Constant { value: f32 },
/// Uniform random value between min and max.
Uniform { min: f32, max: f32 },
/// Binomial distribution.
Binomial { n: f32, p: f32 },
}
/// Parameters for loot bonus functions.
#[derive(Clone, PartialEq, Debug)]
pub enum LootFunctionBonusParameter {
/// Multiplier applied to bonus rolls.
Multiplier { bonus_multiplier: i32 },
/// Probability-based bonus application.
Probability { extra: i32, probability: f32 },
}
/// Single entry in a loot pool with optional conditions and functions.
#[derive(Clone, Debug)]
pub struct LootPoolEntry {
/// The type of entry.
pub content: LootPoolEntryTypes,
/// Relative probability weight; higher values are more likely.
pub weight: i32,
/// Quality of the entry, used to modify weight based on luck.
pub quality: i32,
/// Optional conditions for this entry.
pub conditions: Option<&'static [LootCondition]>,
/// Optional functions for this entry.
pub functions: Option<&'static [LootFunction]>,
}
/// Categories of loot tables.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum LootTableType {
/// Empty loot table.
Empty,
/// Entity loot table.
Entity,
/// Block loot table.
Block,
/// Chest loot table.
Chest,
}
/// Represents structured number providers used in loot rolls.
#[derive(Deserialize, PartialEq, Clone, Copy, Debug)]
#[serde(tag = "type")]
pub enum LootNumberProviderTypesProvider {
/// Uniformly distributes a random number between `min` and `max` (inclusive).
#[serde(rename = "minecraft:uniform")]
Uniform(UniformLootNumberProvider),
}
#[cfg(feature = "codegen")]
impl ToTokens for LootNumberProviderTypesProvider {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
Self::Uniform(uniform) => {
tokens.extend(quote! {
LootNumberProviderTypesProvider::Uniform(#uniform)
});
}
}
}
}
/// Uniform numeric provider for loot rolls.
#[derive(Deserialize, PartialEq, Clone, Copy, Debug)]
pub struct UniformLootNumberProvider {
/// Minimum value (inclusive).
pub min: f32,
/// Maximum value (inclusive).
pub max: f32,
}
#[cfg(feature = "codegen")]
impl ToTokens for UniformLootNumberProvider {
fn to_tokens(&self, tokens: &mut TokenStream) {
let min_inclusive = self.min;
let max_inclusive = self.max;
tokens.extend(quote! {
UniformLootNumberProvider { min: #min_inclusive, max: #max_inclusive }
});
}
}
impl UniformLootNumberProvider {
/// Generates a random number within the uniform range.
///
/// # Parameters
/// - `random`: A random number generator implementing `RandomImpl`.
///
/// # Returns
/// A `f32` between `min` and `max`.
pub fn get(&self, random: &mut impl RandomImpl) -> f32 {
random.next_f32().mul_add(self.max - self.min, self.min)
}
}
/// Numeric providers for loot table rolls (either object or constant).
#[derive(Deserialize, PartialEq, Clone, Copy, Debug)]
#[serde(untagged)]
pub enum LootNumberProviderTypes {
/// A structured provider object (e.g., uniform).
Object(LootNumberProviderTypesProvider),
/// Constant numeric value.
Constant(f32),
}
#[cfg(feature = "codegen")]
impl ToTokens for LootNumberProviderTypes {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
Self::Object(provider) => {
tokens.extend(quote! {
LootNumberProviderTypes::Object(#provider)
});
}
Self::Constant(i) => tokens.extend(quote! {
LootNumberProviderTypes::Constant(#i)
}),
}
}
}
impl LootNumberProviderTypes {
/// Computes a numeric value from this provider.
///
/// # Parameters
/// - `random`: A random number generator implementing `RandomImpl`.
///
/// # Returns
/// A `f32` value as determined by the provider (constant or computed).
pub fn get(&self, random: &mut impl RandomImpl) -> f32 {
match self {
Self::Object(int_provider) => match int_provider {
LootNumberProviderTypesProvider::Uniform(uniform) => uniform.get(random),
},
Self::Constant(i) => *i,
}
}
}