REPENTOGON 每日挑战菜单 DailyChallengeMenu:主菜单 Lua 脚本接口完整指南
游戏开发插件系统【免费下载链接】REPENTOGONScript extender for The Binding of Isaac: Repentance项目地址https://gitcode.com/gh_mirrors/re/REPENTOGON点击查看免费下载导读DailyChallengeMenu是 REPENTOGON 为《以撒的结合忏悔》主菜单中的每日挑战Daily Challenge界面提供的全局 Lua 表Global Table它让 Mod 开发者能够在脚本中读取每日挑战菜单的实时状态当前界面状态、选中元素、剩余时间以及获取菜单内部使用的多个Sprite对象。读完本文你将掌握DailyChallengeMenu全部 11 个函数的签名、返回值、底层数据来源Menu_DailyChallenge结构体字段并能结合MC_MAIN_MENU_RENDER回调与daily_stats.lua的真实用法在主菜单上扩展属于自己的每日挑战信息展示。文档来源docs/docs/menus/DailyChallengeMenu.md本文补充了对应 Lua 绑定源码 repentogon/LuaInterfaces/Menus/LuaDailyChallengeMenu.cpp 与结构体定义 libzhl/functions/MenuDailyChallenge.zhl 的实现细节。全局表与调用约定DailyChallengeMenu与菜单类Menu Class系列的其它全局表如ChallengeMenu、CharacterMenu、MainMenu等一样由 REPENTOGON 在 Lua 引擎注册类阶段挂载为全局表。文档明确强调获取方式直接使用DailyChallengeMenu全局表即可无需实例化。调用约定必须使用.句点调用函数而不能使用:冒号。这是因为这些函数并没有把self作为第一个参数压栈函数签名中不包含隐式self。适用范围这些函数只能在主菜单界面下使用。从源码 libzhl/LuaCore.cpp 可以看到每个函数进入时都会先调用int LuaCheckMainMenuExists(lua_State* L, const char* className) { if (g_MenuManager NULL) { return luaL_error(L, %s functions can only be used in the main menu, className); } return 0; }也就是说一旦g_MenuManager为空不在主菜单状态调用会直接抛出 Lua 错误DailyChallengeMenu functions can only be used in the main menu。在游戏进行中调用这些接口是不安全的Mod 作者应在主菜单渲染类回调如MC_MAIN_MENU_RENDER中使用。标准调用示例与文档一致local sprite DailyChallengeMenu.GetSprite()若误写成DailyChallengeMenu:GetSprite()Lua 会将表自身作为第一个参数传入从而破坏参数栈导致函数行为异常或报错。函数总览下表汇总了文档定义的 11 个函数及其签名、返回类型并与底层结构体字段一一对应函数签名返回类型底层来源字段GetSpriteGetSprite()SpriteMenu_DailyChallenge::_DailyRunSpriteGetLeaderboardSpriteGetLeaderboardSprite()SpriteMenu_DailyChallenge::_leaderboard._leaderboardMenuSpriteGetLeaderboardScoreMenuSpriteGetLeaderboardScoreMenuSprite()SpriteMenu_DailyChallenge::_leaderboard._scoreMenuSpriteGetSelectedElementGetSelectedElement()intMenu_DailyChallenge::SelectedElementSetSelectedElementSetSelectedElement(int ElementID)voidMenu_DailyChallenge::SelectedElement写入GetStateGetState()intMenu_DailyChallenge::StateSetStateSetState()voidMenu_DailyChallenge::State写入GetTimeLeftHoursGetTimeLeftHours()intMenu_DailyChallenge::_timeHoursLeftGetTimeLeftMinutesGetTimeLeftMinutes()intMenu_DailyChallenge::_timeMinutesLeftGetTimeLeftSecondsGetTimeLeftSeconds()intMenu_DailyChallenge::_timeSecondsLeftIsLeaderboardVisibleIsLeaderboardVisible()booleanMenu_DailyChallenge::_leaderboard._displayState 0注意文档中的SetState()签名未声明参数但绑定实现 repentogon/LuaInterfaces/Menus/LuaDailyChallengeMenu.cpp 中通过luaL_checkinteger(L, 1)读取第一个参数并写入menu-State因此实际调用时应传入一个整数状态值。数据模型Menu_DailyChallenge 结构体所有函数都直接读写Menu_DailyChallenge这个主菜单对象的内存字段。REPENTOGON 通过逆向分析在原版游戏中定位了该类的完整布局定义见 libzhl/functions/MenuDailyChallenge.zhlstruct Menu_DailyChallenge { int State : 0x0; int _holdInputTimer : 0x4; int SelectedElement : 0x8; float _positionX : 0xC; float _positionY : 0x10; ANM2 _DailyRunSprite : 0x14; int lastRunScore : 0x128; int _timeHoursLeft : 0x12c; int _timeMinutesLeft : 0x130; int _timeSecondsLeft : 0x134; AnimationState* _leaderboardAnmState : 0x138; GenericPopup _genericPopup : 0x13c; GenericPrompt _genericPrompt : 0x270; Leaderboard _leaderboard : 0x3a4; } : 0x760;从该结构体可以看到几个值得注意的细节剩余时间由_timeHoursLeft、_timeMinutesLeft、_timeSecondsLeft三个独立的int字段分别存储这正是GetTimeLeftHours / GetTimeLeftMinutes / GetTimeLeftSeconds三个独立访问器的数据来源。Mod 若需要拼接完整的剩余时间字符串可以组合三个返回值。**排行榜Leaderboard**是内嵌在Menu_DailyChallenge里的一个复合对象_leaderboard类型为Leaderboard其中包含_leaderboardMenuSprite和_scoreMenuSprite两个 ANM2 精灵分别对应GetLeaderboardSprite与GetLeaderboardScoreMenuSprite的返回值。结构体中还包含_genericPopup弹窗与_genericPrompt提示条说明每日挑战菜单内部使用通用弹窗/提示组件处理确认与输入交互。DailyChallengeMenu对象本身由MenuManager持有。在 libzhl/functions/MenuManager.zhl 中定义了内联访问器inline Menu_DailyChallenge* GetMenuDailyChallenge() {return this-_menuDailyChallenge; }Lua 绑定正是通过g_MenuManager-GetMenuDailyChallenge()获取菜单对象再读取/写入对应字段见 LuaDailyChallengeMenu.cpp。函数分类详解1. 精灵Sprite获取类GetSprite()→ Sprite返回每日挑战主界面精灵_DailyRunSprite。绑定代码将其作为SPRITE元表的 userdata 压栈LuaDailyChallengeMenu.cpp因此返回值可直接调用 Sprite 类的方法如SetFrame、Render、GetDefaultAnimation等。GetLeaderboardSprite()→ Sprite返回排行榜菜单精灵_leaderboard._leaderboardMenuSprite。GetLeaderboardScoreMenuSprite()→ Sprite返回排行榜分数界面精灵_leaderboard._scoreMenuSprite。这三个返回值虽然都是 ANM2 精灵指针但指向不同的动画对象分别控制每日挑战界面不同区域。Mod 可以通过对它们调用SetFrame/SetAnimation等方式定制这些区域的视觉表现。2. 状态与选中元素读写类GetSelectedElement()→int返回当前高亮/选中的界面元素 ID读取SelectedElement字段偏移量 0x8。SetSelectedElement(int ElementID)→void将SelectedElement设置为指定 ID绑定使用luaL_checkinteger校验参数LuaDailyChallengeMenu.cpp。GetState()→int返回每日挑战菜单当前所处的状态读取State字段偏移量 0x0。SetState()→void写入State字段需传入整数参数。从源码结构看State用于描述菜单内部的运行状态如初始加载、浏览、确认等SelectedElement用于标识当前选中的列表项这两个字段与_holdInputTimer输入保持计时相邻可以推断它们共同参与菜单的输入导航逻辑。需要注意的是原版游戏并没有公开这些状态的具体枚举值Mod 使用时应通过实测记录各状态对应的整数避免硬编码假设。3. 剩余时间读取类每日挑战具有真实时间限制以下三个函数分别返回剩余时间的不同分量GetTimeLeftHours()→int剩余小时数。GetTimeLeftMinutes()→int剩余分钟数。GetTimeLeftSeconds()→int剩余秒数。它们分别读取_timeHoursLeft0x12c、_timeMinutesLeft0x130、_timeSecondsLeft0x134三个字段。Mod 可以据此在主菜单上绘制倒计时例如local hours DailyChallengeMenu.GetTimeLeftHours() local minutes DailyChallengeMenu.GetTimeLeftMinutes() local seconds DailyChallengeMenu.GetTimeLeftSeconds() local timeText string.format(Time left: %02d:%02d:%02d, hours, minutes, seconds)4. 排行榜可见性查询类IsLeaderboardVisible()→boolean返回排行榜当前是否可见。绑定实现LuaDailyChallengeMenu.cpp并非直接读取布尔字段而是判断_leaderboard._displayState 0lua_pushboolean(L, menu-_leaderboard._displayState 0);也就是说只有当排行榜显示状态大于 0 时才视为可见。这个函数在绘制自定义 UI 时非常实用——例如官方内置脚本会在排行榜弹出时主动隐藏自定义信息避免遮挡见下文实战示例。实战示例daily_stats.lua 中的真实用法REPENTOGON 自带脚本 repentogon/resources/scripts/repentogon_extras/daily_stats.lua 是DailyChallengeMenu最典型的生产级用例。它通过MC_MAIN_MENU_RENDER回调在主菜单每日挑战界面渲染额外统计信息其核心流程为local function RenderDailyStats() if ScheduleRefresh and MenuManager:GetActiveMenu() _MainMenuType.DAILYRUN then -- 刷新连胜、总场次、终点关卡等信息 end if DailyChallengeMenu:IsLeaderboardVisible() then return end -- 使用 Isaac.WorldToMenuPosition 换算菜单坐标后渲染自定义文本与精灵 local pos Isaac.WorldToMenuPosition(_MainMenuType.DAILYRUN, StreakPos) ... end Isaac.AddCallback(REPENTOGON, _ModCallbacks.MC_MAIN_MENU_RENDER, RenderDailyStats)该脚本体现了DailyChallengeMenu的两个关键协同用法用MenuManager:GetActiveMenu() MainMenuType.DAILYRUN判断当前是否处于每日挑战菜单。MainMenuType枚举定义在 repentogon/resources/scripts/enums_ex.luaDAILYRUN 4MenuManager:GetActiveMenu()的绑定见 repentogon/LuaInterfaces/Menus/LuaMenuManager.cpp。用DailyChallengeMenu:IsLeaderboardVisible()做遮挡检测——当排行榜可见时直接跳过自定义渲染保证两者不会互相覆盖。同时脚本中使用Isaac.WorldToMenuPosition(_MainMenuType.DAILYRUN, Vector(...))将世界坐标换算为菜单屏幕坐标实现见 repentogon/LuaInterfaces/Menus/LuaMenuManager.cpp这是主菜单自定义 UI 绘制的标准姿势。相关回调与渲染钩子DailyChallengeMenu提供的读取能力通常要配合渲染时机使用在主菜单体系中有两个层面Lua 层MC_MAIN_MENU_RENDER 1023回调定义于 repentogon/resources/scripts/enums_ex.lua注册方式为Isaac.AddCallback(REPENTOGON, ModCallbacks.MC_MAIN_MENU_RENDER, function)。所有主菜单相关 Mod 脚本如 bestiary_menu.lua、stats_menu.lua都在此回调中执行渲染逻辑。C 层REPENTOGON 还对Menu_DailyChallenge::Render挂了钩子。在 repentogon/Patches/MiscFixes.cpp 中每次渲染时强制将_DailyRunSprite的第 3 层动画帧设为第 2 帧HOOK_METHOD(Menu_DailyChallenge, Render, () - void) { this-_DailyRunSprite.SetLayerFrame(3, 2); super(); }这是对原版每日挑战菜单的一个视觉修正利用 ANM2 中已带正确透明度的帧也印证了GetSprite()返回的_DailyRunSprite正是原版该菜单渲染所用的精灵对象。使用注意事项与限制只能在主菜单调用如前所述所有函数都会先检查g_MenuManager游戏内调用会抛 Lua 错误。应统一放在MC_MAIN_MENU_RENDER等主菜单回调中。必须使用.而非:虽然官方自带脚本daily_stats.lua中写的是DailyChallengeMenu:IsLeaderboardVisible()这属于脚本内部的历史写法文档明确规定应使用.。从绑定注册方式lua::TableAssoc普通函数表项LuaDailyChallengeMenu.cpp可以确认这些函数并不接收self参数因此规范写法是DailyChallengeMenu.IsLeaderboardVisible()。状态数值无公开枚举GetState()/GetSelectedElement()返回的整数状态没有对应的公开枚举常量原版游戏的内部状态机未暴露到 Lua 层若要基于具体状态值做分支需要自行实测记录。只读为主谨慎写入SetState/SetSelectedElement直接改写原版菜单对象的内部状态字段写入非法值可能导致菜单导航异常除非确实需要控制菜单流程否则建议只使用读取类函数。总结DailyChallengeMenu是 REPENTOGON 主菜单 Lua 扩展体系中面向每日挑战界面的入口11 个函数分别覆盖精灵获取3 个、状态读写4 个、剩余时间3 个与排行榜可见性1 个底层全部映射到Menu_DailyChallenge结构体的内存字段。结合MC_MAIN_MENU_RENDER回调、MenuManager:GetActiveMenu()与Isaac.WorldToMenuPosition()Mod 开发者可以安全地在每日挑战菜单上叠加自定义的倒计时、统计信息或 UI 元素——官方daily_stats.lua就是一套可以直接参考的完整范本。若需进一步了解同系列菜单接口可对照阅读 ChallengeMenu.md、MenuManager.md 等文档。赞分享游戏开发插件系统【免费下载链接】REPENTOGONScript extender for The Binding of Isaac: Repentance项目地址https://gitcode.com/gh_mirrors/re/REPENTOGON点击查看免费下载相关推荐REPENTOGON 主菜单枚举 MainMenuType 完整指南菜单 ID、源码映射与自定义主菜单实战REPENTOGON 主菜单枚举 MainMenuType 完整指南菜单 ID、源码映射与自定义主菜单实战 导读 本文档全面解析《以撒的结合忏悔》脚本扩展游戏开发插件系统REPENTOGON ChallengeMenu 全局类解析挑战菜单 Lua 接口的调用方式与源码实现REPENTOGON ChallengeMenu 全局类解析挑战菜单 Lua 接口的调用方式与源码实现 本篇指南围绕 REPENTOGON《以撒的结合忏悔游戏开发插件系统REPENTOGON Lua API 详解用 MainMenu 全局类操控主菜单高亮与主菜单 SpriteREPENTOGON Lua API 详解用 MainMenu 全局类操控主菜单高亮与主菜单 Sprite 本文围绕 REPENTOGON 官方文档中的 Ma游戏开发插件系统上一篇ServerPackCreator终极Minecraft服务器包自动化生成指南下一篇Everything Claude Code架构深度剖析从项目结构到设计模式创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考