# AGENTS.md 本项目工作:修改 RA3 UI(APT 文件)。 ## 1. 概述 APT 是 RA3 的 Flash-like UI 二进制格式。修改 APT 由两类工作组成: - **修改布局和素材**:调整位置、大小、纹理、角色(shape/sprite/button) - **修改脚本/逻辑**:改写 bytecode(汇编),或通过钩子引入外部 ActionScript ### edit.xml 是什么 `_edit.xml` 是 `.apt` 二进制反编译后的**可编辑 XML 版本**。 修改 `_edit.xml` 后需用工具编译回 `.apt`。 ### 两种生成 APT 的路径 | 路径 | 适用 | 能写 AS? | |---|---|---| | **已有 APT → edit.xml → 修改 → 编译回 APT** | 修改现有 UI | 只能写汇编(bytecode) | | **新 FLA → SWF → APT** | 创建全新 UI | 可在 Flash 中直接写 ActionScript | > 已有 APT **无法逆向转回 FLA/SWF**。这是"为什么必须操作 edit.xml"和"为什么钩子模式有价值"的根本原因。 --- ## 2. 开始前 ### 必读 **APT修改指南.md** — CDATA 格式、指令句法、ru 公式、placeobject flags —— **必须先读**。 ### 分析流程 修改任何 APT 前,按以下顺序分析: 1. **读反编译 AS** → 理解类结构、方法、逻辑流 - `Data/decompiled-as-references//scripts/__Packages/<类名>.as` - 共享类在 `Data/decompiled-as-references/main_mouse/scripts/`(如 `std_mouseRadioButton`、`mouse_chapterSelectComponent`) 2. **读 edit.xml** → 布局、角色(image/shape/sprite/button)、placeobject、bytecode 3. **跨文件追踪** → `export`/`import` 关系:一个 APT 导出的 character 可被其他 APT 引用; 运行时 `attachMovie()` 按符号名解析——只要有一条 import 链可达即可 4. **请求原始参考 APT** → 推断 tx 语义、对齐关系必须对比未修改版; 请求用户提供原始 APT(如本项目中的 `Data/aptui_ref/`) 5. **从原始数据推算** → 不假设 tx 是左边缘或中心,用 shape 尺寸交叉验证; 按钮与背景图对齐关系需从原始坐标推算 --- ## 3. 工具 编辑后需要用 `ra3apt-cli` 编译: ```sh # 反编译 .apt → edit.xml ra3apt-cli expand # 编译 edit.xml → .apt ra3apt-cli build ``` > 工具有可能不在当前工作区;如果可执行,用以上命令转换(生成的文件会替换已有文件);如果不可用,提醒用户手动操作 ### 格式规则 - 所有 bytecode(``、``、``)使用 `` 包裹 - CDATA 内 `&` 等字符**无需** XML 实体转义(写 `&Outline`,非 `&Outline`) - 修改后用 grep 验证关键值(坐标、depth、label) --- ## 4. 修改布局和素材 ### depth 规则 - `placeobject` 的 `depth` 控制 Z 序(渲染和点击优先级) - 新增元素前必须 grep 验证该 depth 未被同一帧的其他 placeobject 占用 ### 单选按钮(std_mouseRadioButton) - `m_width` **不控制视觉宽度**(只用于 `labelTF._x` 文本居中) - 缩放用 `rotm00`(宽)和 `rotm11`(高)。缩放后 `ty` 需补偿垂直偏移: ``` new_ty = old_ty + old_visual_height × (1 − scale) / 2 ``` - mouse_chapterSelectComponent 的特殊情况(不一定适用于其他 APT 文件):按钮与背景图是独立层,对齐偏移需从原始坐标推算 ### 纹理 ru ``` tex_x = m00 × screen_x + tx tex_y = m11 × screen_y + ty ``` - `t` 命令中坐标为**屏幕坐标** - 矩阵为单位矩阵(`1:0:0:1`)时简化为 `tex = screen + tx` - 移动屏幕三角形、保持采样同一纹理区域: `new_tx = tex_left − new_screen_left` - 缩小屏幕三角形后要**完整显示纹理**(非裁切): `m00 = tex_width / new_width`,再重算 `tx` - 多个 shape 可从同一张 `.tga` 图集的不同坐标区域采样(texture atlas 模型) ### Content Sprite 结构 - 使用帧标签定义状态:`_disable` / `_selected` / `_unselected` / `_selectedRollover` / `_unselectedRollover` / `_downState` / `_intro` / `_outtro` - 须包含 `_parent.introComplete()` 和 `_parent.outtroComplete()` 回调 - 新增 sprite 时**必须从同一 APT 现有 content sprite 复制完整帧结构**(帧数可能不同) --- ## 5. 修改脚本/逻辑 ### `_global` 通信总线 - 所有 APT 和外部 AS 共享同一个 `_global` 命名空间 - 一个 APT 中定义的全局变量(如 `_global.ENUM.XXX`)可在其他 APT 的 bytecode 中读取 - 这是钩子模式能成立的基础 ### this 的含义 - `Cafe_BaseUIScreen` 子类的 initaction 中,`this` 是**管理类实例**,不是 MovieClip - MovieClip 通过 `this.m_screen` 或 `Cafe2_BaseUIScreen.m_screen` 访问 - 写 bytecode 或钩子函数都需区分两者 ### 汇编调用与寄存器 `Cafe_BaseUIScreen` 子类方法中:`reg1` = `this`,`reg2` = `_global`。 | 操作 | 格式 | |---|---| | 调函数 | `...args → push nArgs → funcObj → pushundefined → callmethodpop` | | 调方法 | `...args → push nArgs → obj → callnamedmethodpop "methodName"` | - 禁止 `callnamedpop "call"` — 等效 `func["call"]()`,这在 APT 中不被支持 - 新增 label 名用 grep 验证不与已有冲突 ### 汇编 vs 钩子:决策 | 情况 | 做法 | |---|---| | 改动少且机械(复制现有模式) | **直接写汇编**在 edit.xml 中 | | 改动复杂(多分支、循环) | **用钩子** | | 目标类在不可编辑的 APT 中(无 `_edit.xml`) | **只能用钩子** | ### 钩子详解 **原理**:`_edit.xml` 中写少量检测 bytecode,实际逻辑写在**新 FLA→SWF→APT** 的 ActionScript 中, 通过 `_global` 变量传递。 **具体步骤**: 1. 在可编辑 screen 的 `initGUI()` 末尾插入以下 bytecode(位置:`registerIntroOuttroComponents()` / `startIntro()` 调用之后、`end` 之前): ```as // 等效 AS:if (_global.gHookName != undefined) { _global.gHookName(this); } pushglobal getnamedmember "gHookName" pushundefined ActionNewEquals // != undefined ? ActionLogicalNot ActionLogicalNot jumptrue lbl_skip pushreg 1 // this(管理类实例) EAPushOne // 1 个参数 pushglobal getnamedmember "gHookName" pushundefined // 直接调用,非方法查找 callmethodpop lbl_skip: ``` 2. 在新 FLA 的 ActionScript 中编写处理函数,赋值给 `_global.gHookName` 3. 钩子函数接收的 `this` 是**管理类实例**(`Cafe_BaseUIScreen` 子类),不是 MovieClip > 初始化顺序:`frameDelayedInit() → initGUI() → registerIntroOuttroComponents() → startIntro()` 若有需要可以为 `` 添加 `` 从而让 ActionScript 能够访问对应 MovieClip 并进行操作(例如`parent.xxx._y = 100`),但不作为默认要求。 --- ## 6. 输出给用户 - 需用户配合的内容(引擎常量、本地化 key、外部 AS 代码)→ 输出为独立的 `.md` 指南文件 - 指南给出可直接使用的代码/值,而非笼统描述