01 — The Idea & Foundation

Building a Diablo-style loot system inside Pokémon FireRed


I grew up on Pokémon Blue — my first game, played it obsessively, collected the cards. Decades later — after shipping software in more languages than I can count — I picked up a small handheld for emulation and fell back in love with it. Some quality-of-life FireRed ROM hacks rekindled the itch. But not just to play it. To change it. Claude handles the GBA-specific C; I handle the design, the lore, and the decisions.

The Pitch

What if Pokémon had gear? Not held items — real gear. Randomized loot with prefixes, suffixes, rarity tiers, and quality rolls. Diablo 3 meets Pokémon FireRed.

The idea was simple: take a standard Pokémon playthrough and add one major twist. Every battle has a chance to drop gear for your Pokémon. An Amulet with +ATK. Armor with +DEF. Rare drops with exotic affixes. Your Pokémon aren’t just getting stronger through levels — they’re getting kitted out.

And for the trainer? A single relic slot. Equip a Mentor’s Tome for +50% XP. A Lucky Charm for better shiny odds. A Hunter’s Mark for improved drop rates. The kind of passive bonuses that change how you play.

Why a Decompilation?

There are two ways to hack a GBA ROM:

  1. Binary ROM hacking: Hex-edit the compiled ROM directly. Use tools like HxD, AdvanceMap, PGE. Poke bytes at specific offsets.
  2. Decompilation hacking: Start from pret/pokefirered — a full C decompilation of the game. Write real C code. Compile a new ROM.

Binary hacking is the traditional approach. Most ROM hacks use it. But for adding entirely new systems — not just moving trainers or changing Pokémon stats, but creating new game mechanics — decompilation is dramatically more powerful. You get:

The tradeoff: the codebase is enormous (~500K lines of C), the toolchain is arcane (DevKitARM cross-compiler, custom preprocessor for string encoding), and the GBA’s constraints are brutal.

Designing the Gear System

The core data structure had to be tiny. Every Pokémon stores its gear in a u16 field (2 bytes), and the actual gear data lives in a bag stored in the save file. The gear encoding packs everything into a single u32:

Bits 0-1:   Slot type (Accessory / Armor)
Bits 2-3:   Rarity (Common / Uncommon / Rare / Epic)
Bits 4-7:   Base item (8 types: Amulet, Bracelet, Ring, Crown, Vest, Mail, Cloak, Shield)
Bits 8-11:  Prefix 1 (10 options: Lucky, Stalwart, Fierce, etc.)
Bits 12-15: Prefix 2
Bits 16-19: Suffix 1 (8 options: of Focus, of Power, of the Moon, etc.)
Bits 20-21: Suffix 2
Bits 22-25: Quality (85%-130% effectiveness multiplier)
Bit 26:     Is Legendary
Bits 27-30: Legendary ID

One u32. 4 bytes. That’s the entire identity of a piece of gear.

The GearEncode macro assembles it:

#define GearEncode(slot, rarity, base, p1, p2, s1, s2, quality) \
    ((slot) | ((rarity) << 2) | ((base) << 4) | ...)

The Drop System

After every battle, TryDropGear() runs. It rolls against the base drop rate (modified by the Hunter’s Mark relic if equipped), picks a rarity tier, randomizes affixes, and generates a quality roll. The gear appears in a post-battle notification screen.

Drop rates scale with rarity:

Higher rarity means more affix slots filled, and affixes have stronger effect ranges at higher rarities.

The Relic System

Relics are simpler than gear — one slot on the trainer, passive effects that apply globally:

Relic Effect
Lucky Charm Increased shiny encounter rate
Mentor’s Tome Bonus XP from battles
Gold Coin Bonus prize money
Hunter’s Mark Improved gear drop rate
Seeker’s Eye Better item find chance
Breeder’s Loop Faster egg hatching
Sprinter’s Boots Walk/run speed boost

Each relic has 4 rarity tiers with scaling effect strength (stored as per-mille values for integer math on the GBA — no floating point).

The Save File Challenge

GBA save data is fixed-size. FireRed’s SaveBlock1 and SaveBlock2 have specific byte layouts. You can’t just append data — you have to carve space from existing unused filler bytes.

I carved:

Total SaveBlock2 must stay exactly 0xF24 bytes on GBA. I verified this obsessively.

First Code

The initial commit added ~420 lines of core gear logic (gear.h + gear.c), hooked drops into the battle system, added stat bonuses to CalculateMonStats(), and created a “GEAR” option in the party menu. The relic system followed immediately — 6 types with hooks scattered across the codebase: XP in the battle controller, money in battle scripts, shiny in wild encounter generation, egg hatch in daycare, item find in field control.

By the end of this phase, the data layer was solid. What I didn’t have was any way to see it.

That’s what the UI phase would fix — and break — and fix again.

By the Numbers

Metric Value
Active ~4 hours
Commits 2
Lines of C added ~6,300
New source files 12
Systems built gear, relic, legendary gear, shards, merchant, chest, altar, scrapper, drop notify
UI screens 7
Save file carved ~500 bytes from filler
Copilot requests 17
Tool executions ~1,270
Sub-agents 8

Next: 02 — The UI Challenge