弹弓(未接地)

弹弓(未接地)

WIP....... 我们找到了一个方法,在游戏通常拒绝创建有效内部游戏过渡的状态下捕获它并重放。这是基础。v0.1 证明了可以绕过这扇门。下一个前沿是制造。

游戏玩法

施工中


描述

突破性的发现是:普通地面/立足点弹射是一个有效的内部状态机转换。游戏引擎已经完美掌握了如何执行该动作,但在蜘蛛侠处于空中、摆荡或下落状态时,它会拒绝选择该转换。

因此,该模组并非从零创造弹射功能。它记录了一次真实成功的地面弹射转换,然后在游戏正常情况下拒绝该转换时,重复使用那次有效的转换记录。 这就是 v0.1 版本能够工作的原因。

核心发现

游戏拥有一个移动转换系统。当你按下 LT+A / LT+Cross 时,游戏会询问类似这样的问题: “在当前玩家状态、输入、目标数据和事件数据下,是否存在有效的转换?”

在地面或立足点上,答案是肯定的。选择器会返回一个弹射瞄准事件。 在空中,答案通常是否定的。输入发生了,但没有返回任何有效的弹射转换事件。

所以,“门控”并非控制器输入,而是转换选择器在批准的初始状态之外拒绝生成正常的弹射事件。

我们钩住了什么

这个工作模组监控的是游戏正常的转换路径,而不是替换整个动作。

我们识别出的重要运行时函数:

  • 位于 0x3681230SelectTransition
  • 位于 0x3682170ProcessTransitions
  • 位于 0x367E140BuildTransition
  • 位于 0x3680DB0 的 后转换辅助函数
  • 位于 0x2AF8550 的游戏分配器
  • 位于 0x1312140HeroStateSlingshotPlant 构造函数
  • 位于 0x13139C0HeroStateSlingshotPlantLocal 构造函数

这些钩子允许模组看到真正的弹射事件何时被构建,缓存该事件,并在之后将该事件反馈回正常的转换系统。

“记录”的真正含义

它并非记录动画帧、按键按压或控制器输入。 它记录的是游戏内部的弹射转换数据包:

  • 事件字节
  • 事件类型/哈希数据
  • 转换目标指针
  • 转换管理器/自身指针
  • 标志位
  • 值/计时字段
  • 瞄准状态构造函数参数

这就是此方法的强大之处。我们借用了游戏自身有效的转换数据包。

工作源码在此:slingshot_ground_anywhere.cpp

安装:

  1. 加载到 Overstrike
  2. 要求 玩家必须执行一次(在平坦地面上的)普通弹射

工作原理

  1. 钩子检测到真实的 HeroStateSlingshotPlant / HeroStateSlingshotPlantLocal 事件。
  2. 模组将该有效事件数据复制到内存中。
  3. 之后,当玩家在空中按下 LT+A / LT+Cross 时,普通选择器会拒绝该动作。
  4. 模组在输入窗口期间检测到此次拒绝。
  5. 它分配一个全新的事件副本。
  6. 它向选择器返回缓存的真实弹射事件。
  7. 游戏继续通过其自身正常的 BuildTransition 路径执行。 结果:空中弹射得以工作。

这就是其中的奥妙。我们并非强行驱动动画。我们欺骗游戏,使其在一个被禁止的上下文中接受它自己产生的弹射转换。


为何这如此困难

仅靠静态文件是不够的。 提取出的 98 GB 游戏文件帮助我们理解了名称、配置、DLL 结构、资源以及可能的状态名称。但实际的弹射“门控”发生在内存中,位于移动转换系统内部。

AI 可以帮助搜索和解释文件,但在调试器和 .script 模组捕获到实时行为之前,它无法神奇地知道哪个运行时转换被拒绝了。

这就是为什么解决方案需要两者兼备:

  • 来自提取文件的静态知识
  • 来自 OverStrike 脚本 DLL 的实时运行时钩子

失败的尝试与经验教训

v0.2 尝试通过复制附近转换的“实时朝向”数据来改进朝向。这使情况更糟,因为那些实时事件并不一定与弹射兼容。我们复制了无关移动事件的上下文。

v0.3 尝试将疑似朝向转换的数据块清零,以便游戏根据当前摄像头/玩家状态重建面朝方向。这也失败了,因为这些字节并非可有可无。它们很可能是有效弹射瞄准设置的组成部分:蛛网锚点基础、面朝向量、附着变换或必需的事件数据。

所以,重要的教训是: 不要盲目修改缓存的事件。v0.1 能工作是因为它保留了原始有效的负载。

剩余的问题

当前 v0.1 版本的问题在于蓄力朝向。 动作可以生效,发射朝向也基本正确,但在蓄力过程中,蜘蛛侠可能会错误转向或面朝错误的方向。这意味着缓存的地面事件中的某部分包含了来自原始记录弹射的朝向或附着基础数据。

正确的下一步是制作一个仅用于记录的研究版本。不改变任何行为。它应该记录在不同摄像头角度、屋顶角度、位置和面朝方向下进行的多次普通弹射的事件字节。然后我们比较这些字节,精确分离出控制以下内容的字段:

  • 蓄力面朝方向
  • 蛛网附着方向
  • 发射方向
  • 相对于摄像头的朝向
  • 锚点/瞄准变换

其他模组如何使用此方法

此方法可应用于许多“该动作仅在一种状态下有效”的模组。

配方:

  1. 找到一个在某个地方正常工作的动作。
  2. 钩住转换/构建/状态构造函数的路径。
  3. 合法地执行一次该动作。
  4. 缓存真实的转换事件。
  5. 在被禁止的状态下检测到相同输入。
  6. 将缓存的事件反馈回正常的转换系统。
  7. 只有基础转换稳定后,才对更深层的物理/朝向进行修补。

这可以应用于弹射、悬吊、墙壁爬行转换、立足点动作、移动能力、冷却时间限制的动作或其他状态锁定的能力。


未来潜力

v0.1 模组是一个概念验证,但非常坚实。

未来的版本可以增加:

  • 空中专属的发射物理
  • 摆荡动量继承
  • 相对于摄像头的发射向量
  • 俯冲转弹射
  • 墙壁爬行弹射
  • 蛛网悬吊转弹射
  • 自定义输入组合
  • 按状态定义的行为规则
  • 利用现有移动动画实现更平滑的动画混合

完全自定义动画以后也有可能实现,但那是另一个独立的资源管线问题。当前的 .script 方法在运行时逻辑、状态转换、内存钩子和物理调整方面最为擅长。

给社区的简短总结: 我们找到了一种捕捉有效内部游戏转换的方法,并将其在游戏通常拒绝创建的禁止状态下重放。这就是基础。v0.1 证明了门控可以被绕过。下一个前沿是让借用的转换具有上下文感知能力,使得朝向、蛛网附着和发射物理在空中感觉自然。