JavaScript Scripting
EmakiAttribute registers a scripting module through the CoreLib scripting system. The module ID is attribute. emaki.module("attribute") returns a different object depending on context:
- In ordinary trigger scripts or Mythic mechanic scripts it returns the module facade
ScriptAttributeModuleApi, used for source management, item attribute PDC read/write, and damage calculation and application. - In Attribute-specific extension scripts (the
extensions/attribute/directory, loaded throughregister()) it returns the registration API, used to register runtime attributes, damage types, damage pipelines, attribute providers and damage hooks.
This page covers only EmakiAttribute-specific content. For the generic scripting mechanism see CoreLib JavaScript Scripting.
Always guard with
available()before calling any business method. EmakiAttribute is a CoreLib softdepend.
Module facade (trigger script context)
In ordinary trigger scripts, emaki.module("attribute") returns the module facade.
| Method | Returns | Description |
|---|---|---|
available() | boolean | Whether the module is available. |
The module facade only has the
available()probe; there is noready()/apiVersion()/pluginName().
Source management
| Method | Returns | Description |
|---|---|---|
registerSource(sourceId) | boolean | Register an attribute source. |
unregisterSource(sourceId) | void | Unregister a source. |
isRegisteredSource(sourceId) | boolean | Whether the source is registered. |
registeredSources() | list | Return a copy of the registered source list. |
Item attribute PDC read/write
The methods below take an itemKey, which resolves an ItemStack from the current action context by attribute key.
| Method | Returns | Description |
|---|---|---|
read(itemKey, sourceId) | map | Read the attribute payload of a source. |
readAll(itemKey) | map | Read the payload of all sources. |
write(itemKey, sourceId, attributes, meta) | boolean | Write attributes; attributes is converted to a double map, meta to a string map. |
clear(itemKey, sourceId) | boolean | Clear a source. |
clearAll(itemKey) | void | Clear all sources. |
The payload projection seen by scripts contains six fields —
sourceId,attributes,meta,conditions,schemaVersion, andupdatedAt— produced by EmakiAttribute's ownscript/ScriptAttributeDtoMapper.
Damage calculation
| Method | Returns | Description |
|---|---|---|
applyDamage(attacker, target, damageTypeId, baseDamage, damageContext) | boolean | Actually apply damage to the target; returns false when target is null. |
calculateDamage(attacker, target, damageTypeId, baseDamage, damageContext) | map | Calculate only without applying; returns a damage result Map. |
setDamageTypeOverride(entity, damageTypeId) | void | Set a damage-type override for the entity's next damage. |
attackermay be null, buttargetmust resolve to a living entity. A nulldamageContextis treated as an empty Map. These methods are invoked through the EmakiAttribute service by reflection and return false / empty Map / no-op respectively when the service is missing.
Mythic mechanic script example
EmakiAttribute ships the Mythic mechanic script scripts/mythic/mythic_js_damage.js, showing how to call applyDamage from MythicMobs:
function mythicDamage(meta, args) {
const caster = meta.caster();
const target = meta.firstTarget();
if (!target.exists()) {
return { skipped: true, message: "No MythicMobs target." };
}
const damage = Number(read(args, "damage", read(args, "base", meta.power())));
const damageType = String(read(args, "damage_type", read(args, "type", "default")));
if (!Number.isFinite(damage) || damage <= 0) {
return { success: false, message: "damage must be a positive number" };
}
const attribute = emaki.module("attribute");
const applied = attribute.applyDamage(caster, target, damageType, damage, {
source: "mythic_js",
mythic_mechanic: meta.mechanic(),
mythic_power: meta.power(),
mythic_cause: meta.cause()
});
return {
success: applied,
message: applied ? "Mythic JS damage applied." : "Attribute damage pipeline unavailable.",
output: { target: target.uuid(), damage: damage, damage_type: damageType }
};
}
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;
}Action script example
EmakiAttribute ships the trigger-style example script scripts/examples/attribute_buff.js:
function main(ctx) {
const attribute = emaki.module("attribute");
if (!attribute.available()) {
return { success: false, message: "EmakiAttribute module is unavailable" };
}
if (!emaki.player.exists()) {
return { success: false, message: "No player context" };
}
return emaki.action.run("attribute_add", {
effect_id: "js_example_buff",
attribute: "attack",
value: "5",
duration_ticks: "10s"
});
}Registration API (extension script context)
In extension scripts under the extensions/attribute/ directory, CoreLib invokes the script's register() function and binds the registration API as emaki.module("attribute").
| Method | Returns | Description |
|---|---|---|
registerAttribute(definition) | boolean | Register a runtime attribute definition. |
registerDamageType(definition) | boolean | Register a damage type. |
registerDamagePipeline(definition) | boolean | Register a damage pipeline. |
registerProvider(definition) | boolean | Register an attribute contribution provider. |
onDamage(definition) | boolean | Register a damage event hook. |
registerAttribute definition fields
Every field accepts both camelCase and snake_case spellings.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
id | string | yes | none | Attribute id; registration fails when blank. |
displayName | string | no | empty | Display name. |
valueKind | string | no | FLAT | Value kind enum (uppercase, invalid falls back to FLAT). |
targetType | string | no | GENERIC | Target type enum. |
targetId | string | no | empty | Target id. |
mmoItemsStat | string | no | empty | MMOItems stat mapping. |
defaultValue | number | no | 0 | Default value. |
minValue | number | no | null | Minimum value (nullable). |
maxValue | number | no | null | Maximum value (nullable). |
allowNegative | boolean | no | true | Whether negative values are allowed. |
priority | int | no | 0 | Priority. |
loreFormatId | string | no | empty | Lore format id. |
lorePatterns | list | no | empty | Lore parsing regex list. |
description | string | no | empty | Description. |
attributePower | number | no | 1.0 | Attribute power. |
registerDamagePipeline definition fields
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
id | string | yes | none | Pipeline id, also the matching damage type id. |
function | string | yes | none | Calculation function name; calculate also works; fails when both id and function are blank. |
timeoutMillis | number | no | engine default | Per-call script timeout. |
At runtime the pipeline is matched by damage type id (one damage type maps to one pipeline).
registerProvider definition fields
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
id | string | yes | none | Provider id; registration fails when blank. |
priority | int | no | 0 | Priority. |
function | string | no | collect | Collection function name; collect also works. |
The collection function receives an entity and returns an attribute map (or a structure with attributes / sourceId, or an iterable list); items whose attribute value is not numeric are skipped.
onDamage definition fields
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
id | string | yes | none | Hook id; registration fails when blank. |
function | string | yes | none | Callback function name; registration fails when blank. |
priority | int | no | 0 | Priority; runs in descending order (higher first). |
damageTypes | list | no | empty (all types) | Damage types the hook applies to. |
The
registerDamageTypedefinition is parsed by the damage type model and requires a non-blankid.
Damage hook callback event API
A damage hook callback (the onDamage function) receives the damage event API:
| Method | Returns | Description |
|---|---|---|
attacker() / target() / projectile() | entity | Related entities. |
damageTypeId() | string | Damage type id. |
baseDamage() / finalDamage() | double | Base / final damage. |
setFinalDamage(value) | void | Set final damage (clamped non-negative). |
multiplyDamage(multiplier) | void | Multiply (floor 0). |
cancel() / cancelled() | void / boolean | Cancel the event / whether cancelled. |
critical() / roll() | boolean / double | Whether critical / roll value. |
context() | map | Event context (explicit variables injected by script or skill). |
stageValues() | map | Per-stage damage values. |
attackerAttribute(id) / targetAttribute(id) | double | Single attribute value. |
attackerAttributes() / targetAttributes() | map | All attributes. |
attackerSnapshot() / targetSnapshot() | map | Attribute snapshots. |
meta(key) / setMeta(key, value) / meta() | object / void / map | Hook custom metadata. |
Damage pipeline callback ctx API
A damage pipeline callback (the registerDamagePipeline function) receives the damage context API; besides the read methods above it also provides:
| Method | Returns | Description |
|---|---|---|
cause() | string | Damage cause name. |
sourceDamage() / baseDamage() | double | Source / base damage. |
variable(key) / setVariable(key, value) / variables() | object / void / map | Variable read/write. |
setDamage(value) / damage() | void / double | Set / read damage. |
setCritical(value) / critical() | void / boolean | Critical flag. |
setRecovery(value) / recovery() | void / double | Recovery value. |
healAttacker(amount) / healTarget(amount) | void | Direct healing. |
The pipeline callback return object may include: success, cancelled, damage or finalDamage, critical, recovery, stageValues (or stages).
Registration example
EmakiAttribute ships the extension-style example script scripts/examples/js_fire_mastery.js:
function register() {
const attribute = emaki.module("attribute");
attribute.registerAttribute({
id: "js_fire_mastery",
displayName: "Fire Mastery",
valueKind: "FLAT",
targetType: "GENERIC",
defaultValue: 0,
minValue: 0,
allowNegative: false,
priority: 100,
lorePatterns: ["Fire Mastery: \\+(?<value>[0-9.]+)"],
description: "Runtime attribute registered by the bundled JavaScript extension example",
attributePower: 1.0
});
attribute.registerProvider({
id: "js_fire_mastery_provider",
priority: 100,
function: "collectFireMastery"
});
attribute.onDamage({
id: "js_fire_mastery_damage",
priority: 100,
damageTypes: ["fire", "magic", "lightning"],
function: "boostFireDamage"
});
}
function collectFireMastery(entity) {
return [];
}
function boostFireDamage(event) {
const context = event.context();
const mastery = Number(read(context, "js_fire_mastery", 0));
if (!Number.isFinite(mastery) || mastery <= 0) {
return { skipped: true, message: "js_fire_mastery context value not found" };
}
const multiplier = 1 + mastery / 100;
event.multiplyDamage(multiplier);
event.setMeta("js_fire_mastery_boost", mastery);
return {
success: true,
message: "JavaScript fire mastery damage hook applied",
output: { mastery: mastery, multiplier: multiplier, final_damage: event.finalDamage() }
};
}
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
Attribute extension scripts load from extensions/attribute/ under the CoreLib script root:
plugins/EmakiCoreLib/scripts/extensions/attribute/js_fire_mastery.js/corelib script reloadThe example file is released to
plugins/EmakiCoreLib/scripts/examples/by default (the Mythic script is released toscripts/mythic/) and is not auto-enabled. Copy it intoextensions/attribute/and reload to enable it.
Notes
- All registrations / hooks / pipelines / providers are silently skipped when the CoreLib scripting system is not enabled.
- Damage hooks run in descending priority order (higher first), the opposite of Forge / Cooking / Level rules (ascending). Keep this in mind when mixing them.
- A damage hook that throws is caught and logged without interrupting other hooks; once a hook cancels the event, subsequent hooks stop.
- On reload, extension scripts first unregister previous registrations (providers, runtime attributes, damage types, hooks, pipelines), then re-run
register(), and finally refresh caches and resync all players. - When CoreLib is missing the module degrades gracefully.