Coding an entity
In this section, we’ll walk through creating a custom Capybara entity, just like the one in the Bestium Example plugin.
Choose the right base class
Section titled “Choose the right base class”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 capybarasCustomMonster
— 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.
Create the entity class
Section titled “Create the entity class”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:
package com.example.myplugin
import cz.jeme.bestium.api.entity.CustomAnimalimport net.minecraft.server.level.ServerLevelimport net.minecraft.world.entity.AgeableMobimport net.minecraft.world.entity.EntityTypeimport net.minecraft.world.item.ItemStackimport 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") }}
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;
public class Capybara extends CustomAnimal { public Capybara(EntityType<? extends Capybara> entityType, Level level) { super(entityType, level); }
@Override public boolean isFood(ItemStack itemStack) { return false; }
@Override public AgeableMob getBreedOffspring(ServerLevel serverLevel, AgeableMob ageableMob) { return null; }}
Add default attributes
Section titled “Add default attributes”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 animalscreateMonsterAttributes()
for monsters
etc.
companion object { fun createAttributes() = createAnimalAttributes() .add(Attributes.MAX_HEALTH, 10.0) .add(Attributes.MOVEMENT_SPEED, .25)}
public static AttributeSupplier.Builder createAttributes() { return createAnimalAttributes() .add(Attributes.MAX_HEALTH, 10) .add(Attributes.MOVEMENT_SPEED, 0.3);}
Add goals
Section titled “Add goals”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)
goalSelector.addGoal(PRIORITY, GOAL);
PRIORITY
: The importance of this goal over others, lower numbers run firstGOAL
: An instance of an existing goal (check thenet.minecraft.world.entity.ai.goal
package) or your own custom goal
Here’s an example for our capybara:
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))}
@Overrideprotected void registerGoals() { goalSelector.addGoal(0, new FloatGoal(this)); goalSelector.addGoal(1, new PanicGoal(this, 1.25)); goalSelector.addGoal(2, new FollowParentGoal(this, 1.1)); goalSelector.addGoal(3, new WaterAvoidingRandomStrollGoal(this, 1.0)); goalSelector.addGoal(4, new LookAtPlayerGoal(this, Player.class, 6F)); goalSelector.addGoal(5, new RandomLookAroundGoal(this));}
Finishing touches
Section titled “Finishing touches”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:
package com.example.myplugin
import cz.jeme.bestium.api.entity.CustomAnimalimport net.minecraft.server.level.ServerLevelimport net.minecraft.world.entity.AgeableMobimport net.minecraft.world.entity.EntitySpawnReasonimport net.minecraft.world.entity.EntityTypeimport net.minecraft.world.entity.ai.attributes.AttributeSupplierimport net.minecraft.world.entity.ai.attributes.Attributesimport net.minecraft.world.entity.ai.goal.*import net.minecraft.world.entity.player.Playerimport net.minecraft.world.item.ItemStackimport net.minecraft.world.item.Itemsimport 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? }}
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.Item;import net.minecraft.world.item.ItemStack;import net.minecraft.world.item.Items;import net.minecraft.world.level.Level;
public class Capybara extends CustomAnimal { public static AttributeSupplier.Builder createAttributes() { return createAnimalAttributes() .add(Attributes.MAX_HEALTH, 10) .add(Attributes.MOVEMENT_SPEED, 0.3); }
@Override protected void registerGoals() { goalSelector.addGoal(0, new FloatGoal(this)); goalSelector.addGoal(1, new PanicGoal(this, 1.25)); goalSelector.addGoal(2, new FollowParentGoal(this, 1.1)); goalSelector.addGoal(3, new WaterAvoidingRandomStrollGoal(this, 1.0)); goalSelector.addGoal(4, new LookAtPlayerGoal(this, Player.class, 6.0f)); goalSelector.addGoal(5, new RandomLookAroundGoal(this)); }
protected Capybara(EntityType<? extends Capybara> entityType, Level level) { super(entityType, level); }
@Override public boolean isFood(ItemStack itemStack) { Item item = itemStack.getItem(); return item == Items.SEAGRASS || item == Items.MELON_SLICE; }
@Override public AgeableMob getBreedOffspring(ServerLevel level, AgeableMob otherParent) { return (Capybara) bestium_realType().create(level, EntitySpawnReason.BREEDING); }}