JavaScript 脚本
EmakiAttribute 通过 CoreLib 的脚本系统注册脚本模块,模块 ID 为 attribute。emaki.module("attribute") 在不同上下文中返回不同对象:
- 在普通触发脚本或 Mythic 机制脚本中,返回模块门面
ScriptAttributeModuleApi,用于来源管理、物品属性 PDC 读写、伤害计算与施加。 - 在 Attribute 专属扩展脚本(
extensions/attribute/目录,通过register()加载)中,返回注册 API,用于注册运行时属性、伤害类型、伤害管线、属性提供者和伤害钩子。
本页只介绍 EmakiAttribute 专属内容。通用脚本机制请阅读 CoreLib JavaScript 脚本系统。
调用任何业务方法前,先用
available()守卫。EmakiAttribute 是 CoreLib 的 softdepend。
模块门面(触发脚本上下文)
在普通触发脚本中,emaki.module("attribute") 返回模块门面。
| 方法 | 返回 | 说明 |
|---|---|---|
available() | boolean | 模块是否可用。 |
模块门面只有
available()探针,没有ready()/apiVersion()/pluginName()。
来源管理
| 方法 | 返回 | 说明 |
|---|---|---|
registerSource(sourceId) | boolean | 注册一个属性来源。 |
unregisterSource(sourceId) | void | 注销来源。 |
isRegisteredSource(sourceId) | boolean | 来源是否已注册。 |
registeredSources() | list | 返回已注册来源列表副本。 |
物品属性 PDC 读写
以下方法接收 itemKey,表示从当前动作上下文中按 attribute key 解析出的 ItemStack。
| 方法 | 返回 | 说明 |
|---|---|---|
read(itemKey, sourceId) | map | 读取指定来源的属性载荷。 |
readAll(itemKey) | map | 读取全部来源载荷。 |
write(itemKey, sourceId, attributes, meta) | boolean | 写入属性;attributes 转 double 映射,meta 转 string 映射。 |
clear(itemKey, sourceId) | boolean | 清除指定来源。 |
clearAll(itemKey) | void | 清除全部来源。 |
脚本读到的 payload 投影包含
sourceId、attributes、meta、conditions、schemaVersion、updatedAt六个字段,由 EmakiAttribute 自身的script/ScriptAttributeDtoMapper生成。
伤害计算
| 方法 | 返回 | 说明 |
|---|---|---|
applyDamage(attacker, target, damageTypeId, baseDamage, damageContext) | boolean | 对 target 实际施加伤害;target 为 null 返回 false。 |
calculateDamage(attacker, target, damageTypeId, baseDamage, damageContext) | map | 仅计算不施加,返回伤害结果 Map。 |
setDamageTypeOverride(entity, damageTypeId) | void | 为实体设置下一次伤害的类型覆盖。 |
attacker可为 null,target必须可解析为生物实体。damageContext为 null 时按空 Map 处理。这些方法经 EmakiAttribute 服务反射调用,服务缺失时分别返回 false / 空 Map / 无操作。
Mythic 机制脚本示例
EmakiAttribute 自带 Mythic 机制脚本 scripts/mythic/mythic_js_damage.js,演示从 MythicMobs 调用 applyDamage:
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;
}动作脚本示例
EmakiAttribute 自带触发型示例脚本 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"
});
}注册 API(扩展脚本上下文)
在 extensions/attribute/ 目录下的扩展脚本中,CoreLib 调用脚本的 register() 函数并把注册 API 绑定为 emaki.module("attribute")。
| 方法 | 返回 | 说明 |
|---|---|---|
registerAttribute(definition) | boolean | 注册运行时属性定义。 |
registerDamageType(definition) | boolean | 注册伤害类型。 |
registerDamagePipeline(definition) | boolean | 注册伤害管线。 |
registerProvider(definition) | boolean | 注册属性贡献提供者。 |
onDamage(definition) | boolean | 注册伤害事件钩子。 |
registerAttribute definition 字段
每个字段都支持驼峰与下划线两种写法。
| 字段 | 类型 | 必填 | 默认 | 说明 |
|---|---|---|---|---|
id | string | 是 | 无 | 属性 id;为空注册失败。 |
displayName | string | 否 | 空 | 显示名。 |
valueKind | string | 否 | FLAT | 取值类型枚举(大写解析,非法回退 FLAT)。 |
targetType | string | 否 | GENERIC | 目标类型枚举。 |
targetId | string | 否 | 空 | 目标 id。 |
mmoItemsStat | string | 否 | 空 | MMOItems 词条映射。 |
defaultValue | number | 否 | 0 | 默认值。 |
minValue | number | 否 | null | 最小值(可空)。 |
maxValue | number | 否 | null | 最大值(可空)。 |
allowNegative | boolean | 否 | true | 是否允许负值。 |
priority | int | 否 | 0 | 优先级。 |
loreFormatId | string | 否 | 空 | Lore 格式 id。 |
lorePatterns | list | 否 | 空 | Lore 解析正则列表。 |
description | string | 否 | 空 | 描述。 |
attributePower | number | 否 | 1.0 | 属性强度。 |
registerDamagePipeline definition 字段
| 字段 | 类型 | 必填 | 默认 | 说明 |
|---|---|---|---|---|
id | string | 是 | 无 | 管线 id,同时作为匹配的伤害类型 id。 |
function | string | 是 | 无 | 计算函数名,也可用 calculate;与 id 同时为空则失败。 |
timeoutMillis | number | 否 | 引擎默认超时 | 单次脚本超时。 |
运行时按伤害类型 id 匹配对应管线(一个伤害类型对应一个管线)。
registerProvider definition 字段
| 字段 | 类型 | 必填 | 默认 | 说明 |
|---|---|---|---|---|
id | string | 是 | 无 | 提供者 id;为空注册失败。 |
priority | int | 否 | 0 | 优先级。 |
function | string | 否 | collect | 采集函数名,也可用 collect。 |
采集函数收到实体参数,返回属性映射 Map(或含 attributes / sourceId 的结构,或可迭代列表);属性值非数值的项会被跳过。
onDamage definition 字段
| 字段 | 类型 | 必填 | 默认 | 说明 |
|---|---|---|---|---|
id | string | 是 | 无 | 钩子 id;为空注册失败。 |
function | string | 是 | 无 | 回调函数名;为空注册失败。 |
priority | int | 否 | 0 | 优先级,降序执行(高优先级先跑)。 |
damageTypes | list | 否 | 空(全部类型) | 限定生效的伤害类型集合。 |
registerDamageType的 definition 由伤害类型模型解析,需要id非空。
伤害钩子回调的 event API
伤害钩子回调(onDamage 的 function)收到伤害事件 API:
| 方法 | 返回 | 说明 |
|---|---|---|
attacker() / target() / projectile() | entity | 相关实体。 |
damageTypeId() | string | 伤害类型 id。 |
baseDamage() / finalDamage() | double | 基础 / 最终伤害。 |
setFinalDamage(value) | void | 设定最终伤害(取非负)。 |
multiplyDamage(multiplier) | void | 乘系数(下限 0)。 |
cancel() / cancelled() | void / boolean | 取消事件 / 是否已取消。 |
critical() / roll() | boolean / double | 是否暴击 / 掷骰值。 |
context() | map | 事件上下文(脚本或技能注入的显式变量)。 |
stageValues() | map | 伤害各阶段值。 |
attackerAttribute(id) / targetAttribute(id) | double | 单个属性值。 |
attackerAttributes() / targetAttributes() | map | 全部属性。 |
attackerSnapshot() / targetSnapshot() | map | 属性快照。 |
meta(key) / setMeta(key, value) / meta() | object / void / map | 钩子自定义元数据。 |
伤害管线回调的 ctx API
伤害管线回调(registerDamagePipeline 的 function)收到伤害上下文 API,除上面读类方法外还提供:
| 方法 | 返回 | 说明 |
|---|---|---|
cause() | string | 伤害原因名。 |
sourceDamage() / baseDamage() | double | 来源 / 基础伤害。 |
variable(key) / setVariable(key, value) / variables() | object / void / map | 变量读写。 |
setDamage(value) / damage() | void / double | 设定 / 读取伤害。 |
setCritical(value) / critical() | void / boolean | 暴击标记。 |
setRecovery(value) / recovery() | void / double | 回复值。 |
healAttacker(amount) / healTarget(amount) | void | 直接治疗。 |
管线回调返回对象可含:success、cancelled、damage 或 finalDamage、critical、recovery、stageValues(或 stages)。
注册示例
EmakiAttribute 自带扩展型示例脚本 scripts/examples/js_fire_mastery.js:
function register() {
const attribute = emaki.module("attribute");
attribute.registerAttribute({
id: "js_fire_mastery",
displayName: "火焰精通",
valueKind: "FLAT",
targetType: "GENERIC",
defaultValue: 0,
minValue: 0,
allowNegative: false,
priority: 100,
lorePatterns: ["火焰精通: \\+(?<value>[0-9.]+)"],
description: "由捆绑JavaScript扩展示例注册的运行时属性",
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上下文值" };
}
const multiplier = 1 + mastery / 100;
event.multiplyDamage(multiplier);
event.setMeta("js_fire_mastery_boost", mastery);
return {
success: true,
message: "JavaScript火焰精通伤害钩子已应用",
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;
}启用扩展脚本
Attribute 扩展脚本加载自 CoreLib 脚本根目录下的 extensions/attribute/:
plugins/EmakiCoreLib/scripts/extensions/attribute/js_fire_mastery.js/corelib script reload示例文件默认释放到
plugins/EmakiCoreLib/scripts/examples/(Mythic 脚本释放到scripts/mythic/),不会自动启用。需要启用扩展时复制到extensions/attribute/再重载。
注意事项
- 所有注册/钩子/管线/提供者在 CoreLib 脚本系统未启用时静默跳过。
- 伤害钩子按优先级降序执行(高优先级先跑),这与 Forge / Cooking / Level 规则的升序方向相反,混用时请注意。
- 伤害钩子执行抛异常会被捕获记录,不中断其他钩子;某钩子取消事件后停止后续钩子。
- 扩展脚本重载时会先注销旧注册项(Provider、运行时属性、伤害类型、钩子、管线),再重新执行
register(),最后刷新缓存并重同步所有玩家。 - CoreLib 缺失时模块自动降级。