Loadout TC API - 1.0.1

API reference

LoadoutTcApi is a small, optional, client-side API. It lets another mod change the HUD look, add its own slots, animate a slot, show ability timers or place a second overlay next to the HUD. The HUD works with none of these calls.

API 4 Fabric 1.21.1 Client side 90 methods

Entry pointcom.toancao.loadouttc.api.LoadoutTcApi
TypesSlotView, SourceView, SlotSource, ApiSlot, SlotEffect, EffectContext, EffectFrame
SideClient only; never call from a dedicated server
VersionLoadoutTcApi.version() and apiVersion()

All methods are static members of LoadoutTcApi unless a row names another type. Setters save config/loadouttc.json immediately, so do not call them every frame.

Overview#

The API is organised in layers. Most integrations need one or two of them:

LayerUse it to
Slot sourcesContribute slots from your own inventory; they appear as a tab and draw like any other slot.
QueriesRead every slot, its item, durability, cooldown and ability state.
Selection and orderShow, hide, filter and sort slots; open the slot menu.
EffectsPlay a built-in animation, register a custom one or pin a featured slot.
Feedback layersStyle the durability, cooldown and ability feedback.
PanelsSplit slots across independent HUD panels and read their screen bounds.
Look and layoutPick the style, size, accent, arrangement and open the editor.

Common tasks#

TaskCall
Change the whole looksetStyle("hex"), setSize("2x")
Highlight a slot for 3 splayEffect(key, "pulse", 3000)
Show a skill timershowAbility(key, 4500)
Add your own slotsImplement SlotSource, then declare the entrypoint
Move slots to another panelsplitToCluster("Rings", keys)
Keep another overlay clear of the HUDhudBounds()

Getting started#

The API lives inside the mod jar; there is no separate library. Add the jar as a compile-only dependency:

dependencies {
    compileOnly files("libs/loadouttc-1.0.1+1.21.1.jar")
    // Fabric Loom projects can use modCompileOnly instead
}

Imports used in this reference:

import com.toancao.loadouttc.api.ApiSlot;
import com.toancao.loadouttc.api.EffectContext;
import com.toancao.loadouttc.api.EffectFrame;
import com.toancao.loadouttc.api.LoadoutTcApi;
import com.toancao.loadouttc.api.SlotEffect;
import com.toancao.loadouttc.api.SlotSource;

Guard new methods by contract version. It only goes up when a method changes meaning or is removed:

if (LoadoutTcApi.apiVersion() >= 4) {
    // SlotSource / ApiSlot, the extra SlotView fields and sourceViews() exist
    for (LoadoutTcApi.SourceView tab : LoadoutTcApi.sourceViews()) {
        LOG.info("{}: {}", tab.id(), tab.label());
    }
}
MethodReturnsDetails
apiVersion()intAPI contract version, currently 4. Cheap; safe in an entrypoint.
version()StringFabric version string such as 1.0.1+1.21.1; "unknown" only if the mod container is missing.

Optional dependency. If Loadout TC may be absent, check FabricLoader.getInstance().isModLoaded("loadouttc") and keep every API call behind that check, preferably in its own class so the API types are never loaded without the mod.

Slot keys#

Every slot has a stable key. The group segment is omitted when the group and the slot type share a name:

source/slot/index            when group and slot share a name
source/group/slot/index      otherwise
KeySource / group / slot / index
accessories/ring/0accessories / ring / ring / 0
trinkets/chest/necklace/0trinkets / chest / necklace / 0
curios/ring/1curios / ring / ring / 1
vanilla/armor/helmet/0vanilla / armor / helmet / 0
vanilla/offhand/0vanilla / offhand / offhand / 0
vanilla/hotbar/3vanilla / hotbar / hotbar / 3
vanilla/inventory/14vanilla / inventory / inventory / 14
mymod/charm/0your source / charm / charm / 0

Indices start at 0. Keys never change while the game runs, so they are safe to persist, but a removed source makes its keys disappear.

MethodReturnsDetails
slotKeys()List<String>Keys the HUD is drawing right now, in display order. Filtered and sorted.
slots()List<LoadoutTcApi.SlotView>Every known slot, including hidden and empty ones.
visibleSlots()List<LoadoutTcApi.SlotView>Exactly the drawn slots, following each panel's own sort order and slot cap.
slotBounds(String slotKey)int[]x, y, w, h in GUI pixels, or all zeroes when the slot is not drawn.

SlotView and SourceView#

LoadoutTcApi.SlotView is an immutable snapshot of one slot, including its live state at the moment of the call.

FieldMeaning
keyStable slot key, see Slot keys.
source / groupProvider id (for example accessories) and its sub-group (ring).
labelHeading shown for the group, for example Ring.
indexPosition inside that slot type.
itemIdRegistry id such as minecraft:diamond_sword; empty for an empty slot.
emptytrue when the slot has no stack.
visibleWhether the panel that owns the slot is drawing it, using that panel's filter, sort and cap.
stack / countThe live ItemStack and its stack size (0 when empty).
damage / maxDamageDurability values; both 0 for items that cannot take damage.
cooldownRemainingVanilla cooldown fraction: 0 ready, 1 just started.
abilityRemainingAbility timer fraction, 0..1.
abilitySecondsRemaining seconds of the timer started with showAbility; 0 when idle.

Helpers: damageable() is maxDamage > 0, and onCooldown() is cooldownRemaining > 0.

LoadoutTcApi.SourceView is a record of id and label, returned in the order the slot menu shows its tabs. Use it to build a matching tab bar.

Slot sources#

A source contributes slots to the HUD. It gets a tab in the slot menu, and its keys use your id as the prefix. Implement SlotSource with only API types; no internal class is needed.

public final class MySource implements SlotSource {
    public String id() { return "mymod"; }
    public String label() { return "My Mod"; }
    public boolean available() { return true; }

    public void collect(Minecraft minecraft, Player player, boolean includeEmpty,
                        List<ApiSlot> out) {
        ItemStack stack = myEquippedStack(player);
        if (!stack.isEmpty() || includeEmpty) {
            out.add(new ApiSlot(id(), "charm", "Charm", "charm", 0, stack));
        }
    }
}

LoadoutTcApi.registerSource(new MySource());
MemberContract
id()Unique, lowercase. Becomes the key prefix (mymod/...) and the tab id.
label()Tab caption; defaults to id().
available()Return false while the other mod is absent; the tab is hidden and collect is not called.
collect(Minecraft, Player, boolean includeEmpty, List<ApiSlot> out)Fill out in display order. Emit empty stacks only when includeEmpty is true.

ApiSlot#

FieldMeaning
sourceMust equal id().
group / groupLabelSub-group inside the tab (for example ring) and the heading printed for it.
slotSlot type; repeat the group name when they are the same.
indexPosition inside that slot type, starting at 0.
stackItemStack.EMPTY for an empty slot; null becomes empty.

key(), empty() and itemId() are derived for you; key() follows the format in Slot keys.

Self-registering without any Loadout TC change#

Declare the loadouttc:slot_source entrypoint in your own fabric.mod.json and the HUD picks the source up at launch:

{
  "entrypoints": {
    "loadouttc:slot_source": ["com.example.mymod.MySlots"]
  }
}

The class needs a no-arg constructor and implements SlotSource. List one class per source. Registering the same id at runtime replaces the entrypoint version, so a mod can also call registerSource(...) later, for example after its own config loads. A throwing available() or collect() disables only that source and is logged; the rest of the HUD keeps working.

MethodReturnsDetails
registerSource(SlotSource source)booleanAdds or replaces a source; its tab appears immediately. false for a null or blank id.
registerSource(AccessorySource source)booleanLegacy overload. It exposes internal types that may change; prefer SlotSource.
unregisterSource(String id)booleanRemoves a source; false when the id was not registered.
sources()List<String>Installed source ids, vanilla first.
hasSource(String id)booleanWhether a source is installed/registered right now; this is not the enable switch.
sourceViews()List<LoadoutTcApi.SourceView>Id and display label of every source, in slot-menu tab order.
setSourceEnabled(String sourceId, boolean enabled)voidHides or shows a whole source in every panel without uninstalling it.
isSourceEnabled(String sourceId)booleanThe saved enable switch for that source.
setVanillaGroup(String group, boolean enabled)voidAccepts armor, offhand, hotbar or inventory; other names are ignored.

Slot selection and ordering#

These setters apply to every panel. Once a config has more than one panel, each panel keeps its own selection; the getters below return the shared defaults that new panels start from. Ticking a slot in the menu also claims it for the panel being edited.

MethodReturnsDetails
setEmptySlots(boolean enabled)voidDraw empty slots as ghost frames.
setSlotVisible(String slotKey, boolean visible)voidTick or untick one slot using the active filter mode.
isSlotVisible(String slotKey)booleanShared default selection for that key.
showOnlySlots(Collection<String> keys)voidSwitches to selected mode and keeps only these keys.
showAllSlots()voidResets to all mode.
setSlotFilter(String mode)voidall or selected. Selecting selected snapshots the slots visible at that moment; other values are ignored.
slotFilter()StringCurrent shared visibility model.
setSortMode(String mode)voiddefault, name, type, source or custom. custom seeds the manual order from provider order.
sortMode()StringCurrent shared sort mode.
setSlotOrder(List<String> keys)voidReplaces the manual order and switches to custom. Keys missing from the list are appended, so they never disappear.
moveSlot(String slotKey, int delta)voidMoves one slot in the manual order; -1 up, 1 down. Switches to custom when it actually moves.
openSlots()voidOpens the slot menu when a player is in game and no other screen is open.

Panels (clusters)#

A panel is one independent HUD: its own slots, position and optionally its own look. A config always has at least one, and the rest panel catches every slot no other panel claims, so one panel behaves like the classic single HUD.

String panel = LoadoutTcApi.splitToCluster("Rings", List.of("accessories/ring/0"));
LoadoutTcApi.assignSlotToCluster("accessories/ring/1", panel);
int[] rect = LoadoutTcApi.clusterBounds(panel);   // x, y, w, h on screen
int[] all  = LoadoutTcApi.hudBounds();            // union of every panel
MethodReturnsDetails
clusters()List<String>Ids of every panel, in draw order.
clusterOf(String slotKey)StringPanel currently drawing that slot, or an empty string.
clusterSlots(String clusterId)List<String>Slots explicitly owned by a panel; empty means it is the catch-all panel.
createCluster(String name)StringCreates an empty panel and returns its id.
splitToCluster(String name, Collection<String> slotKeys)StringCreates a panel and moves those slots into it, in one call.
assignSlotToCluster(String slotKey, String clusterId)voidMoves one slot. A blank or unknown id leaves it to the catch-all panel.
removeCluster(String clusterId)booleanDeletes the panel and switches its slots off, so deleting never hands them to another panel. The last panel cannot be removed. false when nothing changed.
mergeClusters(String sourceId, String destId, boolean takeSourceLook)booleanMoves all slots of sourceId into destId and removes the source; optionally the source look becomes the destination look. false for equal or unknown ids.
renameCluster(String clusterId, String name)booleanSets the panel label, truncated to 20 characters. false for an unknown id or blank name.
setClusterEnabled(String clusterId, boolean enabled)voidHides or shows one panel; its settings are kept.
clusterBounds(String clusterId)int[]x, y, w, h of one panel, or all zeroes when it is not drawn.
hudBounds()int[]Union of every panel, so another overlay can avoid the HUD.

Effects#

An effect is a per-frame transform of one slot icon. Built-ins cover the common cases; a mod can register its own.

Built-in ids#

IdEffect
spin2D wheel rotation, 360 deg/s (best for flat icons).
spin_3d3D coin rotation, 300 deg/s.
spin_fast2D rotation, 720 deg/s.
spin_3d_fast3D rotation, 720 deg/s.
bobGentle vertical float.
pulseScale and accent glow pulse.
shakeSmall jitter.
flashGlow blink.
rainbowHue-cycling glow.
hoverSmall lift and glow; used automatically on hover.
noneNo-op.
LoadoutTcApi.playEffect("accessories/ring/0", "spin", 3000);           // 3 seconds
LoadoutTcApi.playEffect("accessories/ring/0", "pulse", 0, 0xFFFF6B6B); // until cleared, red
LoadoutTcApi.clearEffect("accessories/ring/0");
LoadoutTcApi.clearAllEffects();

durationMillis <= 0 means "until cleared". color is ARGB; 0 uses the HUD accent. Both playEffect overloads return false for a blank slot key or an unknown effect id.

MethodReturnsDetails
playEffect(String slotKey, String effectId, long durationMillis, int color)booleanPlays one effect on one slot with an ARGB override.
playEffect(String slotKey, String effectId, long durationMillis)booleanSame, using the HUD accent.
clearEffect(String slotKey)voidStops the timed effect on that slot; per-slot settings stay.
clearAllEffects()voidStops every timed effect.
triggerFeaturedEffect(long durationMillis)booleanPlays the pinned effect on the featured slot for that long.
triggerFeaturedEffect()booleanSame with a 2000 ms duration.
setFeaturedSlot(String slotKey)voidPins the featured slot; an empty string clears it. Persisted as featuredSlot.
featuredSlot()StringCurrent pinned key, or an empty string.
setFeaturedEffect(String effectId)voidChanges the pinned effect. Unknown ids are ignored.
registerEffect(String id, SlotEffect effect)booleanRegisters or replaces a custom effect. false for a blank id or null effect.
unregisterEffect(String id)booleanRemoves a custom effect; false when it was not registered.
effects()Set<String>Every available effect id, including built-ins.
setSlotEffect(String slotKey, String effectId)voidPermanent per-slot effect. Unknown ids are ignored; "none" disables it. Glow effects use the slot's light channel.
clearSlotOverrides(String slotKey)voidDrops every per-slot override: effect, cooldown style and durability style.

Custom effects#

LoadoutTcApi.registerEffect("sparkle", context -> {
    float wave = 0.5f + 0.5f * (float) Math.sin(context.seconds() * 9.0f);
    return new EffectFrame(
            0.0f,                       // offsetX
            0.0f,                       // offsetY
            1.0f + wave * 0.06f,        // scale
            0.0f,                       // spinDegrees
            false,                      // threeD
            0xFFFFD166,                 // glowColor (ARGB)
            wave * 0.6f                 // glowStrength 0..1
    );
});

SlotEffect is a functional interface: EffectFrame apply(EffectContext context). It runs on the client render thread once per frame, so keep it cheap.

FieldMeaning
slotKey()Key of the slot being drawn.
stack()Its ItemStack.
seconds()Seconds since the effect started (or since launch for featured/hover).
progress()0..1 for timed effects, 1 for permanent effects.
featured()Whether this is the pinned/featured slot.
hovered()Whether the mouse is over it in the editor.
accentColor()Active accent, or the colour passed to playEffect.
FieldRangeMeaning
offsetX / offsetYGUI pixelsTranslates the icon.
scale> 0Uniform scale; values <= 0 become 1.
spinDegreesdegreesRotation, around the vertical axis when threeD is true.
threeDboolean3D coin spin instead of flat 2D spin.
glowColorARGB0 means no glow.
glowStrength0..1Clamped on construction.

Shortcuts: EffectFrame.NONE, EffectFrame.spin(degrees), EffectFrame.offset(x, y), EffectFrame.scale(s) and EffectFrame.glow(color, strength).

Feedback layers#

Durability, cooldown and ability each own an options object: colour, thickness, opacity, text format and motion. These setters change the global layer options; per-slot styles are set separately at the end of the table. Values are clamped to the stated range.

MethodReturnsDetails
setCooldownThickness(int pixels)voidCooldown stroke or bar height, 1..4.
setCooldownOpacity(int percent)voidCooldown layer opacity, 30..100.
setCooldownText(String kind)voidseconds, int or percent; other values are ignored.
setCooldownPulse(boolean enabled)voidBlink when the cooldown is nearly done.
setCooldownReverse(boolean enabled)voidDraw the arc, bar or frame from the opposite end.
setCooldownTrack(boolean enabled)voidFaint background track behind the fill.
setAbilityThickness(int pixels)voidAbility stroke or bar height, 1..4.
setAbilityOpacity(int percent)voidAbility layer opacity, 30..100.
setAbilityText(String kind)voidseconds, int or percent; other values are ignored.
setAbilityPulse(boolean enabled)voidBlink when the timer is nearly done.
setAbilityReverse(boolean enabled)voidDraw the timer from the opposite end.
setAbilityTrack(boolean enabled)voidFaint background track behind the timer.
setDurabilityThickness(int pixels)voidDurability bar, pips or ring stroke, 1..4.
setDurabilityOpacity(int percent)voidDurability layer opacity, 30..100.
setDurabilityTrack(boolean enabled)voidBackground track behind the durability fill.
setSlotCooldownStyle(String slotKey, String styleId)voidCooldown style for one slot: arc, text, bar, frame or off. Unknown ids fall back to arc.
setSlotDurabilityStyle(String slotKey, String styleId)voidDurability style for one slot: bar, segments, pips, text, ring, battery or off. Unknown ids fall back to bar.

Global shapes are picked with setCooldownStyle and setDurabilityStyle; per-slot effects with setSlotEffect.

Ability timers#

Vanilla exposes item cooldowns only. For skills, channels or buffs, push a timer yourself; the HUD draws a countdown in the configured style.

// 4.5 s cast on the ring slot; colour 0 uses the configured accent
LoadoutTcApi.showAbility("accessories/ring/0", 4500L);
LoadoutTcApi.showAbility("accessories/necklace/0", 4500L, 0xFF7C9EFF);
LoadoutTcApi.hideAbility("accessories/ring/0");
LoadoutTcApi.clearAbilities();
MethodReturnsDetails
showAbility(String slotKey, long durationMillis)voidStarts or restarts a timer using the accent colour.
showAbility(String slotKey, long durationMillis, int argb)voidSame with an explicit ARGB colour; 0 falls back to the accent.
hideAbility(String slotKey)voidStops the timer on one slot.
clearAbilities()voidStops every ability timer at once.
setAbilityStyle(String styleId)voidChooses the drawing style; unknown ids fall back to ring.
StyleLook
ringArc just outside the icon.
bar2 px strip on the slot's top edge.
textRemaining seconds over the icon.
frameProgress traced along the slot frame; follows hex, rounded or square shapes.
offHidden.

Look and feel#

Styles, size, accent and arrangement are global config values. Every setter here saves config/loadouttc.json immediately.

MethodReturnsDetails
styles()List<String>Every style id (31). The first ten are the editor gallery.
setStyle(String styleId)voidSwitches the whole HUD style. Unknown ids fall back to vanilla.
setSize(String size)voidauto, small, medium, large, xlarge, huge, giant or custom; other values are ignored.
setCustomScale(float scale)voidSwitches to custom and clamps to 0.5..3.0.
setDurabilityStyle(String styleId)voidGlobal durability shape; off also disables the layer. Unknown ids fall back to bar.
setCooldownStyle(String styleId)voidGlobal cooldown shape; off also disables the layer. Unknown ids fall back to arc.
cooldownStyles()List<String>Every id setCooldownStyle accepts.
setLayoutMode(String mode)voidvertical, horizontal, grid or circle; other values are ignored.
layoutMode()StringCurrent resolved arrangement.
setColumns(int columns)voidGrid columns, clamped to 1..8.
setCircleRadius(int radius)voidRing radius in GUI pixels, clamped to 16..200.
setAccent(int argb)voidSets the accent; the alpha channel is ignored and only RGB is stored.
setEnabled(boolean enabled)voidHides or shows the whole HUD; all settings are kept.
openEditor()voidOpens the editor when a player is in game and no other screen is open.
reload()voidRe-reads loadouttc.json, for example after another mod edited it.

Size presets#

auto is one HUD unit per GUI unit, so a slot matches a vanilla hotbar slot at every GUI scale. Larger presets exist because the vanilla size can read small on a big screen.

IdScale
auto1.00x of the GUI scale
small0.80x
medium1.00x
large1.25x
xlarge1.50x
huge2.00x
giant3.00x
custom0.50 - 3.00x

Style ids#

styles() returns all 31 ids. Ten are shown in the editor gallery; the rest still load from an existing config:

vanilla, steel, hex, liquid_glass, bare, fantasy_rpg, carbon, parchment, arcane, outline,
notion, glassmorphism, neumorphism, bento, dynamic_island, dock, material_expressive, terminal,
pixel_vanilla, minimal, chips, sticker, clay, neon, brutalist, editorial, frost, arcade,
grouped, compact, scifi_tactical

The first line is the gallery set, in menu order. bare, minimal, chips, sticker and outline draw no outer panel.

Threading and safety#

  • Call from the Minecraft client thread: tick, screen or render callbacks.
  • Never call from a dedicated server; there is no server-side HUD and no network traffic.
  • Effect maps are concurrent, but config setters write the config file. Batch changes instead of calling a setter every frame.
  • If Loadout TC may be absent, guard calls as described in Getting started.