Repository navigation
Items
All item interfaces are located in:
org.carpentry.citrine.api.item
Citrine provides the following item interfaces:
| Interface | Description |
|---|---|
ColoredStackItem |
Applies custom colors to item names. |
CustomKillSourceItem |
Provides a custom damage source for kills. |
CustomHeldPoseItem |
Defines custom arm poses while holding an item. |
ItemWithSkins |
Adds support for multiple item skins. |
OnCritItem |
Executes code on a critical hit. |
OnHitSoundItem |
Defines custom sounds played on entity hit. |
OnKillItem |
Executes code when the upon killing an entity. |
OnUserDeathItem |
Executes code when the holder dies. |
ShieldBreakerItem |
Defines how long shields are disabled. |
UseWhileSprintingItem |
Allows an item to be used while sprinting. |
VaryingModelItem |
Allows an item to use multiple models depending on context. |
Implement the needed interface in your item class:
public class MyItem extends Item implements ShieldBreakerItem {
public MyItem(Settings settings) {
super(settings);
}
@Override
public int getShieldCooldown() {
return 40;
}
}In this example, the item is able to break through shields, setting the cooldown for 40 ticks (2 seconds).
ColoredStackItem allows an item to have a custom name color.
The interface provides a default implementation of getColoredName(), which generates the item's translated name and applies the color returned by getColor().
Methods
getColor(ItemStack stack)
Returns the Color used to color the item's name.
getColoredName(ItemStack stack)
Returns a Text instance containing the item's translated name with the custom color applied.
This method has a default implementation and does not need to be overridden unless custom name formatting is required.
Example
public class ColoredItem extends Item implements ColoredStackItem {
public ColoredItem(Settings settings) {
super(settings);
}
@Override
public Color getColor(ItemStack stack) {
return new Color(255, 79, 0);
}
}CustomHeldPoseItem allows an item to define its own arm pose when held by a player.
The returned pose uses Minecraft's BipedEntityModel.ArmPose enum.
Methods
getArmPose(ItemStack stack, PlayerEntity player)
Returns the arm pose that should be used while holding the item.
Example
public class CustomPoseItem extends Item implements CustomHeldPoseItem {
public CustomPoseItem(Settings settings) {
super(settings);
}
@Override
public BipedEntityModel.ArmPose getArmPose(ItemStack stack, PlayerEntity player) {
if (player.isSneaking()) {
return BipedEntityModel.ArmPose.SPYGLASS;
}
return BipedEntityModel.ArmPose.ITEM;
}
}CustomKillSourceItem allows an item to provide a custom DamageSource for kills.
This can be used to customize how kills are attributed to an item.
Methods
getKillSource(PlayerEntity player, ItemStack stack)
Returns the DamageSource associated with the item.
Example
public class CustomSourceItem extends Item implements CustomKillSourceItem {
public CustomSourceItem(Settings settings) {
super(settings);
}
@Override
public DamageSource getKillSource(PlayerEntity player, ItemStack stack) {
return player.getDamageSources().playerAttack(player);
}
}ItemWithSkins provides support for multiple skins on a single item.
Each skin is represented by an Identifier, allowing developers to associate different textures or models with the same item.
The currently selected skin is stored in the item's NBT data under the "skin" key.
Methods
getSkins()
Returns a List<Identifier> containing every skin available to the item.
The order of the identifiers determines their respective skin indices.
getCurrentSkin(NbtCompound nbt)
Returns the currently selected skin index.
If the "skin" key does not exist, this method returns -1, indicating that no custom skin is selected.
This method has a default implementation.
setSkin(NbtCompound nbt, int skin)
Sets the current skin index in the provided NBT compound.
This method also has a default implementation.
Example
public class SkinsItem extends Item implements ItemWithSkins {
public SkinsItem(Settings settings) {
super(settings);
}
@Override
public List<Identifier> getSkins() {
return List.of(
new Identifier("citrine", "scythe_skin"),
new Identifier("minecraft", "stick"),
new Identifier("minecraft", "blaze_rod")
);
}
public void cycleSkin(ItemStack stack) {
NbtCompound nbt = stack.getOrCreateNbt();
int next = getCurrentSkin(nbt) + 1;
if (next >= getSkins().size()) {
nbt.remove("skin");
if (nbt.isEmpty()) {
stack.setNbt(null);
}
} else {
setSkin(nbt, next);
}
}
}In this example, the item has three available skins. Calling cycleSkin() advances to the next skin, returning to the default appearance once the last skin has been reached.
Important: Skin indices correspond to positions in the list returned by getSkins(). Make sure the stored index is within the list's bounds before accessing it.
OnCritItem allows an item to execute custom code when its holder performs a critical hit.
Methods
onCrit(ItemStack stack, LivingEntity attacker, LivingEntity target)
Called when the implementing item scores a critical hit.
| Parameter | Description |
|---|---|
stack |
The item stack responsible for the critical hit. |
attacker |
The entity performing the attack. |
target |
The entity receiving the attack. |
Example
public class CustomCritItem extends Item implements OnCritItem {
public CustomCritItem(Settings settings) {
super(settings);
}
@Override
public void onCrit(ItemStack stack, LivingEntity attacker, LivingEntity target) {
if (attacker instanceof PlayerEntity player) {
player.sendMessage(Text.literal("Critical hit!"));
}
}
}OnHitSoundItem allows an item to specify the sound played when hitting an entity.
The interface also provides customizable volume and pitch values.
Methods
getHitSound(World world, ItemStack stack, LivingEntity attacker, LivingEntity target)
Returns the SoundEvent that should be played when the item hits an entity.
getPitch(ItemStack stack)
Returns the pitch multiplier of the hit sound.
Defaults to 1.0f.
getVolume(ItemStack stack)
Returns the volume multiplier of the hit sound.
Defaults to 1.0f.
Example
public class CustomSoundItem extends Item implements OnHitSoundItem {
public CustomSoundItem(Settings settings) {
super(settings);
}
@Override
public SoundEvent getHitSound(
World world,
ItemStack stack,
LivingEntity attacker,
LivingEntity target
) {
return SoundEvents.BLOCK_CHAIN_BREAK;
}
@Override
public float getPitch(ItemStack stack) {
return 1.0f;
}
@Override
public float getVolume(ItemStack stack) {
return 2.0f;
}
}OnKillItem allows an item to execute custom code when it kills an entity.
Unlike OnCritItem, this interface returns a boolean value, allowing the callback to influence the target's death handling.
Methods
onKill(ItemStack stack, LivingEntity attacker, LivingEntity target)
Called when the implementing item kills an entity.
| Parameter | Description |
|---|---|
stack |
The item stack responsible for the kill. |
attacker |
The entity performing the attack. |
target |
The entity being killed. |
The returned boolean determines whether the target's death should proceed or whether it should remain alive. Consult the implementation's event handler for the exact meaning of true and false.
Example
public class CustomKillItem extends Item implements OnKillItem {
public CustomKillItem(Settings settings) {
super(settings);
}
@Override
public boolean onKill(ItemStack stack, LivingEntity attacker, LivingEntity target) {
if (attacker instanceof PlayerEntity player) {
player.sendMessage(Text.literal("Entity killed!"));
}
return false;
}
}OnUserDeathItem allows an item to execute custom code when its holder dies.
This can be used to implement custom death-related effects or behavior.
Methods
onDeath(ItemStack stack, LivingEntity dead)
Called when the player holding the implementing item dies.
| Parameter | Description |
|---|---|
stack |
The item stack associated with the callback. |
dead |
The entity that has died. |
The returned boolean determines whether the holder's death should proceed or whether they should remain alive. Consult the implementation's event handler for the exact meaning of true and false.
Example
public class DeathEffectItem extends Item implements OnUserDeathItem {
public DeathEffectItem(Settings settings) {
super(settings);
}
@Override
public boolean onDeath(ItemStack stack, LivingEntity dead) {
if (dead instanceof PlayerEntity player) {
player.sendMessage(Text.literal("Your item has activated!"));
}
return false;
}
}ShieldBreakerItem allows an item to define how long an opponent's shield should be disabled for.
The returned duration is measured in ticks.
Methods
getShieldCooldown()
Returns the duration of the shield disable effect in ticks.
Example
public class ShieldBreakItem extends Item implements ShieldBreakerItem {
public ShieldBreakItem(Settings settings) {
super(settings);
}
@Override
public int getShieldCooldown() {
return 40;
}
}In this example, the item disables shields for 40 ticks (2 seconds).
UseWhileSprintingItem allows an item to define whether its holder can use it while sprinting.
Methods
canSprint(PlayerEntity user)
Returns whether the player should be allowed to use the item while sprinting.
Example
public class SprintItem extends Item implements UseWhileSprintingItem {
public SprintItem(Settings settings) {
super(settings);
}
@Override
public boolean canSprint(PlayerEntity user) {
return true;
}
}Returning true allows the item to be used while sprinting.
VaryingModelItem allows an item to use different models depending on its current context.
This includes the transformation mode, the entity holding the item, and whether the holder is performing certain actions.
All model utilities are located in:
org.carpentry.citrine.api.util.model
Methods
getModels()
Returns a list of every model identifier that the item may use.
All identifiers that can be returned by getModel() should be included in this list.
getModel(ItemStack stack, LivingEntity holder, ModelTransformationMode mode)
Determines which model should be used for the item.
| Parameter | Description |
|---|---|
stack |
The item stack being rendered. |
holder |
The entity currently holding the item. May be null. |
mode |
The current model transformation mode. |
Returns an Identifier corresponding to one of the models provided by getModels().
Returning null causes the item's normal model to be used instead.
Note: holder is null when the item is rendered in the GUI. Always account for this when accessing holder-specific information.
Example
public class ManyModelsItem extends Item implements VaryingModelItem {
private static final Identifier GUI =
new Identifier("citrine", "dev_gui");
private static final Identifier POINTING =
new Identifier("citrine", "dev_pointing");
public ManyModelsItem(Settings settings) {
super(settings);
}
@Override
public List<Identifier> getModels() {
return List.of(GUI, POINTING);
}
@Override
public Identifier getModel(
ItemStack stack,
@Nullable LivingEntity holder,
ModelTransformationMode mode
) {
Identifier id = ModelPresets.handheld(mode, GUI);
if (id != null) {
return id;
}
return ModelPresets.whileSneaking(holder, POINTING);
}
}In this example, the item uses a dedicated GUI model and switches to the pointing model whenever its holder is sneaking. Otherwise, it uses the default item model.
ModelPresets provides helper methods for common model-selection conditions.
These methods return a model identifier when their respective conditions are met, or null when they are not.
All methods are static and can be called directly.
Handheld
handheld(ModelTransformationMode mode, Identifier guiModel)
Returns the provided GUI model when the current transformation mode is either GUI or GROUND.
Otherwise, returns null.
Useful for providing a separate model for inventory displays and dropped item representations.
While Using
whileUsing(LivingEntity holder, ItemStack stack, Identifier usingModel)
Returns the provided model when the holder is actively using an item of the same item type.
Otherwise, returns null.
The holder may be null, in which case the default model is used.
While Sneaking
whileSneaking(LivingEntity holder, Identifier sneakModel)
Returns the provided model when the holder is sneaking.
Otherwise, returns null.
The holder may be null, in which case the default model is used.
Example
@Override
public Identifier getModel(
ItemStack stack,
@Nullable LivingEntity holder,
ModelTransformationMode mode
) {
Identifier id = ModelPresets.handheld(mode, GUI);
if (id != null) return id;
id = ModelPresets.whileUsing(holder, stack, BLOCKING);
if (id != null) return id;
return ModelPresets.whileSneaking(holder, POINTING);
}The order of these checks matters. The first matching preset determines which model is returned.
In this example:
- The GUI model takes priority when rendered in the GUI or on the ground.
- The blocking model takes priority while the item is being used.
- The pointing model is used while sneaking.
- If none of these conditions are met, the default model is used.
ModelUtils provides utility methods for checking the current transformation mode.
isGui(ModelTransformationMode mode)
Returns true when the transformation mode is either GUI or GROUND.
isFixed(ModelTransformationMode mode)
Returns true when the transformation mode is FIXED.
ItemWithSkins and VaryingModelItem can be implemented together to allow an item to have multiple skins, each with its own set of models.
This is particularly useful for items that change appearance depending on their current state while also allowing players to customize their appearance.
For example, an item could have:
- A default skin.
- A separate GUI model for each skin.
- A separate model while using the item.
- Different models for each additional skin.
Example
public class SkinnedItem extends Item implements ItemWithSkins, VaryingModelItem {
private static final Identifier GUI =
new Identifier("citrine", "dev_gui");
private static final Identifier POINTING =
new Identifier("citrine", "dev_pointing");
private static Identifier suffix(Identifier id, String suffix) {
return new Identifier(
id.getNamespace(),
id.getPath() + suffix
);
}
public SkinnedItem(Settings settings) {
super(settings);
}
@Override
public List<Identifier> getSkins() {
return List.of(
new Identifier("citrine", "dev_skin")
);
}
@Override
public List<Identifier> getModels() {
List<Identifier> models = new ArrayList<>(List.of(GUI, POINTING));
for (Identifier skin : getSkins()) {
models.add(skin);
models.add(suffix(skin, "_gui"));
models.add(suffix(skin, "_pointing"));
}
return models;
}
@Override
public Identifier getModel(
ItemStack stack,
@Nullable LivingEntity holder,
ModelTransformationMode mode
) {
NbtCompound nbt = stack.getNbt();
int skin = nbt == null ? -1 : getCurrentSkin(nbt);
List<Identifier> skins = getSkins();
boolean skinned = skin >= 0 && skin < skins.size();
Identifier base = skinned ? skins.get(skin) : null;
Identifier gui = skinned
? suffix(base, "_gui")
: GUI;
Identifier pointing = skinned
? suffix(base, "_pointing")
: POINTING;
Identifier id = ModelPresets.handheld(mode, gui);
if (id != null) return id;
id = ModelPresets.whileUsing(holder, stack, pointing);
if (id != null) return id;
return base;
}
}The example above selects models in the following order:
| Condition | Selected model |
|---|---|
| GUI or ground rendering | Selected skin's _gui model |
| Item is being used | Selected skin's _pointing model |
| No matching condition | Selected skin's base model |
| No custom skin selected | Default item models |
When no custom skin is selected, the item's original models are used instead.
Important: Every model identifier that can be returned by getModel() must also be included in getModels(). This includes the base models, GUI models, and any additional state-specific models.
- Item interfaces can be freely combined. An item can implement as many interfaces as necessary.
- Default interface methods do not need to be overridden unless their behavior needs to be customized.
- Model selection should account for
nullholders to avoid unexpectedNullPointerExceptions. - Skin indices are zero-based and correspond to the order of identifiers returned by
getSkins(). - When combining skins and varying models, ensure every possible model identifier is registered through
getModels().
For further information, refer to the individual interface implementations in Citrine's source code.