Lua 脚本:道具与物品 API
本页涵盖 FreeMinecraftModels 道具和物品脚本可用的全部 API:context.prop、context.item、context.event、context.player、context.world、context.zones、context.scheduler、context.state 与 context.log。如果你是脚本新手,请先阅读 入门。
context.prop
prop 表提供道具实体的信息以及控制其动画的方法。每次钩子调用时都会重新构建。
字段
| 字段 | 类型 | 说明 |
|---|---|---|
prop.model_id | 字符串 | 蓝图模型名称(如 "torch_01") |
prop.current_location | location 表 | 构建上下文时道具的位置 |
location 表具有标准字段:x、y、z、world、yaw、pitch。
示例:读取道具信息
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()
返回该道具的模型中是否定义了挂载点骨骼。
不接受参数。返回 true 或 false。
示例
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 | 字符串 | 必填 | 物品栏标题(支持 & 颜色代码) |
rows | int | 3 | 行数(1-6,其中 6 = 54 格 = 大箱子) |
若物品栏打开成功返回 true,否则返回 false。
prop:is_viewing_inventory(player)
返回指定玩家当前是否打开了该道具的物品栏。
| 参数 | 类型 | 说明 |
|---|---|---|
player | 实体表 | 要检查的玩家 |
返回 true 或 false。
示例:物品栏关闭时播放关闭动画
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)
获取或设置物品的堆叠数量。
| 参数 | 类型 | 说明 |
|---|---|---|
n | int | 新的堆叠数量 |
item:consume(n)
将物品的堆叠数量减少 n(默认 1)。若结果数量小于等于 0,物品会从玩家背包中移除。
| 参数 | 类型 | 默认 | 说明 |
|---|---|---|---|
n | int | 1 | 要消耗的数量 |
item:get_uses() / item:set_uses(n)
获取或设置存储在物品 PersistentDataContainer 中的自定义使用计数。这与原版耐久度无关,可用于实现自定义耐久或充能系统。
| 参数 | 类型 | 说明 |
|---|---|---|
n | int | 新的使用次数 |
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()
返回一个带有 current 与 max 字段的表,代表物品的原版耐久度;若物品没有耐久度条则返回 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.0 到 1.0 的分数形式返回剩余耐久度;若物品没有耐久度条则返回 nil。
item:use_durability(amount, can_break)
按固定数量减少物品的原版耐久度。
| 参数 | 类型 | 默认 | 说明 |
|---|---|---|---|
amount | int | 必填 | 要消耗多少耐久点 |
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_spawn、on_game_tick、on_destroy、on_zone_enter、on_zone_leave、on_equip)中返回 nil。
字段与方法
| 字段或方法 | 类型 | 说明 |
|---|---|---|
event.player | 玩家实体表 | 触发事件的玩家。在 on_left_click、on_right_click 和 on_projectile_hit(射手为玩家时)中可用。完整字段与方法见 玩家实体方法。 |
event.is_cancelled | 布尔 | 该事件当前是否被取消 |
event.cancel() | 函数 | 取消事件(如阻止伤害或交互) |
event.uncancel() | 函数 | 取消对先前取消的事件的取消 |
并非所有事件都可取消。若底层 Bukkit 事件未实现 Cancellable,event.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_later、scheduler:run_repeating)内部,context.event 始终为 nil。事件修改只能在事件钩子自身中进行。
context.world
world API 在所有 Nightbreak 插件之间共享。完整参考见 context.world。
FMM 道具与 EliteMobs Boss 使用相同的 MagmaCore world 表。全局页面上记录的所有方法(get_block_at、set_block_at、spawn_particle、play_sound、strike_lightning、get_time、set_time、get_nearby_entities、get_nearby_players、spawn_entity、get_highest_block_y、raycast、spawn_firework)在 FMM 中均可用。world:raycast()(投射射线并检测命中实体/方块)与 world:spawn_firework()(生成可自定义颜色与形状的烟花火箭)的完整细节见 MagmaCore world API。EliteMobs 在 world 表上扩展了生成 Boss、增援等额外方法 —— 详见 EliteMobs 世界与环境。
玩家实体方法
玩家实体表由 context.event.player、context.world:get_nearby_players() 以及区域回调返回。
实体表、生物实体方法、玩家专有方法以及玩家 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 时填充。包含 level、name、health、max_health、health_multiplier、damage_multiplier、is_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 创建的所有任务都归属于创建它们的运行时。当道具被移除时:
- 运行时关闭。
- 所有归属的任务 —— 一次性的和重复的 —— 都会被自动取消。
- 所有区域监听被清除。
冷却作用域
共享的脚本引擎暴露 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 |