Skip to content

These docs were made completely by AI, so they might be right, or wrong, you'll need to test them yourself. This was made for a easier understanding of everything. So use at your own risk. If anything is wrong, please don't hurt to make a PR on the page you have a problem with. ON GITHUB

Custom Loot & Drops

This guide covers how items get dropped in LCE, both from mobs dying and blocks being broken. We will look at the actual drop systems, every mob’s drops, and show you how to add your own custom loot.

When a mob dies, Mob::die() in Minecraft.World/Mob.cpp handles the whole loot pipeline. Here is the real code:

void Mob::die(DamageSource *source)
{
// ... scoring and kill tracking ...
dead = true;
if (!level->isClientSide)
{
int playerBonus = 0;
shared_ptr<Player> player = dynamic_pointer_cast<Player>(sourceEntity);
if (player != NULL)
{
playerBonus = EnchantmentHelper::getKillingLootBonus(player->inventory);
}
if (!isBaby())
{
dropDeathLoot(lastHurtByPlayerTime > 0, playerBonus);
if (lastHurtByPlayerTime > 0)
{
int rareLoot = random->nextInt(200) - playerBonus;
if (rareLoot < 5)
{
dropRareDeathLoot((rareLoot <= 0) ? 1 : 0);
}
}
}
}
level->broadcastEntityEvent(shared_from_this(), EntityEvent::DEATH);
}

There are a few important things happening here:

  1. Looting enchantment is read from the player’s held weapon via getKillingLootBonus(). This gets passed as playerBonus to the drop functions.
  2. Baby mobs never drop loot. The isBaby() check gates everything.
  3. Regular drops happen through dropDeathLoot(), which gets a bool for whether a player was involved and the looting bonus level.
  4. Rare drops have a 2.5% base chance (5 out of 200). Looting increases this by subtracting from the random roll, so each looting level adds another 0.5% chance.
  5. All of this only runs server-side (!level->isClientSide).

Every mob has three virtual methods that control what it drops:

MethodPurpose
getDeathLoot()Returns a single item ID. Used as the fallback drop.
dropDeathLoot(bool, int)Main drop logic. Override this for custom drop tables.
dropRareDeathLoot(int)Rare/special drops. The int param is the looting quality level.

Here is every mob in the codebase and exactly what it drops, pulled straight from the source files.

File: Cow.cpp

DropCountLootingNotes
Leather0-2+1 per levelBase random->nextInt(3)
Raw Beef1-3+1 per levelBase 1 + random->nextInt(3)
Cooked Beef1-3+1 per levelDrops instead of raw beef if on fire

File: Pig.cpp

DropCountLootingNotes
Raw Porkchop1-3+1 per levelBase 1 + random->nextInt(3)
Cooked Porkchop1-3+1 per levelDrops instead of raw if on fire
Saddle1NoOnly if the pig was saddled

File: Chicken.cpp

DropCountLootingNotes
Feather0-2+1 per levelBase random->nextInt(3) + random->nextInt(1 + playerBonusLevel)
Raw Chicken1NoAlways 1
Cooked Chicken1NoDrops instead of raw if on fire

File: Sheep.cpp

DropCountLootingNotes
Wool1NoOnly if NOT sheared. Aux value matches sheep color.
(nothing)0NoIf sheared, drops nothing on death

Sheep are unique. Unsheared sheep drop 1 wool block matching their color. Sheared sheep drop nothing:

void Sheep::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel)
{
if (!isSheared())
{
spawnAtLocation(shared_ptr<ItemInstance>(
new ItemInstance(Tile::cloth_Id, 1, getColor())), 0.0f);
}
}

File: Squid.cpp

DropCountLootingNotes
Ink Sac1-3+1 per levelBase 1 + random->nextInt(3)

File: Wolf.cpp

DropCountNotes
(nothing)0getDeathLoot() returns -1

File: Ozelot.cpp

DropCountNotes
(nothing)0dropDeathLoot() is empty

File: SnowMan.cpp

DropCountLootingNotes
Snowball0-15NoBase random->nextInt(16)

File: VillagerGolem.cpp

DropCountLootingNotes
Rose0-2NoBase random->nextInt(3)
Iron Ingot3-5NoBase 3 + random->nextInt(3)

File: Zombie.cpp

DropCountLootingNotes
Rotten Flesh(base Mob)YesUses getDeathLoot() which returns rotten flesh

Rare drops (2.5% base, player-kill only):

DropChance
Iron Ingot1/3 of rare drops
Carrot1/3 of rare drops
Potato1/3 of rare drops

The source has commented-out rare drops for iron sword, iron helmet, and iron shovel. These were removed.

File: Skeleton.cpp

DropCountLootingNotes
Arrow0-2+1 per levelBase random->nextInt(3 + playerBonusLevel)
Bone0-2+1 per levelBase random->nextInt(3 + playerBonusLevel)

Rare drops:

DropCondition
Enchanted BowIf rareLootLevel > 0 (looting helped). Enchanted at level 5.
Plain BowIf rareLootLevel == 0 (no looting boost)

File: Creeper.cpp

DropCountLootingNotes
Gunpowder(base Mob)YesUses getDeathLoot() which returns sulphur (gunpowder)

File: Spider.cpp

DropCountLootingNotes
String(base Mob)YesUses getDeathLoot() which returns string
Spider Eye0-1Looting bonus33% chance OR if looting bonus roll succeeds. Player-kill only.

The spider eye logic checks: if (wasKilledByPlayer && (random->nextInt(3) == 0 || random->nextInt(1 + playerBonusLevel) > 0)).

File: EnderMan.cpp

DropCountLootingNotes
Ender Pearl0-1+1 per levelBase random->nextInt(2 + playerBonusLevel)

File: Slime.cpp

DropCountLootingNotes
Slimeball0-2NoOnly from smallest size (size 1). Base random->nextInt(3).
(nothing)0NoLarger slimes drop nothing (they split into smaller slimes)

File: Ghast.cpp

DropCountLootingNotes
Ghast Tear0-1+1 per levelBase random->nextInt(2 + playerBonusLevel)
Gunpowder0-2+1 per levelBase random->nextInt(3 + playerBonusLevel)

File: Blaze.cpp

DropCountLootingNotes
Blaze Rod0-1+1 per levelPlayer-kill only. Base random->nextInt(2 + playerBonusLevel).
Glowstone Dust0-2+1 per levelPlayer-kill only. 4J console addition. Base random->nextInt(3 + playerBonusLevel).

File: LavaSlime.cpp

DropCountLootingNotes
Magma Cream0-2+1 per levelOnly from size > 1. Base random->nextInt(3 + playerBonusLevel).
(nothing)0NoSmallest size drops nothing

File: PigZombie.cpp

DropCountLootingNotes
Rotten Flesh0-1+1 per levelBase random->nextInt(2 + playerBonusLevel)
Gold Nugget0-1+1 per levelBase random->nextInt(2 + playerBonusLevel)

Rare drops:

DropCondition
Enchanted Gold SwordIf rareLootLevel > 0. Enchanted at level 5.
Gold Ingot OR Gold Sword OR Gold HelmetIf rareLootLevel == 0. Random pick from 3.
MobRegular DropsRare DropsNotes
CowLeather (0-2), Beef (1-3)NoneCooked if on fire
PigPorkchop (1-3)NoneCooked if on fire. Saddle if saddled.
ChickenFeather (0-2), Chicken (1)NoneCooked if on fire
SheepWool (1) or nothingNoneOnly unsheared sheep drop wool
SquidInk Sac (1-3)None
WolfNothingNone
OcelotNothingNone
Snow GolemSnowball (0-15)None
Iron GolemRose (0-2), Iron (3-5)None
ZombieRotten FleshIron/Carrot/Potato
SkeletonArrow (0-2), Bone (0-2)Bow (plain or enchanted)
CreeperGunpowderNone
SpiderString + Spider EyeNoneEye is 33% player-kill only
EndermanEnder Pearl (0-1)None
SlimeSlimeball (0-2)NoneSmallest size only
GhastGhast Tear (0-1), Gunpowder (0-2)None
BlazeBlaze Rod (0-1), Glowstone (0-2)NonePlayer-kill only. Glowstone is console-exclusive.
Magma CubeMagma Cream (0-2)NoneSize > 1 only
Zombie PigmanRotten Flesh (0-1), Gold Nugget (0-1)Gold Ingot/Sword/Helmet or Enchanted Gold Sword

The Looting enchantment is checked through EnchantmentHelper::getKillingLootBonus():

int EnchantmentHelper::getKillingLootBonus(shared_ptr<Inventory> inventory)
{
return getEnchantmentLevel(Enchantment::lootBonus->id, inventory->getSelected());
}

This scans the player’s held item for the Looting enchantment and returns its level (0-3). Mob drop methods receive this as playerBonusLevel and typically use it like random->nextInt(3 + playerBonusLevel) to increase drop counts.

For rare drops, the looting level subtracts directly from the random roll, making rare drops more likely. Each looting level effectively adds 0.5% to the base 2.5% chance.

Looting LevelRare Drop Chance
0 (none)2.5% (5/200)
I3.0% (6/200)
II3.5% (7/200)
III4.0% (8/200)

To give your mob custom drops, override dropDeathLoot and optionally dropRareDeathLoot:

void MyMob::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel)
{
// Always drop 1-3 items, with looting adding more
int count = 1 + random->nextInt(3 + playerBonusLevel);
for (int i = 0; i < count; i++)
{
spawnAtLocation(Item::diamond_Id, 1);
}
// Conditional drop: cooked version if on fire
if (isOnFire())
{
spawnAtLocation(Item::beef_cooked_Id, 1);
}
else
{
spawnAtLocation(Item::beef_raw_Id, 1);
}
// Player-only drop
if (wasKilledByPlayer)
{
spawnAtLocation(Item::expBottle_Id, 1);
}
}
void MyMob::dropRareDeathLoot(int rareLootLevel)
{
// rareLootLevel is 1 when looting applies, 0 otherwise
if (rareLootLevel > 0)
{
// Enchanted item drop
shared_ptr<ItemInstance> sword(new ItemInstance(Item::sword_diamond));
EnchantmentHelper::enchantItem(random, sword, 10 + random->nextInt(10));
spawnAtLocation(sword, 0);
}
else
{
spawnAtLocation(Item::goldIngot_Id, 1);
}
}

Block drops follow a different pattern. When a player breaks a block, Tile::playerDestroy() runs the drop logic:

void Tile::playerDestroy(Level *level, shared_ptr<Player> player,
int x, int y, int z, int data)
{
// ... stats tracking ...
if (isSilkTouchable() && EnchantmentHelper::hasSilkTouch(player->inventory))
{
shared_ptr<ItemInstance> item = getSilkTouchItemInstance(data);
if (item != NULL)
{
popResource(level, x, y, z, item);
}
}
else
{
int playerBonusLevel = EnchantmentHelper::getDiggingLootBonus(
player->inventory);
spawnResources(level, x, y, z, data, playerBonusLevel);
}
}

The flow is:

  1. Check for Silk Touch first. If the block supports it, drop the block itself.
  2. Otherwise, get the Fortune level and call spawnResources().

Tile::spawnResources() loops over the drop count and spawns items:

void Tile::spawnResources(Level *level, int x, int y, int z,
int data, float odds, int playerBonusLevel)
{
if (level->isClientSide) return;
int count = getResourceCountForLootBonus(playerBonusLevel, level->random);
for (int i = 0; i < count; i++)
{
if (level->random->nextFloat() > odds) continue;
int type = getResource(data, level->random, playerBonusLevel);
if (type <= 0) continue;
popResource(level, x, y, z, shared_ptr<ItemInstance>(
new ItemInstance(type, 1, getSpawnResourcesAuxValue(data))));
}
}

Key virtual methods for block drops:

MethodPurpose
getResource(data, random, bonus)Which item ID to drop
getResourceCount(random)Base number of items to drop
getResourceCountForLootBonus(bonus, random)Drop count with Fortune applied
getSpawnResourcesAuxValue(data)Aux/data value for the dropped item

Silk Touch is checked with EnchantmentHelper::hasSilkTouch():

bool EnchantmentHelper::hasSilkTouch(shared_ptr<Inventory> inventory)
{
return getEnchantmentLevel(Enchantment::untouching->id,
inventory->getSelected()) > 0;
}

Blocks opt into Silk Touch support by overriding isSilkTouchable() and getSilkTouchItemInstance(). Some examples:

  • Glass returns true for isSilkTouchable() (normally drops nothing)
  • Leaves return the leaf block with the correct type mask: new ItemInstance(id, 1, data & LEAF_TYPE_MASK)
  • Ender Chests support silk touch, dropping the full block instead of obsidian
  • Ice has special handling to avoid placing water when silk-touched
  • Most cube-shaped, non-entity tiles are silk-touchable by default

Fortune is read through EnchantmentHelper::getDiggingLootBonus():

int EnchantmentHelper::getDiggingLootBonus(shared_ptr<Inventory> inventory)
{
return getEnchantmentLevel(Enchantment::resourceBonus->id,
inventory->getSelected());
}

Ores override getResourceCountForLootBonus() to multiply drops with Fortune. Here is the real ore logic:

int OreTile::getResourceCountForLootBonus(int bonusLevel, Random *random)
{
if (bonusLevel > 0 && id != getResource(0, random, bonusLevel))
{
int bonus = random->nextInt(bonusLevel + 2) - 1;
if (bonus < 0)
{
bonus = 0;
}
return getResourceCount(random) * (bonus + 1);
}
return getResourceCount(random);
}

Fortune only applies to ores that drop items different from the block itself (coal, diamond, lapis, emerald, nether quartz). Iron and gold ore drop themselves, so Fortune does nothing to them.

Lapis ore has a special base drop count of 4-8:

int OreTile::getResourceCount(Random *random)
{
if (id == Tile::lapisOre_Id) return 4 + random->nextInt(5);
return 1;
}

Ores also drop experience when mined (unless silk-touched). The OreTile::spawnResources() method handles this after the regular item drop:

void OreTile::spawnResources(Level *level, int x, int y, int z,
int data, float odds, int playerBonusLevel)
{
Tile::spawnResources(level, x, y, z, data, odds, playerBonusLevel);
if (getResource(data, level->random, playerBonusLevel) != id)
{
int magicCount = 0;
if (id == Tile::coalOre_Id)
magicCount = Mth::nextInt(level->random, 0, 2);
else if (id == Tile::diamondOre_Id)
magicCount = Mth::nextInt(level->random, 3, 7);
else if (id == Tile::emeraldOre_Id)
magicCount = Mth::nextInt(level->random, 3, 7);
else if (id == Tile::lapisOre_Id)
magicCount = Mth::nextInt(level->random, 2, 5);
else if (id == Tile::netherQuartz_Id)
magicCount = Mth::nextInt(level->random, 2, 5);
popExperience(level, x, y, z, magicCount);
}
}
OreXP Drop Range
Coal Ore0-2
Diamond Ore3-7
Emerald Ore3-7
Lapis Ore2-5
Nether Quartz2-5
Iron Ore0 (drops itself)
Gold Ore0 (drops itself)

Leaves have their own spawnResources() override. Oak leaves have a 1/20 chance to drop saplings and a 1/200 chance to drop apples. Jungle leaves have a 1/40 sapling chance:

void LeafTile::spawnResources(Level *level, int x, int y, int z,
int data, float odds, int playerBonusLevel)
{
if (!level->isClientSide)
{
int chance = 20;
if ((data & LEAF_TYPE_MASK) == JUNGLE_LEAF)
{
chance = 40;
}
if (level->random->nextInt(chance) == 0)
{
int type = getResource(data, level->random, playerBonusLevel);
popResource(level, x, y, z, shared_ptr<ItemInstance>(
new ItemInstance(type, 1, getSpawnResourcesAuxValue(data))));
}
if ((data & LEAF_TYPE_MASK) == NORMAL_LEAF
&& level->random->nextInt(200) == 0)
{
popResource(level, x, y, z, shared_ptr<ItemInstance>(
new ItemInstance(Item::apple_Id, 1, 0)));
}
}
}

Using shears on leaves bypasses this entirely and drops the leaf block itself.

Leaf TypeSapling ChanceApple Chance
Oak1/20 (5%)1/200 (0.5%)
Spruce1/20 (5%)None
Birch1/20 (5%)None
Jungle1/40 (2.5%)None

Crops use data values (0-7) to track growth. Only fully grown crops (data 7) drop the plant item. Immature crops drop seeds:

int CropTile::getResource(int data, Random *random, int playerBonusLevel)
{
if (data == 7)
{
return getBasePlantId(); // e.g., Item::wheat_Id
}
return getBaseSeedId(); // e.g., Item::seeds_wheat_Id
}

Fully grown crops also have a chance to drop bonus seeds:

void CropTile::spawnResources(Level *level, int x, int y, int z,
int data, float odds, int playerBonus)
{
Bush::spawnResources(level, x, y, z, data, odds, 0);
if (level->isClientSide) return;
if (data >= 7)
{
int count = 3 + playerBonus;
for (int i = 0; i < count; i++)
{
if (level->random->nextInt(5 * 3) > data) continue;
popResource(level, x, y, z, shared_ptr<ItemInstance>(
new ItemInstance(getBaseSeedId(), 1, 0)));
}
}
}

To make a custom block with special drop behavior, override the relevant methods in your Tile subclass:

int MyOreTile::getResource(int data, Random *random, int playerBonusLevel)
{
// Drop a custom item instead of the block
return Item::myCustomGem_Id;
}
int MyOreTile::getResourceCount(Random *random)
{
// Drop 1-3 items
return 1 + random->nextInt(3);
}
int MyOreTile::getResourceCountForLootBonus(int bonusLevel, Random *random)
{
// Apply fortune: multiply base count
if (bonusLevel > 0)
{
int bonus = random->nextInt(bonusLevel + 2) - 1;
if (bonus < 0) bonus = 0;
return getResourceCount(random) * (bonus + 1);
}
return getResourceCount(random);
}
bool MyOreTile::isSilkTouchable()
{
return true; // Allow silk touch to get the block itself
}
shared_ptr<ItemInstance> MyOreTile::getSilkTouchItemInstance(int data)
{
return shared_ptr<ItemInstance>(new ItemInstance(id, 1, 0));
}
FactorEffect
Baby mobNo drops at all
Non-player killwasKilledByPlayer is false, some mobs skip drops
Looting I-IIIIncreases playerBonusLevel by 1-3
Rare drop base2.5% chance (5/200)
Rare drop + Looting+0.5% per looting level
Fire killSome mobs drop cooked meat instead of raw
EnchantmentHelper MethodEffect
Silk TouchhasSilkTouch()Drop block itself instead of resource
Fortune I-IIIgetDiggingLootBonus()Multiply resource drop count
Shears (item)Checked manuallyLeaves drop leaf block, tall grass drops itself
FileWhat it does
Minecraft.World/Mob.cppBase die() and dropDeathLoot() pipeline
Minecraft.World/Tile.cppplayerDestroy(), spawnResources(), and silk touch
Minecraft.World/OreTile.cppFortune logic, ore XP drops
Minecraft.World/LeafTile.cppLeaf drop chances and shears behavior
Minecraft.World/CropTile.cppGrowth-based crop drops
Minecraft.World/EnchantmentHelper.cppLooting, fortune, and silk touch checks
Minecraft.World/Cow.cppCow drops (leather + beef)
Minecraft.World/Pig.cppPig drops (porkchop + saddle)
Minecraft.World/Chicken.cppChicken drops (feather + chicken)
Minecraft.World/Sheep.cppSheep drops (colored wool)
Minecraft.World/Skeleton.cppSkeleton drops (arrow + bone + rare bow)
Minecraft.World/Zombie.cppZombie drops (rotten flesh + rare items)
Minecraft.World/Blaze.cppBlaze drops (blaze rod + glowstone)
Minecraft.World/PigZombie.cppZombie pigman drops (flesh + gold)