跳到主要内容

Lua 脚本:道具与物品 API

本页涵盖 FreeMinecraftModels 道具和物品脚本可用的全部 API:context.propcontext.itemcontext.eventcontext.playercontext.worldcontext.zonescontext.schedulercontext.statecontext.log。如果你是脚本新手,请先阅读 入门


context.prop

prop 表提供道具实体的信息以及控制其动画的方法。每次钩子调用时都会重新构建。

字段

字段类型说明
prop.model_id字符串蓝图模型名称(如 "torch_01"
prop.current_locationlocation 表构建上下文时道具的位置

location 表具有标准字段:xyzworldyawpitch

示例:读取道具信息
return {
api_version = 1,

on_spawn = function(context)
context.log:info("Prop spawned: " .. (context.prop.model_id or "unknown"))
local loc = context.prop.current_location
if loc then
context.log:info("Location: " .. loc.x .. ", " .. loc.y .. ", " .. loc.z)
end
end
}

prop:play_animation(name, blend, loop)

在道具模型上播放指定名称的动画。

参数类型默认说明
name字符串必填模型文件中定义的动画名称
blend布尔true是否与当前动画混合
loop布尔true动画是否循环

若动画被找到并启动则返回 true,否则返回 false

示例
return {
api_version = 1,

on_right_click = function(context)
local success = context.prop:play_animation("open", true, false)
if not success then
context.log:warn("Animation 'open' not found on this model!")
end
end
}

prop:stop_animation()

停止道具上所有正在播放的动画。

不接受参数。

示例
return {
api_version = 1,

on_right_click = function(context)
context.prop:stop_animation()
end
}

prop:hurt_visual()

在道具上播放视觉受伤动画(红色染色闪烁),但不会对其造成实际伤害。

不接受参数。

示例
return {
api_version = 1,

on_left_click = function(context)
-- 被击打时闪烁红色,但不实际受伤
if context.event then
context.event.cancel()
end
context.prop:hurt_visual()
end
}

prop:pickup()

将道具从世界中移除,并在其位置掉落放置用的纸质物品。掉落的物品可以右键点击方块再次放置该道具。

不接受参数。

示例
return {
api_version = 1,

on_right_click = function(context)
-- 让玩家通过右键拾取道具
context.prop:pickup()
end
}

prop:mount(player)

将玩家挂载到道具上第一个可用的挂载点座位。模型必须定义挂载点骨骼。

参数类型说明
player实体表玩家实体表(例如来自 context.event.player
示例
return {
api_version = 1,

on_right_click = function(context)
local player = context.event and context.event.player
if player then
context.prop:mount(player)
end
end
}

prop:dismount(player)

将玩家从道具上的挂载点座位卸下。

参数类型说明
player实体表玩家实体表
示例
return {
api_version = 1,

on_right_click = function(context)
local player = context.event and context.event.player
if player then
-- 切换挂载/下骑
local passengers = context.prop:get_passengers()
for i = 1, #passengers do
if passengers[i].uuid == player.uuid then
context.prop:dismount(player)
return
end
end
context.prop:mount(player)
end
end
}

prop:get_passengers()

返回道具当前所有乘客实体表组成的 Lua 数组。

不接受参数。

示例
return {
api_version = 1,

on_game_tick = function(context)
local passengers = context.prop:get_passengers()
if #passengers > 0 then
context.log:info("Prop has " .. #passengers .. " passenger(s)")
end
end
}

prop:has_mount_points()

返回该道具的模型中是否定义了挂载点骨骼。

不接受参数。返回 truefalse

示例
return {
api_version = 1,

on_right_click = function(context)
local player = context.event and context.event.player
if player and context.prop:has_mount_points() then
context.prop:mount(player)
end
end
}

prop:spawn_elitemobs_boss(filename, x, y, z)

在指定位置生成 EliteMobs 自定义 Boss。需要服务器上安装了 EliteMobs。

参数类型说明
filename字符串自定义 Boss 文件名(如 "my_boss.yml"
x数字X 坐标
y数字Y 坐标
z数字Z 坐标

返回所生成 Boss 的生物实体表;若未安装 EliteMobs 或 Boss 文件不存在,则返回 nil

示例
return {
api_version = 1,

on_right_click = function(context)
local loc = context.prop.current_location
if loc then
local boss = context.prop:spawn_elitemobs_boss("dungeon_guardian.yml", loc.x, loc.y + 1, loc.z)
if boss then
context.log:info("Spawned boss: " .. (boss.name or "unknown"))
else
context.log:warn("Could not spawn boss -- is EliteMobs installed?")
end
end
end
}

prop:open_inventory(player, title, rows)

为玩家打开一个持久化的箱子物品栏 GUI。物品栏关闭时内容会保存到道具的 PersistentDataContainer,下次打开时恢复。

参数类型默认说明
player实体表必填要展示物品栏的玩家
title字符串必填物品栏标题(支持 & 颜色代码)
rowsint3行数(1-6,其中 6 = 54 格 = 大箱子)

若物品栏打开成功返回 true,否则返回 false


prop:is_viewing_inventory(player)

返回指定玩家当前是否打开了该道具的物品栏。

参数类型说明
player实体表要检查的玩家

返回 truefalse

示例:物品栏关闭时播放关闭动画
context.state["task_" .. player.uuid] = context.scheduler:run_repeating(5, 5, function(tick_context)
if not tick_context.prop:is_viewing_inventory(player) then
tick_context.prop:play_animation("close", true, false)
tick_context.scheduler:cancel(tick_context.state["task_" .. player.uuid])
end
end)

prop:place_book(player)

将玩家主手中的成书或书与笔取走,存到道具上。

参数类型说明
player实体表手持书本的玩家

成功时返回 true


prop:read_book(player)

打开存储的书让玩家阅读。

参数类型说明
player实体表要展示书本的玩家

若有书被打开则返回 true


prop:take_book(player)

将存储的书归还到玩家背包,并从道具上移除。

参数类型说明
player实体表接收书本的玩家

成功时返回 true


prop:has_book()

返回该道具上是否存有书。不接受参数。


prop:drop_inventory()

将所有已存储的物品栏内容作为物品实体掉落在道具位置,然后清空存储数据。会自动关闭任何正在查看该物品栏的玩家。

不接受参数。成功时返回 true


prop:drop_book()

将存储的书作为物品实体掉落在道具位置,并清空存储的书数据。

不接受参数。成功时返回 true


prop:set_persistent_data(key, value)

将一个字符串值存储到道具的盔甲架 PersistentDataContainer 中。该数据可在服务器重启和区块卸载后保留。

参数类型说明
key字符串唯一键名(内部以 fmm_lua_<key> 存储)
value字符串要存储的值。数字和布尔值请使用 tostring()

成功时返回 true;若道具没有底层盔甲架则返回 false


prop:get_persistent_data(key)

获取之前用 set_persistent_data 存储的字符串值。若键未设置则返回 nil

参数类型说明
key字符串set_persistent_data 中使用的键名
示例:持久化开关状态
return {
api_version = 1,

on_spawn = function(context)
local saved = context.prop:get_persistent_data("active")
context.state.active = saved == "true"
end,

on_right_click = function(context)
context.state.active = not context.state.active
context.prop:set_persistent_data("active", tostring(context.state.active))
end
}

context.item

item 表仅在物品脚本中可用(道具脚本无此表)。它提供自定义物品的信息以及操作该物品的方法。该表在每次钩子调用时重新构建。

字段

字段类型说明
item.id字符串物品类型 ID(来自 YML 配置中的 fmm_item_id

item:material()

以字符串形式返回物品的材质名称(如 "DIAMOND_SWORD""STICK")。


item:get_amount() / item:set_amount(n)

获取或设置物品的堆叠数量。

参数类型说明
nint新的堆叠数量

item:consume(n)

将物品的堆叠数量减少 n(默认 1)。若结果数量小于等于 0,物品会从玩家背包中移除。

参数类型默认说明
nint1要消耗的数量

item:get_uses() / item:set_uses(n)

获取或设置存储在物品 PersistentDataContainer 中的自定义使用计数。这与原版耐久度无关,可用于实现自定义耐久或充能系统。

参数类型说明
nint新的使用次数

item:get_name() / item:set_name(s)

获取或设置物品的显示名称。通过 & 支持颜色代码。

参数类型说明
s字符串新的显示名称(如 "&b&lFrost Sword"

item:get_lore() / item:set_lore(table)

获取或设置物品的 Lore。get_lore() 返回一个字符串表(每行一个)。set_lore() 接受字符串表。

参数类型说明
table字符串数组,每个对应一行 Lore
示例:跟踪使用次数的物品脚本
return {
api_version = 1,

on_right_click = function(context)
local uses = context.item:get_uses()
if uses <= 0 then
context.player:send_message("&cThis item is out of charges!")
return
end
context.item:set_uses(uses - 1)
context.player:send_message("&aUsed! Charges remaining: " .. (uses - 1))
end
}

item:get_durability()

返回一个带有 currentmax 字段的表,代表物品的原版耐久度;若物品没有耐久度条则返回 nil

示例
local dur = context.item:get_durability()
if dur then
context.player:send_message("Durability: " .. dur.current .. "/" .. dur.max)
end

item:get_durability_percentage()

0.01.0 的分数形式返回剩余耐久度;若物品没有耐久度条则返回 nil


item:use_durability(amount, can_break)

按固定数量减少物品的原版耐久度。

参数类型默认说明
amountint必填要消耗多少耐久点
can_break布尔false若为 true,耐久耗尽时物品被销毁。若为 false,耐久最低停在 1

item:use_durability_percentage(fraction, can_break)

按最大耐久度的百分比减少物品的原版耐久度。

参数类型默认说明
fraction数字必填消耗的最大耐久比例(如 0.1 = 10%)
can_break布尔false若为 true,耐久耗尽时物品被销毁。若为 false,耐久最低停在 1

context.event

当前钩子的事件数据。在道具脚本与物品脚本的点击、战斗和交互钩子中可用。在没有关联事件的钩子(on_spawnon_game_tickon_destroyon_zone_enteron_zone_leaveon_equip)中返回 nil

字段与方法

字段或方法类型说明
event.player玩家实体表触发事件的玩家。在 on_left_clickon_right_clickon_projectile_hit(射手为玩家时)中可用。完整字段与方法见 玩家实体方法。
event.is_cancelled布尔该事件当前是否被取消
event.cancel()函数取消事件(如阻止伤害或交互)
event.uncancel()函数取消对先前取消的事件的取消
信息

并非所有事件都可取消。若底层 Bukkit 事件未实现 Cancellableevent.cancel()event.uncancel() 将不存在,event.is_cancelled 始终为 false

示例:让道具无敌

示例
return {
api_version = 1,

on_left_click = function(context)
if context.event then
context.event.cancel()
end
end
}

示例:检查取消状态

示例
return {
api_version = 1,

on_left_click = function(context)
if context.event and not context.event.is_cancelled then
context.event.cancel()
context.log:info("Damage cancelled!")
end
end
}
警告

在调度回调(scheduler:run_laterscheduler:run_repeating)内部,context.event 始终为 nil。事件修改只能在事件钩子自身中进行。


context.world

world API 在所有 Nightbreak 插件之间共享。完整参考见 context.world

信息

FMM 道具与 EliteMobs Boss 使用相同的 MagmaCore world 表。全局页面上记录的所有方法(get_block_atset_block_atspawn_particleplay_soundstrike_lightningget_timeset_timeget_nearby_entitiesget_nearby_playersspawn_entityget_highest_block_yraycastspawn_firework)在 FMM 中均可用。world:raycast()(投射射线并检测命中实体/方块)与 world:spawn_firework()(生成可自定义颜色与形状的烟花火箭)的完整细节见 MagmaCore world API。EliteMobs 在 world 表上扩展了生成 Boss、增援等额外方法 —— 详见 EliteMobs 世界与环境


玩家实体方法

玩家实体表由 context.event.playercontext.world:get_nearby_players() 以及区域回调返回。

共享 API

实体表、生物实体方法、玩家专有方法以及玩家 UI 方法在所有 Nightbreak 插件之间共享。完整参考见 MagmaCore Lua 脚本引擎,涵盖实体基础字段、生物实体字段与方法、玩家专有字段与方法以及 玩家 UI 方法。新增的玩家方法包括 player:get_target_entity()(射线指向)、player:get_eye_location()player:get_look_direction()player:send_block_change()(每玩家伪造方块)和 player:reset_block() —— 详见 玩家专有方法

FMM 特有的实体字段

在 FMM 脚本内构建的每个实体表都会自动获得这些额外字段(通过 FMM 的 LuaEntityEnricher):

字段类型说明
entity.is_modeled布尔若该 Bukkit 实体是某个 ModeledEntity 的底层实体则为 true
entity.is_prop布尔若该实体是支撑 PropEntity 的盔甲架则为 true
entity.model表或 nil仅在 is_modeled = true 时填充(见下方)

entity.model 存在时,它暴露:

字段 / 方法说明
model.model_id蓝图模型名称(如 "dragon"
model.is_dynamic若是 DynamicEntity(附着到生物实体)则为 true
model:play_animation(name, blend, loop)播放指定名称的动画。成功时返回 true
model:stop_animations()停止所有当前动画
model:remove()立即移除该模型化实体及其所有骨骼
on_attack_entity = function(context)
local target = context.event.target
if target and target.is_modeled then
target.model:play_animation("hurt", true, false)
end
end

EliteMobs 实体字段

当 EliteMobs 已安装时,FMM 会将请求转发到 EliteMobs 的 enricher,因此相同的实体表也会暴露:

字段类型说明
entity.is_elite布尔若实体被 EliteMobs 追踪则为 true
entity.is_custom_boss布尔若是自定义 Boss 配置则为 true
entity.is_significant_boss布尔healthMultiplier > 1 的自定义 Boss 为 true(过滤掉杂兵命名怪物)
entity.elite表或 nil仅在 is_elite = true 时填充。包含 levelnamehealthmax_healthhealth_multiplierdamage_multiplieris_custom_boss,以及 elite:remove()

context.zones

zones API 在所有 Nightbreak 插件之间共享。完整参考见 context.zones


context.scheduler

scheduler API 在所有 Nightbreak 插件之间共享。完整参考见 context.scheduler


context.state

state API 在所有 Nightbreak 插件之间共享。完整参考见 context.state


context.log

日志 API 在所有 Nightbreak 插件之间共享。完整参考见 context.log


运行时模型

每个脚本实例独立运行时

每个附加了脚本的道具实体都获得自己独立的 Lua 运行时实例。当道具被生成时,FMM 加载 Lua 源代码,在新沙箱环境中求值并保存返回的表。道具被移除时,运行时也随之关闭。

对于物品脚本,每个 (玩家, itemId) 对会创建一个运行时。当玩家装备某个自定义物品时,FMM 为该玩家与该物品类型创建一个脚本实例。当物品被卸下时,运行时关闭。

这意味着:

  • 在文件作用域声明的局部变量对该脚本实例私有。
  • 即使两个实例共用同一脚本文件,它们的 context.state 也是完全隔离的。

调度任务归属

通过 context.scheduler 创建的所有任务都归属于创建它们的运行时。当道具被移除时:

  1. 运行时关闭。
  2. 所有归属的任务 —— 一次性的和重复的 —— 都会被自动取消。
  3. 所有区域监听被清除。

冷却作用域

共享的脚本引擎暴露 context.cooldowns:set_local:check_local:set_global:check_global(参见 MagmaCore 冷却参考)。FMM 对这些存储的作用域如下:

脚本类型local 存储作用域global 存储作用域
道具脚本每个 ScriptInstance(道具 + 脚本文件)每个 PropEntity(绑定到该道具的所有脚本共享)
物品脚本每个 (player, itemId, scriptFile) 三元组 —— 即使脚本实例在物品离开活动栏位时被销毁,冷却也会跨重新装备保留每个玩家(该玩家运行的所有 FMM 物品脚本共享)

由于物品脚本在每次装备/卸下周期都会被销毁并重建,物品冷却被保存在以玩家 UUID 为键的静态映射中,而不是存活于 ScriptInstance 上。这就是为什么把物品从快捷栏换出再换回,物品冷却仍然有效的原因。

执行预算

每次钩子调用和每次回调调用都会计时。如果单次调用耗时超过 50 毫秒,脚本会被禁用并在控制台输出警告:

[Lua] my_script.lua took 73ms in 'on_game_tick' (limit: 50ms) -- script disabled to prevent lag.

为保持在预算之内:

  • 避免钩子内的无界循环。
  • on_game_tick 处理器保持轻量 —— 它每一刻都会运行。
  • 使用 context.scheduler:run_repeating(...) 把工作分摊到多个刻。

完整钩子参考

本表列出道具脚本与物品脚本中可用的所有钩子。

道具钩子(8)

钩子触发时机context.event
on_spawn道具被生成到世界中nil
on_game_tick每一服务器刻(50ms)nil
on_destroy道具被移除nil
on_left_click玩家左键点击道具伤害事件
on_right_click玩家右键点击道具交互事件
on_projectile_hit抛射物击中道具抛射物命中事件
on_zone_enter玩家进入受监听区域nil
on_zone_leave玩家离开受监听区域nil

物品钩子(22)

钩子类别触发时机context.event
on_attack_entity战斗玩家攻击实体伤害事件
on_kill_entity战斗玩家击杀实体死亡事件
on_take_damage战斗玩家受到伤害伤害事件
on_shield_block战斗玩家使用盾牌格挡伤害事件
on_shoot_bow战斗玩家射出弓弓射出事件
on_projectile_hit战斗玩家的抛射物命中抛射物命中事件
on_projectile_launch战斗玩家发射抛射物发射事件
on_right_click交互玩家右键点击交互事件
on_left_click交互玩家左键点击交互事件
on_shift_right_click交互玩家 Shift+右键交互事件
on_shift_left_click交互玩家 Shift+左键交互事件
on_interact_entity交互玩家右键点击实体实体交互事件
on_equip装备物品进入活动栏位nil
on_unequip装备物品离开活动栏位nil
on_swap_hands装备在主副手之间切换切换事件
on_drop装备玩家丢弃物品丢弃事件
on_break_block工具玩家破坏方块方块破坏事件
on_consume工具玩家食用物品食用事件
on_item_damage工具物品受到耐久损耗物品损耗事件
on_fish工具玩家使用钓鱼竿钓鱼事件
on_death工具玩家在装备物品时死亡死亡事件
on_game_tick生命周期装备期间的每一刻nil

后续阅读

  • 示例与模式 —— 道具与物品的完整可工作脚本及讲解
  • 故障排查 —— 常见问题、调试技巧和质量检查清单
  • 入门 —— 文件结构、钩子、首个脚本的步步讲解