Skip to content

Coding an entity

In this section, we’ll walk through creating a custom Capybara entity, just like the one in the Bestium Example plugin.

Bestium provides a set of abstract base classes in the cz.jeme.bestium.api.entity package that simplify working with injectable entities.

Pick the one that best fits your entity:

  • CustomAnimal — for passive creatures like cows or capybaras
  • CustomMonster — for hostile mobs like zombies or skeletons

etc.

If you want to extend a concrete vanilla Minecraft class (like Zombie) or create something less conventional, consult the Javadoc for the Injectable interface.

Your class must

  • Extend the appropriate base class
  • Implement all abstract methods
  • Provide a public super-matching constructor

Your initial code should look something like this:

Capybara.kt
package com.example.myplugin
import cz.jeme.bestium.api.entity.CustomAnimal
import net.minecraft.server.level.ServerLevel
import net.minecraft.world.entity.AgeableMob
import net.minecraft.world.entity.EntityType
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.Level
open class Capybara(entityType: EntityType<out Capybara>, level: Level) : CustomAnimal(entityType, level) {
override fun isFood(p0: ItemStack): Boolean {
TODO("Not yet implemented")
}
override fun getBreedOffspring(
p0: ServerLevel,
p1: AgeableMob
): AgeableMob? {
TODO("Not yet implemented")
}
}

Your custom entity needs a set of default attributes, like health, speed, or attack damage, to function properly in the game. These are defined in a static method, typically named createAttributes(). Some attributes are required (like max health and movement speed), you will usually find out, because the server will crash 🙂.

Use the appropriate base method provided by your parent for your entity type:

  • createAnimalAttributes() for animals
  • createMonsterAttributes() for monsters

etc.

Capybara.kt
companion object {
fun createAttributes() = createAnimalAttributes()
.add(Attributes.MAX_HEALTH, 10.0)
.add(Attributes.MOVEMENT_SPEED, .25)
}

Goals power your entity’s AI, how it moves, reacts, or interacts with the world. To add goals, override the registerGoals() method in your custom entity. Use:

goalSelector.addGoal(PRIORITY, GOAL)
  • PRIORITY: The importance of this goal over others, lower numbers run first
  • GOAL: An instance of an existing goal (check the net.minecraft.world.entity.ai.goal package) or your own custom goal

Here’s an example for our capybara:

Capybara.kt
override fun registerGoals() {
goalSelector.addGoal(0, FloatGoal(this))
goalSelector.addGoal(1, PanicGoal(this, 1.25))
goalSelector.addGoal(2, FollowParentGoal(this, 1.1))
goalSelector.addGoal(3, WaterAvoidingRandomStrollGoal(this, 1.0))
goalSelector.addGoal(4, LookAtPlayerGoal(this, Player::class.java, 6.0F))
goalSelector.addGoal(5, RandomLookAroundGoal(this))
}

Now, complete your entity class by implementing the required methods:

  • isFood(ItemStack) defines what items your entity considers food.
  • getBreedOffspring() handles breeding behavior and returns the baby entity.

Your finished class will look something like this:

Capybara.kt
package com.example.myplugin
import cz.jeme.bestium.api.entity.CustomAnimal
import net.minecraft.server.level.ServerLevel
import net.minecraft.world.entity.AgeableMob
import net.minecraft.world.entity.EntitySpawnReason
import net.minecraft.world.entity.EntityType
import net.minecraft.world.entity.ai.attributes.AttributeSupplier
import net.minecraft.world.entity.ai.attributes.Attributes
import net.minecraft.world.entity.ai.goal.*
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.minecraft.world.level.Level
open class Capybara(entityType: EntityType<out Capybara>, level: Level) : CustomAnimal(entityType, level) {
companion object {
fun createAttributes(): AttributeSupplier.Builder {
return createAnimalAttributes()
.add(Attributes.MAX_HEALTH, 10.0)
.add(Attributes.MOVEMENT_SPEED, 0.3)
}
}
override fun registerGoals() {
goalSelector.addGoal(0, FloatGoal(this))
goalSelector.addGoal(1, PanicGoal(this, 1.25))
goalSelector.addGoal(2, FollowParentGoal(this, 1.1))
goalSelector.addGoal(3, WaterAvoidingRandomStrollGoal(this, 1.0))
goalSelector.addGoal(4, LookAtPlayerGoal(this, Player::class.java, 6.0f))
goalSelector.addGoal(5, RandomLookAroundGoal(this))
}
override fun isFood(itemStack: ItemStack) = when (itemStack.item) {
Items.SEAGRASS, Items.MELON_SLICE -> true
else -> false
}
override fun getBreedOffspring(level: ServerLevel, otherParent: AgeableMob): AgeableMob? {
return bestium_realType().create(level, EntitySpawnReason.BREEDING) as Capybara?
}
}