๐Ÿ€ My First Minecraft Mod ยท Episode 6 of 6 ยท See All Episodes
๐Ÿ€ Episode 6 ยท Capstone ยท Lucky Crystal Block

Lucky Crystal Block!
The Final Boss

Break the Lucky Crystal Block and anything could happen frogs rain down, lightning strikes, you get free tools, or a Creeper appears! This is your capstone project you've learned everything you need.

๐Ÿง‘ Ages 10+ โฑ๏ธ ~2 Hours ๐Ÿ€ Fabric 1.21 โœ“ Free
Your Progress 0 / 12 Steps
Prefer no install? Change Minecraft from a plain folder instead. Plan a crafting recipe in our browser Datapack Studio and export a real datapack zip, with no Java and no mod loader. Open the Minecraft Mod Studio →
๐Ÿ“ฆ
Workshop reference pack: download an offline copy of every step in this workshop: โฌ‡ minecraft-lucky-mod-reference.zip. Every code block also has a Copy button.
1
๐Ÿ€
The Plan, What Will Happen?
Design the 5 random events before writing any code
In Progress
๐Ÿ€
Goal, understand the Lucky Crystal Block concept

The Lucky Crystal Block is a special block that overrides the onBreak() method. When a player breaks it, instead of just dropping items, it picks one of 5 random events. Each event is a case in a switch statement.

The 5 random events

๐Ÿธ
Frog Rain
Spawn 5 Crystal Frogs around the player
๐Ÿ’Ž
Gem Shower
Drop 16 Crystal Gems at the block location
โšก
Lightning!
Strike the block position with lightning
๐Ÿ›ก๏ธ
Crystal Kit
Give the player their Crystal Pickaxe and Sword
๐Ÿ’ฅ
Surprise!
Spawn a Creeper right on the player
โ„น๏ธ How the randomness works we'll use world.getRandom().nextInt(5) which returns a number 0, 1, 2, 3, or 4, each with equal probability. Each number triggers one event.
๐Ÿ’ก This is a capstone project! We're using techniques from all 5 previous episodes: block registration (Ep 2), item drops (Ep 1+2), entity spawning (Ep 5), and custom block behaviour (Ep 2). You've learned all the skills you need!
I understand the Lucky Block will override onBreak() to trigger random events
I know all 5 events we'll implement
๐Ÿง 
Knowledge Check
+15 XP
How many random events does the Lucky Crystal Block have?
AThree
BFive
CTen
2
๐Ÿ“
Create LuckyCrystalBlock.java
Write the block class with the random event logic
Locked
๐Ÿ“
Goal, write the block's brain

Create LuckyCrystalBlock.java. It extends Block and overrides onBreak(). When broken, it picks a random number 0-4 and runs the matching event. All the good stuff happens in this one file!

Create the file

Right-click the crystalmod package โ†’ New โ†’ Java Class โ†’ LuckyCrystalBlock

LuckyCrystalBlock.java imports & class header
package com.example.crystalmod; import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.entity.ItemEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; import net.minecraft.server.world.ServerWorld; import net.minecraft.text.Text; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; public class LuckyCrystalBlock extends Block { public LuckyCrystalBlock(Settings settings) { super(settings); } @Override public void onBreak(World world, BlockPos pos, BlockState state, PlayerEntity player) { super.onBreak(world, pos, state, player); // Only run the events on the server, not the client if (!world.isClient()) { ServerWorld serverWorld = (ServerWorld) world; int event = world.getRandom().nextInt(5); // 0, 1, 2, 3, or 4 switch (event) { case 0 -> eventFrogRain(serverWorld, pos, player); case 1 -> eventGemShower(world, pos); case 2 -> eventLightning(serverWorld, pos); case 3 -> eventCrystalKit(player); case 4 -> eventSurprise(serverWorld, player); } } }
โ„น๏ธ Why !world.isClient()? Minecraft runs game logic twice once on the server (real logic) and once on the client (prediction). We cast to ServerWorld to access server-only methods like entity spawning. Always guard world-changing code with this check!
Created LuckyCrystalBlock.java with the class header and onBreak override
Switch statement with 5 cases is in place (methods will be red, that's OK for now!)
๐ŸŽ“ Teacher Note, Step 2The !world.isClient() guard is essential. Without it, all 5 events fire twice, once on the server and once on the client causing double entity spawns and chaos. Students often remove it thinking it's unnecessary. Also: onBreak fires as the block is being broken, before it's fully removed calling super.onBreak() first is required.
๐Ÿšจ Stuck? Events aren't triggering when the block is broken
  • Block breaks but nothing happens check ModBlocks.java registers it as new LuckyCrystalBlock(...), not new Block(...). The onBreak override only exists on your custom class.
  • Everything happens twice you're missing the if (!world.isClient()) guard. Add it back wrapping the switch statement.
  • Cannot cast World to ServerWorld the cast is safe because we already checked !isClient(). If IntelliJ warns, you can add @SuppressWarnings("unchecked") above the line.
  • Method not found errors the 5 event methods must all be inside the LuckyCrystalBlock class (before its closing } but after the closing } of onBreak).
๐Ÿง 
Knowledge Check
+15 XP
What does LuckyCrystalBlock.java contain?
AThe block class with the random event logic
BThe texture
CThe recipe
3
๐Ÿธ
Event 1, Frog Rain
Spawn 5 Crystal Frogs around the player
Locked
๐Ÿธ
Goal, write eventFrogRain()

Add the eventFrogRain method to LuckyCrystalBlock.java. It spawns 5 Crystal Frogs at slightly random positions around the block.

LuckyCrystalBlock.java, add this method
private void eventFrogRain(ServerWorld world, BlockPos pos, PlayerEntity player) { player.sendMessage(Text.literal("๐Ÿธ Frog Rain! The frogs have arrived!"), false); for (int i = 0; i < 5; i++) { CrystalFrogEntity frog = ModEntities.CRYSTAL_FROG.create(world); if (frog != null) { // Spawn each frog at a slightly different spot double offsetX = (world.getRandom().nextDouble() - 0.5) * 6; double offsetZ = (world.getRandom().nextDouble() - 0.5) * 6; frog.refreshPositionAndAngles( pos.getX() + offsetX, pos.getY() + 2, pos.getZ() + offsetZ, 0, 0 ); world.spawnEntity(frog); } } }
๐Ÿ’ก nextDouble() - 0.5 gives a number between -0.5 and +0.5. Multiplying by 6 spreads frogs up to 3 blocks in any direction. The + 2 on Y spawns them a bit above the block so they fall down!
Added eventFrogRain() method to LuckyCrystalBlock.java
๐Ÿง 
Knowledge Check
+15 XP
What happens in the Frog Rain event?
AFive Crystal Frogs spawn around the player
BIt rains gems
CLightning strikes
4
๐Ÿ’Ž
Event 2, Gem Shower
Drop a pile of Crystal Gems at the block location
Locked
๐Ÿ’Ž
Goal, write eventGemShower()

Drop 16 Crystal Gems as item entities at the block's position. Item entities are the spinning items you see floating on the ground they use ItemEntity.

LuckyCrystalBlock.java add this method
private void eventGemShower(World world, BlockPos pos) { ItemStack gems = new ItemStack(ModItems.CRYSTAL_GEM, 16); ItemEntity itemEntity = new ItemEntity( world, pos.getX() + 0.5, pos.getY() + 1.0, pos.getZ() + 0.5, gems ); // Give the items a little upward velocity so they pop out itemEntity.setVelocity( (world.getRandom().nextDouble() - 0.5) * 0.4, 0.5, (world.getRandom().nextDouble() - 0.5) * 0.4 ); world.spawnEntity(itemEntity); }
โ„น๏ธ new ItemStack(item, count) creates a stack of items. The setVelocity call makes the gem stack fly up and scatter sideways, just like when a creeper explodes near chests!
Added eventGemShower() method to LuckyCrystalBlock.java
๐Ÿง 
Knowledge Check
+15 XP
What drops in the Gem Shower event?
AA pile of Crystal Gems
BFrogs
CA Creeper
5
โšก
Event 3, Lightning Strike
Call down lightning at the block's position
Locked
โšก
Goal, write eventLightning()

Summon a LightningBoltEntity at the block location. This is a real lightning bolt it'll set fire to things, transform mobs (pigs into piglins!), and make a big flash.

LuckyCrystalBlock.java, add this import at the top
import net.minecraft.entity.LightningEntity;
LuckyCrystalBlock.java add this method
private void eventLightning(ServerWorld world, BlockPos pos) { LightningEntity lightning = new LightningEntity( EntityType.LIGHTNING_BOLT, world ); lightning.refreshPositionAndAngles( pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, 0, 0 ); world.spawnEntity(lightning); }
โš ๏ธ Lightning is dangerous! It will set fire to nearby flammable blocks. If you want a "cosmetic only" lightning that doesn't damage, call lightning.setCosmetic(true); before spawning it.
โœจ You can also import EntityType from net.minecraft.entity.EntityType to get the LIGHTNING_BOLT type. IntelliJ will offer to add it automatically just press Alt+Enter on the red squiggle!
Added LightningEntity import at the top of the file
Added eventLightning() method to LuckyCrystalBlock.java
๐Ÿง 
Knowledge Check
+15 XP
What does the Lightning Strike event do?
ACalls down lightning at the block
BSpawns a creeper
CGives a pickaxe
6
๐Ÿ›ก๏ธ
Event 4 Crystal Kit
Give the player a Crystal Pickaxe and Crystal Sword
Locked
๐Ÿ›ก๏ธ
Goal, write eventCrystalKit()

Directly give items to the player's inventory using player.getInventory().insertStack(). This is a reward event if you're lucky enough to roll this, you get free tools!

LuckyCrystalBlock.java add this method
private void eventCrystalKit(PlayerEntity player) { player.sendMessage(Text.literal("๐Ÿ’Ž Crystal Kit! You got the tools!"), false); // Give a Crystal Pickaxe ItemStack pickaxe = new ItemStack(ModItems.CRYSTAL_PICKAXE); player.getInventory().insertStack(pickaxe); // Give a Crystal Sword ItemStack sword = new ItemStack(ModItems.CRYSTAL_SWORD); player.getInventory().insertStack(sword); // Give a stack of Magic Cookies too why not! ItemStack cookies = new ItemStack(ModItems.MAGIC_COOKIE, 8); player.getInventory().insertStack(cookies); }
๐Ÿ’ก insertStack() puts the item in the first available inventory slot. If the inventory is full, the item goes on the ground instead. You could also use giveItemStack() which does the same thing.
Added eventCrystalKit() method to LuckyCrystalBlock.java
๐Ÿง 
Knowledge Check
+15 XP
What does the Crystal Kit event give the player?
AA Crystal Pickaxe and Sword
BCrystal Gems
CA spawn egg
7
๐Ÿ’ฅ
Event 5, Surprise Creeper!
Spawn a Creeper right next to the player
Locked
๐Ÿ’ฅ
Goal, write eventSurprise()

The "bad luck" event spawn a fully charged Creeper right next to the player. This is the risky event that makes the Lucky Block exciting!

LuckyCrystalBlock.java add this import at the top
import net.minecraft.entity.mob.CreeperEntity;
LuckyCrystalBlock.java, add this method
private void eventSurprise(ServerWorld world, PlayerEntity player) { player.sendMessage(Text.literal("๐Ÿ’ฅ SURPRISE! Run!!"), false); CreeperEntity creeper = (CreeperEntity) EntityType.CREEPER.create(world); if (creeper != null) { // Spawn 2 blocks in front of the player creeper.refreshPositionAndAngles( player.getX() + 2, player.getY(), player.getZ() + 2, 0, 0 ); world.spawnEntity(creeper); } }
๐ŸŽฎ Want it even scarier? You can make the Creeper already exploding by calling creeper.setFuseSpeed(1); before spawning this makes it start the fuse countdown immediately!
Added CreeperEntity import at the top of the file
Added eventSurprise() method to LuckyCrystalBlock.java
๐Ÿง 
Knowledge Check
+15 XP
What appears in the Surprise event?
AA Creeper next to the player
BA frog
CA chest
8
๐Ÿ“‹
Register the Lucky Crystal Block
Add it to ModBlocks.java and CrystalMod.java
Locked
๐Ÿ“‹
Goal, register the block and its item

Open ModBlocks.java and add the Lucky Crystal Block. It glows (luminance 15 max!) and is a bit harder to mine than the Crystal Block. We want it to feel special.

Add to ModBlocks.java

ModBlocks.java add this field
public static final Block LUCKY_CRYSTAL_BLOCK = registerBlock("lucky_crystal_block", new LuckyCrystalBlock(AbstractBlock.Settings.create() .mapColor(MapColor.MAGENTA) .strength(4.0f, 5.0f) .luminance(state -> 15) // maximum glow! .requiresTool() .sounds(BlockSoundGroup.AMETHYST_BLOCK) ));

Add to ModItems.java, registerModItems()

Add the Lucky Crystal Block item to the creative tab. Open ModItems.java and update registerModItems():

ModItems.java update the entries list
ItemGroupEvents.modifyEntriesEvent(ItemGroups.INGREDIENTS).register(entries -> { entries.add(ModItems.CRYSTAL_GEM); entries.add(ModBlocks.CRYSTAL_BLOCK); entries.add(ModBlocks.LUCKY_CRYSTAL_BLOCK); // <-- new! entries.add(ModItems.MAGIC_COOKIE); entries.add(ModItems.CRYSTAL_PICKAXE); entries.add(ModItems.CRYSTAL_SWORD); entries.add(ModItems.CRYSTAL_FROG_SPAWN_EGG); });

Add language entry

en_us.json, add this line
"block.crystalmod.lucky_crystal_block": "Lucky Crystal Block"
Added LUCKY_CRYSTAL_BLOCK to ModBlocks.java
Added it to the creative tab in ModItems.registerModItems()
Added language entry to en_us.json
๐Ÿง 
Knowledge Check
+15 XP
Where is the Lucky Crystal Block registered?
AModBlocks.java and CrystalMod.java
BOnly the texture file
CThe recipe file
9
๐ŸŽจ
Textures & Models
Create the Lucky Crystal Block's glowing look
Locked
๐ŸŽจ
Goal, create all the JSON files and texture

The Lucky Crystal Block needs the same set of files as the Crystal Block from Episode 2: a blockstate, two model files, a texture, and a loot table. We'll skip the loot table this time the block's special events ARE the loot!

Texture

Create assets/crystalmod/textures/block/lucky_crystal_block.png a 16โœจ16 pixel image. Make it look special try a vivid pink/purple with a star or "?" pattern on it!

Blockstate JSON

assets/crystalmod/blockstates/lucky_crystal_block.json
{ "variants": { "": { "model": "crystalmod:block/lucky_crystal_block" } } }

Block model JSON

assets/crystalmod/models/block/lucky_crystal_block.json
{ "parent": "block/cube_all", "textures": { "all": "crystalmod:block/lucky_crystal_block" } }

Item model JSON

assets/crystalmod/models/item/lucky_crystal_block.json
{ "parent": "crystalmod:block/lucky_crystal_block" }

Loot table, drops nothing (events are the reward!)

data/crystalmod/loot_table/blocks/lucky_crystal_block.json
{ "type": "minecraft:block", "pools": [] }
โ„น๏ธ An empty "pools": [] means the block drops nothing. The onBreak events handle what the player gets we don't want both a loot drop AND event rewards!
Created lucky_crystal_block.png texture (16โœจ16)
Created blockstate JSON
Created block model JSON
Created item model JSON
Created empty loot table JSON
๐Ÿง 
Knowledge Check
+15 XP
What look does the Lucky Crystal Block have?
AA glowing look
BA plain grey look
CA transparent look
10
โš’๏ธ
Crafting Recipe
Make the Lucky Crystal Block craftable in-game
Locked
โš’๏ธ
Goal, create the crafting recipe

The Lucky Crystal Block is crafted from 8 Crystal Blocks surrounding one Crystal Gem an expensive recipe that matches how rare and powerful it should be!

data/crystalmod/recipe/lucky_crystal_block.json
{ "type": "minecraft:crafting_shaped", "pattern": [ "BBB", "BGG", "BBB" ], "key": { "B": { "item": "crystalmod:crystal_block" }, "G": { "item": "crystalmod:crystal_gem" } }, "result": { "id": "crystalmod:lucky_crystal_block", "count": 1 } }
๐Ÿ’ก This recipe uses B for Crystal Block and G for Crystal Gem. The center-right position has the gem, all outer 8 slots have Crystal Blocks. 8 Crystal Blocks = 72 Crystal Gems total that's a proper end-game block!
โœจ Want it easier to craft? Change the recipe to just 4 Crystal Gems in a 2โœจ2 shapeless recipe! Just change the type to "minecraft:crafting_shapeless" and use "ingredients" instead of "pattern".
Created lucky_crystal_block.json recipe in the recipe folder
๐Ÿง 
Knowledge Check
+15 XP
What does the crafting step make the block?
ACraftable in-game
BMineable only
CSpawnable only
11
โ›๏ธ
Mining Tag
Make the Lucky Crystal Block mineable with a pickaxe
Locked
โ›๏ธ
Goal, add the Lucky Crystal Block to the pickaxe mining tag

Just like in Episode 2, blocks need to be in the mineable/pickaxe tag to be properly mined with a pickaxe. Open the existing pickaxe tag and add the new block.

Update the pickaxe mining tag

Open data/minecraft/tags/block/mineable/pickaxe.json and add the new block:

data/minecraft/tags/block/mineable/pickaxe.json
{ "replace": false, "values": [ "crystalmod:crystal_block", "crystalmod:lucky_crystal_block" ] }
โ„น๏ธ You created this file back in Episode 4 for the Crystal Block. Now we're just adding the Lucky Crystal Block to the same list. "replace": false means we ADD to the vanilla list rather than replacing it.
Added crystalmod:lucky_crystal_block to the pickaxe.json mining tag
๐Ÿง 
Knowledge Check
+15 XP
What should mine the Lucky Crystal Block?
AA pickaxe
BA sword
CHand only
12
๐ŸŽจ
Run & Test Everything!
Launch Minecraft and break the Lucky Crystal Block again and again
Locked
๐ŸŽŠ
Goal, test all 5 events and celebrate!

Build the mod and test every event. On average you'll need to break ~10 Lucky Crystal Blocks to see all 5 events. Use /gamemode creative to get infinite blocks!

Build and run

  • 1In IntelliJ: Gradle โ†’ Tasks โ†’ fabric โ†’ runClient
  • 2Create a Creative world
  • 3Open inventory โ†’ search for "Lucky Crystal Block"
  • 4Place the block and break it with your Crystal Pickaxe
  • 5Watch for the chat message, it tells you which event fired!
  • 6Try to get all 5 events!
โš ๏ธ onBreak not firing? Make sure the block is registered with LuckyCrystalBlock (not just Block) in ModBlocks.java. The custom class is what adds the onBreak override!
๐Ÿ’ก Test all events quickly: Use /gamemode creative so you can place and break blocks infinitely. You can also temporarily change nextInt(5) to nextInt(1) to always trigger the same event while testing!
๐ŸŒŸ CHALLENGE Add a 6th event! Can you add a new case to the switch statement? Ideas: give the player a Speed effect, spawn vanilla frogs, drop a Netherite block, or teleport the player 10 blocks up! Change nextInt(5) to nextInt(6) and add case 5.
Mod builds without errors
Lucky Crystal Block appears in creative inventory with correct texture
Breaking the block triggers random events (see chat messages)
All 5 events work: Frog Rain, Gem Shower, Lightning, Crystal Kit, Surprise Creeper
Crafting recipe works (8 Crystal Blocks + 1 Crystal Gem)
๐Ÿง 
Knowledge Check
+15 XP
How do you test the block?
ABreak it again and again to see the random events
BOpen the texture
CRefresh the browser
๐ŸŽŠ ๐Ÿ’Ž ๐Ÿธ โšก ๐Ÿ€ ๐ŸŽŠ
You're a Minecraft Modder!

You built an entire Minecraft mod from scratch a custom item, a glowing block, a magic food, two powerful tools, a brand new animal mob, and a chaotic lucky block with 5 random events. That's real Java programming. That's real game development. You did it!

๐Ÿ’Ž Episode 1: Crystal Gem
๐Ÿชจ Episode 2: Crystal Block
๐Ÿช Episode 3: Magic Cookie
โš’๏ธ Episode 4: Crystal Tools
๐Ÿธ Episode 5: Crystal Frog
๐Ÿ€ Episode 6: Lucky Block
๐Ÿ† Back to All Episodes โ†’

๐Ÿงฉ Minecraft Modding Order

Drag the Minecraft Forge modding steps into the correct order:

Add the texture PNG to assets
Create the model JSON file
Add it to a Creative Tab
Register it in the mod event bus
Create your item or block class
Set up the Forge MDK project