The layout system in NER is built around the RecipeCategory<T> class. It translates raw recipe data into a structured format that the GUI can render.
By defining a category, you can map inputs and outputs to specific inventory slots, display custom textures from AbyssalLib, append probability tooltips, and create multi-stage or paginated recipe sequences.
1. Creating a Category
To define a custom layout, create a class that extends RecipeCategory<T> where T is your custom recipe class. You must implement four abstract methods.
import com.github.darksoulq.abyssallib.server.resource.asset.Font;
import com.github.darksoulq.ner.layout.RecipeCategory;
import com.github.darksoulq.ner.model.ParsedRecipeView;
import com.github.darksoulq.ner.model.RecipeStage;
import com.github.darksoulq.ner.model.PagedSection;
import com.github.darksoulq.ner.model.SectionButton;
import org.bukkit.inventory.ItemStack;
import java.util.Set;
import java.util.List;
public class MyCustomCategory extends RecipeCategory<MyRecipeClass> {
@Override
public Class<MyRecipeClass> getRecipeClass() {
return MyRecipeClass.class;
}
@Override
public ParsedRecipeView parseRecipe(MyRecipeClass recipe, ItemStack catalyst) {
// Assume 'MyTextures.CUSTOM_BG' is a Font.TextureGlyph you registered
ParsedRecipeView.Builder builder = ParsedRecipeView.builder(MyTextures.CUSTOM_BG, -8, catalyst);
// Setting a static item
builder.set(20, recipe.getInput());
// Setting an animated list of items or Bukkit RecipeChoice
builder.setChoice(21, recipe.getBukkitChoice());
builder.set(24, recipe.getResult());
// Adding probability lore to an item
builder.probability(recipe.getResult(), "50%");
// Adding a paginated section for multiple outputs
builder.addSection(new PagedSection(
new int[]{10, 11, 12},
recipe.getPossibleExtraOutputs(),
new SectionButton(9, null), // Pass an ItemStack, or null for default arrow
new SectionButton(13, null)
));
return builder.build();
}
@Override
public Set<Integer> getResultSlots() {
// Used by CraftabilityCheckers to ignore these slots when checking (and manager to detect whether its a USE of the item or its RECIPE)
return Set.of(24, 10, 11, 12);
}
@Override
public Set<Integer> getIgnoredSlots() {
// Used for decorative items like XP icons that aren't inputs or outputs
return Set.of();
}
}
2. The Builder Methods
The ParsedRecipeView.Builder (and its internal RecipeStage.Builder) provide several ways to parse your items securely.
Basic Slots and Choices
set(int slot, ItemStack item) — Sets a single item.
set(int slot, List<ItemStack> items) — Sets multiple items in one slot. The GUI will automatically animate and cycle through them.
setChoice(int slot, RecipeChoice choice) — Automatically unpacks a Bukkit RecipeChoice.MaterialChoice or RecipeChoice.ExactChoice into an animated list.
Probabilities
Use .probability() to automatically append a formatted Chance: X% string to the bottom of the item's lore when viewed in the GUI.
If your recipe produces or accepts a large list of items, use PagedSection. It takes:
int[] slots: The GUI slots to populate.
List<ItemStack> items: The massive list of items to display.
SectionButton prevButton/SectionButton nextButton: Defined using new SectionButton(slot, ItemStack).
NER will automatically paginate the list across your defined slots and make the buttons functional.
Multi-Stage Recipes
If your recipe is a complex, multi-step process, you can append additional sequential stages using RecipeStage. The ParsedRecipeView.Builder acts as the primary stage, but you can append more via .addStage().