
原生热键API
一款适用于《X4: Foundations》的API模组,允许其他模组注册自定义热键操作——玩家可通过游戏原生的按键绑定界面进行自定义,无需外部进程,也不依赖*SirNukes模组支持API*自带的热键API。
查看大图Native Hotkey API
一个用于《X4:基石》的 API 模组,允许其他模组注册自定义热键动作——玩家可以通过游戏原生的按键绑定界面进行绑定,无需外部进程,也不依赖 SirNukes Mod Support APIs 自带的热键 API。
概述
X4 输入系统的特性:它使用专门的 *INPUT_ACTION_ ** 枚举将物理按键分配给游戏内操作。这些枚举被编译进游戏中,无法由模组扩展。 本模组回收了 48 个原本未使用的调试专用动作槽位,并将其作为注册 API 公开:消费模组请求一个 id,框架从池中分配下一个空闲槽位给它,玩家则通过与其他控制项相同的原生重映射界面(及冲突检测)来分配实际的物理按键。 因此,只要 Egosoft 不从游戏中移除这些调试动作槽位或采取其他破坏此功能的措施,本模组就能持续运行。
存在两条集成路径,两者都调用相同的注册/分发核心:
- MD API - 用于任务总监(Mission Director)脚本。使用 Register_Action / Reloaded 触发信号;注册的回调是一个 MD 触发块。
- Lua API - 用于 Lua UI 脚本。直接调用 HotkeyApi.RegisterAction(...);注册的回调是一个 Lua 函数,完全无需 MD/黑板(blackboard)往返。
热键在按下时触发一次(没有单独的按下/释放/重复事件)。每次注册需声明一个强制性的 区域(area)(一个或多个 'map'、'pilot'、'fps',多个用 ; 分隔),描述允许触发的场景以及该场景下“选定对象”的含义,以及一个可选的 isObjectRequired 标志,在没有选定/瞄准对象时跳过触发。
需求
- 《X4:基石》:版本 8.00HF4 或更高,以及 kuertee 制作的 UI Extensions and HUD:版本 v8.0.4.10 或更高。
- 可在 Nexus Mods 上获取:UI Extensions and HUD
- 《X4:基石》:版本 9.00 或更高,以及 kuertee 制作的 UI Extensions and HUD:版本 v9.0.0.7 或更高。
- 可在 Nexus Mods 上获取:UI Extensions and HUD
安装
- Steam 创意工坊:Native Hotkey API
- Nexus Mods:Native Hotkey API
玩家界面
在顶级选项菜单中,设置(Settings) 之前会添加一个 热键管理(Hotkey Management) 条目。
重要:此菜单仅在加载存档或开始新游戏后可用,即在主菜单中无法访问!

它包含:

热键绑定(Hotkey Bindings) - 实际的按键绑定页面(原生重映射界面:点击一行,按下一个键,冲突时确认/替换)。冲突检查既针对通过此 API 注册的其他热键,也针对其他 Controls 页面上的原版绑定。



热键请求(Hotkey Requests) - 列出本会话期间任何模组尝试注册的所有 id,无论其当前是否持有槽位,每行一个复选框:

- 已勾选(Checked)(默认)- 已启用;如果上次注册时槽位空闲,则持有该槽位。
- 未勾选(Unchecked) - 已阻止:槽位(如有)及其按键绑定被立即释放,可供其他 id 认领。
- 状态列显示 可用(Available)(持有槽位,但尚未绑定按键)、已分配:\<key\>(持有槽位和按键)、等待中(池已满)(Waiting (pool full))(已启用,但上次尝试时全部 48 个槽位都被占用),或 已阻止(Blocked)。
- 重新启用被阻止的 id 不会立即重新获得槽位——按 应用(Apply) 为所有项重新运行注册(这也会重新检查孤立槽位,请参阅下方的“清除”)。
一个调试日志记录开关(启用(Enabled)/禁用(Disabled))。
选定/瞄准对象
每次热键触发时,都会尝试为当前检测到的区域解析一个“选定/瞄准对象”,与 isObjectRequired 无关:
- 'map' - 地图当前的选中项。
- 'pilot' - 当前驾驶飞船的目标。
- 'fps' - 玩家准星当前指向的任何物体(软目标)。
isObjectRequired 仅控制当没有选定/瞄准对象时是否跳过热键——它并不限制一旦热键触发后对象本身是否被传递。因此,当 isObjectRequired = false(默认值)时,如果在按键瞬间恰好有对象被选中/瞄准,该对象仍会传递给您的回调函数;isObjectRequired = true 只是在完全相同的查找逻辑之上增加了“当没有东西可传时完全跳过触发”的规则。
- MD 的 $actionCue 将其作为
$object接收,这是一个 MD 组件引用。 - Lua 的 actionLua 将其作为
object接收,这是原始组件 id(一个真实的 Lua 值,而非黑板往返传递)。
当检测到的区域没有选定/瞄准对象时,两者均为空/nil。
此外,Lua 的 actionLua 在 fps 模式下会通过 softTarget 接收软目标数据,这是一个真实的 Lua 值(非黑板往返传递),若没有则为 nil。
typedef struct {
uint64_t softtargetID;
const char* softtargetConnectionName;
uint32_t messageID;
} SofttargetDetails2;
MD API
流程
- 您的触发块监听 md.HotkeyApi.Reloaded
- 调用 md.HotkeyApi.Register_Action 并传入您自己的回调触发块
- 每次 Reloaded 再次触发时重新发送 Register_Action(触发块引用在 Lua 重新加载后不会保留,因此注册本身也不会保留)
- 当玩家按下分配的按键时,您的回调触发块收到信号
Register_Action - 参数说明
- $id (字符串) - 此操作的唯一标识符,由您的模组选择。一旦分配,相同的 id 始终映射到相同的物理热键槽位。
- $version (数字,可选,默认
1) - 此请求所针对的协议版本。高于此版本构建支持的版本将被直接拒绝,而不是冒被静默误解的风险。 - $area (字符串,必需,无默认值) - 一个或多个
'map'、'pilot'、'fps',若多于一个则用 ; 分隔(例如'map;pilot')——指定允许该操作触发的场景以及$isObjectRequired对应的“选定对象”含义。'pilot' 表示玩家正在驾驶飞船(无菜单打开)。'fps' 表示玩家处于步行/第一人称状态——不在任何菜单中,也不在太空驾驶飞船(例如在空间站或飞船内部行走)。每个区域值都有其自身的最低支持 $version(目前版本1起即支持'map'、'pilot'和 'fps')——未来在更高版本中新增的区域值仅在请求自身声明了至少该版本时才会被接受。 - $isObjectRequired (布尔值,可选,默认
false) - 如果为true,则除非当前存在地图选中项('map')、飞船目标('pilot')或准星软目标对象('fps'),否则跳过该操作。 - $name (字符串) - 热键绑定/热键请求页面上该操作行显示的显示名称。
- $actionCue (触发块引用) - 当热键触发且任何目标要求均满足时收到信号。接收
param = table[$id = id],如果存在选中/目标对象,则额外接收 $object(一个 MD 组件引用)——请参阅上方的选定/瞄准对象。
调试日志记录(GetDebugChance) - 建议基于触发块的消费者使用
共享库 include_actions ref="md.HotkeyApi.GetDebugChance" 根据玩家当前的调试日志记录偏好(从热键请求页面切换)计算 $debugChance(0 或 100)。任何触发块——本模组或消费者的——都可以包含它一次,然后在其自己的 debug_text 调用中使用 chance="$debugChance",这样切换该选项会同步静音所有相关内容,而不是每个模组各自跟踪自己的标志。跨扩展引用必须使用完全限定名(ref="md.HotkeyApi.GetDebugChance")——MD 库引用是按脚本命名空间隔离的,不是全局裸地址访问的。
MD 使用示例
<cue name="Register_My_Action" instantiate="true">
<conditions>
<event_cue_signalled cue="md.HotkeyApi.Reloaded" />
</conditions>
<actions>
<signal_cue_instantly
cue="md.HotkeyApi.Register_Action"
param="table[
$id = 'my_mod_my_action',
$area = 'map;pilot',
$isObjectRequired = false,
$name = 'My Action',
$actionCue = My_Action_Cue,
]"/>
</actions>
</cue>
<cue name="My_Action_Cue" instantiate="true" namespace="this">
<conditions>
<event_cue_signalled />
</conditions>
<actions>
<debug_text text="'My action fired! param: %s'.[event.param]" chance="100" filter="general" />
</actions>
</cue>
Lua API
流程 (Lua)
- 监听 "HotkeyApi.Register_Request" 事件(每次 md.HotkeyApi.Reset_On_Lua_Reload 触发时都会触发——与 MD 的 Reloaded 触发块对应)
- 每次响应时调用 HotkeyApi.RegisterAction({...})
- 当分配的按键被按下时,您的 actionLua 函数被直接调用——完全没有 MD/黑板往返
HotkeyApi.RegisterAction - 请求字段
与 MD 的 Register_Action 字段相同(使用普通 Lua 值,无 $ 前缀),另加:
- actionLua (函数) - 当热键触发且任何目标要求均满足时,以 actionLua({id, object, softTarget}) 形式调用。object 是选定/瞄准的组件 id(真实的 Lua 值,而非黑板往返),若没有则为 nil——请参阅上方的选定/瞄准对象。softTarget 是软目标数据(真实的 Lua 值,而非黑板往返),若没有则为 nil。
actionCue 和 actionLua 可以在同一次注册中同时设置;如果两者都设置,则只有 actionLua 会被触发(跳过 MD 分发)。
Lua 使用示例
RegisterEvent("HotkeyApi.Register_Request", function()
HotkeyApi.RegisterAction({
id = "my_mod_my_action",
area = "map;pilot",
isObjectRequired = false,
name = "My Lua Action",
actionLua = function(params)
DebugError("My action fired! id: " .. tostring(params.id))
end,
})
end)
注册表检查 (Lua)
适用于那些在 API 无法访问的地方保留自己注册表副本的消费者——最典型的是隔离的 ui/core/lua/*.xpl 核心脚本,它无法访问注册表、ReadText 或黑板。
- HotkeyApi.GetActionNameByInputId(numericId) - 输入映射 id 对应注册的显示名称,若该 id 当前没有持有热键则返回 nil。numericId 是 GetCompassMenuMappings 和 Controls 页面的 controlsorder 行携带的内容(23-70),而非池槽位名称。
- HotkeyApi.IsDebugEnabled() - 单一调试日志记录开关的状态,以便消费者可以使用同一开关来控制自己的日志记录。
- HotkeyApi.RegisterOnChanged(key, callback) - callback 不接受任何参数,每当槽位/id/名称配对或调试标志发生变化时触发;可通过上述两个 getter 读回当前状态。key 是消费者自己的模组 id,在相同键下重新注册会替换之前的回调,这样 Lua 重新加载后就不会留下失效的闭包。
孤立槽位回收(清除 Clearance)
从先前会话加载的每个槽位在每次重新加载开始时都被标记为未确认;重新注册后则标记为已确认。每次 Reloaded 广播十秒后(足以让所有消费者响应),一次 清除(Clearance) 扫描将清除仍处于未确认状态的任何内容的按键绑定并释放其槽位——例如,一个被移除的模组,或停止注册该 id 的模组。这会自动运行;消费者无需任何操作。
限制
- 48 个操作的硬性上限。 *INPUT_ACTION_ ** 是一个编译进游戏的封闭枚举;此池无法扩展。
- 跨页面冲突检查具有区域感知能力。 重映射此池中的某个按键时,仅会针对与该热键自身 区域(area) 重叠的原版 Controls 页面进行冲突检查(反向亦然,即重映射一个与此池中按键冲突的原版控制项时)——而不是对无关区域的所有页面进行检查。
- 加载没有某些注册过热键的模组的存档将清除其槽位和按键绑定,因此当玩家再次使用这些模组重新加载时,需要重新分配它们。
- X4 中的按键绑定不保存在存档文件中,而是按“配置文件”存储。请注意,为一个游戏创建的按键绑定将“复制”到使用相同配置文件的任何其他存档中。
制作人员
- 作者:Chem O`Dun,可在 Nexus Mods 和 Steam 创意工坊 上找到。
- “X4:基石” 是 Egosoft 的商标。
致谢
更新日志
[8.00.09] - 2026-08-09
新增
- 针对那些自行维护注册表副本的模组的 Lua API:
HotkeyApi.GetActionNameByInputId(numericId)、HotkeyApi.IsDebugEnabled() 和HotkeyApi.RegisterOnChanged(key, callback)。目前仅由 Hotkey Names on Radial Menu 模组使用。 - 新增对
Print Extension List的依赖。
[8.00.08] - 2026-07-14
修复
- 修复了游戏启动或加载时对原版槽位绑定的首次清除问题。
[8.00.07] - 2026-07-12
改进
- 热键的刷新与清理。
[8.00.06] - 2026-07-06
新增
- Lua 的 actionLua 在 fps 模式下通过
softTarget接收软目标数据,这是真实的 Lua 值(非黑板往返传递),若没有则为 nil。
[8.00.05] - 2026-07-04
新增
- FPS 区域现在为 isObjectRequired = true 的操作提供一个选定对象(玩家准星当前指向的对象)。
改进
- 按键绑定冲突检查现在完全具有区域感知能力(仅检查与该热键自身
area重叠的原版 Controls 页面),并且按键绑定可在原版控制项与此池的槽位之间双向转移。即,先前引入的“保守”冲突检查现已完全移除。
[8.00.04] - 2026-06-30
新增
- 新增“fps”区域,用于第一人称步行(非驾驶、非菜单状态)热键支持。
修复
- 额外修复了游戏启动或加载时对原版槽位绑定的初始清除问题。
变更
- 已使用的槽位和被阻止的 id 现在存储在游戏用户数据中,而非黑板中,从而能在任何游戏加载或启动之间保持持久。
[8.00.03] - 2026-06-28
改进
- 热键管理
修复
- Steam 依赖问题
[8.00.01] - 2026-06-23
新增
- 初始版本:48 个操作槽位池、MD 和直接 Lua 注册路径、热键管理/绑定/请求页面、跨页面冲突检查、调试日志记录开关、孤立槽位清除扫描、请求版本控制。
正在加载版本记录…
正在加载评论…
评论在新手盒子客户端中发表,这里同步展示。