Astral Realms Documentation Help

Pet Blueprints

A blueprint defines one pet species: its display item, the entity it spawns as, its rarity, and the effects that scale with level. Blueprints live in plugins/AstralPets/blueprints/*.yml (sub-folders are scanned recursively) and are loaded by BlueprintService — each file deserialises into exactly one PetBlueprint.

Example

A shipped blueprint, blueprints/epic/moohhh.yml:

id: "moohhh" default-name: "<bold><#9441d3>Moohhh" rarity: "epic" max-level: 20 # Skins this pet may wear (ids from skins/). A skin outside this list can never be applied to it. skins: - "baby-skin" item: material: "mooshroom_spawn_egg" name: "%pet_name%" lore: - "<text_color>Niv. %pet_level% | Exp. %pet_experience%/%pet_nextLevelExperience%" - "$apply-transformer(pet-level-gauge)" - "" - "%pet_blueprint_rarity_accent%<bold>| Effets" - "$apply-transformer(pet-effects)" - "$apply-transformer(pet-next-level-section)" entity: type: "mooshroom" mob-blueprint: "pet-moohhh" name: "%pet_name%" sub-name: "Niv. %pet_level%" rideable: true effects: shop-modifier: type: "shop-modifier" label: "Vente Lianes" unit: "percent" key: "astralpets:shop_modifier" scope: SELL modifier-type: "RELATIVE" item-id: "vine" values: 1: 2 5: 2 9: 2 rideable: type: "rideable" label: "Montable" unit: "flag" values: 18: 1

Note what the blueprint no longer carries: the lore calls transformers instead of spelling out how each effect reads, and each effect declares a label and a unit instead of three display strings.

Top-level fields

Field

Type

Required

Description

id

String

Yes

Blueprint identifier. If two files resolve to the same id, the duplicate is logged and skipped — since files are loaded into an unordered Set first, which copy is kept isn't guaranteed to follow file/folder order.

default-name

Component (MiniMessage)

Yes

Name given to a newly-created Pet instance. Players can rename their pet afterwards; this is only the initial value.

rarity

String

Yes

Id of a rarity defined in rarities.yml (see Rarities). Loading fails with Invalid rarity ID: <id> if it doesn't resolve.

max-level

int

No

Highest level the pet can reach. Defaults to 20 if omitted.

item

ItemStackWrapper

Yes

The pet's inventory/menu representation (see item).

entity

EntityBlueprint

Yes

The world entity spawned when the pet is summoned (see entity).

effects

Map<String, WrappedPetEffect>

No

Named effect entries that scale with level (see effects). The map key becomes the effect's id — it is what tells two effects of the same type apart (a farmer money bonus and a farmer XP bonus), and what a display line can switch on.

skins

List<String>

No

Ids of the skins a pet of this blueprint may wear. A skin outside this list can never be applied. Ids that no skins/ file declares are kept as written but logged as a warning after each load.

If id, default-name, item, entity, or rarity is missing, PetBlueprintTypeSerializer throws and the file fails to load (BlueprintService logs the error and continues with the remaining files).

item

item is a standard AstralCore ItemStackWrapper — the same template used by menu items, so material, name, lore, enchantments, item-flags, amount, and item components all work the same way. See Menu Items for the full field reference.

The name and lore are placeholder-aware and are re-rendered against the specific Pet instance every time the item is built (PetService.buildItemStack). In addition to the general AstralCore placeholders, the pet placeholders are available directly (unprefixed, since the item template is resolved with the pet registered as the current placeholder context):

Placeholder

Resolves to

%pet_name%

The pet's current (possibly player-renamed) display name.

%pet_level%

The pet's current level.

%pet_experience%

Experience accumulated toward the next level (resets to 0 on level-up, see How leveling works).

%pet_effects%

Every effect bound to this pet's level, as a provider — iterate it with a list transformer rather than rendering it directly.

$apply-transformer(...)

A transformer from the plugin's transformers/ folder. This is how effect lines, the XP gauge and the "next level" block are drawn.

The built ItemStack carries the full Pet (id, name, blueprint, experience, level, inventory) serialised into its PDC, so a live pet item always reflects that specific pet's state — not just the blueprint template.

entity

entity (PetBlueprint.EntityBlueprint) describes the world entity spawned when the pet is summoned.

Field

Type

Description

type

Bukkit EntityType

The type the pet is rendered as, e.g. cow, wolf, allay. It also decides how much room the spawn-spot search needs. Per-type behaviour comes from mob-blueprint, not from this key — see Where behaviour comes from.

mob-blueprint

String

Id of the AstralMobs blueprint the pet spawns as: its goals (follow-owner, idle animations), movement and skills. Blank or omitted falls back to the pet-<blueprint id> convention. A pet naming an unknown mob blueprint logs an error and does not spawn.

baby

boolean

Spawns the pet as a baby variant, where the rendered type has one.

rideable

boolean

Whether the entity can be mounted at all. This is only half the gate: the pet also needs its rideable effect to reach a value of at least 1 at its current level — the [ride-pet] action (EntityService.ride) checks both and messages the player with the level still required if the effect isn't there yet.

name

Component (MiniMessage)

First line of the floating nametag rendered above the entity. Supports %pet_level% and the other pet placeholders (resolved through the same placeholder container as the name/sub-name pair).

sub-name

Component (MiniMessage)

Second line of the floating nametag, rendered below name.

attributes

Map<Attribute, Double>

Namespaced Bukkit attribute keys (e.g. minecraft:max_health) mapped to their base value. Applied via Attribute#setBaseValue right after the entity spawns — any attribute the target mob type doesn't support is silently skipped. Common keys: minecraft:max_health, minecraft:follow_range, minecraft:movement_speed, minecraft:flying_speed, minecraft:scale, minecraft:jump_strength.

Where behaviour comes from

A pet is spawned through AstralMobs (MobsAPI.spawnOwnedMob), so what it does — how it follows its owner, how it moves, which idle animations it plays, what it is rendered as — is configured in its AstralMobs blueprint, not here. The old per-type PetEntity subclasses are no longer used: EntityFactory builds one PetEntity for every pet whatever its type.

What the pet blueprint still decides is the pet-domain layer on top: the rendered type used to size the spawn spot, the two-line nametag (name/sub-name), whether riding is possible at all (rideable), and attribute overrides.

Attributes are applied in layers, each winning over the last:

  1. the AstralMobs blueprint's own,

  2. this blueprint's entity.attributes,

  3. the pet's skin, applied last.

effects

Each entry under effects is a WrappedPetEffect — it wraps one of the registered effect implementations (type) with the display text and per-level value scaling shared by every effect. Available types: stats, money, potion, storage, rideable, no-fall, shop-modifier, jobs-modifier, rotating-shop-modifier. Field-by-field behavior of each type is documented on Pet Effects; the shared shape every entry accepts is:

Field

Type

Description

type

String

The effect implementation id. Unknown types fail blueprint loading — except for the three hook-backed types, which are skipped with a warning when their plugin is absent.

label

Component

What the effect is called to a player ("Vente Lianes"). The one piece of display text a blueprint still owns.

unit

String

How its value reads — percent, money, flag, level, amount, … Free-form: nothing in Java branches on it, transformers do.

values/modifier-values

Map<int, double>

Per-level value map: key is the pet level, value is the amount added at that level. Either key name is accepted — values is checked first, and modifier-values is used if it's absent or empty.

…effect-specific fields

—

e.g. stat/modifier-key/modifier-type for stats, key/scope for shop-modifier. See Pet Effects.

How the value map drives scaling

values/modifier-values entries do not need one per level — but the two consumers of the map read it differently:

  • The value an effect actually applies (e.g. the stat modifier StatPetEffect grants) sums every entry whose key is at or below the pet's current level, so it's safe to leave gaps between levels.

  • Displayed totals, "is maxed" checks, and the storage effect's inventory-size lookup read a precomputed running total, looked up by the highest level at or below the one asked for. Gaps are therefore fine here too: an effect that gains at level 5 and next gains at 10 reports the level-5 total for levels 6 through 9. The table is stored sorted, so a blueprint may list its levels out of order.

The first level whose entry is non-zero is the effect's unlock level, and the highest key present is its maxLevel (the cumulative total there being its maxValue) — both are what a display line asks about rather than deriving.

The storage effect type is special-cased: PetBlueprint.build() reads its value at level 1 to size the pet's initial storage inventory, and Pet.handleLevelUps() re-reads it at the pet's new level after every level-up to resize the inventory in place.

Rarities

Rarities are shared across all blueprints and defined once in rarities.yml (not per-file), loaded into a RaritiesConfiguration at plugin startup/reload, before blueprints are loaded:

rarities: commun: id: common display: "<#d5d5d5>Commun" level-expression: "32+((level-1)^2/4)" color: "#d5d5d5" accent-color: "#dddddd" uncommun: id: uncommon display: "<#6aa84f>Inhabituel" level-expression: "34+((level-1)^2/2)" color: "#6aa84f" rare: id: rare display: "<#357df8>Rare" level-expression: "36+(level-1)^2" color: "#357df8" epic: id: epic display: "<#9441d3>Épique" level-expression: "38+((level-1)^2*1.5)" color: "#9441d3" legendary: id: legendary display: "<#ffd700>Légendaire" level-expression: "40+((level-1)^2*2)" color: "#ffd700"

Field

Type

Description

id

String

Identifier blueprints reference via their own rarity key. The map key above it (commun, rare, …) is free-form and only used to organise the file — lookups match on id, not the map key.

display

Component (MiniMessage)

Rarity label, e.g. used in menus/lore.

color

TextColor

Hex or named colour the rarity's name is written in.

accent-color

TextColor

Optional. The lighter tone its detail text is written in — effect lines, lore headers. Read as %..._rarity_accent%, which falls back to color when unset. Having it here is what keeps a colour change from being a thirty-file edit.

level-expression

String (Crunch expression)

Formula, in terms of a single variable level, for the experience required to advance into that level. See How leveling works.

rarities.yml ships the five rarities above (common, uncommon, rare, epic, legendary), which are the ids the shipped blueprints reference.

How leveling works

Pet.experienceForLevel(level) compiles the pet's rarity level-expression with Crunch (variable level) and evaluates it for the level being levelled into. That result is the experience required to go from level - 1 to level — not a cumulative lifetime total. PetService.gainExperience adds incoming experience to pet.experience(), then loops: while the accumulated amount is at least experienceForLevel(pet.level() + 1), it subtracts that requirement, fires a PetLevelUpEvent, and increments the pet's level (stopping at max-level, where any surplus experience is discarded). %pet_experience% and %pet_nextLevelExperience% (see Placeholders) reflect this same per-level counter.

Using the rare rarity as an example — 36+(level-1)^2 — reaching level 2 costs 36 + (2-1)^2 = 37 XP, reaching level 3 costs 36 + (3-1)^2 = 40 XP, and so on; each figure is the cost of that single level, not a running total.

Item suppliers

AstralPets registers a pets ItemStackSupplier (PetsItemSupplier) with AstralPaperAPI at startup. Anything elsewhere in AstralCore/AstralItems that accepts a generic supplier-backed item key (menus, shops, kits, /items give-style commands) can reference a pet blueprint as pets:<blueprint-id> — e.g. pets:moohhh. Two sibling suppliers are registered the same way: pets.food:<food-id> for food and pets.skins:<skin-id> for skins. Resolving that key builds a fresh level-1 pet from the blueprint (a new UUID each time) via PetService.buildItemStack(PetBlueprint), distinct from /pets give, which uses the same builder directly rather than going through the supplier lookup. The supplier's completion list is rebuilt from BlueprintService.ids() on every reload, and PetsItemSupplier.keyOf(ItemStack) reverses an existing pet item back to its blueprint's key by reading the Pet stored in its PDC.

Hot reload

/pets reload (pets.reload) calls AstralPets.loadConfiguration(), which reloads rarities.yml before BlueprintService.load() re-reads every file under blueprints/ — so a blueprint referencing a rarity added in the same reload will resolve correctly. Skins are loaded just before the blueprints, and cross-checked against every blueprint's skins list just after. The blueprint registry and the max-level preview cache (BlueprintService.cachedPets(), one synthetic max-level Pet per blueprint, exposed as %pets_blueprints% for menus that list every blueprint without requiring a player to own one) are cleared and rebuilt from scratch, and PetsItemSupplier/PetsFoodItemSupplier completions are refreshed immediately after.

Last modified: 25 September 2026