Skip to content

JavaScript Scripting

EmakiSkills registers a scripting module through the CoreLib scripting system. The module ID is skills. emaki.module("skills") returns a different object depending on context:

  • In ordinary trigger scripts (runjs action, examples/, etc.) it returns the query facade ScriptSkillsModuleApi, used for probing and querying registered skill script actions.
  • In Skills-specific extension scripts (the extensions/skills/ directory, loaded through register()) it returns the registration API, used to register custom skill script actions.

This page covers only EmakiSkills-specific content. For the generic scripting mechanism see CoreLib JavaScript Scripting.

Always guard with available() before calling any business method. EmakiSkills is a CoreLib softdepend.

Query facade (trigger script context)

In ordinary trigger scripts, emaki.module("skills") returns the query facade.

MethodReturnsDescription
available()booleanWhether the module is available.
hasScriptAction(actionId)booleanWhether the registry contains the skill script action id.
registeredScriptActions()listReturn the list of all registered skill script action ids.

The query facade only has the methods above; there is no ready() / apiVersion() / pluginName(). Use .size() on the returned list in JS.

Trigger scripts can read the skill context via emaki.context:

AccessKeyDescription
emaki.context.placeholder("skills_skill_id")skills_skill_idSkill id (placeholder form).
emaki.context.attribute("skill_id")skill_idSkill id (attribute form).

Trigger example

EmakiSkills ships the trigger-style example script scripts/examples/skills_upgrade_success.js:

js
function main(ctx) {
  const skills = emaki.module("skills");
  const skillId = emaki.context.placeholder("skills_skill_id") || emaki.context.attribute("skill_id");
  if (emaki.player.exists()) {
    emaki.player.sendMessage("[EmakiJS] Skill upgrade success script: " + skillId + " skills=" + skills.available());
  }
  return {
    success: true,
    output: {
      skill_id: String(skillId || ""),
      skills_available: skills.available(),
      registered_script_actions: skills.registeredScriptActions().size()
    }
  };
}

Registration API (extension script context)

In extension scripts under the extensions/skills/ directory, CoreLib invokes the script's register() function and binds the registration API as emaki.module("skills").

MethodReturnsDescription
registerAction(definition)booleanRegister a custom skill script action.
unregisterAction(actionId)voidUnregister a skill script action by id.

Action definition fields

FieldTypeRequiredDefaultDescription
idstringyesnoneAction id (normalized); registration fails when blank.
categorystringnojavascriptAction category.
descriptionstringnosame as idDescription.
parameterslistnoemptyParameter definitions, see below.
executionModestringnoSYNCExecution mode, SYNC or ASYNC_IO (case-insensitive, invalid falls back to SYNC).
timeoutMillisnumbernoengine defaultPer-call script timeout.
executestringnoexecuteExecution function name.
validatestringnoempty (no validation)Validation function name.

Each parameters element:

FieldTypeRequiredDefaultDescription
namestringyesnoneParameter name (normalized); skipped when blank.
typestringnoSTRINGParameter type enum (case-insensitive, invalid falls back to STRING).
requiredbool / stringnofalseWhether required.
defaultValuestringnoemptyDefault value, used only for optional parameters.
descriptionstringnoemptyDescription.

Execute and validate functions

  • execute(ctx, args): execution function; ctx is the skill script context API (below), args is the parameter Map.
  • validate(args): validation function; receives only the parameter Map.

Execution mode ASYNC_IO runs on an async thread; SYNC runs synchronously on the calling thread. The return value maps to a skill action result: {skipped:true} skips, success returns output / return / message, and {success:false} fails.

Skill script context API (ctx)

MethodReturnsDescription
skillId()stringCurrent skill id.
triggerId()stringTrigger id.
variable(key)stringRead a variable.
setVariable(key, value)voidSet a variable.
variables()mapAll variables.
caster()entityCaster entity wrapper.
target()entityTarget entity wrapper.
hasTarget()booleanWhether there is a target.
setTarget(entity)voidSet the target.
targetLocation()mapTarget location, keys world / x / y / z / yaw / pitch.
runAction(actionId, arguments)booleanRun a CoreLib action.
runActionLine(line)booleanRun a single action line.
castMythic(mythicSkillId)booleanCast a MythicMobs skill.
castMythic(mythicSkillId, parameters)booleanCast a MythicMobs skill with parameters.
applyDamage(target, damageTypeId, baseDamage)booleanApply damage through EmakiAttribute.
applyDamage(target, damageTypeId, baseDamage, damageContext)booleanApply damage with a damage context.

ctx.state is the cross-call shared state accessor: get(key), set(key, value), has(key), remove(key).

Registration example

EmakiSkills ships the extension-style example script scripts/examples/js_lightning_strike.js:

js
function register() {
  const skills = emaki.module("skills");
  skills.registerAction({
    id: "js_lightning_strike",
    category: "javascript",
    description: "Strike the current skill target and optionally deal damage through EmakiAttribute",
    executionMode: "SYNC",
    timeoutMillis: 1000,
    parameters: [
      { name: "damage", type: "DOUBLE", required: false, defaultValue: "10", description: "Base damage to apply" },
      { name: "damage_type", type: "STRING", required: false, defaultValue: "lightning", description: "Emaki attribute damage type id" }
    ],
    validate: "validateLightning",
    execute: "executeLightning"
  });
}

function validateLightning(args) {
  const damage = Number(read(args, "damage", "10"));
  if (!Number.isFinite(damage) || damage <= 0) {
    return { success: false, message: "Damage must be a positive number" };
  }
  return true;
}

function executeLightning(ctx, args) {
  const caster = ctx.caster();
  const target = ctx.target();
  if (!target.exists()) {
    return { skipped: true, message: "No target selected" };
  }

  const damage = Number(read(args, "damage", "10"));
  const damageType = String(read(args, "damage_type", "lightning"));

  let applied = false;
  const attribute = emaki.module("attribute");
  if (attribute.available()) {
    applied = attribute.applyDamage(caster, target, damageType, damage, {
      source: "js_lightning_strike",
      skill_id: ctx.skillId(),
      trigger_id: ctx.triggerId()
    });
  }
  if (!applied) {
    target.damage(damage);
  }

  return { success: true, message: "JavaScript lightning skill executed" };
}

function read(object, key, fallback) {
  if (object == null) {
    return fallback;
  }
  if (typeof object.get === "function") {
    const value = object.get(key);
    return value == null ? fallback : value;
  }
  const value = object[key];
  return value == null ? fallback : value;
}

Enabling extension scripts

Skills extension scripts load from extensions/skills/ under the CoreLib script root:

text
plugins/EmakiCoreLib/scripts/extensions/skills/js_lightning_strike.js
text
/corelib script reload

The example file is released to plugins/EmakiCoreLib/scripts/examples/ by default and is not auto-enabled. Copy it into extensions/skills/ and reload to enable it.

Built-in skill script actions

EmakiSkills ships a set of built-in Java skill script actions that skill configs can call directly:

Action idCategoryKey parameters
messagefeedbacktext (required); target (default caster).
soundfeedbacksound (required); volume=1; pitch=1; at=caster.
particlefeedbackparticle (required); at=target; count=1; speed=0.
raytargetrange=16; width=1; save=target (key saved into state).
damagecombatamount (required); target=target; damage_type=generic; element=empty.
aoe_damagecombatamount (required); radius=5; center=target; shape=sphere; filter=hostile; max_targets=20; exclude_caster=true.
projectilecombatspeed=1.5; gravity=0.05; lifetime=60; hit_radius=0.5; pierce=0; homing=false; particle=FLAME; damage=0; direction=look.
ignitecombatticks (required); target=target.
healcombatamount (required); target=caster.
mythicbridgeskill (required, Mythic skill id).

Target resolution: caster / self / player is the caster; look is roughly 3 blocks ahead of the line of sight; any other string is first looked up in state shared values, otherwise the target entity is used.

Notes

  • Registration methods require the CoreLib scripting system to be enabled; otherwise registration returns false or is silently skipped.
  • applyDamage is invoked through EmakiAttribute by reflection; it returns false when EmakiAttribute is missing, and the target must be a living entity.
  • When CoreLib is missing the module degrades gracefully.
  • On reload, extension scripts first unregister previously registered actions, then re-run register(), avoiding hot-reload leftovers.