Documentation Index
Fetch the complete documentation index at: https://mintlify.com/toxicity188/BetterModel/llms.txt
Use this file to discover all available pages before exploring further.
The HitBox interface represents an interactive collision box attached to a bone. Hitboxes enable player interaction, damage detection, and entity mounting for models.
Package: kr.toxicity.model.api.nms
Type: Interface
Extends: Identifiable
Since: 1.15.2
Overview
Hitboxes are typically implemented using invisible entities (like Slimes or Interaction entities) that follow bone positions. They enable:
- Click/interact detection
- Damage/attack detection
- Entity mounting and riding
- Custom collision shapes per bone
Creation
Hitboxes are created through RenderedBone:
boolean createHitBox(
BaseEntity entity,
Predicate<RenderedBone> predicate,
HitBoxListener listener
)
Example:
// Create hitbox with listener
HitBoxListener listener = HitBoxListener.builder()
.interact(event -> {
PlatformPlayer player = event.player();
player.sendMessage("You clicked: " + event.getHitBox().groupName());
})
.damage(event -> {
player.sendMessage("Hit for " + event.damage() + " damage!");
event.setCancelled(false); // Allow damage
})
.build();
// Apply to bones with hitbox tag
renderedBone.createHitBox(
entity,
BonePredicate.tag(BoneTag.of("hitbox")),
listener
);
Identification
BoneName groupName()
RenderedBone positionSource()
PlatformEntity source()
Get information about the hitbox’s associated bone and entity.
Example:
HitBox hitBox = bone.getHitBox();
if (hitBox != null) {
System.out.println("Hitbox for: " + hitBox.groupName());
System.out.println("Attached to: " + hitBox.source().name());
System.out.println("Position bone: " + hitBox.positionSource().name());
}
Interaction Events
Internal Trigger Methods
These methods are called internally by the NMS implementation:
@ApiStatus.Internal
void triggerInteract(PlatformPlayer player, ModelInteractionHand hand)
@ApiStatus.Internal
void triggerInteractAt(PlatformPlayer player, ModelInteractionHand hand, Vector3f position)
Note: These are invoked automatically when players interact with the hitbox entity. Use HitBoxListener to handle these events.
Visibility Control
@ApiStatus.Internal
void hide(PlatformPlayer player)
@ApiStatus.Internal
void show(PlatformPlayer player)
Control hitbox visibility per player. These methods manage packet sending for the underlying entity.
Vector3f relativePosition()
Returns the hitbox position relative to its source entity.
Example:
Vector3f relPos = hitBox.relativePosition();
System.out.println("Hitbox offset: " + relPos);
// Get world position from bone
Vector3f worldPos = hitBox.positionSource().worldPosition();
Mounting System
Mount Controller
MountController mountController()
void mountController(MountController controller)
Get or set the mount controller that defines mounting behavior.
Example:
// Create custom mount controller
MountController controller = MountController.builder()
.canControl(true)
.speed(0.2f)
.jumpStrength(0.5f)
.build();
hitBox.mountController(controller);
Mounting Entities
void mount(PlatformEntity entity)
void dismount(PlatformEntity entity)
void dismountAll()
boolean hasMountDriver()
boolean hasBeenControlled()
boolean forceDismount()
boolean onWalk()
Manage entities mounted on this hitbox.
Example:
// Mount player on hitbox
HitBoxListener listener = HitBoxListener.builder()
.interact(event -> {
PlatformPlayer player = event.player();
HitBox hitBox = event.getHitBox();
if (!hitBox.hasMountDriver()) {
hitBox.mount(player.entity());
player.sendMessage("You are now riding!");
} else {
hitBox.dismount(player.entity());
player.sendMessage("You dismounted.");
}
})
.mount((hitBox, entity) -> {
entity.sendMessage("Mounted successfully!");
})
.dismount((hitBox, entity) -> {
entity.sendMessage("Dismounted!");
})
.build();
// Check mount status
if (hitBox.hasBeenControlled()) {
System.out.println("Hitbox is being controlled");
}
if (hitBox.onWalk()) {
System.out.println("Rider is walking");
}
Event Handling
HitBoxListener listener()
Returns the listener attached to this hitbox.
Example:
HitBoxListener currentListener = hitBox.listener();
// Extend existing listener
HitBoxListener newListener = currentListener.toBuilder()
.damage(event -> {
System.out.println("Additional damage handler");
})
.build();
Registry Access
Optional<EntityTrackerRegistry> registry()
Returns the entity tracker registry for the source entity, if available.
Example:
hitBox.registry().ifPresent(registry -> {
System.out.println("Tracker: " + registry.tracker());
System.out.println("Entity: " + registry.entity());
});
Lifecycle
Safely removes the hitbox and cleans up associated resources.
Example:
HitBoxListener listener = HitBoxListener.builder()
.remove(hitBox -> {
System.out.println("Hitbox removed: " + hitBox.groupName());
// Cleanup custom data
})
.build();
bone.createHitBox(entity, predicate, listener);
// Later...
if (bone.getHitBox() != null) {
bone.getHitBox().removeHitBox();
}
Complete Example: Interactive Door
public void createInteractiveDoor(RenderedBone doorBone, BaseEntity entity) {
// Track door state
AtomicBoolean isOpen = new AtomicBoolean(false);
HitBoxListener doorListener = HitBoxListener.builder()
.interact(event -> {
HitBox hitBox = event.getHitBox();
PlatformPlayer player = event.player();
// Toggle door state
boolean open = isOpen.getAndSet(!isOpen.get());
if (open) {
player.sendMessage("Door closed");
// Play close animation
hitBox.positionSource().stopAnimation(
BonePredicate.TRUE,
"door_open",
null
);
} else {
player.sendMessage("Door opened");
// Play open animation
// hitBox.positionSource().addAnimation(...)
}
})
.create(hitBox -> {
System.out.println("Door hitbox created: " + hitBox.groupName());
})
.remove(hitBox -> {
System.out.println("Door hitbox removed");
})
.build();
// Create hitbox for door bones
doorBone.createHitBox(
entity,
BonePredicate.name("door").withChildren(),
doorListener
);
}
Complete Example: Rideable Mount
public void createRideableMount(RenderedBone seatBone, BaseEntity entity) {
// Configure mount controller
MountController mountController = MountController.builder()
.canControl(true)
.speed(0.25f)
.jumpStrength(0.6f)
.build();
HitBoxListener mountListener = HitBoxListener.builder()
.interact(event -> {
PlatformPlayer player = event.player();
HitBox hitBox = event.getHitBox();
if (player.isSneaking()) {
// Dismount
if (hitBox.hasMountDriver()) {
hitBox.dismount(player.entity());
}
} else {
// Mount
if (!hitBox.hasMountDriver()) {
hitBox.mount(player.entity());
}
}
})
.mount((hitBox, rider) -> {
if (rider instanceof PlatformPlayer player) {
player.sendMessage("Press SHIFT to dismount");
}
})
.dismount((hitBox, rider) -> {
if (rider instanceof PlatformPlayer player) {
player.sendMessage("You dismounted");
}
})
.sync(hitBox -> {
// Update every tick while mounted
if (hitBox.onWalk()) {
// Trigger walk animation
}
})
.build();
seatBone.createHitBox(
entity,
BonePredicate.name("seat"),
mountListener
);
// Set controller after creation
HitBox hitBox = seatBone.getHitBox();
if (hitBox != null) {
hitBox.mountController(mountController);
}
}
See Also