Astral Realms Documentation Help

Pet Effects

Effects are the passive bonuses a pet blueprint grants its owner. Each blueprint carries an effects: map — <effect-id>: { type: "...", ... } — deserialized entry-by-entry into a WrappedPetEffect by WrappedPetEffectTypeSerializer. The type key selects a PetEffect implementation by its registered string id in PetEffectsRegistry; everything else in the entry deserializes straight into that implementation's own @ConfigSerializable record.

Pet#applyEffect/Pet#removeEffect iterate a pet's blueprint().effects() and call PetEffect#apply/#remove on each wrapped effect. This runs on PetEquipEvent/PetUnequipEvent, on PlayerDataLoadedEvent, and after every level-up — see Passive effects. Some effect types (money, storage, rideable, no-fall) have no-op apply/remove bodies and are instead read directly off the blueprint at the point of use — each per-effect section below notes which.

Common config keys

Key

Type

Description

type

String

The registered effect id (see the summary table below). Selects which PetEffect class deserializes the rest of the entry.

label

Component (MiniMessage)

What the effect is called to a player — "Vente Lianes", "Revenus". The only display text a blueprint still owns; how it is coloured and laid out belongs to a transformer.

unit

String

How its value should read: percent, money, flag, level, amount — or anything else. Free-form on purpose: nothing in Java branches on it, it exists so a transformer can.

values

Map<Integer, Double>

Level → incremental amount granted at that level (not a running total).

The map key an effect is written under (shop-modifier:, jobs_farmer_experience:…) becomes the effect's id, which is what tells two effects of the same type on one pet apart.

WrappedPetEffectTypeSerializer builds WrappedPetEffect#cumulativeValues by walking values in level order and summing as it goes, into a sorted map — so a blueprint may list its levels out of order, and totalValue(level) answers by looking down to the nearest level the blueprint names rather than by an exact lookup. That is what lets a table name only the levels that change something: an effect gaining at 5 and next at 10 reports the level-5 total throughout 6-9. Independently, every PetEffect implementation exposes the same arithmetic itself through the interface default value(level) (values().entrySet().stream().filter(key <= level).sum()), which is what implementations call from their own apply(). maxLevel is the highest key present in values; maxValue is the cumulative total at that key; unlockLevel is the first level whose entry is non-zero (0 when the table grants nothing at any level, which counts as "nothing to unlock" rather than permanently locked).

Effects backed by an optional plugin (shop-modifier, jobs-modifier, rotating-shop-modifier) are only registered once the matching hook succeeded. On a server without that plugin, a blueprint using one loads with that effect skipped and a warning — Skipping effect '<type>' of pet blueprint '<id>': <plugin> is not installed. — instead of failing outright.

Effects run asynchronously by default (PetEffect#async() defaults to true, so Pet#applyEffect/#removeEffect dispatch them via Bukkit.getScheduler().runTaskAsynchronously) — potion is the only implementation that overrides this to false, since Player#addPotionEffect/#removePotionEffect must run on the main thread.

Rendering an effect

An effect's display lines are built from configuration, not Java. Two pieces make that possible.

PetEffectView — one effect bound to the level a pet is actually at. It is what %pet_effects% hands out, so a line can ask whether the effect is locked or maxed without the level being threaded back in. Its keys resolve under the effect namespace; see Effect view sub-keys.

Transformers — plugins/AstralPets/transformers/*.yml, plugin-scoped AstralCore transformers that a blueprint's lore calls with $apply-transformer(<id>). The shipped set:

Transformer

Type

What it draws

pet-effects

list

One line per effect, over %pet_effects%, delegating to pet-effect-line.

pet-effect-line

conditional

One effect's line — picks between locked, flag, level, maxed and active forms by priority.

pet-effect-value

switch

The effect's current total, formatted by its unit (percent → 12%, money → 400⛁/h).

pet-effect-max-value

switch

The same for the total at the effect's last level.

pet-next-level-section

conditional

The whole "next level" block, header included — and an empty branch at max level, so the block removes itself.

pet-next-level-effects

list

One line per effect that gains next level, over %pet_nextLevelEffects%.

pet-effect-next-line

conditional

One effect's next-level line.

pet-level-gauge

gauge

The XP bar, filled from %pet_experience% against %pet_nextLevelExperience%.

They are declared global: false, so they resolve from AstralPets' own configuration and do not collide with another plugin's transformer of the same name.

Adding a new unit is therefore a config change: give the effect unit: "blocks" and add a blocks case to pet-effect-value/pet-effect-max-value/pet-effect-next-value.

Summary

Registered id

Class

Target system

Async

stats

StatPetEffect

AstralStats

Yes

money

MoneyPetEffect

Core economy (EconomyService)

Yes (no-op apply/remove)

potion

PotionPetEffect

Bukkit PotionEffect

No

storage

StoragePetEffect

Pet's own PetInventory

Yes (no-op apply/remove)

rideable

RideablePetEffect

Pet mounting (EntityService)

Yes (no-op apply/remove)

no-fall

NoFallPetEffect

Fall damage (EffectListener)

Yes (no-op apply/remove)

dummy

DummyPetEffect

Nothing — display only

Yes (no-op apply/remove)

shop-modifier

ShopModifierPetEffect

AstralShop (ShopAPI)

Yes

jobs-modifier

JobModifierPetEffect

AstralJobs (JobsAPI)

Yes

rotating-shop-modifier

RotatingShopModifierPetEffect

AstralRotatingShop (RotatingShopAPI)

Yes

stats

Adds an AstralStats StatModifier to the owner for as long as the pet is equipped. StatPetEffect.apply() calls StatsAPI.addModifier(player, stat, new StatModifier(key, stat, ModifierSource.OTHER, EquipmentSlot.SADDLE, value(level), modifierType)); remove() calls StatsAPI.removeModifier. The modifier's key is modifier-key suffixed with the pet's UUID (PetUtils#modifyKey), so multiple pets — even of the same blueprint — never collide on the same modifier key.

effects: example-stats: type: "stats" label: "Vitesse" unit: "percent" stat: "astralrealms:speed" modifier-key: "astralpets:speed_boost" modifier-type: "PERCENTAGE" values: 1: 10 2: 20 3: 30

Field

Type

Description

stat

Key

AstralStats stat key the modifier applies to (e.g. astralrealms:speed).

modifier-key

Key

Base key for the registered StatModifier; suffixed per-pet as above.

modifier-type

ModifierType

RELATIVE, FLAT, or PERCENTAGE.

values

Map<Integer, Double>

Level → incremental modifier amount.

money

Periodic passive income. MoneyPetEffect.apply()/remove() are no-ops — the payout is driven entirely by EconomyEffectTask, an async repeating task (runTaskTimerAsynchronously, 10s initial delay / 30s period) registered in AstralPets#onEnable. Every run, for every online player's active pets, it finds each MoneyPetEffect on the blueprint and pays out when all of the following hold:

  • moneyEffect.value(pet.level()) (the cumulative amount) is non-zero.

  • pet.equippedSince() is non-zero (the pet is actually equipped).

  • at least interval has elapsed since both equippedSince and the pet's lastMoneyGainTime.

Payouts from every eligible pet on a player are summed into one economyService.deposit(...) call, then each contributing pet's individual %amount% is reported separately via the pet-generated-money message (%pet_name% + %amount%, see messages.yml), and that pet's lastMoneyGainTime is updated.

effects: passive-income: type: "money" label: "Revenus" unit: "money" values: 1: 50 10: 25 interval: "30m"

Field

Type

Description

values

Map<Integer, Double>

Level → incremental currency amount paid per interval.

interval

Duration

Minimum time between payouts for this effect, parsed by DurationParser (y/d/h/m/s/ms, e.g. "30m", "1h 30m").

potion

Applies an infinite-duration Bukkit PotionEffect to the owner while equipped. PotionPetEffect.apply() computes amplifier = (int) value(level) and, if greater than 0, calls effect.createEffect(PotionEffect.INFINITE_DURATION, amplifier - 1) — so values should be keyed with the potion level (1 = the vanilla "I" tier), not the raw Bukkit amplifier; the effect subtracts 1 internally. remove() calls player.removePotionEffect(effect). Runs synchronously (async() returns false).

effects: speed: type: "potion" label: "Rapidité" unit: "level" effect: "speed" values: 1: 1 10: 1

Field

Type

Description

effect

PotionEffectType

Registry key from RegistryKey.MOB_EFFECT (e.g. speed, jump_boost, regeneration).

values

Map<Integer, Double>

Level → incremental potion-level amount; cumulative total minus 1 is the applied amplifier.

storage

Sizes the pet's own PetInventory. StoragePetEffect.apply()/remove() are no-ops — the size isn't tied to the equip lifecycle at all. Pet#build() sets the initial size from getEffectLevel(StoragePetEffect.class, 1), and Pet#handleLevelUps() recomputes it (getEffectLevel(StoragePetEffect.class, level)) after every level-up, resizing the backing array if it changed. Because of this, storage size (and therefore whether [open-pet-container] opens anything) depends only on the pet's own level, not on whether it's currently active/equipped.

effects: storage: type: "storage" label: "Stockage" unit: "amount" values: 1: 9 10: 9

Field

Type

Description

values

Map<Integer, Double>

Level → incremental inventory slots. Cumulative total is the pet's current storage size.

rideable

Gates whether the pet can be mounted. RideablePetEffect.apply()/remove() are no-ops; EntityService#ride checks blueprint().getEffectLevel(RideablePetEffect.class, pet.level()) >= 1 directly. Riding additionally requires the blueprint's own entity.rideable: true — the effect only unlocks it once the pet's level also grants a cumulative value of at least 1; below that, [ride-pet]/ interacting with the spawned pet reports pet-cannot-be-rode with the level at which riding unlocks (PetBlueprint#levelUntilValue).

effects: rideable: type: "rideable" label: "Montable" unit: "flag" values: 5: 1

Field

Type

Description

values

Map<Integer, Double>

Level → incremental value; riding unlocks once the cumulative total reaches 1.

no-fall

Cancels fall damage for the owner. NoFallPetEffect.apply()/remove() are no-ops; EffectListener's EntityDamageEvent handler checks, for every one of the player's active pets, blueprint().getEffectLevel(NoFallPetEffect.class, pet.level()) > 0 and cancels the event if any active pet qualifies — the pet does not need to be spawned, only equipped.

effects: no-fall: type: "no-fall" label: "Chute amortie" unit: "flag" values: 15: 1

Field

Type

Description

values

Map<Integer, Double>

Level → incremental value; fall damage is cancelled once the cumulative total is greater than 0.

dummy

Grants nothing. apply()/remove() are empty and no system reads it — it exists purely so a blueprint can declare a display-only line that scales with level like any other effect: a bonus granted somewhere else entirely, a flavour perk, or a placeholder for an effect not built yet.

Because it is a normal effect entry it still gets a label, a unit, a level table and therefore an unlock level, a maxed state and a next-level value — so it renders through the same transformers as everything else and needs no special-casing in lore.

effects: guild-renown: type: "dummy" label: "Renommée" unit: "amount" values: 1: 5 10: 10

Field

Type

Description

values

Map<Integer, Double>

Level → incremental value. Used only for display and for the unlock/max arithmetic.

shop-modifier

Adds an AstralShop price ShopModifier for the owner. ShopModifierPetEffect.apply() divides the cumulative value by 100 and registers an item-scoped ShopItemModifier (if item-id is set), a category-scoped ShopCategoryModifier (if category-id is set instead), or a global ShopModifier otherwise — via ShopAPI.addModifier. The registered key is key suffixed with the pet's UUID and, when present, the item-id/category-id (PetUtils#modifyKey).

effects: shop-modifier: type: "shop-modifier" label: "Vente Lianes" unit: "percent" key: "astralpets:shop_modifier" scope: BUY # Sell or Both modifier-type: "RELATIVE" # Absolute or Relative values: 1: 5 2: 10

Field

Type

Description

key

Key

Base key for the registered ShopModifier.

scope

ShopModifierScope

BUY, SELL, or BOTH.

modifier-type

ShopModifierType

RELATIVE or ABSOLUTE.

values

Map<Integer, Double>

Level → incremental amount; applied as cumulativeValue / 100.

item-id

String?

Optional. Scopes the modifier to one shop item instead of the whole shop.

category-id

String?

Optional (ignored if item-id is set). Scopes the modifier to one shop category.

jobs-modifier

Adds an AstralJobs JobModifier for the owner. JobModifierPetEffect.apply() registers the modifier via JobsAPI.addModifier using the raw cumulative value (unlike shop-modifier/rotating-shop-modifier, it is not divided by 100). The registered key is modifier-key suffixed with the pet's UUID and the modifier scope name.

effects: jobs-modifier: type: "jobs-modifier" label: "Expérience métier Fermier" unit: "percent" modifier-key: "astralpets:jobs_modifier" modifier-scope: MONEY # MONEY or EXPERIENCE modifier-type: "RELATIVE" # Absolute or Relative job-id: null # Optional, if not specified it will apply to all jobs values: 1: 5 2: 10

Field

Type

Description

modifier-key

Key

Base key for the registered JobModifier.

modifier-scope

JobModifierScope

MONEY, EXPERIENCE, or GLOBAL.

modifier-type

JobModifierType

RELATIVE or ABSOLUTE.

job-id

String?

Optional. Scopes the modifier to one job; omitted/null applies to every job.

values

Map<Integer, Double>

Level → incremental amount, used as-is (not divided by 100).

rotating-shop-modifier

Adds an AstralRotatingShop price modifier for the owner. RotatingShopModifierPetEffect.apply() calls RotatingShopAPI.addModifier(playerId, key, value(level) / 100); remove() calls RotatingShopAPI.removeModifier. The registered key is modifier-key suffixed with the pet's UUID.

effects: rotating-shop-modifier: type: "rotating-shop-modifier" label: "Réduction boutique tournante" unit: "percent" modifier-key: "astralpets:rotating_shop_modifier" values: 1: 5 2: 10

Field

Type

Description

modifier-key

Key

Base key for the registered rotating-shop modifier.

values

Map<Integer, Double>

Level → incremental amount; applied as cumulativeValue / 100.

See Pet Blueprints for the surrounding blueprint shape, Passive effects for the equip/unequip lifecycle that drives most of these, and Placeholders for everything a display line can ask about one effect.

Last modified: 25 September 2026