ArdaNPCs WIKI & GUIDES
/
Guide 05 1.1.0

NPC scripts

Events, common API and ready-to-use examples for healing, conditions, quests and mail.

Your first script

ArdaNPCs 1.1.0 scripts are server events with a limited JavaScript API. A trusted administrator with OP level 2 edits them. CustomNPCs scripts cannot simply be pasted unchanged: event names and exposed objects differ.

  1. Open the NPC editor and choose Open scripts….
  2. Select interact. Paste only the event body, such as api.message("Welcome!");. Do not add a function interact(e) { ... } wrapper.
  3. Use Check to validate every event’s syntax. Test simulates the selected event with the administrator as player; it does not change the world, variables, timers, quests or mail.
  4. Enable scripts and click Save. Wait for server confirmation. The enabled setting applies to every event on this NPC.
  5. Close the editor and trigger a real interaction. Read output and errors with Output.

A test is not a gameplay check: it uses sample values such as event.timer = "test", so it may skip a branch checking your actual ID. Try these examples in a copy of your world first. If an event already contains code, combine the relevant blocks; pasting over the old text replaces it.

Events and context

EventTriggerExtra fields
initFirst NPC load in the server session, and again after saving scripts.—
tickA loaded NPC at the configured interval of 1–3600 seconds.—
interactA player interacts with the NPC.—
dialogAn actual dialog is opened.dialogId
questA player quest change is committed.questId, stage: accepted, completed, failed
damageBefore NPC damage; a player is present when they are the attacker.damage
deathNPC death; a player is present when they are the attacker.—
timerDelivery of a named timer.timer
predicateCondition evaluation; must return a boolean.key

Common fields are event.type, npcId, npcName, hasPlayer, playerId and playerName. Use the event. prefix for each. Without a player, the ID is empty; check event.hasPlayer before calling player API. All host types also expose event.hostId, hostKind, hostName and ownerNpcId. The API exposes no Minecraft or Java objects.

Common API reference

Use complete namespaced item, effect and sound IDs, such as minecraft:bread. Variable keys and timer names contain 1–48 characters from [A-Za-z0-9_.:-]. A predicate is read-only; changing data, timers, quests or the world raises an error.

CallMeaning
api.get(key), api.set(key, value), api.has(key), api.remove(key)Persistent NPC/instance data. Values are strings; a missing key returns an empty string. Use Number(...) for numbers.
api.playerGet(key), api.playerSet(key, value), api.playerHas(key), api.playerRemove(key)Separate data for the event player at this NPC/instance. Require a player.
api.message(text), api.say(text)Message the event player / all players within 32 blocks in the same dimension; up to 256 characters.
api.heal(amount)Heal the event player by 0–40 health points, up to their maximum.
api.give(itemId, count)Give the player 1–64 items, limited by that item’s stack size. Drop any inventory overflow.
api.hasItem(itemId, count)Return a boolean from the player’s inventory; count 1–4096.
api.questProgress(questId, objectiveId, amount)Queue progress for an owned MANUAL objective; integer 1–10,000. See the example below.
api.mail(title, body, key)Queue story mail for the player. Title 1–80 and body 1–2000 characters; stable key of 1–64 characters from [A-Za-z0-9_.:-]. No attachments or arbitrary recipients.
api.sound(soundId, volume, pitch)Sound at the NPC/host position; volume 0–1, pitch 0.5–2. The ID must exist in the registry.
api.effect(effectId, seconds, amplifier)Apply an effect to the player for 1–300 seconds; amplifier 0–4 (0 is level I).
api.spawn(entityId, count)1–2 mobs; only minecraft:zombie, skeleton, spider, slime, cow, sheep, pig, all with the minecraft: prefix. Space and shared quotas still apply.
api.timer(name, seconds, repeating), api.cancelTimer(name)Create/cancel a host timer for 1–3600 seconds. The same name replaces the old timer; true repeats, false runs once.
api.cancel()Consume the normal interaction in an interaction event; prevent the corresponding damage in damage. Does not undo completed changes.
api.log(text)Write diagnostics, up to 300 characters; the last 10 lines are retained.
api.time(), api.dimension(), api.raining()Overworld time of day in ticks / host dimension ID / rain. Time commands and the daylight-cycle rule affect time(); use a timer for a countdown.
api.playerHealth(), api.playerLevel(), api.playerTag(tag)Health / XP level / whether the event player has a command tag.
api.creative(), api.playerGamemode()Creative-mode boolean / numeric player game-mode ID.
api.hostX(), api.hostY(), api.hostZ()Coordinates of the NPC, item or block running the script.
api.playerX(), api.playerY(), api.playerZ()Player coordinates; calling without a player is an error.
api.x(), api.y(), api.z()Player coordinates when present; otherwise host coordinates.

Example: healer with a cooldown

Paste this into interact:

if (!event.hasPlayer) return;
if (api.playerGet("heal_wait") === "1") {
    api.message("Please wait before healing again.");
    return;
}
api.playerSet("heal_wait", "1");
api.timer("heal:" + event.playerId, 30, false);
api.heal(8);
api.message("Your wounds are healing.");

Paste this into the same NPC’s timer event:

if (event.hasPlayer && event.timer === "heal:" + event.playerId) {
    api.playerRemove("heal_wait");
}

The first click heals up to 8 health points (4 hearts). A one-shot timer permits the next heal after 30 game seconds. Its name includes the player UUID, so players do not replace each other’s timers. Delivery waits while the NPC is unloaded or the player is offline. One NPC has at most 16 timers including other scripts; this simple example suits small groups.

Example: XP condition

Paste this into predicate:

return event.key === "experienced"
    && event.hasPlayer
    && api.playerLevel() >= 5;

In a dialog or quest condition, select SCRIPT_PREDICATE and the key experienced. Players at XP level 5 or higher pass. Every execution path must return true or false; do not send messages or write data here. For simple numeric variables, use SCRIPT_VARIABLE_MIN with player:name or npc:name.

Example: a manual quest objective

First create a quest on this NPC and add a MANUAL objective with required count 1. Copy the actual quest and objective IDs from the editor; objective IDs are generated automatically. Replace patrani below with the actual quest ID and stopa_les with the actual objective ID. The player must accept the quest. Paste the adjusted code into interact:

if (api.questProgress("patrani", "stopa_les", 1)) {
    api.message("Recording this clue has been queued.");
} else {
    api.message("You do not need this clue right now.");
}

true means queued, not a confirmed write. Check the quest journal after a real interaction. Inactive or completed objectives and locked SEQUENTIAL steps return false; a missing quest or incorrect objective type is a configuration error. Do not replace actual quest state with a custom “finished” variable.

Example: story mail

Paste this into an NPC’s interact event:

if (api.mail("Invitation", "Return to the keeper for your next task.", "chapter1_invitation")) {
    api.message("Mail queued. Check your inbox.");
}

The stable key prevents repeated delivery for the same NPC and player. false can mean an already-used key, a full inbox or a limit; it is not proof of delivery. The recipient is the event player. Scripted items and blocks need an existing owner NPC for both mail and quest progress.

Persistence and limits

  • One event: 12,288 characters; all events: 32,768. One execution: 60,000 instructions. NPCs, items and blocks share server budgets, including 64 events per tick.
  • Each NPC/instance or instance + player data scope: 64 keys, 1,024 characters per value and 16,384 characters total. The world supports up to 4,096 scopes.
  • 16 timers per host and 1,024 total. Timers use game time, survive restarts and do not accumulate missed repetitions.
  • Up to 8 external effects and 2 spawned mobs per event. Additional per-minute quotas limit spam.

Code is saved with the NPC configuration; state and timers live in <world>/data/ardanpcs/scripts-state.json. A clone retains the code but has a new UUID and fresh state. Variables and timers commit before world effects; a server crash between these steps may leave an effect unapplied. This is not a single guaranteed transaction across every system.

A faulty event blocks that host’s scripts. Read diagnostics, fix the error and save again. Do not run unreviewed third-party code: the environment is limited but runs in the server process. Java, files, network access, commands, eval, new constructors and parts of ordinary JavaScript are unavailable. Continue to scripted items and blocks or troubleshooting.