Astral Realms Documentation Help

Abilities & Input

Overview

This page documents how player input maps to abilities and how abilities trigger AstralSkill skills. This is not a guide to the skill DSL — see AstralSkill for what skills do once they are cast. This page covers:

  • Input types and how they are captured from player packets and events.

  • How input resolves to an ability in the player's selected subclass.

  • The binding layer: per-player input rebinding and force-binding settings.

  • Cooldown calculation and scaling.

  • Auto-attack toggling.

  • Trigger throttling to prevent simultaneous casts.

  • Weak-hit cancellation (vanilla melee suppression).

  • Gating by skill enablement group.

Input Types

InputType is an enum of the valid player inputs AstralClasses can bind abilities to:

Input

Triggered by

Notes

LEFT_CLICK

Attack key (main hand)

Fired by: packet-based START_DIGGING, ANIMATION, and INTERACT_ENTITY ATTACK events. Also fired by auto-attack toggle.

RIGHT_CLICK

Use key (main hand)

Fired by: packet-based USE_ITEM (main hand only) and INTERACT_ENTITY non-ATTACK interaction.

DROP

Drop key

Fired by: PLAYER_DIGGING DROP_ITEM or DROP_ITEM_STACK packets. Suppresses same-tick ANIMATION LEFT_CLICK.

SPACE

Jump key

Buffered (rising-edge detection). Trigger fires only on jump press, not on hold. Also drives Double Jump.

SNEAK

Sneak key (shift)

Buffered (rising-edge detection). Trigger fires only on sneak toggle, not on hold.

SWAP_HANDS

Swap hands key

Fired by: Bukkit PlayerSwapHandItemsEvent.

HOTBAR_1 through HOTBAR_8

Hotbar slots 1–8

Enum-only. Not currently wired to packet listeners.

Input Capture

Player inputs flow through InputPacketListener (using PacketEvents) and SkillTriggerListener (using Bukkit events):

Packet-based inputs (via InputPacketListener):

  • USE_ITEM packetRIGHT_CLICK (main hand only).

  • PLAYER_DIGGING packet:

    • START_DIGGINGLEFT_CLICK.

    • DROP_ITEM or DROP_ITEM_STACKDROP (also records trigger to block same-tick ANIMATION).

  • PLAYER_INPUT packetSPACE and SNEAK (buffered by InputBuffer to detect rising edge).

  • INTERACT_ENTITY packet:

    • ATTACK action → LEFT_CLICK.

    • Non-ATTACK action → RIGHT_CLICK.

    • Packet is cancelled to prevent vanilla processing.

  • ANIMATION packet (main hand)LEFT_CLICK (unless DROP trigger was just recorded, in which case it is skipped).

Event-based input (via SkillTriggerListener):

  • PlayerSwapHandItemsEventSWAP_HANDS.

Input buffering (SPACE and SNEAK):

InputBuffer detects state changes for SPACE (jump) and SNEAK (shift). A trigger fires only on a false→true transition (key press), not on hold or release. This prevents repeated triggers from a single long press. All other input types always trigger immediately.

Ability Resolution

When a player presses an input, SkillService.tryTriggerSkill looks up which ability to cast:

  1. Check the player's per-input input bindings override (ClassPlayerData.inputBindings): a map of InputType → ability id.

  2. If no override exists, pick the subclass ability whose default-input matches the input.

  3. If no ability is found, the trigger is ignored.

The matched ability yields a list of 5 values:

[skillId, cooldown, classLevel, skillLevel, boosted]
  • skillId (String): The underlying AstralSkill skill id to cast.

  • cooldown (long): Base cooldown in milliseconds (after attack-speed scaling — see Cooldowns).

  • classLevel (int): Player's current class level.

  • skillLevel (int): Unlocked skill level for this ability (0 if not unlocked).

  • boosted (boolean): Whether the ability is boosted (per-class unlock system).

These are passed as placeholders to the skill during cast.

Input Bindings

Each player's inputBindings map (in ClassPlayerData) can override the default input for any ability. This is persisted across sessions. The force-input-binding flag on an ability controls whether its input binding is:

  • true (forced): Players cannot rebind this ability to a different input.

  • false (rebindable): Players can reassign it via the binding system (exact command/UI not documented here; see Commands).

The default out-of-box binding is default-input in the class configuration.

Example: In the Mage class (mage.yml), the ability attack-1 has default-input: LEFT_CLICK and force-input-binding: true, so players cannot rebind it.

Cooldowns

Cooldown Formula

When an ability is triggered, its base cooldown (from the ability config, in milliseconds) is scaled by the player's ATTACK_SPEED stat:

modified_cooldown = base_cooldown / attackSpeed

The attackSpeed is looked up per InputType via StatsAPI.stat(player, "astralstats:attack_speed", inputType). A player with attackSpeed = 1.0 sees no cooldown change. A player with attackSpeed = 1.5 triggers abilities 1.5× faster (cooldown is 1 ÷ 1.5 of the base).

Cooldown Tracking

CooldownManager records a timestamp when an ability is used and checks it on the next trigger. The player may not trigger the same ability again until the cooldown has elapsed.

Cooldown Placeholders

The remaining cooldown time for a specific ability is exposed via:

  • %classes_cooldown_<ability-id>%: Remaining cooldown in seconds (rounded up). Returns 0 if not on cooldown.

  • %classes_cooldown_<ability-id>_max%: Maximum (base) cooldown of the ability in seconds.

For example, %classes_cooldown_attack-1% returns the remaining cooldown for the attack-1 ability.

See Placeholders for a full list.

Auto-Attack Toggle

Players can toggle auto-attack with the /class toggle command (see Commands). When enabled:

  • An async 50 ms loop (SkillService.processToggle) repeatedly fires LEFT_CLICK triggers.

  • This causes the player's primary ability (bound to LEFT_CLICK in the selected subclass) to auto-cast at the loop's cadence (50 ms ≈ 20 ticks/sec), subject to cooldown.

The toggle state is persisted in ClassPlayerData.classSettings.toggleAutoAttack. You can check the current state via the placeholder:

  • %classes_toggle%: Boolean (true if auto-attack is enabled).

Trigger Throttling

SkillTriggerManager enforces a minimum of 1 server tick between ability triggers per player to prevent simultaneous skill casts when multiple packets arrive in the same tick (e.g., LEFT_CLICK and RIGHT_CLICK pressed together).

The tick counter increments every 50 ms asynchronously and is checked when canTrigger() is called. If the player triggered an ability within the minimum interval, the new trigger is rejected.

Special case: When a player presses DROP, the trigger is recorded immediately, which suppresses any ANIMATION packet (LEFT_CLICK) in the same tick.

Casting

When all checks pass (skill enabled for group, input resolved to an ability, not on cooldown, throttle allows), SkillService calls the skill:

  1. Captures the player's eye location (with a +0.15 Y offset) and view direction.

  2. Creates a PlaceholderContainer and registers the ability's class-level, skill-level, and boosted as direct placeholders.

  3. Calls SkillAPI.service().castAtPos(player, uuid, skillId, eyeLocation, direction, container) asynchronously.

The skill is cast at the player's eye position and direction, with class/skill level and boost status available to the skill DSL. See AstralSkill for skill execution semantics.

Weak-Hit Cancellation

SkillTriggerListener monitors EntityDamageByEntityEvent damage caused by players (melee hits). Any damage event where the damage value is less than 2.0 is cancelled. This suppresses weak vanilla melee hits so ability-driven combat is not muddied by incidental unarmed damage.

Enablement Gating

Input processing is gated by the configuration flag skill-enable-groups. If the player's group is not in this list, all input triggers are rejected before ability resolution. This allows disabling the ability system for certain groups without losing other class features (e.g., stats, level progression, etc.).

Check Configuration for the skill-enable-groups key and how to set it.

Example Configuration

From mage.yml (excerpt):

sub-classes-abilities: default: - id: attack-1 name: "Lance Flamme" info: "Tire une ligne de feu infligeant de légers dégâts." skill-id: mage-attack-1-laser cooldown: 350 default-input: LEFT_CLICK force-input-binding: true - id: attack-2 name: "SUPER Flamme" info: "Tire une puissante colonne de feu pouvant infliger de lourds dégâts." skill-id: mage-attack-2-laser cooldown: 1000 default-input: RIGHT_CLICK force-input-binding: true - id: attack-3 name: "Meteore" info: "Invoque un météore explosant au contact des monstres ou du terrain." skill-id: mage-attack-3-projectile cooldown: 5000 default-input: SWAP_HANDS force-input-binding: false

This class defines three abilities:

  • attack-1: LEFT_CLICK, 350 ms cooldown (forced binding).

  • attack-2: RIGHT_CLICK, 1000 ms cooldown (forced binding).

  • attack-3: SWAP_HANDS, 5000 ms cooldown (rebindable).

Each maps to an underlying skill (e.g., mage-attack-1-laser) that handles the actual effect. See AstralSkill to learn what these skills do.

  • Classes — Class configuration and structure.

  • AstralSkill — Skill DSL and execution (separate product).

  • Commands/class toggle and other commands.

  • Placeholders — Full placeholder reference including %classes_toggle% and %classes_cooldown_*%.

  • Double Jump — SPACE input handling for double-jump.

Last modified: 25 July 2026