Chapter 18, The Toolbelt
A pickaxe that is really a tagged stone, a sword that is really a sword, food that bites back, and a full set of violet armor borrowed from the Aether.
Tools, food, and armor are the three things players reach for first, and in beta each is handled differently. A tool is sometimes just an item wearing a tag and sometimes a real vanilla tool class. Food is a property you bolt onto any item, not a subclass. Armor is a genuine ArmorItem that happens to wear your art. This chapter walks all three using the showcase's own gear, and every line of code below is lifted byte-for-byte from ExampleMod.java.
Tools
There are two honest ways to make a tool, and the showcase ships one of each. The split is the same one you met with items in Chapter 5: the tiny builder for the easy case, plain subclassing for the rest.
The tag way: the Ruby Pick
The Ruby Pick is not a ToolItem at all. It is a perfectly plain Item that has been told it counts as an iron-tier pickaxe, through the same mineable-tag and tool-tier system you wired up for blocks back in Chapter 4. Two chained calls do it:
// Tool KIND and TIER, declared in one chain: this plain Item (no ToolItem
// subclass anywhere) counts as an iron-tier pickaxe for the tag system, so it
// harvests anything in mineable/pickaxe up through needs_iron_tool, including
// the ruby ore above. Vanilla tools never need this, they infer kind from
// their class and tier from their material. The handheld item model
// (retroapi/models/item/ruby_pick.json) gives it the diagonal in-hand pose.
RUBY_PICK = (Item) RetroItemAccess.create()
.maxStackSize(1)
.tool(com.periut.retroapi.tag.RetroTool.PICKAXE)
.tier(com.periut.retroapi.tag.RetroToolTier.IRON)
.register(id("ruby_pick"));src/main/resources/assets/example_mod/textures/item/ruby_pick.png
.tool(RetroTool.PICKAXE) declares the kind, this item harvests anything in the mineable/pickaxe tag, and .tier(RetroToolTier.IRON) declares how good it is, so it satisfies needs_iron_tool gating. Both feed the exact same harvest hook the ruby ore uses, so the pick mines the ruby ore it was meant to mine. Vanilla tool classes never call these, they infer the kind from their class and the tier from their ToolMaterial; the two declarations exist precisely for the case where there is no tool class, just an item you want to behave like one. The full tier ladder, the tag JSON, and the harvest rules all live in the tool-tiers section of Chapter 4; this is the same Ruby Pick seen from the item side.
The diagonal through-the-fist pose comes free here because the showcase ships a handheld item model at retroapi/models/item/ruby_pick.json; an item without such a model would opt in with .handheld(), the same one-call trick the Jump Stick used in Chapter 5.
That model is a plain generated item pointing at its own texture, with the handheld parent supplying the in-hand angle:
{
"parent": "minecraft:item/handheld",
"textures": {
"layer0": "example_mod:item/ruby_pick"
}
}The vanilla way: the Zanite Sword
When a real vanilla tool class already exists, use it. A sword is the cleanest example: SwordItem takes a ToolMaterial and hands you attack damage, durability, and block-breaking speed for nothing. You construct it yourself and wrap it with RetroItemAccess.of(...), exactly like any custom item class:
// A custom TOOL the vanilla way: a SwordItem subclass. Unlike the ruby pick (which is
// a plain item TAGGED as a pickaxe), a sword is a real vanilla tool class, so it comes
// with attack damage, durability, and the block-breaking speed of its ToolMaterial
// (IRON tier here) built in. Texture is the Aether's zanite sword art.
ZANITE_SWORD = (Item) RetroItemAccess.of(
new net.minecraft.item.SwordItem(RetroItemAccess.allocateId(), net.minecraft.item.ToolMaterial.IRON))
.maxStackSize(1)
.handheld()
.texture(id("zanite_sword"))
.register(id("zanite_sword"));Notice what is not here: no .tool(...), no .tier(...). A real SwordItem already knows it is a sword and already knows its material is iron, so the tag system reads both off the class. The constructor takes its numeric id from RetroItemAccess.allocateId() (same id story as any subclassed item), the ToolMaterial.IRON sets durability and damage, .handheld() gives it the in-hand tool angle, and .texture(...) points it at the Aether's zanite sword sprite. That is the whole difference between the two approaches: tag a plain item when no tool class fits, subclass the real one when it does.
Food
Food in RetroAPI is a property, not a subclass. You do not write a FoodItem; you chain .food(...) onto whatever item builder you already have, the same way modern Minecraft attaches a FoodComponent. The registry behind it is small enough to read in one sitting:
/** Runs the instant a food is eaten, after the heal. Use it for effects beyond healing. */
@FunctionalInterface
public interface OnEaten {
void onEaten(ItemStack stack, World world, PlayerEntity player);
}
/** Eats one from the stack: heal, then run the effect. Called by the use hook in ItemMixin. */
public static ItemStack eat(ItemStack stack, World world, PlayerEntity player) {
Props props = FOODS.get(stack.getItem());
if (props == null) {
return stack;
}
stack.count--;
player.heal(props.health);
if (props.onEaten != null) {
props.onEaten.onEaten(stack, world, player);
}
return stack;
}Read that closely, because it tells you what beta food can and cannot do. Beta 1.7.3 predates the hunger bar, so player.heal(props.health) restores health points directly, two points to a heart. There is no saturation, no nutrition, just hearts. And beta predates status effects too, so the only thing fancier than healing rides the OnEaten callback that fires right after the heal. That callback is the entire reason this API exists: it lets a food do something other than heal.
The simple case: the Candied Apple
One call, one number. The Candied Apple is edible and heals four points, two hearts, and that is all it does:
CANDIED_APPLE = RetroItemAccess.create()
.maxStackSize(16)
.food(4) // eat to heal 4 (two hearts)
.register(id("candied_apple"));That is the whole food declaration. Right-click to eat, lose one from the stack, gain two hearts. The candied apple also happens to be a layered texture stacked over vanilla's apple sprite, which is the texture trick from Chapter 5 rather than anything to do with food; here we care only about the one-word .food(4).
The showcase also ships an alternate item model for it, retroapi/models/item/candied_apple_glint.json, whose layer0 points at a sugar_sparkle sprite to give the apple a glint variant; like the layering above it is purely cosmetic and has nothing to do with the food property.
Three files back the candied apple. The items/ file binds the item to a composite model; the base is vanilla's apple, and the glint is the sugar_sparkle overlay variant:
{
"model": {
"type": "minecraft:composite",
"models": [
{ "type": "minecraft:model", "model": "example_mod:item/candied_apple_base" },
{ "type": "minecraft:model", "model": "example_mod:item/candied_apple_glint" }
]
}
}{
"parent": "minecraft:item/generated",
"textures": { "layer0": "minecraft:item/apple" }
}{
"parent": "minecraft:item/generated",
"textures": { "layer0": "example_mod:item/sugar_sparkle" }
}src/main/resources/assets/example_mod/textures/item/sugar_sparkle.png
The interesting case: the Poisonous Apple
Now use the second overload, .food(health, onEaten), and pass a lambda. The Poisonous Apple heals a single point, then turns around and hurts you:
// A food with a CUSTOM ON-EAT EFFECT: the Poisonous Apple heals a little, then bites
// back. The .food(health, onEaten) overload runs our callback the instant it is eaten;
// beta has no poison effect, so we just deal damage for a net-harmful bite. The
// callback is the whole reason the food API exists: food that does more than heal.
POISONOUS_APPLE = RetroItemAccess.create()
.maxStackSize(16)
.texture(id("poisonous_apple"))
.food(1, (stack, world, player) -> player.damage(null, 6))
.register(id("poisonous_apple"));Trace it against the eat method above. The player heals 1, then the lambda runs and calls player.damage(null, 6), so the net result is a six-point hit softened by a one-point heal. Because beta has no poison status effect to apply, raw damage is the poison. The order matters and is fixed by the registry: heal first, callback second. Anything you can express in that lambda, a hit, a buff you simulate by hand, a teleport, becomes a food effect. That is the payoff of food being a property with a hook rather than a rigid subclass.
| Method | What it does |
|---|---|
.food(int health) | edible, heals health points (two per heart), no effect |
.food(int health, OnEaten) | edible, heals, then runs your callback (the interesting one) |
.food(int health, boolean meat, OnEaten) | full control, including whether it counts as meat |
Armor
A full armor set is the showcase's most involved item, and yet the code is short, because RetroArmor does the heavy lifting. It is a real vanilla ArmorItem, so it equips into the armor slots, soaks up damage, and takes durability exactly like iron or diamond. What makes it modded is that it also implements RetroArmorTexture, so the renderer draws your art on the player instead of a vanilla material's. The showcase's set is Zanite, ported from aether-fabric-b1.7.3.
// Custom ARMOR: a full zanite set, ported from aether-fabric-b1.7.3. RetroArmor is a
// vanilla ArmorItem (material 2 = iron-tier protection and durability) that draws OUR
// worn texture on the player through RetroArmorTexture; the inventory icons are the
// vanilla armor sprites tinted zanite violet, so the set needs only the two worn
// sheets, textures/armor/zanite_1.png (body + feet) and _2.png (legs). Slots are
// 0 helmet, 1 chestplate, 2 leggings, 3 boots.
String zaniteTex = "/assets/example_mod/textures/armor/zanite";
int zaniteTint = 0x711EE8;
ZANITE_HELMET = (Item) RetroItemAccess.of(
new com.periut.retroapi.register.item.RetroArmor(2, 0, zaniteTex, zaniteTint)).register(id("zanite_helmet"));
ZANITE_CHESTPLATE = (Item) RetroItemAccess.of(
new com.periut.retroapi.register.item.RetroArmor(2, 1, zaniteTex, zaniteTint)).register(id("zanite_chestplate"));
ZANITE_LEGGINGS = (Item) RetroItemAccess.of(
new com.periut.retroapi.register.item.RetroArmor(2, 2, zaniteTex, zaniteTint)).register(id("zanite_leggings"));
ZANITE_BOOTS = (Item) RetroItemAccess.of(
new com.periut.retroapi.register.item.RetroArmor(2, 3, zaniteTex, zaniteTint)).register(id("zanite_boots"));Four pieces, four constructor calls, and the four arguments are the whole story. RetroArmor(material, slot, wornTextureBase, tint):
| Argument | Meaning |
|---|---|
material | protection and durability tier: 0 leather, 1 chain, 2 iron, 3 diamond, 4 gold. The set is iron-tier. |
slot | 0 helmet, 1 chestplate, 2 leggings, 3 boots |
wornTextureBase | resource path without the _1/_2.png suffix, here /assets/example_mod/textures/armor/zanite |
tint | color the vanilla inventory icon is dyed, here 0x711EE8, zanite violet |
Why a set needs only two textures
This is the clever part, and it is worth understanding so you do not ship art you do not need. The inventory icon for each piece is not a PNG you draw. RetroArmor reuses the vanilla iron armor sprite for that slot and tints it to your set color through getColorMultiplier, the same leather-dye trick the game already does. So the helmet, chestplate, leggings, and boots icons all come free from vanilla, recolored violet.
The worn texture, the armor drawn on the player's body, is the part you actually supply, through the RetroArmorTexture interface:
@Override
public String getArmorTexture(int layer) {
return this.wornTextureBase + "_" + (layer == 2 ? 2 : 1) + ".png";
}Beta draws armor on the body in two layers, and that is why there are exactly two sheets. Layer 1 is the body-and-feet sheet, zanite_1.png, covering helmet, chestplate, and boots. Layer 2 is the legs sheet, zanite_2.png. RetroAPI's PlayerArmorTextureMixin consumes getArmorTexture(layer) when the renderer binds the armor, swapping beta's fixed material path for yours. So a complete four-piece set, with custom worn art on the player, needs only those two PNG files plus tinted vanilla icons. That is the whole trick the Aether's zanite armor used, and it is the whole trick you inherit.
The two-arg constructor RetroArmor(material, slot, wornTextureBase) exists too, defaulting the tint to white (0xFFFFFF), in case you would rather the inventory icon show vanilla iron's own gray. Most sets want a color, so the four-arg form is the one you will reach for.
Tools that mine, food that heals or bites, armor that wears your colors: the toolbelt is full. Next we hand the world an ear, sound effects, streaming music, and a jukebox record of your own, in Chapter 19, The Soundtrack.





