Skip to content

LootHelper

ShaneeexD edited this page Jan 31, 2026 · 3 revisions

LootHelper

Utilities for customizing block drops and mob loot tables. Allows you to override default drops with custom items, quantities, and chance-based mechanics.

Overview

What it does:

  • Customize what items drop when blocks are broken
  • Add custom loot to mob deaths with chance-based drops
  • Replace default block/mob drops entirely or add bonus drops
  • Spawn physical item entities with realistic physics
  • Support conditional drops based on position, depth, or custom logic
  • Random drop quantities, velocities, and drop chances

When to use:

  • Custom ore drops (e.g., diamonds from stone)
  • Bonus loot systems (e.g., extra coal from coal ore)
  • Depth-based drops (e.g., rare items only below Y=20)
  • Mob loot tables with rare drops (e.g., 10% chance for legendary sword)
  • Random loot tables with percentage-based chances
  • Custom resource gathering mechanics

Available Methods

Block Drop Methods

registerBlockLoot(world, blockTypeId, lootProvider)

Register custom loot that is added alongside default block drops.

Parameters:

  • World world - The world to register the loot table in
  • String blockTypeId - The block type ID (e.g., "Rock_Stone", "Ore_Gold_Stone")
  • BiFunction<Vector3i, String, List<ItemDrop>> lootProvider - Function that returns list of custom drops

Example:

// Add bonus cobble drops (keeps default drops)
LootHelper.registerBlockLoot(world, "Rock_Stone", (pos, blockType) -> {
    return Arrays.asList(
        new LootHelper.ItemDrop("Rock_Stone_Cobble", 2) // +2 bonus coal
    );
});

registerBlockLootReplacement(world, blockTypeId, lootProvider)

Register custom loot that completely replaces default block drops.

Parameters:

  • World world - The world to register the loot table in
  • String blockTypeId - The block type ID (e.g., "Rock_Stone", "Ore_Gold_Stone")
  • BiFunction<Vector3i, String, List<ItemDrop>> lootProvider - Function that returns list of custom drops

How it works:

  • Removes the block immediately to prevent default drops
  • Spawns only your custom items as physical entities
  • Items have proper physics and can be picked up normally

Example:

// Make stone drop 2-4 diamonds instead
LootHelper.registerBlockLootReplacement(world, "Rock_Stone", (pos, blockType) -> {
    Random random = new Random();
    int quantity = random.nextInt(3) + 2; // 2-4 diamonds
    return Arrays.asList(
        new LootHelper.ItemDrop("Rock_Gem_Diamond", quantity)
    );
});

clearLootTables(world)

Clear all custom loot tables for a world.

Parameters:

  • World world - The world to clear loot tables from

Example:

LootHelper.clearLootTables(world);

Mob Drop Methods

registerMobLoot(world, entityTypeId, lootProvider)

Register custom loot that is added alongside default mob drops.

Parameters:

  • World world - The world to register the mob loot in
  • String entityTypeId - The entity type ID (e.g., "Fen_Stalker", "Kweebec", "Trork")
  • Function<Vector3d, List<ItemDrop>> lootProvider - Function that returns list of custom drops

Important: You must call LootHelper.handleMobDeath(death, world) from your DeathHelper.onEntityDeath() callback for this to work!

Example:

// Add custom drops to Fen_Stalker
LootHelper.registerMobLoot(world, "Fen_Stalker", (position) -> {
    return Arrays.asList(
        // 75% chance for adamantite longsword
        LootHelper.ItemDrop.withChance("Weapon_Longsword_Adamantite", 1, 0.75f),
        
        // 50% chance for diamonds
        LootHelper.ItemDrop.withChance("Ingredient_Diamond", 3, 0.5f),
        
        // 10% rare drop
        LootHelper.ItemDrop.withChance("Weapon_Sword_Iron", 1, 0.1f)
    );
});

// In your death handler
DeathHelper.onEntityDeath(world, (death) -> {
    LootHelper.handleMobDeath(death, world); // Handle mob loot
    // Your custom death logic here...
});

registerMobLootReplacement(world, entityTypeId, lootProvider)

Register custom loot that replaces default mob drops entirely.

Parameters:

  • World world - The world to register the mob loot in
  • String entityTypeId - The entity type ID (e.g., "Fen_Stalker", "Kweebec", "Trork")
  • Function<Vector3d, List<ItemDrop>> lootProvider - Function that returns list of custom drops

Note: Currently adds custom drops but doesn't remove default drops yet.

Example:

// Replace Trork drops entirely
LootHelper.registerMobLootReplacement(world, "Trork", (position) -> {
    return Arrays.asList(
        new LootHelper.ItemDrop("Weapon_Sword_Iron", 1),
        new LootHelper.ItemDrop("Ingredient_Bone_Fragment", 10)
    );
});

handleMobDeath(death, world)

Handle mob death and spawn custom loot if registered. Call this from your DeathHelper.onEntityDeath() callback.

Parameters:

  • DeathHelper.EntityDeath death - The EntityDeath object from DeathHelper
  • World world - The world where the death occurred

Example:

DeathHelper.onEntityDeath(world, (death) -> {
    // Handle mob loot drops
    LootHelper.handleMobDeath(death, world);
    
    // Your custom death logic
    LOGGER.log("Entity died: " + death.getEntityName());
});

spawnItemAtLocation(world, position, itemId, quantity, velocity)

Spawn an item drop at a specific location programmatically.

Parameters:

  • World world - The world to spawn the item in
  • Vector3d position - The position to spawn the item at
  • String itemId - The item ID to spawn
  • int quantity - How many items to spawn
  • Vector3d velocity - The velocity to apply to the item

Example:

LootHelper.spawnItemAtLocation(world, 
    new Vector3d(100, 64, 100), 
    "Ingredient_Diamond", 
    5, 
    Vector3d.ZERO);

clearMobLootTables(world)

Clear all custom mob loot tables for a world.

Parameters:

  • World world - The world to clear mob loot tables from

Example:

LootHelper.clearMobLootTables(world);

ItemDrop Class

Represents an item drop with quantity, optional velocity, and drop chance.

Constructors

ItemDrop(String itemId, int quantity)

  • Creates an item drop with default velocity and 100% drop chance

ItemDrop(String itemId, int quantity, Vector3d velocity)

  • Creates an item drop with custom velocity and 100% drop chance

ItemDrop(String itemId, int quantity, Vector3d velocity, float dropChance)

  • Creates an item drop with custom velocity and drop chance (0.0 to 1.0)

Static Methods

ItemDrop.withRandomVelocity(String itemId, int quantity)

  • Creates an item drop with random velocity spread and 100% drop chance

ItemDrop.withChance(String itemId, int quantity, float dropChance)

  • Creates an item drop with a specific drop chance (0.0 to 1.0 = 0% to 100%)

ItemDrop.withRandomVelocityAndChance(String itemId, int quantity, float dropChance)

  • Creates an item drop with random velocity and a specific drop chance

Example:

// 100% guaranteed drops
LootHelper.ItemDrop.withRandomVelocity("Rock_Gem_Diamond", 1)
new LootHelper.ItemDrop("Rock_Gem_Diamond", 3, new Vector3d(0.1, 0.3, -0.1))
new LootHelper.ItemDrop("Rock_Gem_Diamond", 1)

// Chance-based drops
LootHelper.ItemDrop.withChance("Weapon_Longsword_Adamantite", 1, 0.75f) // 75% chance
LootHelper.ItemDrop.withChance("Ingredient_Diamond", 3, 0.5f) // 50% chance
LootHelper.ItemDrop.withRandomVelocityAndChance("Ingredient_Gold", 5, 0.25f) // 25% with scatter
LootHelper.ItemDrop.withChance("Weapon_Sword_Iron", 1, 0.1f) // 10% rare drop

Advanced Examples

Random Drop Quantities

LootHelper.registerBlockLootReplacement(world, "Ore_Gold_Stone", (pos, blockType) -> {
    Random random = new Random();
    int dropAmount = random.nextInt(3) + 1; // 1-3 gold
    
    // 10% chance for bonus diamond
    List<LootHelper.ItemDrop> drops = new ArrayList<>();
    drops.add(new LootHelper.ItemDrop("Rock_Gem_Diamond", dropAmount));
    
    if (random.nextDouble() < 0.1) {
        drops.add(new LootHelper.ItemDrop("Rock_Gem_Diamond", 1));
    }
    
    return drops;
});

Depth-Based Drops

LootHelper.registerBlockLootReplacement(world, "Rock_Stone", (pos, blockType) -> {
    List<LootHelper.ItemDrop> drops = new ArrayList<>();
    
    // Different drops based on depth
    if (pos.getY() < 10) {
        // Deep underground - rare items
        drops.add(new LootHelper.ItemDrop("Rock_Gem_Diamond", 2));
    } else if (pos.getY() < 30) {
        // Mid-depth - uncommon items
        drops.add(new LootHelper.ItemDrop("Rock_Gem_Ruby", 1));
    } else {
        // Surface - common items
        drops.add(new LootHelper.ItemDrop("Rock_Gem_Topaz", 1));
    }
    
    return drops;
});

Multiple Items with Random Velocity

LootHelper.registerBlockLootReplacement(world, "Rock_Gem_Diamond", (pos, blockType) -> {
    return Arrays.asList(
        LootHelper.ItemDrop.withRandomVelocity("Rock_Gem_Ruby", 1),
        LootHelper.ItemDrop.withRandomVelocity("Rock_Gem_Topaz", 1)
    );
});

Conditional Bonus Drops

// Add bonus drops alongside default
LootHelper.registerBlockLoot(world, "Rock_Stone", (pos, blockType) -> {
    Random random = new Random();
    List<LootHelper.ItemDrop> bonusDrops = new ArrayList<>();
    
    // 25% chance for extra coal
    if (random.nextDouble() < 0.25) {
        bonusDrops.add(new LootHelper.ItemDrop("Ingredient_Charcoal", 1));
    }
    
    // 5% chance for diamond
    if (random.nextDouble() < 0.05) {
        bonusDrops.add(new LootHelper.ItemDrop("Rock_Gem_Ruby", 1));
    }
    
    return bonusDrops;
});

Mob Loot with Chance-Based Drops

// Register custom mob drops with different chances
LootHelper.registerMobLoot(world, "Fen_Stalker", (position) -> {
    return Arrays.asList(
        // 75% chance for adamantite longsword
        LootHelper.ItemDrop.withChance("Weapon_Longsword_Adamantite", 1, 0.75f),
        
        // 50% chance for diamonds
        LootHelper.ItemDrop.withChance("Ingredient_Diamond", 3, 0.5f),
        
        // 25% chance for gold with random velocity
        LootHelper.ItemDrop.withRandomVelocityAndChance("Ingredient_Gold", 5, 0.25f),
        
        // 10% rare drop
        LootHelper.ItemDrop.withChance("Weapon_Sword_Iron", 1, 0.1f)
    );
});

// Set up death handler to process mob loot
DeathHelper.onEntityDeath(world, (death) -> {
    // Handle mob loot drops
    LootHelper.handleMobDeath(death, world);
    
    // Your custom death logic
    LOGGER.log("Entity died: " + death.getEntityName());
});

Boss Mob with Guaranteed and Rare Drops

// Boss mob with mix of guaranteed and rare drops
LootHelper.registerMobLoot(world, "Boss_Dragon", (position) -> {
    return Arrays.asList(
        // 100% guaranteed drops
        new LootHelper.ItemDrop("Weapon_Longsword_Legendary", 1),
        new LootHelper.ItemDrop("Ingredient_Dragon_Scale", 10),
        
        // 50% chance for extra loot
        LootHelper.ItemDrop.withChance("Ingredient_Diamond", 20, 0.5f),
        
        // 25% chance for rare weapon
        LootHelper.ItemDrop.withChance("Weapon_Bow_Legendary", 1, 0.25f),
        
        // 5% ultra-rare drop
        LootHelper.ItemDrop.withChance("Weapon_Staff_Mythic", 1, 0.05f)
    );
});

Complete Usage Example

@Override
public void start() {
    EventHelper.onPlayerJoinWorld(this, world -> {
        // Custom ore drops
        LootHelper.registerBlockLootReplacement(world, "Rock_Stone", (pos, blockType) -> {
            Random random = new Random();
            int diamonds = random.nextInt(3) + 2; // 2-4 diamonds
            return Arrays.asList(
                LootHelper.ItemDrop.withRandomVelocity("Rock_Gem_Diamond", diamonds)
            );
        });
        
        // Stone drops iron swords (for testing)
        LootHelper.registerBlockLootReplacement(world, "Rock_Stone", (pos, blockType) -> {
            return Arrays.asList(
                new LootHelper.ItemDrop("Weapon_Sword_Iron", 1)
            );
        });
        
        // Bonus coal with default drops
        LootHelper.registerBlockLoot(world, "Rock_Stone", (pos, blockType) -> {
            return Arrays.asList(
                new LootHelper.ItemDrop("Resource_Coal", 2)
            );
        });
        
        // Depth-based stone drops
        LootHelper.registerBlockLootReplacement(world, "Rock_Stone", (pos, blockType) -> {
            if (pos.getY() < 20) {
                return Arrays.asList(
                    new LootHelper.ItemDrop("Rock_Gem_Diamond", 1)
                );
            }
            return Arrays.asList(
                new LootHelper.ItemDrop("Ingredient_Charcoal", 1)
            );
        });
    });
}

Technical Details

Item Entity Spawning:

  • Uses ItemComponent.generateItemDrop() - the same method Hytale uses internally
  • Spawns physical item entities with proper physics and collision
  • Items can be picked up naturally by players
  • Items despawn after the normal item lifetime
  • Supports custom velocities for realistic drop patterns
  • Uses CommandBuffer.addEntity() for proper ECS system integration

ECS System:

  • Registers a BreakBlockEvent system per world
  • Intercepts block breaks before default drops occur
  • For replacements: Sets block to air (ID 0) to prevent default drops
  • Spawns custom items using the entity component system
  • Uses CommandBuffer to queue entity additions (required for ECS systems)
  • Entities are added after the system finishes processing

Performance:

  • Loot tables stored in concurrent maps for thread-safety
  • One ECS system per world (registered on first loot table)
  • Minimal overhead - only processes blocks with custom loot
  • CommandBuffer ensures thread-safe entity spawning

See Also

Clone this wiki locally