Astral Realms Documentation Help

Casting Skills

A casting skill is an attack pattern where a mob casts a skill registered in AstralSkill. The cast-skill AI goal is the only mechanism by which an AstralMobs blueprint triggers AstralSkill skills.

Every cast-skill goal instance casts a single skill on each tick it's active, holding position at a configured preferred-range from the target. The skill is cast from the mob's eye position through the target's eye position, allowing line-of-sight projectiles and area effects. AstralSkill is a soft dependency — if it is absent or not yet initialized, casts silently no-op and the kiting still works.

Configuration

A cast-skill goal in goal-selectors inherits all fields from KiteGoal plus two of its own:

Field

Type

Required

Description

skill

String

Yes

Id of a skill registered in AstralSkill. If the skill does not exist in AstralSkill's registry when the cast fires, the cast fails silently.

force-cast

boolean

No, default false

When true, bypasses the reachable-check and line-of-sight requirement, allowing the mob to cast even when it has no navigable path to or line of sight on the target.

preferred-range

double

No, default 8.0

Distance in blocks the mob tries to maintain from the target while kiting.

range-tolerance

double

No, default 1.5

Half-width of the dead band around preferred-range where the mob holds position without moving. The mob moves if distance > preferred-range + range-tolerance or < preferred-range - range-tolerance.

speed-modifier

double

No, default 1.0

Movement speed multiplier while repositioning to or holding the preferred range.

priority

int

No, default 0

Goal priority — higher numbers = lower priority (runs only if no higher-priority goal is active).

flags

List

No, default [MOVE, LOOK]

Movement control flags (e.g., [LOOK] to keep distance but not move; [MOVE, LOOK] to move and turn).

can-use-requirements

Requirements list

No

Gating conditions (e.g., cooldown timers, memory counters) that must pass before the goal activates. See Requirements & Actions.

can-continue-requirements

Requirements list

No

Conditions checked every tick; when any fails, the goal stops immediately.

can-continue-to-use-also-can-use

boolean

No, default true

If false, omits the check of can-use-requirements inside canContinueToUse(), so the goal runs to completion even if activation conditions no longer hold. Used for wind-up phases.

start-actions

Actions list

No

Fired once when the goal starts (e.g., start-timer, store-memory, send-entity-status). See Requirements & Actions.

stop-actions

Actions list

No

Fired once when the goal stops.

interrupt-actions

Actions list

No

Fired when can-continue-requirements fail.

is-interruptible

boolean

No, default true

If false, a higher-priority goal cannot interrupt this one.

requires-update-every-tick

boolean

No, default false

If true, canContinueToUse() is checked every tick; if false, the goal runs to completion once started.

Inherited from KiteGoal

All cast-skill goals inherit from KiteGoal, which inherits from AstralGoal. Casting is purely overlaid on top of the kiting mechanics — every tick, the parent KiteGoal.tick() runs first (repositioning the mob and facing the target), then cast() casts the skill and stops the goal.

Minimal example

goal-selectors: cast-skill: - skill: skeleton-cast # REQUIRED: id of the AstralSkill skill preferred-range: 8.0 # Keep 8 blocks from target range-tolerance: 1.5 # Dead band: [6.5, 9.5] blocks speed-modifier: 1.0 # Normal movement speed priority: 1 # Lower priority (runs after higher-numbered goals)

Casting Mechanics

Tick-by-tick behavior

On each tick the goal is active:

  1. Kite — move toward/away from the target to maintain preferred-range, face the target, and hold position inside the tolerance band. This is inherited from KiteGoal.

  2. Cast — if AstralSkill is loaded and initialized:

    • Get the skill by id from SkillAPI.service().

    • Call SkillService.castThroughPos(mob, mob.getUUID(), skill-id, mob-eye-location, target-eye-location, container, force-cast).

    • Register the mob placeholder (accessible to the skill as %mob%).

    • Stop the goal (single cast per activation cycle).

If AstralSkill is absent or not yet initialized, the cast is skipped and the goal continues to hold position (kiting still works).

Line of sight and reachability

By default, SkillService.castThroughPos checks whether the target is reachable (navigable path exists) and in line of sight. When force-cast: true, these checks are bypassed, allowing the mob to cast at unreachable or obstructed targets (e.g., through walls, across chasms).

Placeholder registration

When a skill is cast, the mob placeholder is registered in the skill's execution context, making the mob entity available to the skill's triggers, selectors, and actions. See Placeholders for the full list.

Soft Dependency on AstralSkill

CastSkillGoal checks two conditions before casting:

if (!Bukkit.getPluginManager().isPluginEnabled("AstralSkill")) return null; try { return SkillAPI.service(); } catch (IllegalStateException notInitialised) { return null; }

When either check fails (plugin absent or not initialized), skillService() returns null, and the cast is skipped silently on every tick. The kiting still works — the mob maintains distance and faces the target, but no skill fires. Once AstralSkill is loaded and initialized (or on the next reload), casts resume automatically.

The Charge → Fire Pattern

Real bosses use a two-phase attack: a high-priority warning/charge cast-skill goal that starts timers, followed by a lower-priority actual-attack goal that fires inside the timer window. The warning phase has can-continue-to-use-also-can-use: false so it runs to completion even if the target moves out of range; the actual phase gates itself with %mob_timer_*% and %mob_memory_*% compare requirements.

Why two separate goals?

  • Separation of concerns — wind-up animation/particles are separate from the projectile cast, each with its own skill.

  • Timing precision — timer checks ensure the projectile fires at the exact frame the server and client visual windup align.

  • Stall prevention — if a target moves out of range during the charge, the actual-attack goal can still fire if the timer window is open.

Structure

goal-selectors: # Phase 1: Warning (LOOK-only, high priority) cast-skill: - flags: [LOOK] # Face target but don't move priority: 3 # Highest priority skill: evoker-laser-warning-cast # Particle column, windup sound preferred-range: 40.0 range-tolerance: 40.0 can-continue-to-use-also-can-use: false # Run to completion start-actions: - "[start-timer] cooldown-cast-laser" # Start timer 1 - "[store-memory] shot-laser 0" # Flag: not yet fired can-use-requirements: min-requirements: 1 requirements: - "[compare] %mob_timer_cooldown-cast-laser% > 15000" # Off cooldown - "[compare] %mob_timer_cooldown-cast-laser% == 0" # OR first tick # Phase 2: Actual attack (LOOK-only, lower priority, gated) cast-skill: - flags: [LOOK] priority: 2 # Lower priority skill: evoker-laser-actual-cast # Projectile preferred-range: 40.0 range-tolerance: 40.0 start-actions: - "[store-memory] shot-laser 1" # Flag: fired once can-use-requirements: min-requirements: 4 # AND all conditions requirements: - "[compare] %mob_timer_cooldown-cast-laser% > 2000" # 2s after warning - "[compare] %mob_timer_cooldown-cast-laser% < 2999" # Before 3s (1-tick window) - "[compare] %mob_timer_cooldown-cast-laser-actual% > 500" - "[compare] %mob_memory_shot-laser% == 0" # Not yet fired

In this pattern:

  • The warning goal has priority: 3 and no requirement gates, so it activates immediately.

  • It starts a timer cooldown-cast-laser and sets a flag shot-laser 0.

  • The actual goal has priority: 2 (lower = runs only if warning is not active).

  • It gates itself with 2000 < timer < 2999, ensuring the projectile fires exactly 2-3 seconds after the warning started.

  • After firing, it sets the flag to 1, preventing the actual goal from firing again until the warning resets the flag to 0.

Worked Example: Evoker Boss

The evoker-boss blueprint demonstrates a complex multi-attack pattern: two simultaneous charge → fire cycles (laser and spread attack) plus a fallback basic cast:

id: evoker-boss type: EVOKER health: 300.0 spawn-actions: - "[start-timer] cooldown-cast-goal" # Global cooldown to space out attacks goal-selectors: cast-skill: # --- Laser Attack --- # Phase 1: Warning - flags: [LOOK] priority: 3 skill: evoker-laser-warning-cast preferred-range: 40.0 range-tolerance: 40.0 can-continue-to-use-also-can-use: false start-actions: - "[start-timer] cooldown-cast-goal" - "[start-timer] cooldown-cast-laser" - "[store-memory] shot-laser 0" - "[start-timer] cooldown-cast-laser-actual" can-use-requirements: min-requirements: 2 # OR: first tick (timer == 0) or off cooldown requirements: - "[compare] %mob_timer_cooldown-cast-laser% > 15000" - "[compare] %mob_timer_cooldown-cast-laser% == 0" - "[compare] %mob_timer_cooldown-cast-goal% > 6000" can-continue-requirements: - "[compare] %mob_timer_cooldown-cast-laser% < 2000" # 2s max charge # Phase 2: Fire - flags: [LOOK] priority: 2 skill: evoker-laser-actual-cast preferred-range: 40.0 range-tolerance: 40.0 start-actions: - "[store-memory] shot-laser $e(%mob_memory_shot-laser%+1)" # Increment counter - "[start-timer] cooldown-cast-laser-actual" can-use-requirements: min-requirements: 4 requirements: - "[compare] %mob_timer_cooldown-cast-laser% > 2000" - "[compare] %mob_timer_cooldown-cast-laser% < 2999" - "[compare] %mob_timer_cooldown-cast-laser-actual% > 500" - "[compare] %mob_memory_shot-laser% < 2" # Fire up to 2 shots # --- Spread Attack (same pattern) --- - flags: [LOOK] priority: 5 skill: evoker-spread-warning-cast preferred-range: 40.0 range-tolerance: 40.0 can-continue-to-use-also-can-use: false start-actions: - "[start-timer] cooldown-cast-goal" - "[start-timer] cooldown-cast-spread" - "[store-memory] shot-spread 0" - "[start-timer] cooldown-cast-spread-actual" can-use-requirements: min-requirements: 2 requirements: - "[compare] %mob_timer_cooldown-cast-spread% > 15000" - "[compare] %mob_timer_cooldown-cast-spread% == 0" - "[compare] %mob_timer_cooldown-cast-goal% > 4000" - flags: [LOOK] priority: 4 skill: evoker-spread-actual-cast preferred-range: 40.0 range-tolerance: 40.0 start-actions: - "[store-memory] shot-spread $e(%mob_memory_shot-spread%+1)" - "[start-timer] cooldown-cast-spread-actual" can-use-requirements: min-requirements: 4 requirements: - "[compare] %mob_timer_cooldown-cast-spread% > 2000" - "[compare] %mob_timer_cooldown-cast-spread% < 2999" - "[compare] %mob_timer_cooldown-cast-spread-actual% > 250" - "[compare] %mob_memory_shot-spread% < 4" # --- Fallback Basic Cast --- - flags: [LOOK] priority: 1 skill: skeleton-cast preferred-range: 8.0 range-tolerance: 4.0 start-actions: - "[start-timer] cooldown-cast" - "[store-memory] shot 0" can-use-requirements: min-requirements: 1 requirements: - "[compare] %mob_timer_cooldown-cast% > 4000" - "[compare] %mob_timer_cooldown-cast% == 0" - flags: [LOOK] priority: 0 skill: evoker-attack-1 preferred-range: 8.0 range-tolerance: 4.0 start-actions: - "[store-memory] shot 1" can-use-requirements: min-requirements: 3 requirements: - "[compare] %mob_timer_cooldown-cast% > 3000" - "[compare] %mob_timer_cooldown-cast% < 3999" - "[compare] %mob_memory_shot% == 0"

How it plays:

  • On spawn, cooldown-cast-goal starts (global spacing timer).

  • Laser warning (priority 3) fires first, resets the global timer to 6s, starts its own laser timer.

  • Spread warning (priority 5) fires when its own 4s window opens, resets global timer.

  • Laser actual (priority 2) fires 2-3s after laser warning, increments shot counter, fires up to 2 shots.

  • Spread actual (priority 4) fires 2-3s after spread warning, up to 4 shots.

  • Fallback basic cast (priorities 1 & 0) fires only when both laser and spread are cooling down.

Interplay with Actions & Timers

start-actions and stop-actions orchestrate the visual sync between the mob's pose, animation state, and the skill execution:

cast-skill: - flags: [LOOK] priority: 3 skill: warden-laser-warning-cast start-actions: - "[start-timer] cooldown-cast-laser" # Timer for wait gate - "[store-memory] shot-laser 0" # Memory flag - "[send-entity-status] 62" # Warden sonic-boom pose - "[edit-entity-metadata] 6 ENTITY_POSE STANDING" # Standing (vs ROARING)

Common action patterns:

  • [start-timer] name — starts a timer named name at 0 ms, incremented each tick. Used as gates in requirements.

  • [store-memory] key value — stores a numeric value in mob memory (per-entity). Use $e(...) math expressions to increment/compute values.

  • [send-entity-status] code — sends an entity-status packet (e.g., 62 = Warden's sonic-boom charge).

  • [edit-entity-metadata] index type value — edits metadata to pose/animation states (e.g., ENTITY_POSE STANDING).

See Mob Actions for the full action reference and Placeholders for timer and memory placeholder syntax (%mob_timer_*%, %mob_memory_*%).

Requirements & Actions

Both can-use-requirements and can-continue-requirements accept a list of requirement expressions (e.g., [compare] gates on timer/memory values). The min-requirements field gates whether ALL (AND) or ANY (OR) requirements must pass:

  • min-requirements: 1 → Fire if ANY requirement is true (OR logic)

  • min-requirements: 2 → Fire if ANY 2+ requirements are true

  • min-requirements: 3 → Fire if ALL 3 requirements are true (AND logic when == number of requirements)

  • Omitted → Fire if ANY requirement is true (default 1)

See AI Goals, Targeting & Movement for the shared can-use-requirements/can-continue-requirements fields.

Cross-Reference: AstralSkill Skills

Skill ids referenced in the skill: field must exist in AstralSkill's registry. A skill file defines:

  • Cast type — how the skill is cast and resolved (dummy, projectile, laser, impact, auto_cast, n_cast)

  • Targeting — who/what the skill affects (caster, target, area) via relationship and selection rules

  • Behavior — cast/target effects, damage, status effects, and custom actions

AstralMobs does not define or configure skills; it only references them by id via CastSkillGoal, which casts through SkillService.castThroughPos. To add or modify a skill, edit the AstralSkill skill files and reload/restart the server.

See the AstralSkill documentation for:

  • Overview — skill types and the casting engine

  • Cast Types — how skills reach and affect targets

  • Targeting — relationship rules and target/impact selection

  • Skill Files — the skill YAML structure cast-skill goals reference by id

Last modified: 25 July 2026