Skip to main content
The RPGMobs API allows other Hytale mods to react to RPG mob events, inspect mob state, and modify behavior at runtime. It ships as a separate RPGMobs-api artifact so your mod only depends on the lightweight API interfaces, not the full plugin.

Setup

Dependency

Add the API JAR as a compile-only dependency in your build.gradle:
dependencies {
    compileOnly files('libs/RPGMobs-API-1.3.0.jar')
}
Or if published to a Maven repository:
dependencies {
    compileOnly 'Frotty27:RPGMobs-api:1.3.0'
}
The -javadoc.jar and -sources.jar are available as additional downloads. Place all three in your libs/ folder for full IDE support (autocomplete, inline docs, click-to-source).

Mod Manifest

Declare RPGMobs as a dependency in your mod’s manifest.json so Hytale loads it before your mod:
{
  "Dependencies": {
    "Frotty27:RPGMobs": "*"
  }
}

Entry Point

All API access goes through the static RPGMobsAPI class:
import com.frotty27.rpgmobs.api.RPGMobsAPI;

// Register a listener
RPGMobsAPI.registerListener(myListener);

// Query mob state
RPGMobsAPI.query().getTier(entityRef);

// Unregister when your mod shuts down
RPGMobsAPI.unregisterListener(myListener);
If RPGMobs has not initialized yet, calling these methods throws RPGMobsNotInitializedException.

Event System

Implement IRPGMobsEventListener and override the events you care about. All handler methods have default no-op implementations, so you only need to override what you use.
import com.frotty27.rpgmobs.api.IRPGMobsEventListener;
import com.frotty27.rpgmobs.api.events.*;

public class MyEliteListener implements IRPGMobsEventListener {

    @Override
    public void onRPGMobSpawned(RPGMobsSpawnedEvent event) {
        int tier = event.getTier();
        // Cancel the spawn if needed
        // event.setCancelled(true);
    }

    @Override
    public void onRPGMobDrops(RPGMobsDropsEvent event) {
        // Add custom items to the drop list
        event.getDrops().add(myCustomItem);
    }

    @Override
    public void onRPGMobDamageDealt(RPGMobsDamageDealtEvent event) {
        // Reduce elite damage by 20%
        event.setMultiplier(event.getMultiplier() * 0.8f);
    }
}
Register it during your mod’s initialization:
RPGMobsAPI.registerListener(new MyEliteListener());

Available Events

EventFired whenCancellableMutable fields
RPGMobsSpawnedEventElite enters the worldYes-
RPGMobsDeathEventElite or minion diesNoisMinion()
RPGMobsDropsEventLoot is about to spawnYesgetDrops() (live list)
RPGMobsDamageDealtEventElite deals outgoing damageYessetMultiplier(float)
RPGMobsDamageReceivedEventElite takes incoming damageNo-
RPGMobsAbilityStartedEventAbility execution beginsYes-
RPGMobsAbilityCompletedEventAbility finishes normallyNo-
RPGMobsAbilityInterruptedEventAbility was interruptedNogetReason()
RPGMobsAggroEventElite targets an entityNogetTargetRef()
RPGMobsDeaggroEventElite loses its targetNo-
RPGMobsScalingAppliedEventScaling parameters computedNogetHealthMultiplier(), getDamageMultiplier(), getModelScale()
RPGMobsReconcileEventConfig reconciliation passNogetWorldName(), getEntityCount()

Base Event Fields

Every event extends RPGMobsEvent which provides:
  • getWorld() - the World in which the event occurred (since 1.1.0)
  • getEntityRef() - the RPG mob’s Ref<EntityStore>
  • getEntityUuid() - the entity’s UUID, or null if unavailable (since 1.1.0)
  • getTier() - zero-based tier index (0 = Tier 1, 4 = Tier 5)
  • getRoleName() - the NPC role identifier (e.g. "Skeleton_Frost_Knight")

Cancelling Events

Events that implement ICancellable can be cancelled to prevent the action:
@Override
public void onRPGMobSpawned(RPGMobsSpawnedEvent event) {
    if (event.getTier() >= 4) {
        event.setCancelled(true); // Block Tier 5 spawns
    }
}
Cancellable events: RPGMobsSpawnedEvent, RPGMobsDropsEvent, RPGMobsDamageDealtEvent, RPGMobsAbilityStartedEvent.

Query API

The Query API provides read-only access to any RPG mob’s current state. Access it via RPGMobsAPI.query().
import com.frotty27.rpgmobs.api.query.IRPGMobsQueryAPI;

IRPGMobsQueryAPI query = RPGMobsAPI.query();

Identity

MethodReturnsDescription
isRPGMob(ref)booleanWhether the entity is an RPGMobs elite
isMinion(ref)booleanWhether the entity is a summoned minion
getTier(ref)Optional<Integer>Zero-based tier index

Scaling

MethodReturnsDescription
getHealthMultiplier(ref)Optional<Float>Applied health multiplier
getDamageMultiplier(ref)Optional<Float>Applied damage multiplier
getModelScale(ref)Optional<Float>Visual size scale
isHealthFinalized(ref)booleanWhether health scaling has been verified

Progression

MethodReturnsDescription
getSpawnDistance(ref)Optional<Float>Distance from world origin at spawn
getDistanceHealthBonus(ref)Optional<Float>Bonus health from distance
getDistanceDamageBonus(ref)Optional<Float>Bonus damage from distance

Combat

MethodReturnsDescription
isInCombat(ref)booleanWhether the mob has an active target
getLastAggroTarget(ref)Optional<Ref<EntityStore>>Current or last aggro target
getLastAggroTick(ref)Optional<Long>Server tick of last aggro event

Summons

MethodReturnsDescription
getSummonedMinionCount(ref)Optional<Integer>Number of active summoned minions

Query Examples

Check if an entity is an elite and read its tier

IRPGMobsQueryAPI query = RPGMobsAPI.query();

if (query.isRPGMob(entityRef)) {
    int tier = query.getTier(entityRef).orElse(0);
    System.out.println("RPG mob detected  -  Tier " + (tier + 1));
}

Read scaling values for a HUD or tooltip

IRPGMobsQueryAPI query = RPGMobsAPI.query();

Optional<Float> health = query.getHealthMultiplier(entityRef);
Optional<Float> damage = query.getDamageMultiplier(entityRef);
Optional<Float> scale  = query.getModelScale(entityRef);

health.ifPresent(h -> System.out.println("Health multiplier: " + h + "x"));
damage.ifPresent(d -> System.out.println("Damage multiplier: " + d + "x"));
scale.ifPresent(s ->  System.out.println("Model scale: " + s + "x"));

Check combat state and current target

IRPGMobsQueryAPI query = RPGMobsAPI.query();

if (query.isInCombat(entityRef)) {
    query.getLastAggroTarget(entityRef).ifPresent(targetRef -> {
        System.out.println("Currently fighting a target");
    });
    query.getLastAggroTick(entityRef).ifPresent(tick -> {
        System.out.println("Aggro started at server tick " + tick);
    });
}

Read distance-based progression bonuses

IRPGMobsQueryAPI query = RPGMobsAPI.query();

query.getSpawnDistance(entityRef).ifPresent(distance -> {
    System.out.println("Spawned " + distance + "m from world origin");
});

Optional<Float> healthBonus = query.getDistanceHealthBonus(entityRef);
Optional<Float> damageBonus = query.getDistanceDamageBonus(entityRef);

if (healthBonus.isPresent() && damageBonus.isPresent()) {
    System.out.println("Distance bonuses  -  health: +" + healthBonus.get()
        + "x, damage: +" + damageBonus.get() + "x");
}

Monitor summoned minions

IRPGMobsQueryAPI query = RPGMobsAPI.query();

if (query.isMinion(entityRef)) {
    System.out.println("This entity is a summoned minion");
}

query.getSummonedMinionCount(entityRef).ifPresent(count -> {
    System.out.println("Active minions: " + count);
});

Inspect combat state (since 1.2.0)

IRPGMobsQueryAPI query = RPGMobsAPI.query();

// Which mob rule matched this elite
String ruleKey = query.getMatchedMobRuleKey(entityRef, store);
// e.g. "Skeleton_Fighter", "Trork_Warrior"

// Currently executing ability (null if idle)
String abilityId = query.getActiveAbilityId(entityRef, store);
// e.g. "multi_slash_medium", "charge_leap", null

// Combat style driving the CAE behavior
String style = query.getCombatStyle(entityRef, store);
// e.g. "Disciplined", "Berserker", "Tactical", "Chaotic"

Filter minion deaths

@Override
public void onRPGMobDeath(RPGMobsDeathEvent event) {
    if (event.isMinion()) {
        // Skip minion deaths  -  only reward XP for real elites
        return;
    }
    int tier = event.getTier();
    UUID uuid = event.getEntityUuid();
    World world = event.getWorld();
    System.out.println("Elite killed in " + world + "  -  tier " + (tier + 1));
}

Ability IDs

Abilities are identified by string IDs passed in ability events (RPGMobsAbilityStartedEvent, RPGMobsAbilityCompletedEvent, RPGMobsAbilityInterruptedEvent). The built-in ability IDs are:
IDDescription
"charge_leap"Leap toward target with area slam damage
"heal_leap"Jump to safety and drink a healing potion
"undead_summon"Spawn role-based reinforcements
"dodge_roll"Reactive/preemptive lateral dodge with invulnerability frames
"multi_slash_short"1-2 hit quick strikes (3 random variations per weapon type)
"multi_slash_medium"2-4 hit combos (2 random variations per weapon type)
"multi_slash_long"4-6 hit full combos (1 variation per weapon type)
"enrage"Berserk fist-fighting mode ending in exhaustion death
"volley"Ranged projectile burst for bow/crossbow/gun wielders

Example: Full Integration

import com.frotty27.rpgmobs.api.RPGMobsAPI;
import com.frotty27.rpgmobs.api.IRPGMobsEventListener;
import com.frotty27.rpgmobs.api.events.*;

public class MyMod implements IRPGMobsEventListener {

    public void onEnable() {
        RPGMobsAPI.registerListener(this);
    }

    public void onDisable() {
        RPGMobsAPI.unregisterListener(this);
    }

    @Override
    public void onRPGMobSpawned(RPGMobsSpawnedEvent event) {
        // Log spawns for analytics
        System.out.println("Elite spawned: tier=" + event.getTier()
            + " role=" + event.getRoleName());
    }

    @Override
    public void onRPGMobDrops(RPGMobsDropsEvent event) {
        // Double drops for Tier 5 elites
        if (event.getTier() == 4) {
            var drops = event.getDrops();
            drops.addAll(new ArrayList<>(drops));
        }
    }

    @Override
    public void onRPGMobDamageDealt(RPGMobsDamageDealtEvent event) {
        // Check if mob is far from spawn using query API
        var distance = RPGMobsAPI.query()
            .getSpawnDistance(event.getEntityRef());

        if (distance.isPresent() && distance.get() > 5000f) {
            // Extra 10% damage for mobs far from spawn
            event.setMultiplier(event.getMultiplier() * 1.1f);
        }
    }
}

Spawn API (since 1.3.0)

The Spawn API lets other mods programmatically create RPGMobs elites. Access it via RPGMobsAPI.spawn().

Quick Reference

import com.frotty27.rpgmobs.api.RPGMobsAPI;
import com.frotty27.rpgmobs.api.spawn.SpawnResult;
MethodDescription
RPGMobsAPI.spawn().spawnElite(world, roleName, tier, position, rotation, weaponCategory)Creates an NPC and promotes it to an elite in one call
RPGMobsAPI.spawn().applyEliteTier(world, npcRef, tier, weaponCategory)Promotes an already-spawned NPC to an elite
Parameters:
ParameterTypeDescription
worldWorldThe world to spawn in
roleNameStringNPC role name (e.g. "Skeleton_Fighter", "Trork_Warrior", "Goblin_Duke")
tierintTier number 1-5 (clamped if out of range). 1 = Common, 5 = Legendary
positionVector3dSpawn position in world coordinates
rotation@Nullable Vector3fSpawn rotation, or null for default
weaponCategory@Nullable StringWeapon category override (see table below), or null to use the mob rule’s default
npcRefRef<EntityStore>(applyEliteTier only) Reference to an existing NPC entity

Weapon Categories

Pass one of these exact strings as weaponCategory to force the elite to spawn with that weapon type. Pass null to let RPGMobs pick based on the mob rule’s configured categories.
CategoryTypeExample weapons
"Swords"MeleeIron Sword, Steel Sword
"Longswords"MeleeSteel Longsword, Adamantite Longsword
"Daggers"MeleeIron Daggers, Bone Daggers
"Axes"MeleeIron Axe, Steel Axe
"Battleaxes"MeleeIron Battleaxe, Adamantite Battleaxe
"Maces"MeleeIron Mace, Steel Mace
"Clubs"MeleeWooden Club, Bone Club
"ClubsFlail"MeleeFlail variants
"Spears"MeleeIron Spear, Steel Spear
"Pickaxes"MeleeIron Pickaxe, Steel Pickaxe
"Shortbows"RangedShortbow variants (uses vanilla ranged AI)
"Crossbows"RangedCrossbow variants (uses vanilla ranged AI)
"Guns"RangedGun variants (uses vanilla ranged AI)
"Staves"RangedStaff variants (uses vanilla ranged AI)
"Spellbooks"RangedSpellbook variants (uses vanilla ranged AI)
Ranged weapon categories use vanilla Hytale AI instead of the CAE combat system.

Spawn a New Elite

SpawnResult result = RPGMobsAPI.spawn().spawnElite(
    world,                  // the world to spawn in
    "Skeleton_Fighter",     // NPC role name
    3,                      // tier (1-5)
    position,               // Vector3d spawn position
    null,                   // rotation (null = default)
    null                    // weapon category (null = mob rule default)
);

if (result instanceof SpawnResult.Success s) {
    Ref<EntityStore> eliteRef = s.entityRef();  // the spawned entity
    int tier = s.tier();                        // 0-4 (zero-indexed)
    String role = s.roleName();                 // resolved role name
}

Elite-ify an Existing NPC

If you already spawned an NPC via NPCPlugin and want to promote it:
SpawnResult result = RPGMobsAPI.spawn().applyEliteTier(
    world, existingNpcRef, 5, "Longswords");

Force a Weapon Category

// Spawn a T4 Trork with an axe
RPGMobsAPI.spawn().spawnElite(
    world, "Trork_Warrior", 4, pos, null, "Axes");

// Spawn a T5 Skeleton with a longsword
RPGMobsAPI.spawn().spawnElite(
    world, "Skeleton_Fighter", 5, pos, null, "Longswords");

Handle Failures

SpawnResult is a sealed interface. Check success or match on the failure reason:
SpawnResult result = RPGMobsAPI.spawn().spawnElite(world, "Skeleton_Fighter", 3, pos, null, null);

if (result.isSuccess()) {
    var success = (SpawnResult.Success) result;
    // use success.entityRef()
    return;
}

var failure = (SpawnResult.Failure) result;
switch (failure.reason()) {
    case CONFIG_NOT_LOADED       -> log("RPGMobs config not ready");
    case NPC_SPAWN_FAILED        -> log("Hytale couldn't spawn the NPC");
    case NO_MOB_RULE             -> log("No mob rule matches this role");
    case MOB_RULE_DISABLED       -> log("Mob rule is disabled in this world");
    case RPGMOBS_DISABLED_IN_WORLD -> log("RPGMobs is off in this world");
    case EVENT_CANCELLED         -> log("A listener cancelled the spawn");
    case NOT_INITIALIZED         -> log("RPGMobs hasn't started yet");
    case TIER_APPLY_FAILED       -> log("Internal error: " + failure.message());
}

Threading

All spawn methods must be called on the world’s thread. From other threads, wrap in world.execute():
world.execute(() -> {
    RPGMobsAPI.spawn().spawnElite(world, "Goblin_Duke", 5, pos, null, null);
});

Thread Safety

Event callbacks are dispatched from the server’s main tick thread. Do not perform blocking I/O or long-running computations inside event handlers. If you need async processing, queue work to a separate thread and return immediately. The Query API reads component data from the entity store and should only be called from contexts where the store is accessible (event handlers, tick systems, or world.execute() callbacks).