Pixel Dream StudiosWiki

For mod developers

Adding Familiars Lib to a mod and making your own familiar.

Getting started

Add the Curse Maven snippet of the Familiars Lib version you want to your dependencies in build.gradle:

implementation "curse.maven:familiarslib-1316458:7415920"

Creating your own familiar

You need a few things before coding:

  • The familiar's model: a .geo.json file, exported from your GeckoLib Blockbench project.
  • The familiar's texture: saved from your Blockbench project.
  • Animations, saved as JSON:
    • idle: loop animation when idle
    • interact: plays when right-clicking the familiar
    • long_cast: plays when casting spells with a long cast time
    • skill: plays when casting instant or short spells
    • walk: loop animation when walking
    • spawn: plays when the familiar spawns
    • sleep: plays when sleeping in a familiar bed, and when stunned if the familiar has no sit animation
    • continuous: loop animation for continuous spells
    • sit (optional): loop animation when sitting or stunned. Only used if the familiar enables it (see Sitting and sleeping)

Put them here:

  • Model: resources/assets/<your_mod_id>/geo/
  • Texture: resources/assets/<your_mod_id>/textures/entity/
  • Animations: all of them (except the optional ones you don't use), with the exact names above, in one animation file in resources/assets/<your_mod_id>/animations/

Then create the entity class. Pick the base class that matches how your familiar moves:

Base classFor
AbstractTerrestrianSpellCastingPetWalking familiars. No fall damage, and a random stroll goal that respects sitting and houses
AbstractFlyingSpellCastingPetFlying casters, with flying navigation and hovering
AbstractFlyingMeleeSpellCastingPetFlying melee fighters with Vex-style movement. Its attack damage grows with its spell power. Pair it with FlyingSkirmisherAttackGoal

All of them extend AbstractSpellCastingPet, which holds everything else in this section (taming, colors, cosmetics, compendium...). A flying caster example:

public class ExampleFamiliarEntity extends AbstractFlyingSpellCastingPet {
    public static final ResourceLocation textureResource = ResourceLocation.fromNamespaceAndPath(YourMainClass.MODID, "textures/entity/familiar_texture.png");

    public ExampleFamiliarEntity(EntityType<? extends AbstractFlyingSpellCastingPet> pEntityType, Level pLevel) {
        super(pEntityType, pLevel);
    }

    @Override
    protected void registerGoals() {
        super.registerGoals();
        initializeAttackGoal(2);
        //Any other goal specific to this familiar
    }

    @Override
    protected WizardAttackGoal createAttackGoal(float min, float max) {
        //The spells the familiar uses. Spell tags are recommended for easier management and flexibility.
        List<AbstractSpell> attackSpells = HelperMethods.getSpellsFromTag(YourModTags.EXAMPLE_ATTACK_SPELLS);
        List<AbstractSpell> defenseSpells = HelperMethods.getSpellsFromTag(YourModTags.EXAMPLE_DEFENSE_SPELLS);
        List<AbstractSpell> movementSpells = HelperMethods.getSpellsFromTag(YourModTags.EXAMPLE_MOVEMENT_SPELLS);
        List<AbstractSpell> supportSpells = HelperMethods.getSpellsFromTag(YourModTags.EXAMPLE_SUPPORT_SPELLS);

        //Only return one of these goals
        //If it's a flying familiar
        return new FamiliarGoals.FlyingWizardAttackGoal(this, 1f, 30, 40)
                .setPreferredHeight(4.0f)
                .setCombatRanges(5.0f, 7.0f, 10.0f)
                .setThreatAwarenessRadius(10.0f)
                .setSpells(attackSpells, defenseSpells, movementSpells, supportSpells)
                .setSingleUseSpell(SpellRegistry.RAY_OF_FROST_SPELL.get(), 160, 300, 3, 5) //Optional signature spell
                .setIsFlying()
                .setAllowFleeing(true)
                .setSpellQuality(min, max);

        //If it's a terrestrian familiar
        return new WizardAttackGoal(this, 1.25f, 40, 50)
                .setSpells(attackSpells, defenseSpells, movementSpells, supportSpells)
                .setSingleUseSpell(SpellRegistry.ROOT_SPELL.get(), 340, 400, 8, 10) //Optional signature spell
                .setAllowFleeing(true)
                .setSpellQuality(min, max);
    }

    //Be careful: some attributes differ between terrestrian, melee and flying familiars
    public static AttributeSupplier.Builder prepareAttributes() {
        return LivingEntity.createLivingAttributes()
                .add(Attributes.ATTACK_DAMAGE, 3.0)
                .add(Attributes.ATTACK_KNOCKBACK, 0.0)
                .add(Attributes.MAX_HEALTH, 30.0)
                .add(Attributes.FOLLOW_RANGE, 24.0)
                .add(Attributes.FLYING_SPEED, .1F)
                .add(Attributes.MOVEMENT_SPEED, .2);
    }
}

The model class:

public class ExampleFamiliarModel extends DefaultedEntityGeoModel<ExampleFamiliarEntity> {
    public static final ResourceLocation MODEL = ResourceLocation.fromNamespaceAndPath(YourMainClass.MODID, "geo/familiar_model.geo.json");
    public static final ResourceLocation ANIMATIONS = ResourceLocation.fromNamespaceAndPath(YourMainClass.MODID, "animations/familiar_animations.animation.json");

    public ExampleFamiliarModel() {
        super(ResourceLocation.fromNamespaceAndPath(YourMainClass.MODID, "example_familiar"));
    }

    protected TransformStack transformStack = new TransformStack();

    @Override
    public ResourceLocation getModelResource(ExampleFamiliarEntity object) {
        return MODEL;
    }

    @Override
    public ResourceLocation getAnimationResource(ExampleFamiliarEntity animatable) {
        return ANIMATIONS;
    }

    @Override
    public void setCustomAnimations(ExampleFamiliarEntity entity, long instanceId, AnimationState<ExampleFamiliarEntity> animationState) {
        super.setCustomAnimations(entity, instanceId, animationState);

        if (Minecraft.getInstance().isPaused() || !entity.shouldBeExtraAnimated())
            return;

        transformStack.popStack();
    }
}

The renderer class. It uses culled render types: models with zero-thickness planes (capes, crowns, wings...) flicker with the default no-cull types, because both faces of each plane are drawn in the same place.

public class ExampleFamiliarRenderer extends HumanoidRenderer<ExampleFamiliarEntity> {
    public ExampleFamiliarRenderer(EntityRendererProvider.Context renderManager, ExampleFamiliarModel model) {
        super(renderManager, model);
        //Color layers, hat and weapon layers go here, before the glow layer
        addRenderLayer(new AutoGlowingGeoLayer<>(this));
    }

    @Override
    public ResourceLocation getTextureLocation(ExampleFamiliarEntity entity) {
        return ExampleFamiliarEntity.textureResource;
    }

    @Override
    public void render(ExampleFamiliarEntity entity, float entityYaw, float partialTick, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight) {
        super.render(entity, entityYaw, partialTick, poseStack, bufferSource, packedLight);

        SpellRenderingHelper.renderSpellHelper(ClientMagicData.getSyncedSpellData(animatable), animatable, poseStack, bufferSource, partialTick);
    }

    @Override
    public RenderType getRenderType(ExampleFamiliarEntity animatable, ResourceLocation texture, @Nullable MultiBufferSource bufferSource, float partialTick) {
        return animatable.isInvisible() ? RenderType.itemEntityTranslucentCull(texture) : RenderType.entityCutout(texture);
    }
}

Then register the entity like any other modded entity, and your familiar is complete. The Familiar Camera works with it automatically.

On this page