Slot Modifiers
A tutorial on how to add and remove slots from the curios inventory on entities.
Overview
Slot modifiers are a way to dynamically add and/or remove slots from entities the same way developers could add and/or
remove health or attack damage. In fact, it uses the exact same AttributeModifier
system to accomplish this.
Getting Started
The main way to interact with and add slot modifiers is through the top.theillusivec4.curios.api.type.capability.ICuriosItemHandler
interface. A developer can grab the specific instance of this on the entity by following the steps from the inventory
guide to query the capability from Curios.
Adding or Removing Slots
Adding slots can be done through ICuriosItemHandler#addTransientSlotModifiers(Multimap<String, AttributeModifier>)
and
ICuriosItemHandler#addPermanentSlotModifiers(Multimap<String, AttributeModifier>)
.
A transient slot modifier is a slot modifier that is not serialized to the player data, while a permanent slot modifier is serialized. The former will disappear upon players relogging into a world and the latter will always remain until manually removed. Transient slot modifiers are often used for effects that may not always be present and need to be verified at certain times while permanent slot modifiers are often used as rewards or other effects that are not meant to be removed often, if ever.
For slot types that should not appear until given by a relevant slot modifier, be sure to set the default amount of slots to 0 so that it does not give any slots by default.
An example of adding a transient slot modifier:
CuriosApi.getCuriosInventory(livingEntity).ifPresent(inventory -> {
inventory.addTransientSlotModifier("ring", uuid, "name", 2, AttributeModifier.Operation.ADDITION));
});
This will add 2 slots to the ring
slot type, with the specified name
and uuid
(UUID is not provided so developers
will need to generate or substitute one). Note that each key for the map must be a valid SlotType
identifier, such as
"ring"
or "necklace"
.
In the case of adding multiple slot modifiers at once:
CuriosApi.getCuriosInventory(livingEntity).ifPresent(inventory -> {
Map<String, AttributeModifier> map = LinkedHashMultimap.create();
map.put("ring", new AttributeModifier(uuid, "name", 2, AttributeModifier.Operation.ADDITION));
map.put("necklace", new AttributeModifier(uuid, "name", 1, AttributeModifier.Operation.ADDITION));
inventory.addTransientSlotModifiers(map);
});
If slots need to be removed, this is as simple as stating a negative amount for an AttributeModifier.Operation.ADDITION
operation in the AttributeModifier
:
inventory.addTransientSlotModifier("ring", uuid, "name", -2, AttributeModifier.Operation.ADDITION));
Instead of adding 2 slots, this will remove 2 slots.
If the total slot amount results in a number less than 0, the slots will remain at 0 but the slot modifiers will remain and existing slot modifiers will stack with each other. If a slot modifier removes 2 slots and the base amount is already 0, there will seemingly be no effect. However, if another slot modifier adds 3 slots on top of that, it will add to the existing -2 and result in a total of 1 slot.
Slot modifiers can only add slots for slot types that are already assigned to entities. If a slot type exists but is not assigned to the specified entity, then the slot modifier will not be able to add any slots of that slot type. Be sure to go over how to assign slots to entities if slot modifiers do not appear to be adding any slots.
Removing Slot Modifiers
Removing slot modifiers can be done through ICuriosItemHandler#removeSlotModifiers(Multimap<String, AttributeModifier>)
.
This follows very similar logic as the preceding section on adding slot modifiers:
CuriosApi.getCuriosInventory(livingEntity).ifPresent(inventory -> {
inventory.removeSlotModifier("ring", uuid);
})
This will remove the slot modifier with UUID uuid
from the ring
slot type.
In the case of removing multiple slot modifiers at once:
CuriosApi.getCuriosInventory(livingEntity).ifPresent(inventory -> {
Map<String, AttributeModifier> map = LinkedHashMultimap.create();
map.put("ring", new AttributeModifier(uuid, "name", 2, AttributeModifier.Operation.ADDITION));
inventory.removeSlotModifiers(map);
})
The AttributeModifier
used is mostly filler, the amount and operation do not matter. The key and the uuid
, however,
must match the ones used for the slot modifier being removed.
Items
The methods above are helpful for universal applications, but there are more streamlined methods for developers that want to attach slot modifiers to specific items so that they are only applied when those items are equipped and then removed when unequipped.
Interfaces
Both the ICurio
and ICurioItem
interfaces expose a method called getAttributeModifiers
that returns a
Multimap<Attribute, AttributeModifier>
denoting attributes and their modifiers, in the same way that items themselves
have a similar method when equipped in vanilla slots. For more information on attaching these interfaces to items, see
the curio creation page.
- ICurio
- ICurioItem
@Override
public Multimap<Attribute, AttributeModifier> getAttributeModifiers(SlotContext slotContext, UUID uuid) {
Multimap<Attribute, AttributeModifier> map = LinkedHashMultimap.create();
// Add attribute modifiers
return map;
}
@Override
public Multimap<Attribute, AttributeModifier> getAttributeModifiers(SlotContext slotContext, UUID uuid, ItemStack stack) {
Multimap<Attribute, AttributeModifier> map = LinkedHashMultimap.create();
// Add attribute modifiers
return map;
}
In order to add slot modifiers specifically, developers can leverage the
CuriosApi#addSlotModifier(Multimap, String, UUID, double, AttributeModifier.Operation)
method:
- ICurio
- ICurioItem
@Override
public Multimap<Attribute, AttributeModifier> getAttributeModifiers(SlotContext slotContext, UUID uuid) {
Multimap<Attribute, AttributeModifier> map = LinkedHashMultimap.create();
CuriosApi.addSlotModifier(map, "ring", uuid, 2, AttributeModifier.Operation.ADDITION);
return map;
}
@Override
public Multimap<Attribute, AttributeModifier> getAttributeModifiers(SlotContext slotContext, UUID uuid, ItemStack stack) {
Multimap<Attribute, AttributeModifier> map = LinkedHashMultimap.create();
CuriosApi.addSlotModifier(map, "ring", uuid, 2, AttributeModifier.Operation.ADDITION);
return map;
}
This will add 2 slots of the ring
slot type using the passed in uuid
from the second parameter of the method. The
slots will only be added when this item is equipped and those slots will be removed when this item is unequipped.
Although the uuid
in the preceding section can be decided on a case-by-case basis, the uuid
in this section related
to items is strongly encouraged to be fed from the second parameter of the getAttributeModifiers
method. This is
because the UUID in the parameter is guaranteed to be slot-specific, which avoids any collision issues in case multiple
instances of the same item are equipped.
NBT Tag
The previous method works for items that developers can directly register or implement interfaces or capabilities for, but this can fall short if developers want to add these modifiers dynamically or even override previously registered behavior.
As an alternative, developers can directly add slot modifiers to item NBT tags as well using CuriosApi#addSlotModifier(ItemStack, String, String, UUID, double, AttributeModifier.Operation, String)
:
CuriosApi.addSlotModifier(stack, "ring", "name", uuid, 2, AttributeModifier.Operation.ADDITION, "necklace");
There are two slot identifiers in this method. The first one, "ring"
above, denotes the slot type that the slot modifier
gives or removes slots from. The second one, "necklace"
above, denotes the slot type that the slot modifier becomes
active in. In other words, the above example will give 2 ring
slots when the item is equipped in a necklace
slot.
It's usually best to use null
in place of the uuid
, to avoid collisions as described in the preceding caution note,
unless there is justification for providing a static UUID. Providing a null
UUID will allow Curios to provide a
slot-specific UUID in its place.