-
Notifications
You must be signed in to change notification settings - Fork 2
LootHelper
Utilities for customizing block drops and mob loot tables. Allows you to override default drops with custom items, quantities, and chance-based mechanics.
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
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
);
});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)
);
});Clear all custom loot tables for a world.
Parameters:
-
World world- The world to clear loot tables from
Example:
LootHelper.clearLootTables(world);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...
});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)
);
});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());
});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);Clear all custom mob loot tables for a world.
Parameters:
-
World world- The world to clear mob loot tables from
Example:
LootHelper.clearMobLootTables(world);Represents an item drop with quantity, optional velocity, and drop chance.
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)
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 dropLootHelper.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;
});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;
});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)
);
});// 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;
});// 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 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)
);
});@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)
);
});
});
}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
BreakBlockEventsystem 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
CommandBufferto 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
- EcsEventHelper - For block break event handling
- ItemHelper - For item creation and management
- BlockHelper - For block manipulation
- Home - Back to main page