All Projects

Myth & Metal

A Minecraft mod that adds tiered loot, procedural dungeons, and custom boss fights—with way too much rendering code.

What Is This?

Myth & Metal overhauls Minecraft's progression with a seven-tier rarity system, procedurally generated dungeons, custom boss fights, and new biomes. The interesting part isn't the features—it's how deep I had to go into Minecraft's internals to make them work.

Mixins into the rendering pipeline. Custom world generation via Terrablender. NBT-driven item states. This project showed me that "modding" and "reverse engineering" are pretty much the same thing.

The Rarity System

Seven tiers. Damage multipliers from 1.0× up to 5.0×. The key design choice: rarity is stored per-item, not per-item-type. Two identical swords can have different tiers. This lives in NBT tags.

Making this actually work required Mixins into ItemStack:

  • Tooltip injection@Inject on getTooltipLines appends the rarity display
  • Damage calculation@Redirect on EnchantmentHelper.getDamageBonus intercepts the damage math and applies tier multipliers before enchantment bonuses

Upgrading items happens at a custom anvil that extends ItemCombinerMenu. Drop in a higher-tier upgrade ingot, and the item inherits that tier.

Custom Glint Rendering

This is where things got complicated.

Each tier needed its own enchantment glint effect. That meant ~42 custom RenderType instances—7 tiers times 6 variants (basic glint, direct, entity-held, entity-direct, armor, armor-direct). Each one built with RenderType.CompositeState.builder() pointing to tier-specific glint textures.

Mixins into ItemRenderer intercept vanilla glint buffer requests and substitute the right render type based on the item's rarity. The armor mixin is more aggressive—it completely replaces render() via @Inject(at = HEAD, cancellable = true), then manually iterates equipment slots and applies tier-appropriate glints after base armor rendering.

I could have used Forge events. Mixins gave me finer control at the cost of more fragile code.

Procedural Dungeons

Dungeons are wave-based combat arenas that generate when you first enter a portal. The architecture is weird but effective: invisible trigger blocks with BlockEntity instances that manage room state.

Here's how a room activates:

  1. Player steps on a trigger block
  2. Recursive 6-directional flood-fill calculates room boundaries by propagating through adjacent DungeonAirEntity blocks
  3. Indestructible iron bars (strength = -1.0F) spawn on room edges, trapping the player
  4. Wave spawning begins—16 mobs total, max 6 alive at once
  5. When all mobs die, barriers vanish and trigger blocks replace themselves with air

Dungeon mobs extend vanilla counterparts with fire immunity, bonus fire damage, and die() overrides that call back to the room entity. Every mob knows which room it belongs to.

Generation Algorithm

When you enter a portal, placedungeon() runs:

  1. Spawn room placed at a unique offset (tracked via SavePortalData to prevent regeneration)
  2. Loop: randomly pick a room type (tree, nether, bone), place the NBT structure via StructureTemplateManager, add a hallway, advance Z by 17 blocks
  3. Each iteration has a 10% + (10% × iteration count) chance to stop (capped at 95%)
  4. Boss room placed at the end

The probabilistic termination creates dungeons with an expected length of ~10 rooms but high variance. Some are short sprints. Some are marathons.

Boss Fights

Bosses like Magmaraakh (Lava Dungeon) have animation state machines with timeout counters managing idle/attack transitions. The AI goal stack prioritizes floating, then custom attack behavior, then melee, then movement.

The custom attack goal extends MeleeAttackGoal with a 40-tick wind-up before damage lands—synced to the attack animation. Stats are beefy: 550 HP, 25 armor, full knockback resistance. Boss death triggers room completion via a back-reference to DungeonRoomEntity.

World Generation

Custom biomes integrate through Terrablender, registering overworld regions with weight 4. Each region maps climate parameters to custom biomes:

  • Ash Forest — Hot, arid, inland. Surface lava lakes, light-emitting ash logs, magma boulder decorations
  • Enchanted Forest — Ambient ENCHANTED_HIT particles at 0.01f density, custom sound loop, mega jungle-style trees with 2×2 trunks

Tiered ores get progressively rarer: T2 spawns 12 times per chunk across all heights, T7 spawns once per chunk below Y=-16 with triangle distribution. Tree placement uses countExtra(9, 0.5f, 7)—base 9 plus a coin flip for 7 more.

Dimension System

The dungeon dimension is registered via Forge's ResourceKey system. Fixed noon lighting, no skylight, zero ambient light, disabled beds. A FixedBiomeSource points to the dungeon biome. Portal blocks handle bidirectional transport with return coordinates stored in player persistent data.

Patterns I Relied On

  • Deferred Registration — All blocks, items, and entities use Forge's lazy initialization pattern
  • Distributed room state — Each trigger block has its own BlockEntity, but they share a single DungeonRoomEntity via flood-fill propagation
  • NBT persistence — Room state survives world saves via saveAdditional()/load()
  • Structure composition — Dungeons are multiple NBT structures placed sequentially along Z