Skip to content

JavaScript 脚本

EmakiAttribute 通过 CoreLib 的脚本系统注册脚本模块,模块 ID 为 attributeemaki.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 投影包含 sourceIdattributesmetaconditionsschemaVersionupdatedAt 六个字段,由 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

js
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

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 字段

每个字段都支持驼峰与下划线两种写法。

字段类型必填默认说明
idstring属性 id;为空注册失败。
displayNamestring显示名。
valueKindstringFLAT取值类型枚举(大写解析,非法回退 FLAT)。
targetTypestringGENERIC目标类型枚举。
targetIdstring目标 id。
mmoItemsStatstringMMOItems 词条映射。
defaultValuenumber0默认值。
minValuenumbernull最小值(可空)。
maxValuenumbernull最大值(可空)。
allowNegativebooleantrue是否允许负值。
priorityint0优先级。
loreFormatIdstringLore 格式 id。
lorePatternslistLore 解析正则列表。
descriptionstring描述。
attributePowernumber1.0属性强度。

registerDamagePipeline definition 字段

字段类型必填默认说明
idstring管线 id,同时作为匹配的伤害类型 id。
functionstring计算函数名,也可用 calculate;与 id 同时为空则失败。
timeoutMillisnumber引擎默认超时单次脚本超时。

运行时按伤害类型 id 匹配对应管线(一个伤害类型对应一个管线)。

registerProvider definition 字段

字段类型必填默认说明
idstring提供者 id;为空注册失败。
priorityint0优先级。
functionstringcollect采集函数名,也可用 collect

采集函数收到实体参数,返回属性映射 Map(或含 attributes / sourceId 的结构,或可迭代列表);属性值非数值的项会被跳过。

onDamage definition 字段

字段类型必填默认说明
idstring钩子 id;为空注册失败。
functionstring回调函数名;为空注册失败。
priorityint0优先级,降序执行(高优先级先跑)。
damageTypeslist空(全部类型)限定生效的伤害类型集合。

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直接治疗。

管线回调返回对象可含:successcancelleddamagefinalDamagecriticalrecoverystageValues(或 stages)。

注册示例

EmakiAttribute 自带扩展型示例脚本 scripts/examples/js_fire_mastery.js

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/

text
plugins/EmakiCoreLib/scripts/extensions/attribute/js_fire_mastery.js
text
/corelib script reload

示例文件默认释放到 plugins/EmakiCoreLib/scripts/examples/(Mythic 脚本释放到 scripts/mythic/),不会自动启用。需要启用扩展时复制到 extensions/attribute/ 再重载。

注意事项

  • 所有注册/钩子/管线/提供者在 CoreLib 脚本系统未启用时静默跳过。
  • 伤害钩子按优先级降序执行(高优先级先跑),这与 Forge / Cooking / Level 规则的升序方向相反,混用时请注意。
  • 伤害钩子执行抛异常会被捕获记录,不中断其他钩子;某钩子取消事件后停止后续钩子。
  • 扩展脚本重载时会先注销旧注册项(Provider、运行时属性、伤害类型、钩子、管线),再重新执行 register(),最后刷新缓存并重同步所有玩家。
  • CoreLib 缺失时模块自动降级。