RedFunctions

RedFunctions

I need more functions, man. Like, what's a girl need to do to get some?! \(°Ω°)/

- 简介 -

有一整类小事情是赛博朋克模组只能依赖 Cyber Engine Tweaks 才能做到的——不是因为它们需要 Lua,而是因为没有人把引擎调用暴露给 Redscript。检查一个存档是否已安装就是一个很好的例子:纯 RedScript 模组根本无法查询。

RedFunctions 是一个 RED4ext 插件,旨在一次一个小的原生函数地填补这些空白。存档和资源查询、真实世界时钟、受限文件访问、操作系统剪贴板、原始鼠标输入、运行时生成的存档,以及用于覆盖层的输入抑制——所有这些都可以从纯 Redscript 中调用。

简而言之——引擎调用本来就是 RedScript 应得的!(ノ◕ヮ◕)ノ


- 面向模组作者 -

一个导入,涵盖全部功能:

import RedFunctions.*

// 存档和资源
RedFunc.ArchiveExists("my_cool_mod")        // -> Bool。".archive" 可省略,不区分大小写
RedFunc.ArchiveList()                       // -> array<String>,所有已挂载的模组存档,已排序
RedFunc.ResourceExists("base\\path\\file.xbm") // -> Bool。此路径能否从任何来源解析
RedFunc.Version()                           // -> String,插件语义化版本号

// 真实世界时钟(RedScript 从未拥有的 os.date / os.time)
RedFunc.RealTimestamp()                     // -> Double,Unix 纪元秒数
RedFunc.RealDate()                          // -> String,本地时间 "YYYY-MM-DD HH:MM:SS"
RedFunc.RealDateFormat("%H:%M", false)      // -> String,strftime;第二个参数 = UTC
RedFunc.RealClockMs()                       // -> Uint64,单调递增毫秒数 - 用于计时

// 只读文本文件,限制在游戏目录内
RedFunc.FileExists("r6\\config\\thing.json")   // -> Bool
RedFunc.ReadTextFile("r6\\config\\thing.json", 200) // -> array<String>,前 N 行(最多 2000)

// 生成的 TweakXL 文件 - 将 .yaml 写入 r6\tweaks,下次启动时加载
RedFunc.WriteTweakFile("MyMod/prices.yaml", lines)  // -> Bool;父目录会自动创建
RedFunc.DeleteTweakFile("MyMod/prices.yaml")        // -> Bool

// 模组状态 - 将 .json/.txt 写入 r6\storages,用 ReadTextFile 读回
RedFunc.WriteStorageFile("MyMod/state.json", lines) // -> Bool
RedFunc.DeleteStorageFile("MyMod/state.json")       // -> Bool

// 操作系统剪贴板 - ink 文本字段从未拥有过
RedFunc.GetClipboardText()                  // -> String(UTF-8,如果不是文本则为空)
RedFunc.SetClipboardText("some text")       // -> Bool

// 鼠标:绝对位置(菜单)和原始增量(游戏玩法)
RedFunc.MouseX01()                          // -> Float 窗口内 0..1,-1 = 不可用
RedFunc.MouseY01()
RedFunc.MouseButton(1)                      // -> Bool。1 左键,2 右键,3 中键
RedFunc.IsGameForeground()                  // -> Bool
RedFunc.MouseHook()                         // -> Bool,安装一次原始输入读取器
RedFunc.MouseTakeDeltaX()                   // -> Float,自上次调用以来的移动量
RedFunc.MouseTakeDeltaY()
RedFunc.MouseTakeWheel()                    // -> Float,自上次调用以来的滚轮格数

// 运行时存档:修补模板的标记浮点数,实时挂载结果
RedFunc.MountPatchedArchive(templatePath, outName, newResourcePath, markers, values)
RedFunc.ArchiveLastError()                  // -> String,上次调用失败的原因

// 输入抑制 - 覆盖层拥有键盘
RedFunc.SetScriptInputBlocked(true)         // 没有其他模组的监听器能听到输入
RedFunc.AllowScriptInputFor(myListener)     // 除了这一个(null 清除)
RedFunc.IsScriptInputBlocked()              // -> Bool
RedFunc.ScriptInputHookReady()              // -> Bool,在不支持的构建上为 false
RedFunc.ScriptInputHookError()              // -> String

通用文件原生函数特意设计为只读,且无法离开游戏目录——不允许 "..",不允许绝对路径。未知的 strftime %-代码会被剥离而不是崩溃。

有两个写入接口,都受扩展名限制,因此两者都不能生成脚本、存档或配置。TweakXL 生成器将 .yaml 写入 r6\tweaks:模组一次性计算其 TweakDB 记录,从下次启动起 TweakXL 就会像加载任何手动编写的模组一样加载它们——不再需要每次启动时重新推送数百个 flats。存储写入器对您自己的状态做同样的事情,将 .json 或 .txt 写入 r6\storages,用 ReadTextFile 读回。MO2 用户:生成的文件会落在 Overwrite 中,这是正常的。

存档答案来自引擎的 ResourceDepot——即实际已挂载的内容,而非磁盘上的内容。REDmod 文件夹、传统的 archive\pc\mod 安装以及运行时挂载的存档都会如实回答,而存在但加载失败的文件则会说不行!使用 ArchiveExists 按名称查找特定存档;ResourceExists 则询问内容,因此即使存档被重命名或重新打包,它仍然有效。


- 面向覆盖层构建者 -

有三个原生函数是因为 Redscript UI 无法自行到达那里。它们是 RedWindows 运行的基础。

鼠标。 在游戏过程中,游戏每帧都会将操作系统光标固定在屏幕中央,因此读取其位置毫无意义。MouseHook 会安装一个读取器,用于获取游戏本身使用的原始移动数据;MouseTakeDeltaX/Y 和 MouseTakeWheel 会将自上次调用以来到达的所有数据交给您,因此一个 tick 可以将它们整合成光标。在菜单中,光标可以自由移动,MouseX01/MouseY01 是更简单的答案。按钮直接读取,这意味着点击您的窗口不会泄漏到游戏中。

输入抑制。 消耗一个动作并不会阻止其他模组:每个已注册的监听器仍然会收到回调,因此玩家在您的文本字段中输入 "n" 就会触发其他模组绑定到 N 键的任何动作。SetScriptInputBlocked 会同时静默游戏中所有脚本输入监听器;AllowScriptInputFor 会豁免您的监听器,因此您仍然可以自己消耗动作。始终在每条退出路径上解除阻止。如果游戏更新后钩子不再适用,ScriptInputHookReady 会返回 false 并且不会阻止任何内容——请检查它并回退,而不是假设它有效。

运行时存档。 MountPatchedArchive 接受一个包含单个 CR2W 文件的模板 .archive,其参数您设置为独特的标记值,将这些标记替换为真实数字,为结果赋予一个新的 depot 路径并挂载它,而无需重启游戏。将 ResRef.FromString 指向新路径,引擎就会像加载任何随附资源一样加载它。每个标记必须在文件中恰好出现一次,否则调用会拒绝,ArchiveLastError 会解释原因。

像往常一样进行软依赖,如果您想在没有插件的情况下运行:

@if(ModuleExists("RedFunctions"))
import RedFunctions.*

- 更新日志 -

0.9.1 - 地址哈希现在软失败。 0.9.0 - 输入抑制:SetScriptInputBlocked / AllowScriptInputFor / IsScriptInputBlocked / ScriptInputHookReady / ScriptInputHookError。覆盖层可以拥有键盘,而不会让其他模组对您输入的内容做出反应。 0.8.0 - 运行时存档:MountPatchedArchive / ArchiveLastError。从模板在运行时生成资源变体,无需重启即可挂载。 0.7.0 - 模组状态存储:WriteStorageFile / DeleteStorageFile,限制在 r6\storages 且仅限 .json/.txt。 0.6.0 - 原始鼠标:MouseHook / MouseTakeDeltaX / MouseTakeDeltaY / MouseTakeWheel。游戏为了隐藏而固定的光标移动。 0.5.0 - 绝对鼠标:MouseX01 / MouseY01 / MouseButton / IsGameForeground。仅限菜单——游戏玩法会重新居中光标。 0.4.0 - 公开发布。剪贴板访问:GetClipboardText / SetClipboardText。 0.3.0 - 生成的 TweakXL 文件:WriteTweakFile / DeleteTweakFile,限制在 r6\tweaks 且仅限 .yaml。计算其 TweakDB 记录的模组现在可以持久化它们,供 TweakXL 在启动时加载,而不是每次启动时重新推送 flats。 0.2.0 - 真实世界时钟(RealTimestamp / RealDate / RealDateFormat / RealClockMs)和只读游戏目录文本文件(FileExists / ReadTextFile)。 0.1.0 - ArchiveExists, ArchiveList, ResourceExists, Version.


- 计划中 -

目标很简单:CET 赋予模组制作者的每一项能力,纯 Redscript 都能使用。欢迎提出请求——如果您的 Redscript 模组仍然因为某一个特定调用而需要 CET,请发布它,它将成为下一版本的候选。


- 需求 -

此模组要求您安装以下内容: RED4ext Redscript

可选: Redscript Configuration Framework 用于游戏内 WIKI 文档。


- 安装 -

  1. 安装所有依赖项。
  2. 将下载的文件解压到您的主游戏目录中。

- 卸载 -

删除目录 \Cyberpunk 2077\red4ext\plugins\RedFunctions 删除目录 \Cyberpunk 2077\r6\scripts\RedFunctions

存档中不会写入任何信息,因此随时可以安全移除。


- 致谢 -

基于 psiberx 的 RedLib 和 WopsS 的 RED4ext.SDK 构建,均为 MIT 许可。运行时存档挂载遵循 psiberx 在 ArchiveXL 中建立的机制。

感谢 RED4ext 和 Redscript 社区提供的工具和文档,也感谢 Spuddeh 的可爱。

DigitalVixen 模组套件的一部分。 您现在可以在 Discord [这里](https://discord.gg/TRuuDSrGWJ) 找到我!